Ⅰ 虚拟化运维--容器网络--Flannel(十三)
Flannel是CoreOS开源的CNI网络插件,Flannel是一个专为kubernetes定制的三层网络解决方案,主要用于解决容器的跨主机通信问题。
首先,flannel利用Kubernetes API或者etcd用于存储整个集群的网络配置,其中最主要的内容为设置集群的网络地址空间。例如,设定整个集群内所有容器的IP都取自网段“10.1.0.0/毕拿16”。
接着,flannel在每个主机中运行flanneld作为agent,它会为所在主机从集群的网络地址空间中,获取一个小的网段subnet,本主机内所有容器的IP地址都将从中分配。
然后,flanneld再将本主机获取的subnet以及用于主机间通信的Public IP,同样通过kubernetes API或者etcd存储起来。
最后,flannel利用各种backend mechanism,例如udp,vxlan等等,跨主机转发容器间的网络流量,完成容器间的跨主机通信。
ETCD监听的服务端口碰数历默认是2379 、2380。
设置分配给docker网络的网段
Flannel默认采用vxlan作为backend,使用kernel vxlan默认的udp 8742端口。Flannel还支持udp的backend,使用udp 8285端口。
启动后查看下启动的docker是不是被flannel托管了:
如果有--bip=172.17.28.1/24(xxxx) ,说明Flannel管理了Docker。
可以看到docker启动后被加上了flanneld的相关配置项了(bip, ip-masq 和 mtu)
重启后的docker服务和flanned关联了,网络会随着flanned自动配置而变化。
在每个Node上启动flannelid
flannel在每个Node上启动了一个flanneld的服务,在flanneld启动后,将从etcd中读取配置信息,并请求获取子网的租约。所有Node上的flanneld都依赖etcd cluster来做集中配置服务,etcd保证了所有node上flanned所看到的配置是一致的。同时每个node上的flanned监听etcd上的数据变化,实时感知集群中node的变化。flanneld一旦获取子网租约、配置后端后,会将一些信息写入/run/flannel/subnet.env文件。
三台机器都配置好了之后,我们在三台机器上分别开启一个docker容器,测试它们的网络是不是通的。
node1主机容器:
node1:
在实际的生产环境总,最常用的还是vxlan模式笑搜。
常见问题:
Ⅱ Kubernetes之NetworkPolicy,Flannel和Calico
Pod是Kubernetes调度的最小单元。一个Pod可以包含一个或多个容器,因此它可以被看作是内部容器的逻辑宿主机。Pod的设计理念是为了支持多个容器在一个Pod中共享网络和文件系统。那么为什么Pod内的容器能够共享网络,IPC和PID命名空间?
原因:Kubernetes在每个Pod启动时,会自动创建一个镜像为gcr.io/google_containers/pause:version的容器,所有处于该Pod中的容器在启动时都会添加诸如--net=container:pause --ipc=contianer:pause --pid=container:pause的启动参数,因此Pod内所有容器共用pause容器的network,IPC和PID命名空间。所有容器共享pause容器的IP地址,也被称为Pod IP。因此处于同一个Pod内的容器,可以通过localhost进行相互访问。
在讲K8s Pod间通信前,我们先复习一下Docker容器间的网络通信。默认情况下,Docker使用一种名为bridge的网络模型。如下图所示:
Docker引擎在启动时,会在宿主机上创建一个名为docker0的虚拟网桥,这个虚拟网桥负责给所有容器分配不重复的ip地址以及容器间的网络通信。首先,Docker在创建一个容器时,会执行以下操作:
通过这个docker0网桥,同一宿主机上的容器可以互相通信。然而由于宿主机的IP地址与容器veth pair的 IP地址均不在同一个网段,故仅仅依靠veth pair和namespace的技术,还不足以使宿主机以外的网络主动发现容器的存在。为了使外界可以访问容器中的进程,docker采用了端口绑定的方式,也就是通过iptables的NAT,将宿主机上的端口端口流量转发到容器内的端口上。
K8s 默认不提供网络功能,所有Pod的网络功能,都依赖于宿主机上的Docker。因此,Pod IP即是依靠docker0网桥分配给pause容器的虚拟IP。
同一个Node内,不同的Pod都有一个docker0网桥分配的IP,可以直接通过这个IP进行通信。Pod IP和docker0在同一个网段。因此,当同节点上的Pod-A发包给Pod-B时,包传送路线如下:
不同的Node之间,Node的IP相当于外网IP,可以直接访问,而Node内的docker0和Pod的IP则是内网IP,无法直接跨Node访问。因此,不同Node上的Pod间需要通信,需要满足以下两个条件:
因此,为了实现以上两个需求,K8s提供了CNI(Container Network Interface)供第三方实现从而进行网络管理。由此出现了一系列开源渣陵的Kubernetes中的网络插件与方案,包括:flannel,calico,cilium等等。这些网络插件集中解决了以下需求:
CNI的介绍请参考这篇文章: https://jimmysong.io/kubernetes-handbook/concepts/cni.html
在默认的Docker配置中,每个节点上的Docker服务会分别负责所在节点容器的IP分配。这样导致的一个问题是,不同节点上容器可能获得相同的内外冲梁高IP地址。
Flannel的设计目的就是为集群中的所有节点重新规划IP地址的使用规则,从而使散尺得不同节点上的容器能够获得“同属一个内网”且”不重复的”IP地址,并让属于不同节点上的容器能够直接通过内网IP通信。
Flannel实质上是一种“覆盖网络(overlay network)”,也就是将TCP数据包装在另一种网络包里面进行路由转发和通信,目前已经支持UDP、VxLAN、AWS VPC和GCE路由等数据转发方式,默认的节点间数据通信方式是UDP转发。下图展示了数据包在flannel中的流转:
Flannel是一种典型的Overlay网络,它将已有的物理网络(Underlay网络)作为基础,在其上建立叠加的逻辑网络,实现网络资源的虚拟化。Overlay网络有一定额外的封包和解包等网络开销,对网络通信的性能有一定损耗。
Calico是纯三层的SDN 实现,它基于BPG 协议和Linux自身的路由转发机制,不依赖特殊硬件,容器通信也不依赖iptables NAT或Tunnel 等技术。能够方便的部署在物理服务器、虚拟机(如 OpenStack)或者容器环境下。同时calico自带的基于iptables的ACL管理组件非常灵活,能够满足比较复杂的安全隔离需求。
Calico 还基于 iptables 还提供了丰富而灵活的网络 policy, 保证通过各个节点上的 ACLs 来提供 workload 的多租户隔离、安全组以及其他可达性限制等功能。
核心问题是,nodeA怎样得知下一跳的地址?答案是node之间通过BGP协议交换路由信息。
每个node上运行一个软路由软件bird,并且被设置成BGP Speaker,与其它node通过BGP协议交换路由信息。
可以简单理解为,每一个node都会向其它node通知这样的信息:
我是X.X.X.X,某个IP或者网段在我这里,它们的下一跳地址是我。
通过这种方式每个node知晓了每个workload-endpoint的下一跳地址。
K8s NetworkPoclicy 用于实现Pod间的网络隔离。在使用Network Policy前,必须先安装支持K8s NetworkPoclicy的网络插件,包括:Calico,Romana,Weave Net,Trireme,OpenContrail等。
在未使用NetworkPolicy前,K8s中所有的Pod并不存在网络隔离,他们能够接收任何网络流量。一旦使用NetworkPolicy选中某个namespace下的某些Pod,那么这些Pod只能接收特定来源的流量(由Ingress属性定义),并且只能向特定出口发送网络请求(由Egress属性定义)。其他未被这个NetworkPolicy选中的Pod,依然不具备网络隔离。
下面是一个NetworkPolicy的例子:
下面的例子表示默认禁止所有Pod间的Ingress流量:
默认拒绝所有 Pod 之间 Egress 通信的策略为:
而默认允许所有 Pod 之间 Ingress 通信的策略为:
默认允许所有 Pod 之间 Egress 通信的策略为:
以 calico 为例看一下 Network Policy 的具体用法。首先配置 kubelet 使用 CNI 网络插件:
安装 calio 网络插件:
首先部署一个 nginx 服务,此时,通过其他 Pod 是可以访问 nginx 服务的:
开启 default namespace 的 DefaultDeny Network Policy 后,其他 Pod(包括 namespace 外部)不能访问 nginx 了:
最后再创建一个运行带有 access=true label的 Pod 访问的网络策略: