CNI网络插件指南
Container Network Interface (CNI) 最早是由CoreOS发起的容器网络规范,是Kubernetes网络插件的基础。其基本思想为:Container Runtime在创建容器时,先创建好network namespace,然后调用CNI插件为这个netns配置网络,其后再启动容器内的进程。现已加入CNCF,成为CNCF主推的网络模型。
CNI插件包括两部分:
-
CNI Plugin负责给容器配置网络,它包括两个基本的接口
- 配置网络: AddNetwork(net NetworkConfig, rt RuntimeConf) (types.Result, error)
- 清理网络: DelNetwork(net NetworkConfig, rt RuntimeConf) error
- IPAM Plugin负责给容器分配IP地址,主要实现包括host-local和dhcp。
所有CNI插件均支持通过环境变量和标准输入传入参数,如
$ echo '{"cniVersion": "0.3.1","name": "mynet","type": "macvlan","bridge": "cni0","isGateway": true,"ipMasq": true,"ipam": {"type": "host-local","subnet": "10.244.1.0/24","routes": [{ "dst": "0.0.0.0/0" }]}}' | sudo CNI_COMMAND=ADD CNI_NETNS=/var/run/netns/a CNI_PATH=./bin CNI_IFNAME=eth0 CNI_CONTAINERID=a CNI_VERSION=0.3.1 ./bin/bridge
$ echo '{"cniVersion": "0.3.1","type":"IGNORED", "name": "a","ipam": {"type": "host-local", "subnet":"10.1.2.3/24"}}' | sudo CNI_COMMAND=ADD CNI_NETNS=/var/run/netns/a CNI_PATH=./bin CNI_IFNAME=a CNI_CONTAINERID=a CNI_VERSION=0.3.1 ./bin/host-local
Bridge
Bridge是最简单的CNI网络插件,它首先在Host创建一个网桥,然后再通过veth pair连接该网桥到container netns。
注意,Bridge模式下,多主机网络通信需要额外配置主机路由。可以借助 Flannel 或者Quagga动态路由等来自动配置。
配置示例
{
"cniVersion": "0.3.0",
"name": "mynet",
"type": "bridge",
"bridge": "mynet0",
"isDefaultGateway": true,
"forceAddress": false,
"ipMasq": true,
"hairpinMode": true,
"ipam": {
"type": "host-local",
"subnet": "10.10.0.0/16"
# export CNI_PATH=/opt/cni/bin
# ip netns add ns
# /opt/cni/bin/cnitool add mynet /var/run/netns/ns
"interfaces": [
"name": "mynet0",
"mac": "0a:58:0a:0a:00:01"
"name": "vethc763e31a",
"mac": "66:ad:63:b4:c6:de"
"name": "eth0",
"mac": "0a:58:0a:0a:00:04",
"sandbox": "/var/run/netns/ns"
"ips": [
"version": "4",
"interface": 2,
"address": "10.10.0.4/16",
"gateway": "10.10.0.1"
"routes": [
"dst": "0.0.0.0/0",
"gw": "10.10.0.1"
"dns": {}
# ip netns exec ns ip addr
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
9: eth0@if8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 0a:58:0a:0a:00:04 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 10.10.0.4/16 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::8c78:6dff:fe19:f6bf/64 scope link tentative dadfailed
valid_lft forever preferred_lft forever
# ip netns exec ns ip route
default via 10.10.0.1 dev eth0
10.10.0.0/16 dev eth0 proto kernel scope link src 10.10.0.4
IPAM
DHCP
DHCP插件是最主要的IPAM插件之一,用来通过DHCP方式给容器分配IP地址,在macvlan插件中也会用到DHCP插件。
在使用DHCP插件之前,需要先启动dhcp daemon:
/opt/cni/bin/dhcp daemon &
然后配置网络使用dhcp作为IPAM插件
{
"ipam": {
"type": "dhcp",
host-local
host-local是最常用的CNI IPAM插件,用来给container分配IP地址。
IPv4:
{
"ipam": {
"type": "host-local",
"subnet": "10.10.0.0/16",
"rangeStart": "10.10.1.20",
"rangeEnd": "10.10.3.50",
"gateway": "10.10.0.254",
"routes": [
{ "dst": "0.0.0.0/0" },
{ "dst": "192.168.0.0/16", "gw": "10.10.5.1" }
"dataDir": "/var/my-orchestrator/container-ipam-state"
IPv6:
{
"ipam": {
"type": "host-local",
"subnet": "3ffe:ffff:0:01ff::/64",
"rangeStart": "3ffe:ffff:0:01ff::0010",
"rangeEnd": "3ffe:ffff:0:01ff::0020",
"routes": [
{ "dst": "3ffe:ffff:0:01ff::1/64" }
"resolvConf": "/etc/resolv.conf"
ptp
ptp插件通过veth pair给容器和host创建点对点连接:veth pair一端在container netns内,另一端在host上。可以通过配置host端的IP和路由来让ptp连接的容器之前通信。
{
"name": "mynet",
"type": "ptp",
"ipam": {
"type": "host-local",
"subnet": "10.1.1.0/24"
"dns": {
"nameservers": [ "10.1.1.1", "8.8.8.8" ]
IPVLAN
IPVLAN 和 MACVLAN 类似,都是从一个主机接口虚拟出多个虚拟网络接口。一个重要的区别就是所有的虚拟接口都有相同的 mac 地址,而拥有不同的 ip 地址。因为所有的虚拟接口要共享 mac 地址,所有有些需要注意的地方:
- DHCP 协议分配 ip 的时候一般会用 mac 地址作为机器的标识。这个情况下,客户端动态获取 ip 的时候需要配置唯一的 ClientID 字段,并且 DHCP server 也要正确配置使用该字段作为机器标识,而不是使用 mac 地址
IPVLAN支持两种模式:
- L2 模式:此时跟macvlan bridge 模式工作原理很相似,父接口作为交换机来转发子接口的数据。同一个网络的子接口可以通过父接口来转发数据,而如果想发送到其他网络,报文则会通过父接口的路由转发出去。
- L3 模式:此时ipvlan 有点像路由器的功能,它在各个虚拟网络和主机网络之间进行不同网络报文的路由转发工作。只要父接口相同,即使虚拟机/容器不在同一个网络,也可以互相 ping 通对方,因为 ipvlan 会在中间做报文的转发工作。注意 L3 模式下的虚拟接口 不会接收到多播或者广播的报文(这个模式下,所有的网络都会发送给父接口,所有的 ARP 过程或者其他多播报文都是在底层的父接口完成的)。另外外部网络默认情况下是不知道 ipvlan 虚拟出来的网络的,如果不在外部路由器上配置好对应的路由规则,ipvlan 的网络是不能被外部直接访问的。
创建ipvlan的简单方法为
ip link add link <master-dev> <slave-dev> type ipvlan mode { l2 | L3 }
cni配置格式为
{
"name": "mynet",
"type": "ipvlan",
"master": "eth0",
"ipam": {
"type": "host-local",
"subnet": "10.1.2.0/24"
需要注意的是
- ipvlan插件下,容器不能跟Host网络通信
- 主机接口(也就是master interface)不能同时作为ipvlan和macvlan的master接口
MACVLAN
MACVLAN可以从一个主机接口虚拟出多个macvtap,且每个macvtap设备都拥有不同的mac地址(对应不同的linux字符设备)。MACVLAN支持四种模式
- bridge模式:数据可以在同一master设备的子设备之间转发
- vepa模式:VEPA 模式是对 802.1Qbg 标准中的 VEPA 机制的软件实现,MACVTAP 设备简单的将数据转发到master设备中,完成数据汇聚功能,通常需要外部交换机支持 Hairpin 模式才能正常工作
- private模式:Private 模式和 VEPA 模式类似,区别是子 MACVTAP 之间相互隔离
- passthrough模式:内核的 MACVLAN 数据处理逻辑被跳过,硬件决定数据如何处理,从而释放了 Host CPU 资源
创建macvlan的简单方法为
ip link add link <master-dev> name macvtap0 type macvtap
cni配置格式为
{
"name": "mynet",
"type": "macvlan",
"master": "eth0",
"ipam": {