kubernetes CNI

时间:May 8, 2021 分类:

目录:

CNI

Pod在启动的时候,会先创建Infra(Pause)容器,以此来hold住network namespace,之后我们需要的container就可以加入到对应的network namespace了

实现了

  • 可以通过localhost通信
  • 网络设备在容器内都一致
  • IP地址都是networknamespace对应地址
  • pod的生命周期与Infra容器一致

所以CNI的重点就是如何配置Pod的network namespace,而不是每个container如何配置网络

network namespace对应的就是一个网络栈,包括

  • 网卡
  • 回环设备
  • 路由表
  • iptables规则

cni插件的可执行文件在/opt/cni/bin目录,通过kubernetes-cni安装的

cni插件分为三类

  • main插件,用来创建具体的网络设备的二进制文件,例如briage,ipvlan,loopback、macvlan、ptp(veth pair)和vlan
  • IPAM插件,用来分配IP地址的二进制文件,例如dhcp、host-local
  • CNI社区维护插件,例如flannel、tuning(用来调整sysctl网络配置)、portmap(iptable端口映射)等

容器的网络实现,例如flannel主要有两部分

  1. 创建和配置网络设备、配置宿主机路由、配置ARP和FBP表信息
  2. 配置Infra容器内的网络栈

flannel是如果是通过daemonset,在启动的时候会将相关文件cp到cni插件目录

容器网络处理的部分在CRI中完成,docker的CRI为dockershim

在创建和启动Infra容器之后,会执行一个SetUpPod方法,会执行两个步骤

  1. 为CNI准备参数
  2. 加载CNI的配置文件,目前只加载/etc/cni/net.d目录的第一个配置,配置内plugins字段可以定义多个插件协作,示例插件配置

准备参数就是为dockershim设置环境变量,包括

  • CNI_COMMAND:对容器添加还是删除网络
  • CNI_IFNAME:容器内网卡名称
  • CNI_NETNS:network namespace的文件路径
  • CNI_CONTAINERID:容器ID
  • CNI_ARGS:网卡插件的自定义数据
$ cat /etc/cni/net.d/10-flannel.conflist 
{
  "name": "cbr0",
  "plugins": [
    {
      "type": "flannel",
      "delegate": {
        "hairpinMode": true,
        "isDefaultGateway": true
      }
    },
    {
      "type": "portmap",
      "capabilities": {
        "portMappings": true
      }
    }
  ]
}

完整定义参考CNI Network Configuration

依次完成配置容器和配置端口映射

delegate的意思是CNI不会自己完成,而是调用指定的CNI插件完成,对于flannel就是bridge。flannel插件的主要作用是补全Network Configuration,例如将delegate的Type设置为bridge,IPAM设置为host-local,补全之后delegate就是


{
    "hairpinMode":true,
    "ipMasq":false,
    "ipam":{
        "routes":[
            {
                "dst":"10.244.0.0/16"
            }
        ],
        "subnet":"10.244.1.0/24",
        "type":"host-local"
    },
    "isDefaultGateway":true,
    "isGateway":true,
    "mtu":1410,
    "name":"cbr0",
    "type":"bridge"
}

ipam的数据来自flannel的配置文件/run/flannel/subnet.env

然后由flannel把补全的delegate以stdin的方式交由briage来完成,并且字段也会保存到/var/lib/cni/flannel便于删除使用

bridge会检查CNI网桥是否存在,没有则创建

$ ip link add cni0 type bridge
$ ip link set cni0 up

然后bridge插件会通过Infra容器进入对应的network namespace,创建Veth Pair设备,将另一端移动到宿主机


#在容器里
# 创建一对Veth Pair设备。其中一个叫作eth0,另一个叫作vethb4963f3
$ ip link add eth0 type veth peer name vethb4963f3

# 启动eth0设备
$ ip link set eth0 up 

# 将Veth Pair设备的另一端(也就是vethb4963f3设备)放到宿主机(也就是Host Namespace)里
$ ip link set vethb4963f3 netns $HOST_NS

# 通过Host Namespace,启动宿主机上的vethb4963f3设备
$ ip netns exec $HOST_NS ip link set vethb4963f3 up 

再bridge将vethb4963f3连接到CNI网桥,并且配置为HairpinMode(发夹模式针对有nat端口映射的时候,访问nat地址)

# 在宿主机上
$ ip link set vethb4963f3 master cni0

再调用ipam插件分配地址,并设置到网卡上


# 在容器里
$ ip addr add 10.244.0.2/24 dev eth0
$ ip route add default via 10.244.0.1 dev eth0

最后bridge会为CNI网桥添加路由

# 在宿主机上
$ ip addr add 10.244.0.1/24 dev cni0

然后这些ip等信息会返回给dockershim,然后返回给kubelet添加到pod的status字段

最后Kubernetes的CNI网络实现的网络模型

  • 所有容器都可以直接使用IP地址与其他容器通信,而无需使用NAT
  • 所有宿主机都可以直接使用IP地址与所有容器通信,而无需使用NAT,反之亦然
  • 容器自己看到的自己的IP地址,和别人(宿主机或者容器)看到的地址是完全一样的