网络原理
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实现网络命名空间的核心
![](https://img2020.cnblogs.com/blog/1296886/202004/1296886-20200409151711803-1182729913.png)
在新生成的私有命名空间中只有回环设备(名为“lo”且是停止状 态),其他设备默认都不存在,如果我们需要,则要一一手工建立。 Docker容器中的各类网络栈设备都是Docker Daemon在启动时自动创建 和配置的
所有的网络设备(物理的或虚拟接口、桥等在内核里都叫作Net Device)都只能属于一个命名空间。当然,物理设备(连接实际硬件的 设备)通常只能关联到root这个命名空间中。虚拟的网络设备(虚拟的 以太网接口或者虚拟网口对)则可以被创建并关联到一个给定的命名空 间中,而且可以在这些命名空间之间移动
由于网络命名空间代表的是一个独立的协议栈,所以它 们之间是相互隔离的,彼此无法通信,在协议栈内部都看不到对方。那 么有没有办法打破这种限制,让处于不同命名空间的网络相互通信,甚 至和外部的网络进行通信呢?答案就是“有,应用Veth设备对即可”。 Veth设备对的一个重要作用就是打通互相看不到的协议栈之间的壁垒, 它就像一条管子,一端连着这个网络命名空间的协议栈,一端连着另一 个网络命名空间的协议栈。所以如果想在两个命名空间之间通信,就必 须有一个Veth设备对。后面会介绍如何操作Veth设备对来打通不同命名 空间之间的网络
我们可以使用Linux iproute2系列配置工具中的IP命令来操作网络命 名空间。注意,这个命令需要由root用户运行.创建一个命名空间:
![](https://img2020.cnblogs.com/blog/1296886/202004/1296886-20200409152803075-1725170536.png)
![](https://img2020.cnblogs.com/blog/1296886/202004/1296886-20200409152834355-1204268641.png)
也可以先通过bash命令进入内部的shell界面,然后执行各种命令
![](https://img2020.cnblogs.com/blog/1296886/202004/1296886-20200409152855421-1397276564.png)
Veth设备对
引入Veth设备对是为了在不同的网络命名空间之间通信,利用它可 以直接将两个网络命名空间连接起来。由于要连接两个网络命名空间, 所以Veth设备都是成对出现的,很像一对以太网卡,并且中间有一根直 连的网线。既然是一对网卡,那么我们将其中一端称为另一端的peer在Veth设备的一端发送数据时,它会将数据直接发送到另一端,并触发 另一端的接收操作
![](https://img2020.cnblogs.com/blog/1296886/202004/1296886-20200409153455205-1449297309.png)
1.Veth设备对的操作命令
接下来看看如何创建Veth设备对,如何连接到不同的命名空间,并 设置它们的地址,让它们通信。
创建Veth设备对:
创建后,可以查看Veth设备对的信息。使用ip link show命令查看所有网络接口:
![](https://img2020.cnblogs.com/blog/1296886/202004/1296886-20200409154820499-404186030.png)
![](https://img2020.cnblogs.com/blog/1296886/202004/1296886-20200409154858790-633175891.png)
这时可在外面这个命名空间中看两个设备的情况
只剩一个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网络插件实 现)
![](https://img2020.cnblogs.com/blog/1296886/202004/1296886-20200415142152125-1114166585.png)
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),通过这个覆盖网络,将数据包原封不动地传递到目标容器内
![](https://img2020.cnblogs.com/blog/1296886/202004/1296886-20200415155540259-430850976.png)
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实现了可靠传输,但在大流量、高并发的应用场景下还需要反复测试,确保没有问题
![](https://img2020.cnblogs.com/blog/1296886/202004/1296886-20200415161931441-1714092857.png)
![](https://img2020.cnblogs.com/blog/1296886/202004/1296886-20200415162021422-298058888.png)
![](https://img2020.cnblogs.com/blog/1296886/202004/1296886-20200415162132277-1171432292.png)
Calico的主要组件如下
◎ Felix:Calico Agent,运行在每个Node上,负责为容器设置网 络资源(IP地址、路由规则、iptables规则等),保证跨主机容器网络互 通。
◎ etcd:Calico使用的后端存储。
◎ BGP Client:负责把Felix在各Node上设置的路由信息通过BGP 协议广播到Calico网络。
◎ Route Reflector:通过一个或者多个BGP Route Reflector来完成 大规模集群的分级路由分发
◎ CalicoCtl:Calico命令行管理工具。