1、为什么CNI
CNI是Container Network Interface的是一个标准的,通用的接口。现在容器平台:docker,kubernetes,mesos,容器网络解决方案:flannel,calico,weave。只要提供一个标准的接口,就能为同样满足该协议的所有容器平台提供网络功能,而CNI正是这样的一个标准接口协议。
2、什么CNI
CNI用于连接容器管理系统和网络插件。提供一个容器所在的network namespace,将network interface插入该network namespace中(比如veth的一端),并且在宿主机做一些必要的配置(例如将veth的另一端加入bridge中),最后对namespace中的interface进行IP和路由的配置。
CNI的工作是从容器管理系统处获取运行时信息,包括network namespace的路径,容器ID以及network interface name,再从容器网络的配置文件中加载网络配置信息,再将这些信息传递给对应的插件,由插件进行具体的网络配置工作,并将配置的结果再返回到容器管理系统中。
3、怎么CNI
CNI插件是可执行文件,会被kubelet调用。启动kubelet --network-plugin=cni,--cni-conf-dir 指定networkconfig配置,默认路径是:/etc/cni/net.d,并且,--cni-bin-dir 指定plugin可执行文件路径,默认路径是:/opt/cni/bin;
CNI plugin 只需要通过 CNI 库实现两类方法, 一类事创建容器时调用, 一类是删除容器时调用.
(1)、编译安装CNI
官方提供了三种类型的插件:main,meta和ipam:
- main插件:提供某种网络功能,比如使用的brdige,以及loopback,ipvlan,macvlan等等
- meta插件:不能作为独立的插件使用,需要调用其他插件,例如flannel,或者配合其他插件使用,例如portmap
- ipam插件:对所有CNI插件共有的IP管理部分的抽象,从而减少插件编写过程中的重复工作,官方提供的有dhcp和host-local两种类型
git clone https://github.com/containernetworking/plugins.git
./build.sh
(2)、创建配置文件
工作目录/etc/cni/net.d默认的网络配置文件目录,从中加载配置文件进行容器网络的创建
# cat >/etc/cni/net.d/10-mynet.conflist <<EOF
{
"cniVersion": "0.3.0",
"name": "mynet",
"plugins": [
{
"type": "bridge",
"bridge": "cni0",
"isGateway": true,
"ipMasq": true,
"ipam": {
"type": "host-local",
"subnet": "10.22.0.0/16",
"routes": [
{ "dst": "0.0.0.0/0" }
]
}
}
]
}
EOF
# cat >/etc/cni/net.d/99-loopback.conf <<EOF
{
"cniVersion": "0.3.0",
"type": "loopback"
}
EOF
- cniVersion(string):指定了插件使用的CNI版本
- name (string):Network name。这应该在整个管理域中都是唯一的
- type (string):插件类型,也代表了CNI插件可执行文件的文件名
- args (dictionary):由容器运行时提供的可选的参数。比如,可以将一个由label组成的dictionary传递给CNI插件,通过在args下增加一个labels字段来实现
- ipMasqs (boolean):可选项(如果插件支持的话)。为network在宿主机创建IP masquerade。如果需要将宿主机作为网关,为了能够路由到容器分配的IP,这个字段是必须的
- ipam:由特定的IPAM值组成的dictionary
- type (string):IPAM插件的类型,也表示IPAM插件的可执行文件的文件名
- dns:由特定的DNS值组成的dictionary
- nameservers (list of strings):一系列对network可见的,以优先级顺序排列的DNS nameserver列表。列表中的每一项都包含了一个IPv4或者一个IPv6地址
- domain (string):用于查找short hostname的本地域
- search (list of strings):以优先级顺序排列的用于查找short domain的查找域。对于大多数resolver,它的优先级比domain更高
- options(list of strings):一系列可以被传输给resolver的可选项
(3)、模拟CNI的执行过程,创建network namespace
cnitool是一个模拟程序,创建一个ns的network namespace,模拟一个新创建的容器,再调用cnitool对该network namespace进行网络配置,从而模拟一个新建的容器加入一个容器网络的过程
git clone https://github.com/containernetworking/cni.git
./build.sh
export CNI_PATH=$GOPATH/src/github.com/containernetworking/plugins/bin
ip netns add ns
# ./cnitool add mynet /var/run/netns/ns
{
"cniVersion": "0.3.0",
"interfaces": [
{
"name": "cni0",
"mac": "f2:62:59:82:c0:b7"
},
{
"name": "veth3ad23ebb",
"mac": "ba:d8:65:00:23:92"
},
{
"name": "eth0",
"mac": "fa:c8:0f:b8:83:3f",
"sandbox": "/var/run/netns/ns"
}
],
"ips": [
{
"version": "4",
"interface": 2,
"address": "10.22.0.2/16",
"gateway": "10.22.0.1"
}
],
"routes": [
{
"dst": "0.0.0.0/0"
}
],
"dns": {}
}
cnitool的执行结果,返回一个包含了interface,IP,路由等等各种信息的
# ip netns exec ns ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 10.22.0.2 netmask 255.255.0.0 broadcast 0.0.0.0
ether fa:c8:0f:b8:83:3f txqueuelen 0 (Ethernet)
RX packets 2 bytes 84 (84.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 1 bytes 42 (42.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
4、源码分析
(1)、kubelet启动时,调用InitNetworkPlugin初始化网络插件
plug, err := network.InitNetworkPlugin(kubeDeps.NetworkPlugins, crOptions.NetworkPluginName, &criNetworkHost{&networkHost{klet}, &network.NoopPortMappingGetter{}}, hairpinMode, nonMasqueradeCIDR, int(crOptions.NetworkPluginMTU))
if err != nil {
return nil, err
}
参考:
https://github.com/keontang/k8s-notes/blob/master/kubernetes-network.md