CNI網絡模型
隨着容器技術在企業生產系統中的逐步落地,跨主機容器間的網絡互通已經成為基本要求,更高的要求包括容器固定IP地址、一個容器多個IP地址、多個子網隔離、ACL控制策略、與SDN集成等。目前主流的容器網絡模型主要有Docker公司提出的Container Network Model(CNM)模型和CoreOS公司提出的Container Network Interface(CNI)模型。
CNM模型
CNM模型主要通過Network Sandbox、Endpoint和Network這三個組件進行實現。
- Network Sandbox:容器內部的網絡棧,包括網絡接口、路由 表、DNS等配置的管理。Sandbox可用Linux網絡命名空間、FreeBSD Jail等機制進行實現。一個Sandbox可以包含多個Endpoint。
- Endpoint:用於將容器內的Sandbox與外部網絡相連的網絡接 口。可以使用veth對、Open vSwitch的內部port等技術進行實現。一個 Endpoint僅能夠加入一個Network。
- Network:可以直接互連的Endpoint的集合。可以通過Linux網 橋、VLAN等技術進行實現。一個Network包含多個Endpoint。
CNI模型
CNI是由CoreOS公司提出的另一種容器網絡規范,讓各個容器管理平台(k8s,mesos等)都可以通過相同的接口調用各式各樣的網絡插件來為容器配置網絡。
CNI定義的是容器運行環境與網絡插件之間的簡單接口規范,通過 一個JSON Schema定義CNI插件提供的輸入和輸出參數。一個容器可以通過綁定多個網絡插件加入多個網絡中。
CNI規范概述
CNI提供了一種應用容器的插件化網絡解決方案,定義對容器網絡進行操作和配置的規范,通過插件的形式對CNI接口進行實現。CNI僅關注在創建容器時分配網絡資源,和在銷毀容器時刪除 網絡資源,這使得CNI規范非常輕巧、易於實現,得到了廣泛的支持。
CNI規范的一些要點:
- CNI規范為一個容器定義一個Linux網絡命名空間
- CNI的網絡定義存儲為JSON格式
- 網絡定義通過STDIN輸入流傳輸到插件,這意味着宿主機上不會存儲網絡配置文件
- 其他的配置參數通過環境變量傳遞給插件
- CNI插件為可執行文件
- CNI插件負責連通容器網絡,它要完成所有的工作才能使容器連入網絡
- CNI插件負責調用IPAM插件
CNI模型只涉及兩個概念:容器和網絡
- 容器(Container):是擁有獨立Linux網絡命名空間的環境, 例如使用Docker或rkt創建的容器。關鍵之處是容器需要擁有自己的 Linux網絡命名空間,這是加入網絡的必要條件。
- 網絡(Network):表示可以互連的一組實體,這些實體擁有 各自獨立、唯一的IP地址,可以是容器、物理機或者其他網絡設備(比 如路由器)等。
對容器網絡的設置和操作都通過插件(Plugin)進行具體實現, CNI插件包括兩種類型:CNI Plugin和IPAM(IP Address Management) Plugin。CNI Plugin負責為容器配置網絡資源,IPAM Plugin負責對容器 的IP地址進行分配和管理。IPAM Plugin作為CNI Plugin的一部分,與 CNI Plugin一起工作。
CNI Plugin插件詳解
CNI Plugin包括3個基本接口的定義:添加(ADD)、刪除 (DELETE)、檢查(CHECK)和版本查詢(VERSION)。這些接口的具體實現要求插件提供一個可執行的程序,在容器網絡添加或刪除時進行調用,以完成具體的操作。比如添加接口的參數有Version、ContanierID、Network namespace、Network configuration、Extra arguments、Name of the interface inside the container。
網絡配置參數(Network configuration)則由一個JSON報文組成,以標准輸入(stdin)的方 式傳遞給可執行程序。下面例子定義了一個名為dbnet的網絡配置參數:
{
"cniVersion": "0.4.0", //cni版本號
"name": "dbnet", //網絡名稱,應在一個管理域內唯一
"type": "bridge", //CNI插件的可執行文件的名稱
"args": "", //其他參數
"ipam": { //IP地址管理的相關配置
"type": "host-local", //IPAM可執行文件名
"subnet": "10.1.0.0.0/16",
"gateway": "10.1.0.1"
},
"dns": { //dns服務相關配置
"nameservers": ["10.1.0.1"],
"domain": "" //本地域名
"search": "" //按優先級排序的域名查詢列表
"options": "" //傳遞給resolver的選項列表
}
}
CNI插件應能夠支持通過環境變量和標准輸入傳入參數。可執行文 件通過網絡配置參數中的type字段標識的文件名在環境變量CNI_PATH 設定的路徑下進行查找
IPAM Plugin插件
為了減輕CNI Plugin對IP地址管理的負擔,在CNI規范中設置了一個 新的插件專門用於管理容器的IP地址(還包括網關、路由等信息),被稱為IPAM Plugin。通常由CNI Plugin在運行時自動調用IPAM Plugin完 成容器IP地址的分配。
如果成功完成了容器IP地址的分配,則IPAM插件應該通過標准輸 出(stdout)返回以下JSON報文:
{
"cniVerison": "0.4.0",
"ips": [
{
"version": "<4-or-6>",
"address": "<ip-and-prefix-in-CIDR>"
"gateway": "<ip-address-of-the-gateway>" //optional
},
//......
],
"routes": [
{
"dst": "ip-and-prefix-in-cidr",
"gw": "<ip-of-next-hop>" //optional
},
//......
],
"dns": {
"nameservers": "<list-of-nameservers>" //optional
"domain": "<name-of-local-domain>" //optional
"search": "<list-of-search-domains>" //optional
"options": "<list-of-options>" //optional
}
}
多網絡插件
在很多情況下,一個容器需要連接多個網絡,CNI規范支持為一個 容器運行多個CNI Plugin來實現這個目標。多個網絡插件將按照網絡配置列表中的順序執行,並將前一個網絡配置的執行結果傳遞給后面的網絡配置。
下面的例子定義了兩個網絡配置參數,分別作用於兩個插件,第1 個為bridge,第2個為tuning。CNI將首先執行第1個bridge插件設置容器的網絡,然后執行第2個tuning插件:
{
"cniVersion": "0.4.0",
"name": "dbnet",
"plugin": [
{
"type": "bridge",
"args": {
"labels": {
"appVersion": "1.0"
}
},
"ipam": {
"type": "host-local",
"subnet": "10.1.0.0/16"
"gateway": "10.1.0.1"
},
"dns": {
"nameservers": ["10.1.0.1"]
}
},
{
"type": "tuning",
"sysctl": {
"net.core.somaxconn": "500"
}
}
]
}
在Kubernetes中使用網絡插件
CNI插件是可執行文件,會被kubelet調用。Kubernetes目前支持兩種網絡插件的實現:
- CNI插件:根據CNI規范實現其接口,以與插件提供者進行對接。
- kubenet插件:使用bridge和host-local CNI插件實現一個基本的cbr0。
為了在k8s集群中使用網絡插件,需要在kubelet服務的啟動參數上設置下面兩個參數
- --network-plugin-dir:kubelet啟動時掃描網絡插件的目錄
- --network-plugin:網絡插件名稱,對於CNI插件為cni(無須關注--network-plugin-dir路徑),kubenet插件為kubenet。
設置為cni時,還需要兩個參數--cni-conf-dir
(默認/etc/cni/net.d)和--cni-bin-dir
(默認/opt/cni/bin),作用看名字就知道。
目前已有多個開源項目支持以CNI網絡插件的形式部署到Kubernetes集群中,進行Pod的網絡設置和網絡策略的設置。
使用CNI插件的簡單示例
Docker有自己的CNM標准,那我們可以將CNI與Docker一起使用嗎?答案是肯定的,但這不是個完整的解決方案。CNI插件負責連接容器,因此有可能只是用Docker的容器運行時,而不調用Docker的網絡端的工作。
CNI的工作是從容器管理系統處獲取運行時信息,包括network namespace的路徑,容器ID以及network interface name,再從容器網絡的配置文件中加載網絡配置信息,再將這些信息傳遞給對應的插件,由插件進行具體的網絡配置工作,並將配置的結果再返回到容器管理系統中。
下載CNI二進制文件
# -O 參數來告訴 curl 保存為文件,使用 -L 參數來允許 curl 跟隨重定向
curl -O -L https://github.com/containernetworking/cni/releases/download/v0.4.0/cni-amd64-v0.4.0.tgz
tar -xzvf cni-amd64-v0.4.0.tgz
讓我們聚焦到網絡插件bridge文件,Bridge是CNI官網插件之一,它的工作是將容器依附到網橋接口上。網絡配置是通過STDIN流傳輸到插件中的,其他信息通過環境變量傳遞到插件。
首先定義一個網橋的網絡配置文件
cat > mybridge.conf <<"EOF"
{
"cniVersion": "0.2.0",
"name": "mybridge",
"type": "bridge",
"bridge": "cni_bridge0",
"isGateway": true,
"ipMasq": true,
"ipam": {
"type": "host-local",
"subnet": "10.15.20.0/24",
"routes": [
{ "dst": "0.0.0.0/0" },
{ "dst": "1.1.1.1/32", "gw":"10.15.20.1"}
]
}
}
EOF
除了可以設置系統級別的環境變量外,我們還可以把環境變量直接傳遞給命令。
sudo CNI_COMMAND=ADD CNI_CONTAINERID=xingns CNI_NETNS=/var/run/netns/xingns CNI_IFNAME=eth12 CNI_PATH=`pwd` ./bridge < mybridge.conf
- CNI_COMMAND = ADD:告訴CNI要添加連接
- CNI_CONTAINER = xingns:CNI要使用xingns網絡命名空間
- CNI_NETNS = /var/run/netns/xingns:網絡命名空間路徑
- CNI_IFNAME = eth12:命名空間里使用的網絡接口名
- CNI_PATH =
pwd
:CNI插件的可執行文件路徑
在運行命令前,我們還需要創建插件將要配置的網絡命名空間,通常容器運行時會自動創建命名空間,但由於我們是自己手動實驗,所以得自己創建。
ip netns add xingns
創建完成后,運行插件
執行完后返回兩部分輸出
- 由於IPAM找不到本地存儲的保留IP分配信息文件,因此返回錯誤。如果我們對其他網絡命名空間再次運行此命令,則不會出現此錯誤了,因為該文件在我們首次運行插件時創建了。
- 其次是返回一個JSON格式的IP配置,在本例中,網橋本身配置為10.15.20.1/24的IP,而網絡命名空間接口將會分配到10.15.20.2/24,它還設置了默認網關和我們在網絡配置JSON中定義的1.1.1.1/32路由。
我們現在又了一個名為cni_bridge0
的網橋接口,接口IP也和我們預期的一致,注意底部有veth設備的一端。我們還啟用了ipMasq,(是否為該網絡配置出站地址轉換)如果我們查看主機的iptables,將看到如下規則...
在看一下網絡命名空間:

網絡配置和預期的也一致,命名空間有一個名為”eth12”的網絡接口,其IP地址為10.15.20.2/24,我們之前定義的路由也在那里。
參考:
《Kubernetes權威指南》
http://www.dasblinkenlichten.com/understanding-cni-container-networking-interface/