Container Networking Interface 入門簡介

CNI 簡介

John-Lin
8 min readJul 13, 2017

Container Networking Inteferface (以下簡稱 CNI)是一個由 CoreOS 所提出的一個容器網路規範並開發了一個專案 containernetworking/cni ,他的核心目標是針對容器提出一個解決方案將網路功能插件化(模組化)。

詳細的 CNI 規格說明可以參考: cni/SPEC

當啟用一個 CNI 插件,運作流程是在創建容器時,調用這個 CNI 插件並為這個容器配置網路設定,最後再啟動容器內的 Process ,最後當容器結束前 CNI 插件會將網路功能終止再清除設定。

目前這個網路模型已經廣泛的受到社群的認可,例如知名的容器集群管理平台 Kubernetes 即是支援這種 CNI 容器網路規範,目前依不同需求,或不同社群及偏好可以在 Kubernetes 上選用不同插件像是 Calico, Weave, SRIOV, Ciliuim, Canal 和 Flannel ,而上述的插件都是遵循 CNI 容器網路規範,所開發而來。

CNI 基本使用方法

既然提到容器,我們可以回到容器的最基本核心概念,Linux Network Namespace 它提供了一個獨立的網路環境,包括網卡、路由、iptables 規則等都與其他的 Network Namespace 隔離。而最基礎操作 Network Namespace 方法可以透過 netns 來創建或刪除。對於想了解 netns 如何使用的朋友,不妨參考 手把手打造仿 mininet 網路 的 Step2。

接著我們要做的步驟大致如下:

  1. 創建 Network Namespace
  2. 建立一個 network configuration
  3. 運行 CNI Plugins 餵入 network configuration(本例將使用 linux bridge 的 plugin)
  4. 測試網路互通性

在 Ubuntu 64bit 系統上實驗,在開始之前,我們先將 CNI 插件備齊

$ mkdir myCNI
$ cd myCNI
$ curl -O -L https://github.com/containernetworking/cni/releases/download/v0.5.2/cni-amd64-v0.5.2.tgz
$ tar -xzvf cni-amd64-v0.5.2.tgz
$ ls
bridge cni-amd64-v0.5.2.tgz cnitool dhcp flannel host-local ipvlan loopback macvlan noop ptp tuning

這裡面的 network plugin binary 分別有不同的網路功能,我們將在這篇用到 bridgehost-local

Step1. 取得 root 權限後,透過 netns創建兩個 network namespace

$ ip netns add ns1
$ ip netns add ns2

step2. 建立一個 network configuration

cat > mybridge.conf <<"EOF"
{
"name": "mybridge",
"type": "bridge",
"bridge": "kbr0",
"isGateway": true,
"isDefaultGateway": true,
"ipMasq": true,
"ipam": {
"type": "host-local",
"subnet": "10.244.0.0/16",
"routes": [
{ "dst": "0.0.0.0/0" }
],
"gateway": "10.244.1.1"
}
}
EOF

這個 configuration 十分重要,它除了定義 bridge 上的設定,也定義了容器接上 bridge 所配發的 IP 及所屬的子網域。稍微指出幾個比較特別的欄位:

  1. type : 是用來指定其對應的 binary 檔案名稱
  2. ipam : 又作 IP Allocation 是用來分配容器IP及切割子網域,注意到裡面又有一個 type 呼叫 host-local

詳細的 bridge 個欄位設定資料可以參考: https://github.com/containernetworking/plugins/tree/master/plugins/main/bridge#network-configuration-reference

Step3. 運行 CNI Plugins 餵入 network configuration

  1. CNI_COMMAND 指定 action (目前CNI套件有 ADDDEL 兩個command);
  2. CNI_CONTAINERID 給予 namespace name;
  3. CNI_NETNS mount 路徑;
  4. CNI_IFNAME 創建容器內部 interface 名稱
  5. CNI_PATH 告訴 CNI plugin 的所在位置(目前剛好在myCNI目錄裡面所以用 pwd
$ CNI_COMMAND=ADD CNI_CONTAINERID=ns1 CNI_NETNS=/var/run/netns/ns1 CNI_IFNAME=eth3 CNI_PATH=`pwd` ./bridge <mybridge.conf
{
"ip4": {
"ip": "10.244.0.1/16",
"gateway": "10.244.1.1",
"routes": [
{
"dst": "0.0.0.0/0",
"gw": "10.244.1.1"
},
{
"dst": "0.0.0.0/0",
"gw": "10.244.1.1"
}
]
},
"dns": {}
}
$ CNI_COMMAND=ADD CNI_CONTAINERID=ns2 CNI_NETNS=/var/run/netns/ns2 CNI_IFNAME=eth3 CNI_PATH=`pwd` ./bridge <mybridge.conf
{
"ip4": {
"ip": "10.244.0.2/16",
"gateway": "10.244.1.1",
"routes": [
{
"dst": "0.0.0.0/0",
"gw": "10.244.1.1"
},
{
"dst": "0.0.0.0/0",
"gw": "10.244.1.1"
}
]
},
"dns": {}
}

Step4. 最後可以由 ip netns exec ${name} ifconfg 來看容器所分配的 IP 進行互通測試。

$ip netns exec ns1 ifconfig
eth3 Link encap:Ethernet HWaddr 0a:58:0a:f4:00:01
inet addr:10.244.0.1 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::903f:40ff:fea6:adc4/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:23 errors:0 dropped:0 overruns:0 frame:0
TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:1854 (1.8 KB) TX bytes:648 (648.0 B)
$ip netns exec ns2 ifconfig
eth3 Link encap:Ethernet HWaddr 0a:58:0a:f4:00:02
inet addr:10.244.0.2 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::10e0:5aff:feca:442a/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:8 errors:0 dropped:0 overruns:0 frame:0
TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:648 (648.0 B) TX bytes:648 (648.0 B)
$ ip netns exec ns2 ping 10.244.0.1
PING 10.244.0.1 (10.244.0.1) 56(84) bytes of data.
64 bytes from 10.244.0.1: icmp_seq=1 ttl=64 time=0.360 ms
64 bytes from 10.244.0.1: icmp_seq=2 ttl=64 time=0.093 ms
64 bytes from 10.244.0.1: icmp_seq=3 ttl=64 time=0.086 ms

這篇算是個入門的操作簡介,讓大家更快瞭解 CNI 的使用,如果有在使用 Docker container 也是可以透過類似相同的手法來操作將 CNI 的 Plugin 接上,只要取得 container ID 及 network namespace 的 mount 路徑,即可做到一樣的操作方法。

未來幾篇會再介紹 CNI 怎麼跟 Docker 一起使用,以及最近我正在開發的 CNI Plugin 開發的動機與使用方法。

--

--