介绍CNI
什么是CNI?
CNI (Container Network Interface) 。容器网络接口。由CNCF出品,旨在配置linux 容器的网络规范的库。现在已经成最流行的网络标准,被包含k8s在内的众多容器解决方案采用。
CNI长啥样?
- CNI的插件都以可执行文件的形式存放在集群节点上
1
2[root@node1 amd64]# ls /opt/cni/bin/
bandwidth bridge dhcp firewall flannel host-device host-local ipvlan loopback macvlan portmap ptp sbr static tuning vlan vrf
CNI插件怎么生效
kubelet将按照NETCONFPATH的环境变量找相关的cni配置文件,比如我在配置文件中声明了flannel
和bandwidth
这两个插件,他将会到CNI_PATH
中查找相应的插件执行。
CNI规范要求所有参数要么以标准输入的json字符串的形式,要么以环境变量的形式给到插件。同时插件也应json形式标准输出结果。
一个官方的CNI NETCONF 例子如下
1 | { |
插件的分类?
Main:接口创建
- bridge:创建网桥,并添加主机和容器到该网桥
- ipvlan:在容器中添加一个 ipvlan 接口
- loopback:创建一个回环接口
- macvlan:创建一个新的 MAC 地址,将所有的流量转发到容器
- ptp:创建 veth 对
- vlan:分配一个 vlan 设备
IPAM:IP 地址分配
- dhcp:在主机上运行守护程序,代表容器发出 DHCP 请求
- host-local:维护分配 IP 的本地数据库
Meta:其它插件
- flannel:根据 flannel 的配置文件创建接口
- tuning:调整现有接口的 sysctl 参数
- portmap:一个基于 iptables 的 portmapping 插件。将端口从主机的地址空间映射到容器。
单个CNI插件的职责是单一的,比如bridge插件负责网桥的相关配置, firewall插件负责防火墙相关配置, portmap 插件负责端口映射相关配置。
命令看起来像是这样:
1 | # CNI_COMMAND=ADD 顾名思义表示创建。 |
插件有那些参数?
- CNI_COMMAND :定义期望的操作,可以是ADD,DEL,CHECK或VERSION。通常用于添加网络还是删除网络。
- CNI_CONTAINERID : 容器ID,由容器运行时管理的容器唯一标识符。
- CNI_NETNS:容器网络命名空间的路径。(形如 /run/netns/[nsname] )。
- CNI_IFNAME :需要被创建的网络接口名称,例如eth0。
- CNI_ARGS :运行时调用时传入的额外参数,格式为分号分隔的key-value对,例如 FOO=BAR;ABC=123
- CNI_PATH : CNI插件可执行文件的路径,例如/opt/cni/bin。
项目结构
- cnitool 一个用于执行插件的工具。工具执行依赖两个环境变量:
NETCONFPATH
配置文件路径,默认是在/etc/cni/net.d
目录下搜索 *.conflist文件。CNI_PATH
这个环境变量保存插件。 - libcni 这里面就是CNI规范中的接口,里面是golang的interface。
- plugins 这里有些自己开发插件的模板项目
一个例子
我们下面演示下内置的一些简单插件的使用,我们以bridge为例:
首先在linux内部 /opt/cni/bin 下部署插件组
以无网络模式启动docker 容器
1
2
3
4contid=$(docker run -d --net=none --name nginx nginx) # 容器ID
pid=$(docker inspect -f '{{ .State.Pid }}' $contid) # 容器进程ID
netnspath=/proc/$pid/ns/net # 命名空间路径
nsenter进入相应namespace可以看到只有回环网卡。
1 | nsenter -t $pid -n ip a |
- 配置参数json
1 | { |
执行ADD操作
1 | CNI_COMMAND=ADD CNI_CONTAINERID=$contid CNI_NETNS=$netnspath CNI_IFNAME=eth0 CNI_PATH=~/cni/bin ~/cni/bin/bridge < bridge.json |
结果返回
1 | { |
这是执行nsenter到容器命名空间已经可以看到我们的网卡了,这里我们还配置ipam为host-local
,所以这里插件只会为我们分配本机上 子网唯一的 ip地址。显然这种简单的思路是不能满足生产中全局唯一的clusterip需求的。
后续我们将看下flannel在这块是如何实现的。