Kubernetes集群的网络实现

集群网络

容器网络回顾

Docker 创建一个容器的时候,会执行如下操作:

  • 创建一对虚拟接口/网卡,也就是veth pair;
  • veth pair的一端桥接 到默认的 docker0 或指定网桥上,并具有一个唯一的名字,如 vethxxxxxx;
  • veth pair的另一端放到新启动的容器内部,并修改名字作为 eth0,这个网卡/接口只在容器的命名空间可见;
  • 从网桥可用地址段中(也就是与该bridge对应的network)获取一个空闲地址分配给容器的 eth0
  • 配置容器的默认路由

Pod IP唯一性保障

Kubernetes网络模型的核心要求之一是每个Pod应该获得自己的IP地址,如何实现?

$ cat /etc/kubernetes/manifests/kube-controller-manager.yaml
...
- --cluster-cidr=10.244.0.0/16
...

# 为每个节点从cidr中分配一个网段,供该节点分配pod ip
$ kubectl describe node k8s-slave1

思考:谁来配置Pod网络?当Pod调度到 k8s-slave1节点后,网络的配置流程是怎样

Pod配置网络流程

CNI:容器网络接口(Container Network Interface), 主要能力是对接 Kubelet 完成容器网卡的创建,申请和设置 ip 地址,路由设置,网关设置,实现kubernetes集群的Pod网络通信及管理。

CNI的具体实现有很多种:

  • 通用类型:flannel、calico、Cilium 等,部署使用简单
  • 其他:根据具体的网络环境及网络需求选择,比如
    • 公有云机器,可以选择厂商与网络插件的定制Backend,如AWS、阿里、腾讯针对flannel均有自己的插件,也有AWS ECS CNI
    • 私有云厂商,比如Vmware NSX-T等
    • 网络性能等,MacVlan

k8s本身不提供cni的实现,因此安装完k8s集群后,需要单独安装网络组件

  1. Pod调度到k8s的节点k8s-slave1中
  2. slave1的kubelet调用containerd创建Pod
  3. containerd创建Pod沙箱和pod所用的网络空间
  4. containerd查找配置目录/etc/cni/net.d/,发现10-flannel.conflist,使用flannel作为网络插件
  5. CNI开始为Pod配置网络
    • flannel启动时候,写入了本机配置文件/run/flannel/subnet.env
    • flannel将本机网段等信息传递给bridge插件
      • bridge插件创建cni0网桥
      • 创建veth pair,分别接入到pod网络空间和cni0网桥
      • 调用 本地IPAM CNI 插件,设置pod ip地址并记录已分配地址
    • IPAM CNI 插件 从子网返回容器的IP地址,并将分配的IP本地存储在主机上 /var/lib/cni/networks/cbr0/
  6. containerd创建pause容器,并配置到新建的网络空间
  7. kubelet调用containerd开始拉取业务镜像
  8. 启动业务容器并设置namespace和cgroup

上图可以清晰的看到如下重点:

  • kubelet、containerd、cni的工作边界和职责
  • cni是被containerd进行调用,cni的实现是可以根据不同的网络环境配置的
  • 配置Pod网络的过程,实际上是一个网络工具的链式调用

更细致的CNI调用过程:

  • Flannel CNI

    • 使用Flannel作为网络提供程序时,Containered CRI插件使用CNI配置文件/etc/cni/net.d/10-flannel.conflist调用Flannel CNI插件
    • Fannel CNI插件与Flanneld结合使用。当Flanneld启动时,它会从apiserver中获取podCIDR和其他与网络相关的详细信息,并将它们存储在文件/run/flannel/subnet.env
    • Flannel CNI插件使用/run/flannel/subnet.env中的信息来配置和调用网桥CNI插件。
  • Bridge CNI

    首次调用Bridge CNI插件时,它将使用配置文件中指定的"name": "cni0"创建一个Linux网桥。然后,它为每个Pod创建veth对-该对的一端在容器的网络名称空间中,另一端连接到主机网络上的linux桥。使用Bridge CNI插件,主机上的所有容器都连接到主机网络上的linux网桥。

    配置完veth对后,Bridge插件将调用主机本地IPAM CNI插件

  • host-local IPAM CNI

    Host-local IPAM(IP地址管理)插件从子网返回容器的IP地址,并将分配的IP本地存储在主机上

经过Pod网络配置后,本机的Pod应该是这样的:

思考:

  • flannel插件和kube-flannel的Pod什么关系
  • 跨主机的Pod间如何实现通信

kube-flannel的作用

从Pod的启动进程,看flannel的Pod:

$ kubectl -n kube-flannel get pod kube-flannel-ds-gdvpx -oyaml

# 1. /opt/bin/flanneld --ip-masq --kube-subnet-mgr --iface=eth0
# 2. cp -f /flannel /opt/cni/bin/flannel
# 3. cp -f /etc/kube-flannel/cni-conf.json /etc/cni/net.d/10-flannel.conflist

从进程可以得知:

  1. 启动flanneld进程,功能未知
  2. 拷贝flannel的网络插件到宿主机中,为containerd调用
  3. 考虑flannel的配置文件,当成宿主机的CNI配置,告知containerd使用flannel

