1、简介
Kubernetes是一种开源的容器编排系统,用于自动化部署、扩展和管理容器化应用程序。在Kubernetes集群中,网络是一个关键的组成部分,因为它负责实现Pod之间、Pod与服务之间的通信。Container Network Interface (CNI) 是一个云原生计算基金会(CNCF)维护的标准,用于定义容器与其网络的接口。CNI的设计目标是简单和通用,适用于各种网络环境,允许不同的网络插件和方案共存。它提供了定义如何设置和清理容器网络的规范。
2、CNI工作原理
容器网络接口(Container Network Interface),实现kubernetes集群的Pod网络通信及管理。CNI的工作流程主要包括以下几个步骤:
插件调用:当Kubernetes需要为一个新创建的Pod分配网络时,Kubelet调用CNI插件。
网络配置:CNI插件根据预定义的配置(如IP分配、路由、DNS设置等)为Pod分配网络资源。
网络清理:当Pod被删除时,CNI插件负责清理与该Pod相关的网络资源。
CNI插件由两部分组成:一个负责网络创建(如桥接、VLAN、Overlay等),另一个负责IP地址管理(IPAM)。
CNI Plugin负责给容器配置网络,它包括两个基本的接口:配置网络: AddNetwork(net NetworkConfig, rt RuntimeConf) (types.Result, error)清理网络: DelNetwork(net NetworkConfig, rt RuntimeConf) error
IPAM Plugin负责给容器分配IP地址,主要实现包括host-local和dhcp。
3、常见CNI插件
以下是几种常见的CNI插件,它们在功能和性能上各有优势,适用于不同的集群需求。
Flannel
Flannel 是一个简单易用的CNI插件,旨在为Kubernetes提供基本的网络功能。它使用Overlay网络(如VXLAN、IPsec、GRETAP)来实现跨节点的Pod通信。
优点:易于部署和管理,配置简单,适合小规模集群。
缺点:性能较低,缺乏高级网络功能(如网络策略)。
Calico
Calico 是一个功能强大的CNI插件,提供了高性能的网络和网络安全功能。它通过BGP协议分发路由信息,实现高效的Pod间通信,并支持网络策略和网络隔离。
优点:高性能,支持网络策略,适合大规模集群。
缺点:部署和配置相对复杂,对底层网络要求较高。
Weave Net
Weave Net 是一个易于使用的CNI插件,提供安全加密的Overlay网络。它不依赖于外部数据库,适用于多种网络环境。
优点:易于部署,支持加密通信。
缺点:性能较低,缺乏一些高级功能。
Cilium
Cilium 是一个新兴的CNI插件,基于eBPF技术,提供高性能的网络和安全功能。它支持透明加密、负载均衡和网络策略。
优点:高性能,支持高级网络功能,适合大规模集群。
缺点:相对较新,社区支持尚在发展中。
4、集群网络选型
选择适合的CNI插件是Kubernetes网络实现的重要步骤。以下是几种常见场景及其推荐的CNI插件:
小规模集群
对于小规模集群,网络流量和负载较小,通常不需要高级的网络功能。推荐使用:
Flannel:部署简单,足以满足基本的Pod通信需求。
中等规模集群
对于中等规模的集群,既需要良好的性能,也需要一定的网络策略支持。推荐使用:
Calico:提供高性能和网络策略支持,适合中等规模集群。
Weave Net:如果需要简单部署且支持加密通信,可以选择Weave Net。
大规模集群
对于大规模集群,性能和网络安全是关键考量。推荐使用:
Calico:高性能和全面的网络策略支持,非常适合大规模集群。
Cilium:基于eBPF技术,提供极高的性能和灵活的网络策略。
5、容器网络创建流程
创建pause容器:
Kubelet首先创建一个pause容器,这个容器的唯一作用是生成并管理对应的network namespace。
调用CNI Driver:
Kubelet调用网络driver(因为配置的是CNI),CNI driver会识别并加载
/etc/cni/net.d目录下的CNI配置文件。
执行CNI插件:
根据CNI配置文件,CNI driver在
/opt/cni/bin目录下找到并执行相应的CNI插件,这些插件负责实际的网络配置。
配置网络:
CNI插件根据配置文件为pause容器配置网络,包括分配IP地址、设置路由等。Pod中其他容器共享这个pause容器的网络命名空间,从而使用相同的网络配置。
6、vxlan介绍及点对点通信的实现
VXLAN 全称是虚拟可扩展的局域网( Virtual eXtensible Local Area Network),它是一种 overlay 技术,通过三层的网络来搭建虚拟的二层网络。
它创建在原来的 IP 网络(三层)上,只要是三层可达(能够通过 IP 互相通信)的网络就能部署 vxlan。在每个端点上都有一个 vtep 负责 vxlan 协议报文的封包和解包,也就是在虚拟报文上封装 vtep 通信的报文头部。物理网络上可以创建多个 vxlan 网络,这些 vxlan 网络可以认为是一个隧道,不同节点的虚拟机能够通过隧道直连。每个 vxlan 网络由唯一的 VNI 标识,不同的 vxlan 可以不相互影响。
VTEP(VXLAN Tunnel Endpoints):vxlan 网络的边缘设备,用来进行 vxlan 报文的处理(封包和解包)。vtep 可以是网络设备(比如交换机),也可以是一台机器(比如虚拟化集群中的宿主机)
VNI(VXLAN Network Identifier):VNI 是每个 vxlan 的标识,一共有 2^24 = 16,777,216,一般每个 VNI 对应一个租户,也就是说使用 vxlan 搭建的公有云可以理论上可以支撑千万级别的租户
在k8s-work01和k8s-work02两台机器间,利用vxlan的点对点能力,实现虚拟二层网络的通信
k8s-work01节点
# 创建vTEP设备,对端指向k8s-work02节点,指定VNI及underlay网络使用的网卡
[root@work01 ~]# ip link add vxlan20 type vxlan id 20 remote 192.168.58.118 dstport 4789 dev ens33
[root@work01 ~]# ip -d link show vxlan20
6: vxlan20: <BROADCAST,MULTICAST> mtu 1450 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether aa:c0:4d:73:0d:49 brd ff:ff:ff:ff:ff:ff promiscuity 0
vxlan id 20 remote 192.168.58.118 dev ens33 srcport 0 0 dstport 4789 ageing 300 noudpcsum noudp6zerocsumtx noudp6zerocsumrx addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
# 启动设备
[root@work01 ~]# ip link set vxlan20 up
# 设置ip地址
[root@work01 ~]# ip addr add 10.0.136.11/24 dev vxlan20
k8s-work02节点
# 创建VTEP设备,对端指向k8s-work02节点,指定VNI及underlay网络使用的网卡
[root@work02 ~]# ip link add vxlan20 type vxlan id 20 remote 192.168.58.117 dstport 4789 dev ens33
# 启动设备
[root@work02 ~]# ip link set vxlan20 up
# 设置ip地址
[root@work02 ~]# ip addr add 10.0.136.12/24 dev vxlan20
在k8s-work01节点访问k8s-work02的地址
隧道是一个逻辑上的概念,在 vxlan 模型中并没有具体的物理实体相对应。隧道可以看做是一种虚拟通道,vxlan 通信双方(图中的虚拟机)认为自己是在直接通信,并不知道底层网络的存在。从整体来说,每个 vxlan 网络像是为通信的虚拟机搭建了一个单独的通信通道,也就是隧道。
[root@work01 ~]# ping 10.0.136.12
PING 10.0.136.12 (10.0.136.12) 56(84) bytes of data.
64 bytes from 10.0.136.12: icmp_seq=1 ttl=64 time=0.817 ms
64 bytes from 10.0.136.12: icmp_seq=2 ttl=64 time=0.515 ms
实现的过程
虚拟机的报文通过 vtep 添加上 vxlan 以及外部的报文层,然后发送出去,对方 vtep 收到之后拆除 vxlan 头部然后根据 VNI 把原始报文发送到目的虚拟机
# 查看k8s-work01主机路由
[root@work01 ~]# route -n
10.0.136.0 0.0.0.0 255.255.255.0 U 0 0 0 vxlan20
# 到了vxlan的设备后,
[root@work01 ~]# ip -d link show vxlan20
6: vxlan20: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/ether aa:c0:4d:73:0d:49 brd ff:ff:ff:ff:ff:ff promiscuity 0
vxlan id 20 remote 192.168.58.118 dev ens33 srcport 0 0 dstport 4789 ageing 300 noudpcsum noudp6zerocsumtx noudp6zerocsumrx addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
# 查看fdb地址表,主要由MAC地址、VLAN号、端口号和一些标志域等信息组成,vtep 对端地址为192.168.58.118,换句话说,如果接收到的报文添加上 vxlan 头部之后都会发到192.168.58.118
[root@work01 ~]# bridge fdb show|grep vxlan20
00:00:00:00:00:00 dev vxlan20 dst 192.168.58.118 via ens33 self permanent
在k8s-slave2机器抓包,查看vxlan封装后的包
# 在k8s-slave2机器执行
[root@work02 ~]# tcpdump -i ens33 host 192.168.58.118 -w vxlan.cap
# 在k8s-slave1机器执行
[root@work01 ~]# ping 10.0.136.12
使用wireshark分析ICMP类型的数据包
7、跨主机容器网络的通信
在Kubernetes集群中,通过手动创建网桥并使用VTEP(虚拟隧道端点)可以实现跨主机容器的网络通信。
目的容器的流量要通过vtep设备进行转发。
利用vxlan实现跨主机容器网络通信
<!--为了不影响已有的网络,因此创建一个新的网桥,创建容器接入到新的网桥来演示效果-->
k8s-work01节点
[root@work01 ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
28cd8574733f bridge bridge local
fce0b7b91f2e host host local
2127d8fb99b4 none null local
# 创建新网桥,指定cidr段
[root@work01 ~]# docker network create --subnet 172.18.0.0/16 network-huqi
cc7ac5661714d9e08bbb8d7c8ea1032cfaaa1789410bbb3c9fa0b34f2705b45c
# 新建容器,接入到新网桥
[root@work01 ~]# docker run -d --name vxlan-test --net network-huqi --ip 172.18.0.2 registry.cn-shanghai.aliyuncs.com/study-03/nginx:alpine
de81859abfe4201c88055fbb0245909854a5af2d77aaf8a4ea94738d47abf1c7
[root@work01 ~]# docker exec vxlan-test ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:AC:12:00:02
inet addr:172.18.0.2 Bcast:172.18.255.255 Mask:255.255.0.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:13 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:1102 (1.0 KiB) TX bytes:0 (0.0 B)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
k8s-work02节点
# 创建新网桥,指定cidr段
[root@work02 ~]# docker network create --subnet 172.18.0.0/16 network-huqi
99913f8fc7def63368576990b498c654080b9bfbe513d18a56bacc2391ee40ad
# 新建容器,接入到新网桥
[root@work02 ~]# docker run -d --name vxlan-test --net network-huqi --ip 172.18.0.3 registry.cn-shanghai.aliyuncs.com/study-03/nginx:alpine
faa8737c32f5cfc9728a97719d15eb3db1c7a0e94393c7b0bf7ac87a074d02c7
此时执行ping测试:(网络不通)
[root@work01 ~]# docker exec vxlan-test ping 172.18.0.3
PING 172.18.0.3 (172.18.0.3): 56 data bytes
数据到了网桥后,出不去。结合前面的示例,因此应该将流量由vtep设备转发,联想到网桥的特性,接入到桥中的端口,会由网桥负责转发数据,因此,相当于所有容器发出的数据都会经过到vxlan的端口,vxlan将流量转到对端的vtep端点,再次由网桥负责转到容器中。
将网络接口接入网桥
k8s-work01节点
# 删除旧的vtep
[root@work01 ~]# ip link del vxlan20
# 新建vtep[root@work01 ~]# ip link add vxlan_docker type vxlan id 100 remote 192.168.58.118 dstport 4789 dev ens33
[root@work01 ~]# ip link set vxlan_docker up
# 接入到网桥中
[root@work01 ~]# ip link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
link/ether 00:0c:29:77:f9:a8 brd ff:ff:ff:ff:ff:ff
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default
link/ether 02:42:1b:52:08:5c brd ff:ff:ff:ff:ff:ff
4: br-cc7ac5661714: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
link/ether 02:42:61:49:c7:b4 brd ff:ff:ff:ff:ff:ff
5: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN mode DEFAULT group default
link/ether e6:48:24:53:83:41 brd ff:ff:ff:ff:ff:ff
8: veth169a12a@if7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-cc7ac5661714 state UP mode DEFAULT group default
link/ether e2:f1:7e:eb:8f:f2 brd ff:ff:ff:ff:ff:ff link-netnsid 0
9: vxlan_docker: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/ether 46:7b:49:5b:be:3d brd ff:ff:ff:ff:ff:ff
[root@work01 ~]# brctl addif br-cc7ac5661714 vxlan_docker
k8s-work02节点
# 删除旧的vtep
[root@work02 ~]# ip link del vxlan20
# 新建vtep
[root@work02 ~]# ip link add vxlan_docker type vxlan id 100 remote 192.168.58.117 dstport 4789 dev ens33
[root@work02 ~]# ip link set vxlan_docker up
# 接入到网桥中
[root@work02 ~]# ip link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
link/ether 00:0c:29:c6:c0:0f brd ff:ff:ff:ff:ff:ff
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default
link/ether 02:42:e3:93:e8:50 brd ff:ff:ff:ff:ff:ff
4: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN mode DEFAULT group default
link/ether 06:55:4a:57:8e:e8 brd ff:ff:ff:ff:ff:ff
6: br-99913f8fc7de: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
link/ether 02:42:81:b6:af:b3 brd ff:ff:ff:ff:ff:ff
8: vethadbb848@if7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-99913f8fc7de state UP mode DEFAULT group default
link/ether fa:d6:cc:0a:a3:b5 brd ff:ff:ff:ff:ff:ff link-netnsid 0
9: vxlan_docker: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/ether 66:dd:aa:62:d9:85 brd ff:ff:ff:ff:ff:ff
[root@work02 ~]# brctl addif br-99913f8fc7de vxlan_docker
再次执行ping测试
[root@work01 ~]# docker exec vxlan-test ping 172.18.0.3
PING 172.18.0.3 (172.18.0.3): 56 data bytes
64 bytes from 172.18.0.3: seq=0 ttl=64 time=1.702 ms
64 bytes from 172.18.0.3: seq=1 ttl=64 time=1.039 ms
64 bytes from 172.18.0.3: seq=2 ttl=64 time=2.332 ms
64 bytes from 172.18.0.3: seq=3 ttl=64 time=1.484 ms
64 bytes from 172.18.0.3: seq=4 ttl=64 time=11.223 ms




