kubernetes权威指南 第4版 第7章节读书笔记
网络原理
Kubernetes网络模型设计的一个基础原则是:每个Pod都拥有一个独 立的IP地址,并假定所有Pod都在一个可以直接连通的、扁平的网络空 间中。所以不管它们是否运行在同一个Node(宿主机)中,都要求它们 可以直接通过对方的IP进行访问。设计这个原则的原因是,用户不需要 额外考虑如何建立Pod之间的连接,也不需要考虑如何将容器端口映射 到主机端口等问题
在Kubernetes的世界里,IP是以Pod为单位进行分配的。一 个Pod内部的所有容器共享一个网络堆栈(相当于一个网络命名空间, 它们的IP地址、网络设备、配置等都是共享的)。按照这个网络原则抽 象出来的为每个Pod都设置一个IP地址的模型也被称作IP-per-Pod模型
由于Kubernetes的网络模型假设Pod之间访问时使用的是对方Pod的 实际地址,所以一个Pod内部的应用程序看到的自己的IP地址和端口与 集群内其他Pod看到的一样。它们都是Pod实际分配的IP地址。将IP地址 和端口在Pod内部和外部都保持一致,也就不需要使用NAT来进行地址 转换了。Kubernetes的网络之所以这么设计,主要原因就是可以兼容过 去的应用。当然,我们使用Linux命令“ip addr show”也能看到这些地 址,和程序看到的没有什么区别。所以这种IP-per-Pod的方案很好地利 用了现有的各种域名解析和发现机制
为每个Pod都设置一个IP地址的模型还有另外一层含义,那就是同 一个Pod内的不同容器会共享同一个网络命名空间,也就是同一个Linux 网络协议栈。这就意味着同一个Pod内的容器可以通过localhost来连接对 方的端口。这种关系和同一个VM内的进程之间的关系是一样的,看起 来Pod内容器之间的隔离性减小了,而且Pod内不同容器之间的端口是共享的,就没有所谓的私有端口的概念了
Kubernetes对集群网络有如下要求:
(1)所有容器都可以在不用NAT的方式下同别的容器通信
(2)所有节点都可以在不用NAT的方式下同所有容器通信,反之 亦然。
(3)容器的地址和别人看到的地址是同一个地址
在Linux的网络命名空间中可以有自己独立的路由表及独立的 iptables设置来提供包转发、NAT及IP包过滤等功能
为了隔离出独立的协议栈,需要纳入命名空间的元素有进程、套接 字、网络设备等。进程创建的套接字必须属于某个命名空间,套接字的 操作也必须在命名空间中进行。同样,网络设备也必须属于某个命名空 间。因为网络设备属于公共资源,所以可以通过修改属性实现在命名空 间之间移动。当然,是否允许移动与设备的特征有关
1.网络命名空间的实现
Linux的网络协议栈是十分复杂的,为了支持独立的协议栈,相关 的这些全局变量都必须被修改为协议栈私有。最好的办法就是让这些全 局变量成为一个Net Namespace变量的成员,然后为协议栈的函数调用 加入一个Namespace参数。这就是Linux实现网络命名空间的核心
在新生成的私有命名空间中只有回环设备(名为“lo”且是停止状 态),其他设备默认都不存在,如果我们需要,则要一一手工建立。 Docker容器中的各类网络栈设备都是Docker Daemon在启动时自动创建 和配置的
所有的网络设备(物理的或虚拟接口、桥等在内核里都叫作Net Device)都只能属于一个命名空间。当然,物理设备(连接实际硬件的 设备)通常只能关联到root这个命名空间中。虚拟的网络设备(虚拟的 以太网接口或者虚拟网口对)则可以被创建并关联到一个给定的命名空 间中,而且可以在这些命名空间之间移动
由于网络命名空间代表的是一个独立的协议栈,所以它 们之间是相互隔离的,彼此无法通信,在协议栈内部都看不到对方。那 么有没有办法打破这种限制,让处于不同命名空间的网络相互通信,甚 至和外部的网络进行通信呢?答案就是“有,应用Veth设备对即可”。 Veth设备对的一个重要作用就是打通互相看不到的协议栈之间的壁垒, 它就像一条管子,一端连着这个网络命名空间的协议栈,一端连着另一 个网络命名空间的协议栈。所以如果想在两个命名空间之间通信,就必 须有一个Veth设备对。后面会介绍如何操作Veth设备对来打通不同命名 空间之间的网络
我们可以使用Linux iproute2系列配置工具中的IP命令来操作网络命 名空间。注意,这个命令需要由root用户运行.创建一个命名空间:
也可以先通过bash命令进入内部的shell界面,然后执行各种命令
Veth设备对
引入Veth设备对是为了在不同的网络命名空间之间通信,利用它可 以直接将两个网络命名空间连接起来。由于要连接两个网络命名空间, 所以Veth设备都是成对出现的,很像一对以太网卡,并且中间有一根直 连的网线。既然是一对网卡,那么我们将其中一端称为另一端的peer在Veth设备的一端发送数据时,它会将数据直接发送到另一端,并触发 另一端的接收操作
1.Veth设备对的操作命令
接下来看看如何创建Veth设备对,如何连接到不同的命名空间,并 设置它们的地址,让它们通信。
创建Veth设备对:
创建后,可以查看Veth设备对的信息。使用ip link show命令查看所有网络接口:
这时可在外面这个命名空间中看两个设备的情况
只剩一个veth0设备了,已经看不到另一个设备了,另一个设备已经被转移到另一个网络命名空间中了
现在看到的结果是,两个不同的命名空间各自有一个Veth的“网线 头”,各显示为一个Device(在Docker的实现里面,它除了将Veth放入容 器内,还将它的名字改成了eth0,简直以假乱真,你以为它是一个本地 网卡吗)
现在可以通信了吗?不行,因为它们还没有任何地址,我们现在给 它们分配IP地址:
网桥
Linux可以支持多个不同的网络,它们之间能够相互通信,如何将 这些网络连接起来并实现各网络中主机的相互通信呢?可以用网桥。网 桥是一个二层的虚拟网络设备,把若干个网络接口“连接”起来,以使得 网络接口之间的报文能够互相转发。网桥能够解析收发的报文,读取目 标MAC地址的信息,和自己记录的MAC表结合,来决策报文的转发目 标网络接口。为了实现这些功能,网桥会学习源MAC地址(二层网桥 转发的依据就是MAC地址)。在转发报文时,网桥只需要向特定的网 口进行转发,来避免不必要的网络交互。如果它遇到一个自己从未学习 到的地址,就无法知道这个报文应该向哪个网络接口转发,就将报文广 播给所有的网络接口(报文来源的网络接口除外)
在实际的网络中,网络拓扑不可能永久不变。设备如果被移动到另 一个端口上,却没有发送任何数据,网桥设备就无法感知到这个变化, 网桥还是向原来的端口转发数据包,在这种情况下数据就会丢失。所以 网桥还要对学习到的MAC地址表加上超时时间(默认为5min)。如果 网桥收到了对应端口MAC地址回发的包,则重置超时时间,否则过了 超时时间后,就认为设备已经不在那个端口上了,它就会重新广播发送
在实际的网络中,网络拓扑不可能永久不变。设备如果被移动到另 一个端口上,却没有发送任何数据,网桥设备就无法感知到这个变化, 网桥还是向原来的端口转发数据包,在这种情况下数据就会丢失。所以 网桥还要对学习到的MAC地址表加上超时时间(默认为5min)。如果 网桥收到了对应端口MAC地址回发的包,则重置超时时间,否则过了 超时时间后,就认为设备已经不在那个端口上了,它就会重新广播发 送
Linux内核支持网口的桥接(目前只支持以太网接口)。但是与单 纯的交换机不同,交换机只是一个二层设备,对于接收到的报文,要么 转发,要么丢弃。运行着Linux内核的机器本身就是一台主机,有可能 是网络报文的目的地,其收到的报文除了转发和丢弃,还可能被送到网 络协议栈的上层(网络层),从而被自己(这台主机本身的协议栈)消 化,所以我们既可以把网桥看作一个二层设备,也可以把它看作一个三层设备
Linux内核是通过一个虚拟的网桥设备(Net Device)来实现桥接 的。这个虚拟设备可以绑定若干个以太网接口设备,从而将它们桥接起 来。如图7.3所示,这种Net Device网桥和普通的设备不同,最明显的一 个特性是它还可以有一个IP地址
网桥设备br0绑定了eth0和eth1。对于网络协议栈的上 层来说,只看得到br0就行。因为桥接是在数据链路层实现的,上层不 需要关心桥接的细节,所以协议栈上层需要发送的报文被送到br0,网 桥设备的处理代码判断报文该被转发到eth0还是eth1,或者两者皆转 发;反过来,从eth0或从eth1接收到的报文被提交给网桥的处理代码, 在这里会判断报文应该被转发、丢弃还是被提交到协议栈上层
而有时eth0、eth1也可能会作为报文的源地址或目的地址,直接参 与报文的发送与接收,从而绕过网桥
brctl addbr xxxxx
brctl addif xxxxx ethx
网桥的物理网卡作为一个网口,由于在链路层工作,就不再需要IP 地址了,这样上面的IP地址自然失效
ifconfig ethx 0.0.0.0
给网桥配置一个IP地址
ifconfig brxx xxx.xxx.xxx.xxx
在Linux网络协议栈中有一组回调函数挂接点,通过这些挂接点挂 接的钩子函数可以在Linux网络栈处理数据包的过程中对数据包进行一 些操作,例如过滤、修改、丢弃等。整个挂接点技术叫作Netfilter和 iptables
Netfilter负责在内核中执行各种挂接的规则,运行在内核模式中; 而iptables是在用户模式下运行的进程,负责协助和维护内核中Netfilter 的各种规则表。二者互相配合来实现整个Linux网络协议栈中灵活的数 据包处理机制
这些挂接点能挂接的规则也分不同的类型(也就是规则表 Table),我们可以在不同类型的Table中加入我们的规则。目前主要支 持的Table类型有:RAW、MANGLE、NAT和FILTER
上述4个Table(规则链)的优先级是RAW最高,FILTER最低
路由
如果主机与目的主机没有直接相连,那么主机会将IP报文发送给默认的路由器,然后由路由 器来决定往哪里发送IP报文
标准的Docker支持以下4类网络模式。
◎ host模式:使用--net=host指定
◎ container模式:使用--net=container:NAME_or_ID指定
◎ none模式:使用--net=none指定
◎ bridge模式:使用--net=bridge指定,为默认设置
在Kubernetes管理模式下通常只会使用bridge模式
在bridge模式下,Docker Daemon第1次启动时会创建一个虚拟的网桥,默认的名称是docker0,然后按照RPC1918的模型在私有网络空间中 给这个网桥分配一个子网。针对由Docker创建的每一个容器,都会创建 一个虚拟的以太网设备(Veth设备对),其中一端关联到网桥上,另一 端使用Linux的网络命名空间技术,映射到容器内的eth0设备,然后从网桥的地址段内给eth0接口分配一个IP地址
其中ip1是网桥的IP地址,Docker Daemon会在几个备选地址段里给 它选一个地址,通常是以172开头的一个地址。这个地址和主机的IP地 址是不重叠的。ip2是Docker在启动容器时,在这个地址段选择的一个 没有使用的IP地址分配给容器。相应的MAC地址也根据这个IP地址,在 02:42:ac:11:00:00和02:42:ac:11:ff:ff的范围内生成,这样做可以确保不会 有ARP冲突
启动后,Docker还将Veth对的名称映射到eth0网络接口。ip3就是主 机的网卡地址。在一般情况下,ip1、ip2和ip3是不同的IP段,所以在默认不做任何 特殊配置的情况下,在外部是看不到ip1和ip2的
这样做的结果就是,在同一台机器内的容器之间可以相互通信,不 同主机上的容器不能相互通信,实际上它们甚至有可能在相同的网络地 址范围内(不同主机上的docker0的地址段可能是一样的)
为了让它们跨节点互相通信,就必须在主机的地址上分配端口,然后通过这个端口路由或代理到容器上。这种做法显然意味着一定要在容 器之间小心谨慎地协调好端口的分配,或者使用动态端口的分配技术
在我们 的例子中,容器就是图7.8中的容器1和容器2。容器1和容器2共享一个 网络的命名空间,共享一个命名空间的结果就是它们好像在一台机器上 运行,它们打开的端口不会有冲突,可以直接使用Linux的本地IPC进行 通信(例如消息队列或者管道)。其实,这和传统的一组普通程序运行 的环境是完全一样的,传统程序不需要针对网络做特别的修改就可以移 植了,它们之间的互相访问只需要使用localhost就可以
Pod之间的通信
每一个Pod都有一个真实的全局IP地址,同一个Node内的不同Pod之间可以直接采用对方Pod的IP地址通信,而且不需要采用其他发现机制,例如DNS、Consul或者etcd
Pod容器既有可能在同一个Node上运行,也有可能在不同的Node上运行,所以通信也分为两类:同一个Node内Pod之间的通信和不同Node上Pod之间的通信
1.同一个Node内Pod之间的通信
我们看一下同一个Node内两个Pod之间的关系
可以看出,Pod1和Pod2都是通过Veth连接到同一个docker0网桥上 的,它们的IP地址IP1、IP2都是从docker0的网段上动态获取的,它们和 网桥本身的IP3是同一个网段的
另外,在Pod1、Pod2的Linux协议栈上,默认路由都是docker0的地址,也就是说所有非本地地址的网络数据,都会被默认发送到docker0 网桥上,由docker0网桥直接中转
综上所述,由于它们都关联在同一个docker0网桥上,地址段相 同,所以它们之间是能直接通信的
Pod的地址是与docker0在同一个网段的,我们知道docker0网段与宿 主机网卡是两个完全不同的IP网段,并且不同Node之间的通信只能通过 宿主机的物理网卡进行,因此要想实现不同Node上Pod容器之间的通 信,就必须想办法通过主机的这个IP地址进行寻址和通信
要想支持不同Node上Pod之间的通信,就要满足两个条 件:
(1)在整个Kubernetes集群中对Pod的IP分配进行规划,不能有冲 突;
(2)找到一种办法,将Pod的IP和所在Node的IP关联起来,通过这 个关联让Pod可以互相访问
根据条件1的要求,我们需要在部署Kubernetes时对docker0的IP地址 进行规划,保证每个Node上的docker0地址都没有冲突。我们可以在规划后手工配置到每个Node上,或者做一个分配规则,由安装的程序自己 去分配占用。例如,Kubernetes的网络增强开源软件Flannel就能够管理 资源池的分配
根据条件2的要求,Pod中的数据在发出时,需要有一个机制能够知 道对方Pod的IP地址挂在哪个具体的Node上。也就是说先要找到Node对 应宿主机的IP地址,将数据发送到这个宿主机的网卡,然后在宿主机上 将相应的数据转发到具体的docker0上。一旦数据到达宿主机Node,则 那个Node内部的docker0便知道如何将数据发送到Pod
在图7.10中,IP1对应的是Pod1,IP2对应的是Pod2。Pod1在访问 Pod2时,首先要将数据从源Node的eth0发送出去,找到并到达Node2的 eth0。即先是从IP3到IP4的递送,之后才是从IP4到IP2的递送
这里使用虚拟机来完成实验。如果要部署在物理机器上或者云服务 商的环境中,则涉及的网络模型很可能稍微有所不同。不过,从网络角 度来看,Kubernetes的机制是类似且一致的
Kubernetes的网络模型要求每个Node上的容器都可以相互访问
默认的Docker网络模型提供了一个IP地址段是172.17.0.0/16的docker0网桥。每个容器都会在这个子网内获得IP地址,并且将docker0 网桥的IP地址(172.17.42.1)作为其默认网关。需要注意的是,Docker 宿主机外面的网络不需要知道任何关于这个172.17.0.0/16的信息或者知 道如何连接到其内部,因为Docker的宿主机针对容器发出的数据,在物 理网卡地址后面都做了IP伪装MASQUERADE(隐含NAT)。也就是 说,在网络上看到的任何容器数据流都来源于那台Docker节点的物理IP 地址。这里所说的网络都指连接这些主机的物理网络
在Kubernetes的网络模型中,每台主机上的docker0网桥都是可以被 路由到的。也就是说,在部署了一个Pod时,在同一个集群内,各主机 都可以访问其他主机上的Pod IP,并不需要在主机上做端口映射。综上 所述,我们可以在网络层将Kubernetes的节点看作一个路由器。如果将 实验环境改画成一个网络图
为了支持Kubernetes网络模型,我们采取了直接路由的方式来实 现,在每个Node上都配置相应的静态路由项,例如在192.168.1.129这个 Node上配置了两个路由项
# route add -net 10.1.20.0 netmask 255.255.255.0 gw 192.168.1.130 # route add -net 10.1.30.0 netmask 255.255.255.0 gw 192.168.1.131
这意味着,每一个新部署的容器都将使用这个Node(docker0的网 桥IP)作为它的默认网关。而这些Node(类似路由器)都有其他 docker0的路由信息,这样它们就能够相互连通了
我们检查的第1个容器是运行 了“google_containers/pause:latest”镜像的容器,它使用了Docker默认的网 络模型 bridge;而我们检查的第2个容器,也就是在RC/Pod中定义运行 的php-redis容器,使用了非默认的网络配置和映射容器的模型,指定了 映射目标容器为“google_containers/ pause:latest”
首先,一个Pod内的所有容器都需要共用同一个IP地址,这就意味 着一定要使用网络的容器映射模式。然而,为什么不能只启动第1个Pod 中的容器,而将第2个Pod中的容器关联到第1个容器呢?我们认为 Kubernetes是从两方面来考虑这个问题的:首先,如果在Pod内有多个容 器的话,则可能很难连接这些容器;其次,后面的容器还要依赖第1个 被关联的容器,如果第2个容器关联到第1个容器,且第1个容器死掉的 话,第2个容器也将死掉。启动一个基础容器,然后将Pod内的所有容器 都连接到它上面会更容易一些。因为我们只需要为基础的这个 Google_containers/pause容器执行端口映射规则,这也简化了端口映射的 过程。所以我们启动Pod后的网络模型类似于图7.13
在这种情况下,实际Pod的IP数据流的网络目标都是这个 google_containers/pause容器。图7.13有点儿取巧地显示了是 google_containers/pause容器将端口80的流量转发给了相关的容器。而 pause容器只是看起来转发了网络流量,但它并没有真的这么做。实际 上,应用容器直接监听了这些端口,和google_containers/pause容器共享 了同一个网络堆栈。这就是为什么在Pod内部实际容器的端口映射都显 示到google_containers/pause容器上了
综上所述,google_containers/pause容器实际上只是负责接管这个 Pod的Endpoint,并没有做更多的事情
生成service之后iptables变化
第1行是挂在PREROUTING链上的端口重定向规则,所有进入的流 量如果满足20.1.244.75: 80,则都会被重定向到端口33761。第2行是挂 在OUTPUT链上的目标地址NAT,做了和上述第1行规则类似的工作, 但针对的是当前主机生成的外出流量。所有主机生成的流量都需要使用 这个DNAT规则来处理。简而言之,这两个规则使用了不同的方式做了 类似的事情,就是将所有从节点生成的发送给20.1.244.75:80的流量重定 向到本地的33761端口
至此,目标为Service IP地址和端口的任何流量都将被重定向到本地 的33761端口。这个端口连到哪里去了呢?这就到了kube-proxy发挥作用 的地方了。这个kube-proxy服务给每一个新创建的服务都关联了一个随 机的端口号,并且监听那个特定的端口,为服务创建相关的负载均衡对象。在我们的实验中,随机生成的端口刚好是33761。通过监控Node1上 的Kubernetes-Service的日志,在创建服务时可以看到下面的记录
Kubernetes的kube-proxy看起来只是一个夹层,但实际上它只是在 Node上运行的一个服务。上述重定向规则的结果就是针对目标地址为服 务IP的流量,将Kubernetes的kube-proxy变成了一个中间的夹层
CNI是由CoreOS公司提出的另一种容器网络规范,现在已经被 Kubernetes、rkt、Apache Mesos、Cloud Foundry和Kurma等项目采纳。 另外,Contiv Networking, Project Calico、Weave、SR-IOV、Cilium、 Infoblox、Multus、Romana、Plumgrid和Midokura等项目也为CNI提供网 络插件的具体实现。图7.18描述了容器运行环境与各种网络插件通过 CNI进行连接的模型
1.CNI规范概述
CNI提供了一种应用容器的插件化网络解决方案,定义对容器网络 进行操作和配置的规范,通过插件的形式对CNI接口进行实现。CNI是 由rkt Networking Proposal发展而来的,试图提供一种普适的容器网络解 决方案。CNI仅关注在创建容器时分配网络资源,和在销毁容器时删除 网络资源,这使得CNI规范非常轻巧、易于实现,得到了广泛的支持
在CNI模型中只涉及两个概念:容器和网络
◎ 容器(Container):是拥有独立Linux网络命名空间的环境, 例如使用Docker或rkt创建的容器。关键之处是容器需要拥有自己的 Linux网络命名空间,这是加入网络的必要条件
◎ 网络(Network):表示可以互连的一组实体,这些实体拥有 各自独立、唯一的IP地址,可以是容器、物理机或者其他网络设备(比 如路由器)等
对容器网络的设置和操作都通过插件(Plugin)进行具体实现, CNI插件包括两种类型:CNI Plugin和IPAM(IP Address Management) Plugin。CNI Plugin负责为容器配置网络资源,IPAM Plugin负责对容器 的IP地址进行分配和管理。IPAM Plugin作为CNI Plugin的一部分,与 CNI Plugin一起工作
2.CNI Plugin插件详解
CNI Plugin包括3个基本接口的定义:添加(ADD)、删除 (DELETE)、检查(CHECK)和版本查询(VERSION)。这些接口的具体实现要求插件提供一个可执行的程序,在容器网络添加或删除时 进行调用,以完成具体的操作
(1)添加:将容器添加到某个网络。主要过程为在Container Runtime创建容器时,先创建好容器内的网络命名空间(Network Namespace),然后调用CNI插件为该netns进行网络配置,最后启动容 器内的进程
......
3.IPAM Plugin插件详解
为了减轻CNI Plugin对IP地址管理的负担,在CNI规范中设置了一个 新的插件专门用于管理容器的IP地址(还包括网关、路由等信息),被 称为IPAM Plugin。通常由CNI Plugin在运行时自动调用IPAM Plugin完 成容器IP地址的分配
IPAM Plugin负责为容器分配IP地址、网关、路由和DNS,典型的 实现包括host-local和dhcp。与CNI Plugin类似,IPAM插件也通过可执行 程序完成IP地址分配的具体操作。IPAM可执行程序也处理传递给CNI插 件的环境变量和通过标准输入(stdin)传入的网络配置参数
Kubernetes目前支持两种网络插件的实现
◎ CNI插件:根据CNI规范实现其接口,以与插件提供者进行对接
◎ kubenet插件:使用bridge和host-local CNI插件实现一个基本的 cbr0。
为了在Kubernetes集群中使用网络插件,需要在kubelet服务的启动 参数上设置下面两个参数
◎ --network-plugin-dir:kubelet启动时扫描网络插件的目录
◎ --network-plugin:网络插件名称,对于CNI插件,设置为cni即 可,无须关注--network-plugin-dir的路径。对于kubenet插件,设置为 kubenet,目前仅实现了一个简单的cbr0 Linux网桥
目前已有多个开源项目支持以CNI网络插件的形式部署到 Kubernetes集群中,进行Pod的网络设置和网络策略的设置,包括 Calico、Canal、Cilium、Contiv、Flannel、Romana、Weave Net等
为了实现细粒度的容器间网络访问隔离策略,Kubernetes从1.3版本 开始,由SIG-Network小组主导研发了Network Policy机制,目前已升级 为networking.k8s.io/v1稳定版本。Network Policy的主要功能是对Pod间 的网络通信进行限制和准入控制,设置方式为将Pod的Label作为查询条 件,设置允许访问或禁止访问的客户端Pod列表。目前查询条件可以作 用于Pod和Namespace级别
为了使用Network Policy,Kubernetes引入了一个新的资源对象 NetworkPolicy,供用户设置Pod间网络访问的策略。但仅定义一个网络 策略是无法完成实际的网络隔离的,还需要一个策略控制器(Policy Controller)进行策略的实现。策略控制器由第三方网络组件提供,目前 Calico、Cilium、Kube-router、Romana、Weave Net等开源项目均支持网 络策略的实现
Network Policy的工作原理如图7.19所示,policy controller需要实现 一个API Listener,监听用户设置的NetworkPolicy定义,并将网络访问规 则通过各Node的Agent进行实际设置(Agent则需要通过CNI网络插件实 现)
apiversion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: test-network-policy namespace: default spec: podSelector: matchLabels: role: db PolicyTypes: - Ingress - Egress ingress: - from: - ipBlock: cidr: 172.17.0.0/16 except: - 172.17.1.0/24 - namespaceSelector: matchLabels: role: frontend ports: - protocol: TCP port: 6379 egress: - to: - ipBlock: cidr: 10.0.0.0/24 ports: - protocol: TCP port: 5987
主要的参数说明如下。 ◎ podSelector:用于定义该网络策略作用的Pod范围,本例的选 择条件为包含“role=db”标签的Pod。 ◎ policyTypes:网络策略的类型,包括ingress和egress两种,用 于设置目标Pod的入站和出站的网络限制。 ◎ ingress:定义允许访问目标Pod的入站白名单规则,满足from条件的客户端才能访问ports定义的目标Pod端口号.- from:对符合条件的客户端Pod进行网络放行,规则包括基于客户 端Pod的Label、基于客户端Pod所在的Namespace的Label或者客户端的IP 范围。- ports:允许访问的目标Pod监听的端口号。
◎ egress:定义目标Pod允许访问的“出站”白名单规则,目标Pod 仅允许访问满足to条件的服务端IP范围和ports定义的端口号
- to:允许访问的服务端信息,可以基于服务端Pod的Label、基于服 务端Pod所在的Namespace的Label或者服务端IP范围。
- ports:允许访问的服务端的端口号。
通过本例的NetworkPolicy设置,对目标Pod的网络访问的效果如 下。
◎ 该网络策略作用于Namespace“default”中含有“role=db”Label的 全部Pod。
◎ 允许与目标Pod在同一个Namespace中的包 含“role=frontend”Label的客户端Pod访问目标Pod。
◎ 允许属于包含“project=myproject”Label的Namespace的客户端 Pod访问目标Pod。
◎ 允许从IP地址范围“172.17.0.0/16”的客户端Pod访问目标Pod, 但是不包括IP地址范围“172.17.1.0/24”的客户端。
◎ 允许目标Pod访问IP地址范围“10.0.0.0/24”并监听5978端口的服 务
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default-deny spec: podSelector: {} policyType: - Ingress
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadate: name: allow-all spec: podSelector: {} ingress: - {} policyType: - Ingress
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default-deny spec: podSelector: {} policyTypes: - Egress
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default-allow spec: podSelector: {} egress: - {} policyType: - Egress
kind: NetworkPolicy metadata: name: deny-all spec: podSelector: {} policyType: - Ingress - Egress
Flannel之所以可以搭建Kubernetes依赖的底层网络,是因为它能实 现以下两点
(1)它能协助Kubernetes,给每一个Node上的Docker容器都分配互 相不冲突的IP地址。
(2)它能在这些IP地址之间建立一个覆盖网络(Overlay Network),通过这个覆盖网络,将数据包原封不动地传递到目标容器内
flanneld进程并不简单,它上连etcd,利用etcd来管理可分配的IP地 址段资源,同时监控etcd中每个Pod的实际地址,并在内存中建立了一 个Pod节点路由表;它下连docker0和物理网络,使用内存中的Pod节点 路由表,将docker0发给它的数据包包装起来,利用物理网络的连接将 数据包投递到目标flanneld上,从而完成Pod到Pod之间的直接地址通 信
Flannel之间的底层通信协议的可选技术包括UDP、VxLan、AWS VPC等多种方式。通过源flanneld封包、目标flanneld解包,最终docker0 收到的就是原始的数据,对容器应用来说是透明的,感觉不到中间 Flannel的存在
--bip=10.244.1.5/24
Flannel完美地实现了对Kubernetes网络的支持,但是它引入了多个 网络组件,在网络通信时需要转到flannel0网络接口,再转到用户态的 flanneld程序,到对端后还需要走这个过程的反过程,所以也会引入一 些网络的时延损耗
另外,Flannel模型默认采用了UDP作为底层传输协议,UDP本身是 非可靠协议,虽然两端的TCP实现了可靠传输,但在大流量、高并发的应用场景下还需要反复测试,确保没有问题
Calico的主要组件如下
◎ Felix:Calico Agent,运行在每个Node上,负责为容器设置网 络资源(IP地址、路由规则、iptables规则等),保证跨主机容器网络互 通。
◎ etcd:Calico使用的后端存储。
◎ BGP Client:负责把Felix在各Node上设置的路由信息通过BGP 协议广播到Calico网络。
◎ Route Reflector:通过一个或者多个BGP Route Reflector来完成 大规模集群的分级路由分发
◎ CalicoCtl:Calico命令行管理工具。