跨主机Pod通信

跨主机间的通信流程:

flannel的网络有多种后端实现:

  • udp
  • vxlan
  • host-gw
  • ...

不特殊指定的话,默认会使用vxlan技术作为Backend,可以通过如下查看:

$ kubectl -n kube-flannel exec  kube-flannel-ds-amd64-cb7hs cat /etc/kube-flannel/net-conf.json
{
  "Network": "10.244.0.0/16",
  "Backend": {
    "Type": "vxlan"
  }
}

使用vxlan作为后端实现时,flanneld进程的作用:

  • 启动flannel.1作为VTEP设备,用来封包、解包,实现跨主机通信
  • 监听宿主机的Pod CIDR,维护本机路由表

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-slave1和k8s-slave2两台机器间,利用vxlan的点对点能力,实现虚拟二层网络的通信

172.21.65.227节点:

# 创建vTEP设备,对端指向172.21.65.228节点,指定VNI及underlay网络使用的网卡
$ ip link add vxlan20 type vxlan id 20 remote 172.21.65.228 dstport 4789 dev eth0

$ ip -d link show vxlan20

# 启动设备
$ ip link set vxlan20 up 

# 设置ip地址
$ ip addr add 10.245.1.3/24 dev vxlan20

172.21.65.228节点:

# 创建VTEP设备,对端指向172.21.65.227节点,指定VNI及underlay网络使用的网卡
$ ip link add vxlan20 type vxlan id 20 remote 172.21.65.227 dstport 4789 dev eth0

# 启动设备
$ ip link set vxlan20 up 

# 设置ip地址
$ ip addr add 10.245.2.5/24 dev vxlan20

172.21.65.227节点:

$ ping 10.245.2.5

# 走vtep封包解包

$ ip route add 10.245.2.0/24 dev vxlan20

# 在172.21.65.228机器
$ ip route add 10.245.1.0/24 dev vxlan20

# 再次ping
$ ping 10.245.2.5

隧道是一个逻辑上的概念,在 vxlan 模型中并没有具体的物理实体相对应。隧道可以看做是一种虚拟通道,vxlan 通信双方(图中的虚拟机)认为自己是在直接通信,并不知道底层网络的存在。从整体来说,每个 vxlan 网络像是为通信的虚拟机搭建了一个单独的通信通道,也就是隧道。

实现的过程:

虚拟机的报文通过 vtep 添加上 vxlan 以及外部的报文层,然后发送出去,对方 vtep 收到之后拆除 vxlan 头部然后根据 VNI 把原始报文发送到目的虚拟机。

# 查看172.21.65.227主机路由
$ route -n
10.0.51.0       0.0.0.0         255.255.255.0   U     0      0        0 vxlan20
10.0.52.0       0.0.0.0         255.255.255.0   U     0      0        0 vxlan20

# 到了vxlan的设备后,
$ ip -d link show vxlan20
    vxlan id 20 remote 172.21.65.228 dev eth0 srcport 0 0 dstport 4789 ...

清理:

$ ip link del vxlan20

演示跨主机Pod通信的流量详细过程:

$ kubectl -n luffy get po -o wide
eladmin-api-6b5d9664d8-gj87w   1/1     Running   1    11d     10.244.1.35   k8s-slave1
eladmin-web-6c5dc87987-ncm5d   1/1     Running   0    4d23h   10.244.0.21   k8s-master

$ kubectl -n luffy exec eladmin-web-6c5dc87987-ncm5d  -- ping 10.244.1.35 -c 2
PING 10.244.1.35 (10.244.1.35): 56 data bytes
64 bytes from 10.244.1.35: seq=0 ttl=62 time=0.473 ms
64 bytes from 10.244.1.35: seq=1 ttl=62 time=0.413 ms

--- 10.244.1.35 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.413/0.443/0.473 ms

# 查看路由
$ kubectl -n luffy eladmin-web-6c5dc87987-ncm5d -- route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         10.244.0.1      0.0.0.0         UG    0      0        0 eth0
10.244.0.0      0.0.0.0         255.255.255.0   U     0      0        0 eth0
10.244.0.0      10.244.0.1      255.255.0.0     UG    0      0        0 eth0

# 查看k8s-master 的veth pair 和网桥
$ brctl show
bridge name     bridge id               STP enabled     interfaces
cni0            8000.6a9a0b341d88       no              veth048cc253
                                                        veth76f8e4ce
                                                        vetha4c972e1
# 流量到了cni0后,查看master节点的route
$ route -n
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.136.2   0.0.0.0         UG    100    0        0 eth0
10.0.136.0      0.0.0.0         255.255.255.0   U     0      0        0 vxlan20
10.244.0.0      10.244.0.0      255.255.255.0   UG    0      0        0 flannel.1
10.244.1.0      0.0.0.0         255.255.255.0   U     0      0        0 cni0
10.244.2.0      10.244.2.0      255.255.255.0   UG    0      0        0 flannel.1
172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 docker0
192.168.136.0   0.0.0.0         255.255.255.0   U     100    0        0 eth0

