Kubernets网络模型及CI插件
——(Docker网络模型、K8S网络模型、Pod网络实现方式、常见CNI插件有哪些)
Docker 的传统网络模型在应用日趋复杂的实际业务场景时必将导致复杂的几何级数上升,由此,Kubernetes 设计了一种网络模型,它要求所有容器都能够通过一个扁平的挽留过平面直接进行通信(在同一个 IP 网络中),无论它们是否运行于集群中的同一个节点。不过在 Kubernets 集群中,IP 地址分配是以 Pod 对象为单位,而非容器,同一 Pod 内的所有容器共享同一网络名称空间。
一、Docker容器的网络模型
Docker 容器网络的原始模型主要有三种:
- Bridge(网桥): 借助于虚拟网桥设备为容器建立网络连接。
- Host(主机): 设定容器直接共享使用节点主机的网络名称空间。
- Container(容器): 指多个容器共享一个网络名称空间,从而彼此之间能够以本地通信的方式建立连接。
Docker 守护进程首次启动时,它会在当前节点上创建一个名为 docker0 的网桥设备,并默认配置其使用 172.17.0.0/16 网络,该网络是 Bridge 模型的一种实现,也是创建 Docker 容器时默认使用的网络模型。
创建 Docker 容器时,默认有四种网络可供选择使用,从而表现出了四种不同类型的容器,具体如下:
-
- Close container(封闭式容器): 此类容器使用 "None" 挽留过,它们没有对外通信的网络接口,而是仅具有 I/O 接口,通常仅用于不需要网络的后端作业处理场景。
- Bridged container(桥接式容器): 此类容器使用 "Bridge" 模型的网络,对于每个网络接口,容器引擎都会为每个容器创建一对(两个)虚拟以太网设备,一个配置为容器的接口设备,另一个则在节点主机上接入指定的虚拟网桥设备(默认为 docker)。
- Open container(开放式容器): 此类容器使用 "Host" 模型的网络,它们共享使用 Docker 主机的网络及其接口。
- Joined container(联盟式容器): 此类容器共享使用某个已存在的容器的网络名称空间,即共享并使用指定的容器的网络及接口。
上述的容器通信仅描述了同一个节点上的容器间通信的可用方案,这应该也是 Docker 设计者早期最关注的容器通信目标。然而,在生产环境中使用容器技术时,跨节点的容器间通信反倒更为常见,可根据网络类型将其实现方式简单划分为如下几种:
-
- 为各 Docker 节点创建物理网络桥接接口,设定各节点上的容器使用此桥设备从而直接暴露于物理网络中。
- 配置各节点上的容器直接共享使用其节点的网络名称空间。
- 将容器接入指定的桥设备,如 docker0,并设置其借助 NAT 机制进行通信。
第三种方案是较为流行且默认的解决方案。每个节点上的容器都将从同一个网络 172.17.0.0/16 中获取 IP 地址,这就意味着不同 Docker 主机上的容器可能会使用相同的地址,因此他们会出现无法通信,解决此类问题的通信方式是为 NAT。所有接入此桥设备上的容器均会被 NAT 隐藏,它们发往 Docker 主机外部的所有流量都会在执行过源地址转换后发出,并且默认是无法直接接收节点之外的其他主机发出来的请求。如要接入 Docker 主机外部流量,则需要事先通过目标地址转换甚至额外的端口转换将其暴露于外部网络中。
故此,传统的解决方案中,多节点上的 Docker 容器间通信依赖于 NAT 机制转发实现。这种解决方案在网络规模庞大时变得极为复杂,对系统资源的消耗较大且转发效率低下。同时,docker host 的端口也是一种稀缺资源,静态分配和映射极易导致冲突,而动态分配又很容易导致模型进一步复杂化。
Docker 网络也可借助于第三方解决方案来规避 NAT 通信模型导致的复杂化问题,后来还发布了 CNM(Container Network Model)规范,不过这种模型被采用时,也就属于容器编排的范畴了。
二、Kubernetes网络模型
Kubernetes 的网络模型主要可用于解决四类通信需求:
-
- 同一 Pod 内容器间的通信(Container to Container)
- Pod 间的通信(Pod to Pod)
- Service 到 Pod 间的通信(Service to Pod)
- 集群外部与 Service 之间的通信(external to Service)
1)同一 Pod 容器间通信(Container to Container)
Pod 对象内的各容器共享同一网络名称空间,它通常由构建 Pod 对象的基础架构容器所提供,例如,由 pause 镜像启动的容器。所有运行于同一个 Pod 内的容器与同一主机上的多个进程类似,彼此之间可通过 lo 接口完成交互。
2)Pod 间通信(Pod to Pod)
各 Pod 对象需要运行于同一个平面网络中,每个 Pod 对象拥有一个集群全局唯一的地址并可直接用于其他 Pod 进行通信,此网络被称为 Pod 网络。
3)Service 与 Pod 间的通信
Service 资源的专用网络也被称为集群网络(Cluster Network),需要在启动 kube-apiserver 时经过由 "--service-cluster-ip-range" 选项进行制定,如 10.96.0.0/12,而每个 Service 对象在此网络中均拥有一个称为 Cluster-IP 的固定地址。管理员或用户对 Service 对象的创建或更改操作由 API Server 存储完成后触发各节点上 kube-proxy,并根据代理模式的不同将其定义为相应节点上的 iptables 规则或者是 ipvs 规则,借此完成从 Service 的 Cluster-IP 与 Pod-IP 之间的报文转发。
4)集群外部到 Pod 对象之间的通信
将集群外部的流量引入到 Pod 对象的方式有受限于 Pod 所在的工作节点范围的节点端口(nodePort)和主机网络(hostNetwork)两种,以及工作于集群级别的 NodePort 或 LoadBalancer 类型的 Service 对象。
不过,即便是四层代理的模式也要经过两级转发才能到达目标 Pod 资源:请求流量首先到达外部负载均衡器,由其调度至某个工作节点之上,而后再工作节点的 kube-proxy 组件上的规则(iptables 或 ipvs) 调度至某个目标 Pod 对象上。
三、Pod 网络的实现方式
每个 Pod 对象内的基础架构容器均使用一个独立的网络名称空间,并共享给同一个 Pod 内其他容器使用。每个名称空间均有其专用的独立网络协议栈及其相关的网络接口设备。
一个网络接口仅能属于一个网络名称空间,于是,运行多个 Pod 必然要求使用多个网络名称空间,也就需要用到多个网络接口设备。不过,一个易于实现的方案是使用软件实现的伪网络接口及模拟线缆将其连接至物理接口。伪网络接口的实现方案常见的有虚拟网桥、多路复用及硬件交换三种:
- 虚拟网桥:创建一对虚拟以太网接口(veth),一个接入容器内部,另一个留置于根名称空间内并借助于 Linux 内核桥接功能或 OpenVSwitch(OVS)关联至真实的物理接口。
- 多路复用:多路复用可以由一个中间网络设备组成,它暴露了多个虚拟接口,可使用数据包转发规则来控制每个数据包转发到的目标接口。MACVLAN 为每个虚拟接口配置一个 MAC 地址并基于此地址完成二层报文收发,而 IPVLAN 是基于 IP 地址的并使用单个 MAC,从而使其更适合 VM。
- 硬件交换:现今市面上的多数 NIC 都支持的那根 I/O 虚拟化(SR-IOV),它是创建虚拟设备的一种实现方式。每个虚拟设备自身均表现为一个独立的 PCI 设备,并有着自己的 VLAN 及硬件强制关联的 QoS。SR-IOV 提供了接近硬件级别的性能,但在公共云中通常是不可用的。
大多数情况下,用户希望创建跨多个 L2 或 L3 的逻辑网络子网,这就要借助于叠加封装协议来实现(最常见的是 VXLAN,它将叠加流量封装到 UDP 数据包中)。不过,由于控制平面缺乏标准化,VXLAN 可能会引入更高的开销,并且来自不同供应商的多个 VXLAN 网络通畅无法互相操作。而 Kubernetes 还将大量使用 iptables 和 NAT 来拦截进入逻辑/虚拟地址的流量并将其路由到适合的物理目的地。
无论上述哪种方式应用于容器环境中,其实实现过程都需要大量的操作步骤。不过目前 Kubernetes 支持使用 CNI 插件来编排网络,以实现 Pod 及集群网络管理功能的自动化。每次 Pod 被初始化或删除时,kubelet 都会调用默认的 CNI 插件创建一个虚拟设备接口附加到相关的底层网络,为其设置 IP 地址、路由信息并将其映射到 Pod 对象的网络名称空间。
配置 Pod 网络时,kubelet 首先在默认的 /etc/cni/net.d/ 目录中查找 CNI JSON 配置文件,接着基于 type 属性到 /opt/cni/bin 中查找相关的插件二进制文件,如下面示例中的 "portmap"。随后,由 CNI 插件调用 IPAM 插件(IP 地址管理)来设置每个接口的 IP 地址,如 host-local 或 dhcp 等。
[root@mh-k8s-master-247-10 ~]# cat /etc/cni/net.d/10-calico.conflist
{
"name": "k8s-pod-network",
"cniVersion": "0.3.1",
"plugins": [
{
"type": "calico",
"log_level": "info",
"log_file_path": "/var/log/calico/cni/cni.log",
"datastore_type": "kubernetes",
"nodename": "mh-k8s-master-247-10",
"mtu": 1440,
"ipam": {
"type": "calico-ipam"
},
"policy": {
"type": "k8s"
},
"container_settings": {
"allow_ip_forwarding": true
},
"kubernetes": {
"kubeconfig": "/etc/cni/net.d/calico-kubeconfig"
}
},
{
"type": "portmap",
"snat": true,
"capabilities": {"portMappings": true}
},
{
"type": "bandwidth",
"capabilities": {"bandwidth": true}
}
]
}[root@mh-k8s-master-247-10 ~]#
四、CNI插件及常见实现
Kubernetes 设计了网络模型,但将实现给了网络插件。于是,各种解决方案不断涌现。为了规范兼容各种解决方案,CoreOS 和 Google 联合制定了 CNI(Container Network Interface)标准,旨在定义容器网络模型规范。
它连接了2个插件:
-
- 容器管理系统
- 网络插件
容器管理系统和网络插件之间通过 JSON 格式的文件进行通信,以实现容器的网络功能,具体的实现工作,均有插件来实现,包括创建容器 netns、关联网络接口到对应的 netns 以及为网络接口分配了 IP 等。
CNI 的基本思想是:容器运行时环境在创建容器时,先创建好网络名称空间(netns),然后调用 CNI 插件为这个 netns 配置网络,而后再启动容器内的进程。
CNI 目前已经是 Kubernetes 当前推荐的网络方案。常见的 CNI 网络插件包含如下这些主流的项目:
- Flannel: 一个为 Kubernetes 提供叠加网络的网络插件,它基于 Linux TUN/TAP,使用 UDP 封装 IP 报文来创建叠加网络,并借助 etcd 维护网络的分配情况。
- Calico: 一个基于 BGP 的三层网络插件,并且也支持网络策略来实现网络的访问控制;它在每台机器上运行一个 vRouter,利用 Linux 内核来转发网络数据包,并借助 iptables 实现防火墙功能等。
- Canal: 由 Flannel 和 Calico 联合发布的一个统一挽留过插件,提供 CNI 网络插件,并支持网络策略。
- Weave Net: Weave Net 是一个多主机容器的网络方案,支持去中心化的控制平面,在各个 host 上的 wRouter 间简历 Full Mesh 的 TCP 连接,并通过 Gossip 来同步控制信息。数据平面上,Weave 通过 UDP 封装实现 L2 Overlay,封装支持两种模式,一种是运行在 user space 的 sleeve mode,另一种是运行在 kernel space 的 fastpath mode。
- ……
网络方案 | 网络模型 | 路由分发 | 网络策略 | 跨集群网络 | 外部数据存储 | 加密 | Ingress/Egress 策略 |
---|---|---|---|---|---|---|---|
Canal | 封装技术 (VXLAN) | 不支持 | 支持 | 不支持 | K8S API | 不加密 | 支持 |
Flannel | 封装技术 (VXLAN) | 不支持 | 不支持 | 不支持 | K8S API | 不加密 | 不支持 |
Calico | 非封装技术 | 支持 | 支持 | 支持 | Etcd | 加密 | 支持 |
Weave | 封装技术 | 支持 | 支持 | 支持 | 不支持 | 加密 | 支持 |
下列表格总结了不同网络项目在 Github 上的热度指标。数据更新于 2022 七月四日。
网络方案 | Github 项目 | Stars | Forks | Watching |
---|---|---|---|---|
Canal | https://github.com/projectcalico/canal | 689 | 104 | 64 |
flannel | https://github.com/coreos/flannel | 7.3k | 2.7k | 241 |
Calico | https://github.com/projectcalico/calico | 3.6 | 872 | 105 |
Weave | https://github.com/weaveworks/weave/ | 6.3k | 642 | 254 |