在上一篇文章中 《「深刻淺出」來解讀Docker網絡核心原理》 你們瞭解了Docker中libnetwrok提供的4種驅動,它們各有千秋,但實際上每一種方式都有必定的侷限性。假設須要運營一個數據中心的網絡,咱們有許多的宿主機,每臺宿主機上運行了成千上萬個Docker容器,若是使用4種網絡驅動的話會是怎麼樣的呢,咱們來分析一下:linux
可見,爲了實現數據中心大量容器間的跨主機網絡通訊,爲了更靈活地實現容器間網絡的共享與隔離,也爲了在管理成千上萬個容器時能夠更加自動化地進行網絡配置,咱們須要來了解更高級的網絡方案。git
本文及後期的文章將經過一些工具和額外的操做來突破Docker網絡原有的限制,實現一些更高級的功能來知足實際運用中的複雜需求。github
在上一篇文章中已經介紹過了linux network namespace,在本文中咱們將從實踐的角度來了解如何在linux系統下操做linux network namespace。docker
ip是linux系統下一個強大的網絡配置工具,它不只能夠替代一些傳統的網絡管理工具,如ifconfig、route等,還能夠實現更豐富的功能。下面將介紹如何使用ip命令來管理network namespace。shell
ip netns命令是用來操做network namespace的指令,具體使用方法以下。安全
建立一個network namespace:bash
#建立一個名爲net-test的network namespace [root@ganbing ~]# ip netns add net-test
列出系統中已存在的network namespace:服務器
[root@ganbing ~]# ip netns ls net-test
刪除一個network namespace:網絡
[root@ganbing ~]# ip netns delete net-test
#命令格式
ip netns exec <network nameapce name> <command>ide
#好比顯示net-test namespace的網卡信息,路由信息
[root@ganbing ~]# ip netns exec net-test ip addr 1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 [root@ganbing ~]# ip netns exec net-test route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface
其實,你若是以爲ip netns exec 來執行命令比較麻煩,還可使用啓動一個shell來配合:
#命令格式
ip netns exec <network nameapce name> bash
這樣,就能夠在上面執行命令,就好像使用者進入了這個network namespace中;若是要退出這個bash,則輸入exit便可。
當使用ip netns add命令建立了一個network namespace後,就擁有了一個獨立的網絡空間,能夠根據需求來配置該網絡空間,如添加網卡,配置IP,設置路由等。下面以以前創建的名爲net-test的network namespace爲例來演示如何進行這些操做。
當使用ip命令建立一個network namespace時,會默認建立一個迴環設備(loopback interface:lo)。該設備默認不啓動,最好將其啓動。
[root@ganbing ~]# ip netns exec net-test ip link set dev lo up
在主機上建立兩張虛擬網卡veth-1 和 veth-2:
[root@ganbing ~]# ip link add veth-1 type veth peer name veth-2
將veth-2設備添加到net-test這個network namespace中,veth-1留在宿主機中:
[root@ganbing ~]# ip link set veth-2 netns net-test
如今net-test這個network namespace就有兩塊網卡了(lo和veth-2),驗證看一下:
[root@ganbing ~]# ip netns exec net-test ip link 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 222: veth-2@if223: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT qlen 1000 link/ether 92:24:fd:44:c6:00 brd ff:ff:ff:ff:ff:ff link-netnsid 0
接下來能夠爲網卡分配IP並啓動網卡:
#在主機上爲veth-1配置IP並啓動 [root@ganbing ~]# ip addr add 10.0.0.1/24 dev veth-1 [root@ganbing ~]# ip link set dev veth-1 up #爲net-test中的veth-2配置IP並啓動 [root@ganbing ~]# ip netns exec net-test ip addr add 10.0.0.2/24 dev veth-2 [root@ganbing ~]# ip netns exec net-test ip link set dev veth-2 up
給兩張網卡配置了IP後,會在各自的network namespace中生成一條路由,用ip route 或者 route -n查看:
#在主機中查看路由 [root@ganbing ~]# route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface ... 10.0.0.0 0.0.0.0 255.255.255.0 U 0 0 0 veth-1 ... #在net-test中查看路由 [root@ganbing ~]# ip netns exec net-test route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 10.0.0.0 0.0.0.0 255.255.255.0 U 0 0 0 veth-2
上面這兩條路由代表的意義是目的地址 10.0.0.0/24網絡的IP包分別從veth-1和veth-2發出。
如今net-test這個network namespace有了本身的網卡、IP地址、路由表等信息,就至關於成了一臺小型的「虛擬機」了。測試一下它的連通性,來檢查配置是否正確。
從主機的veth-1網卡ping net-test的veth-2網卡:
從net-test的veth-2網卡ping主機的veth-1網卡:
不少時候,想搭建一個複雜的網絡環境來測試數據,每每受困於沒有足夠的資源來建立虛擬機。咱們掌握了配置network namespace後,即可以解決這個問題。能夠在一臺普通的機器上,以簡單的方式建立多個相互隔離的network namespace,而後經過網卡、網橋等虛擬設備將它們鏈接起來,組成想要的網絡拓撲。
下面咱們來演示一個簡單的例子,將兩個network namespace經過veth pair設備連起來。過程以下:
一、建立兩個network namespace ns一、ns2,名稱可自行定義:
[root@ganbing ~]# ip netns add ns1 [root@ganbing ~]# ip netns add ns2 [root@ganbing ~]# ip netns ls ns2 ns1
二、建立veth pair設備veth-a,veth-b:
[root@ganbing ~]# ip link add veth-a type veth peer name veth-b
三、將網卡分別放到兩個network namespace中:
[root@ganbing ~]# ip link set veth-a netns ns1 [root@ganbing ~]# ip link set veth-b netns ns2
四、啓動這兩個網張:
[root@ganbing ~]# ip netns exec ns1 ip link set dev lo up [root@ganbing ~]# ip netns exec ns1 ip link set dev veth-a up [root@ganbing ~]# ip netns exec ns2 ip link set dev lo up [root@ganbing ~]# ip netns exec ns2 ip link set dev veth-b up
五、分配IP:
[root@ganbing ~]# ip netns exec ns1 ip addr add 10.0.0.1/24 dev veth-a [root@ganbing ~]# ip netns exec ns2 ip addr add 10.0.0.2/24 dev veth-b
六、驗證連通
經過veth pair設備鏈接起來的兩個network namespace就好像直接經過網線鏈接起來的兩臺機器,它的拓撲圖以下所示:
你們想一下,若是有更多的network namespace須要鏈接怎麼辦?是否是就須要引入虛擬網橋了,就如同Docker網絡同樣。
在上一篇文章 <「深刻淺出」來解讀Docker網絡核心原理> 介紹過,Docker是使用Linux namespace技術進行資源隔離的,網絡也是如此。當用默認網絡模式(bridge模式)啓動一個Docker容器時,必定是在主機上新建了一個Linux network namespace。咱們能夠按照在network namespace中配置網絡的方法來配置Docker 容器的網絡。
首先,啓動一個名爲test1的Docker容器:
[root@ganbing ~]# docker run -itd --name test1 busybox
而後,使用ip netns list命令查看是否能夠看到新建的network namespace。執行命令後發現並無看到新建的network namespace。這並不表明Docker容器沒有建立network namespace,只是ip netns 命令沒法查看而以,這個與ip netns命令工做方式有關。
當使用ip netns命令建立了兩個network namespace(ns一、ns2)後,會在/var/run/netns目錄下看到ns1和ns2:
[root@ganbing ~]# ls -la /var/run/netns/ total 0 drwxr-xr-x 2 root root 80 Mar 19 18:25 . drwxr-xr-x 40 root root 1240 Mar 19 15:08 .. -r--r--r-- 1 root root 0 Mar 19 18:22 ns1 -r--r--r-- 1 root root 0 Mar 19 18:22 ns2
ip netns list命令在/var/run/netns目錄下查找network namespace。因爲Docker建立的network namespace並不在此目錄下建立任何選項,所以,須要一些額外的操做來使ip命令能夠操縱Docker建立的network namespace。
Linux下的每個進程都會屬於一個特定的network namespace,來看一下不一樣network namespace環境中/pro/$PID/ns目錄下有何區別。
#/proc/self 連接到當前正在運行的進程 [root@ganbing ~]# ls -la /proc/self/ns/ ...... lrwxrwxrwx 1 root root 0 Mar 19 19:17 net -> net:[4026531956] ...... #在ns1和ns2中 [root@ganbing ~]# ip netns exec ns1 ls -la /proc/self/ns ...... lrwxrwxrwx 1 root root 0 Mar 19 19:18 net -> net:[4026533018] ...... [root@ganbing ~]# ip netns exec ns2 ls -la /proc/self/ns lrwxrwxrwx 1 root root 0 Mar 19 19:18 net -> net:[4026533116]
從上面能夠發現,不一樣network namespace中的進程有不一樣的net:[]號碼發配。這些號碼錶明着不一樣的network namespace,擁有相同net:[]號碼的進程屬於同一個network namesapce。只要將表明Docker建立的network namesapce的文件連接到/var/run/netns目錄下,就可使用ip netns命令操做了,步驟方法以下:
一、用docker inspect查看test1容器的PID
[root@ganbing ~]# docker inspect --format '{{.State.Pid}}' test1 17037
二、若是/var/run/netns目錄不存在,就要手工建立(通常都有),而後在/var/run/netns目錄下建立軟連接,指向test1容器的network namespace
[root@ganbing ~]# ln -s /proc/17037/ns/net /var/run/netns/test1
三、測試是否成功
[root@ganbing ~]# ip netns list test1 ns2 ns1 [root@ganbing ~]# ip netns exec test1 ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever 226: eth0@if227: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0 valid_lft forever preferred_lft forever
完成上面的配置後,就能夠自行配置Docker的網絡環境了。除了ip netns命令外,還有一些工具能夠進入linux network namespace,好比nsenter。但須要額外的安裝這個工具。
Docker現有的網絡比較簡單,擴展性和靈活性都不能知足不少複雜的應用場景。不少時候都須要自定義Docker容器的網絡。好比,爲了使容器各節點之間通訊、各節點和本地主機之間的通訊,比較簡單的作法就是將Docker容器網絡配置到本地主機網絡的網段中。咱們來看一下怎麼實現。
若是想要使Docker容器和容器主機處於同一網絡,那麼容器和主機應該處在一個二層網絡中。就是把兩臺機器連在同一個交換機上,或者連在不一樣的級聯交換機上。在虛擬場影 下,虛擬網橋能夠將容器連在一個二層網絡中,只要將主機的網卡橋接到虛擬網橋中,就能將容器和主機的網絡連起來,再給Docker容器分配一個本地局域網IP就OK了。
咱們來通個一個例子分析一下這個過程 :本地網絡爲 172.18.18.0/24,網關爲 172.18.18.1,宿主機IP爲172.18.18.34(網卡ens160),要在這臺宿主機上啓動一個名爲test的Docker容器,並給它配置IP爲 172.18.18.36。因爲並不須要Docker提供的網絡,因此用--net=none參數來啓動容器。操做以下:
一、啓動一個test容器
[root@docker ~]# docker run -itd --name test01 --network none busybox 39ea5fac5ebb8bd25372d04efb6b662a18cd6fdf85105c22df0796087d776280
二、建立一個供容器鏈接的網橋br0
[root@docker ~]# brctl addbr br0 [root@docker ~]# ip link set dev br0
三、將主機ens160網卡橋接到br0上,並把ens160的IP配置在br0上。因爲筆者是遠程操做服務器,因此執行這一步的時候會致使網絡斷開,所以這裏放在一條命令執行
[root@docker ~]# ip addr add 172.18.18.34/24 dev br0; \ > ip addr del 172.18.18.34/24 dev ens160; \ > brctl addif br0 ens160; \ > ip route del default; \ > ip route add default via 172.18.18.1 dev br0
四、找到test01的PID
[root@docker ~]# docker inspect --format '{{.State.Pid}}' test01 4557
五、將容器的network namespace添加到/var/run/netns/目錄下
[root@docker ~]# mkdir /var/run/netns [root@docker netns]# ln -s /proc/4557/ns/net /var/run/netns/test01
六、建立用於鏈接網橋和Docker容器的網卡設備
#將veth-a鏈接到br0網橋中 [root@docker ~]# ip link add veth-a type veth peer name veth-b [root@docker ~]# brctl addif br0 veth-a [root@docker ~]# ip link set dev veth-a up #將veth-b放在test的network namespace中,重命令eth0,併爲其配置IP和默認路由 [root@docker ~]# ip netns exec test01 ip link set dev lo up [root@docker ~]# ip link set veth-b netns test01 [root@docker ~]# ip netns exec test01 ip link set dev veth-b name eth0 [root@docker ~]# ip netns exec test01 ip link set eth0 up [root@docker ~]# ip netns exec test01 ip addr add 172.18.18.36/24 dev eth0 [root@docker ~]# ip netns exec test01 ip route add default via 172.18.18.1
七、查看一下test01的網卡狀況,並測試
[root@docker ~]# ip netns exec test01 ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever 9: eth0@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP qlen 1000 link/ether 66:fa:71:ba:0e:fb brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 172.18.18.36/24 scope global eth0 valid_lft forever preferred_lft forever [root@docker ~]# ip netns exec test01 route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 172.18.18.1 0.0.0.0 UG 0 0 0 eth0 172.18.18.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
完成配置扣,Docker容器和宿主機鏈接的網絡圖以下所示:
如今test01容器能夠與本地主機相互訪問,而且test01容器能夠經過本地網絡的網關172.18.18.1訪問外部網絡。
從上面的過程能夠發現,配置Docker容器的網絡是至關繁瑣的。若是須要常常自定義Docker網絡,能夠把上面的步驟編寫成shell腳本,這樣方便操做。事實上,目前已有了一個這樣的工具解脫咱們繁瑣的步驟,就是由Docker公司工程師Jerome Petazzoni在Githu上發佈的pipework的工具。pipwork號稱是容器的SDN解決方案,能夠在復場景下將容器鏈接起來。其實隨着Docker網絡的不斷改進,piipwork工具的不少功能會被Docker原生支持,所以pipework當初只是過渡方案之一而以,你們只要知道了解就好了。下面來看一下pipework的功能。
* 支持linux網橋鏈接到容器並配置容器IP
一、下載pipework
[root@docker ~]# git clone https://github.com/jpetazzo/pipework
二、將pipework腳本放處指定的目錄,/usr/local/bin
[root@docker ~]# cp ./pipework/pipework /usr/local/bin/
三、對test01容器進行配置
[root@docker /]# docker run -itd --name test01 --network none busybox [root@docker /]# pipework br0 test01 172.18.18.36/24@172.18.18.1
上面配置命令操做以下:
這個過程和以前採用ip命令配置的過程相似,pipework其實就是用shell寫的代碼。
pipework其實還有其它的不少功能,好比還支持open vswitch、支持dhcp獲取容器的IP等等,本文只要是和你們瞭解一下它的做用和功能,其它詳細的功能就不做介紹了。
看到這裏的朋友應該對network namespace和pipework有了更好的理解