# 流量转发到了flannel.1网卡,查看该网卡,其实是vtep设备
$ ip -d link show flannel.1
602: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN mode DEFAULT group default
    link/ether 1a:ef:13:b7:44:9c brd ff:ff:ff:ff:ff:ff promiscuity 0
    vxlan id 1 local 172.21.65.226 dev eth0 srcport 0 0 dstport 8472 nolearning ageing 300 noudpcsum noudp6zerocsumtx noudp6zerocsumrx addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535

# 该转发到哪里,通过etcd查询数据,然后本地缓存,流量不用走多播发送
$ bridge fdb show dev flannel.1
4a:4d:9d:3a:c5:f0 dst 172.21.51.68 self permanent
76:e7:98:9f:5b:e9 dst 172.21.51.67 self permanent

# 对端的vtep设备接收到请求后做解包,取出源payload内容,查看k8s-slave1的路由
$ route -n
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         172.21.64.190   0.0.0.0         UG    0      0        0 eth0
10.244.0.0      10.244.0.0      255.255.255.0   UG    0      0        0 flannel.1
10.244.1.0      0.0.0.0         255.255.255.0   U     0      0        0 cni0
10.244.2.0      10.244.2.0      255.255.255.0   UG    0      0        0 flannel.1
169.254.0.0     0.0.0.0         255.255.0.0     U     1002   0        0 eth0
172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 docker0

#根据路由规则转发到cni0网桥,然后由网桥转到具体的Pod中

实际的请求图:

利用host-gw模式提升集群网络性能

vxlan模式适用于三层可达的网络环境,对集群的网络要求很宽松,但是同时由于会通过VTEP设备进行额外封包和解包,因此给性能带来了额外的开销。

网络插件的目的其实就是将本机的cni0网桥的流量送到目的主机的cni0网桥。实际上有很多集群是部署在同一二层网络环境下的,可以直接利用二层的主机当作流量转发的网关。这样的话,可以不用进行封包解包,直接通过路由表去转发流量。

为什么三层可达的网络不直接利用网关转发流量?

内核当中的路由规则,网关必须在跟主机当中至少一个 IP 处于同一网段。
由于k8s集群内部各节点均需要实现Pod互通,因此,也就意味着host-gw模式需要整个集群节点都在同一二层网络内。

修改flannel的网络后端:

$ kubectl edit cm kube-flannel-cfg -n kube-flannel
...
net-conf.json: |
    {
      "Network": "10.244.0.0/16",
      "Backend": {
        "Type": "host-gw"
      }
    }
kind: ConfigMap
...

重建Flannel的Pod

$ kubectl -n kube-flannel get pod
kube-flannel-ds-amd64-5dgb8          1/1     Running   0          15m
kube-flannel-ds-amd64-c2gdc          1/1     Running   0          14m
kube-flannel-ds-amd64-t2jdd          1/1     Running   0          15m

$ kubectl -n kube-flannel delete po kube-flannel-ds-amd64-5dgb8 kube-flannel-ds-amd64-c2gdc kube-flannel-ds-amd64-t2jdd

# 等待Pod新启动后,查看日志,出现Backend type: host-gw字样
$  kubectl -n kube-system logs -f kube-flannel-ds-amd64-4hjdw
I0704 01:18:11.916374       1 kube.go:126] Waiting 10m0s for node controller to sync
I0704 01:18:11.916579       1 kube.go:309] Starting kube subnet manager
I0704 01:18:12.917339       1 kube.go:133] Node controller sync successful
I0704 01:18:12.917848       1 main.go:247] Installing signal handlers
I0704 01:18:12.918569       1 main.go:386] Found network config - Backend type: host-gw
I0704 01:18:13.017841       1 main.go:317] Wrote subnet file to /run/flannel/subnet.env

查看节点路由表:

$ route -n 
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         172.21.64.190   0.0.0.0         UG    0      0        0 eth0
10.244.0.0      0.0.0.0         255.255.255.0   U     0      0        0 cni0
10.244.1.0      172.21.65.227   255.255.255.0   UG    0      0        0 eth0
10.244.2.0      172.21.65.228   255.255.255.0   UG    0      0        0 eth0
169.254.0.0     0.0.0.0         255.255.0.0     U     1002   0        0 eth0
172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 docker0

总结

flannel pod做的几个事情:

  • 为了配置本机的Pod网络
    • flannel pod启动时,拷贝cni的配置文件/etc/cni/net.d/10-flannel.conflist,告知CRI(containerd) 使用flannel插件进行配置网络
    • 拷贝一份可执行文件到宿主机 /opt/cni/bin/flannel
    • 调用apiserver,得到分配到本机的PodCIDR,写入到/run/flannel/subnet.env
    • 调用本机的bridge插件,创建本地cni0网桥,创建虚拟网卡对,链接cni0和Pod网络空间
    • bridge cni调用local ipam插件(host-local),记录并分配pod ip,写入到/var/lib/cni/networks/cbr0/
  • 为了使得跨主机实现Pod的访问
    • 创建flannel.1 vtep设备,支持vxlan模式下封包解包
    • 维护本机路由表,转发Pod的流量

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值