docker網絡2

http://blog.51cto.com/ganbing/2088899

前言

在上一篇文章中 《“深入淺出”來解讀Docker網絡核心原理》 大家了解了Docker中libnetwrok提供的4種驅動,它們各有千秋,但實際上每一種方式都有一定的局限性。假設需要運營一個數據中心的網絡,我們有許多的宿主機,每臺宿主機上運行了成千上萬個Docker容器,如果使用4種網絡驅動的話會是怎么樣的呢,我們來分析一下:

  1. 使用host驅動可以讓容器與宿主機共用同一個網絡棧,這么做看似解決了網絡問題,可實際上并未使用network namespace的隔離,缺乏安全性。
  2. 使用Docker默認的bridge驅動,容器沒有對外IP,只能通過NAT來實現對外通信。這種方式不能解決跨主機容器間直接通信的問題,難以滿足復雜場景下的業務需求。
  3. 使用overlay驅動,可以用于支持跨主機的網絡通信,但必須配合swarm進行配置和使用才能實現跨主機的網絡通信。
  4. 使用null驅動實際上不進行任何網絡配置。

可見,為了實現數據中心大量容器間的跨主機網絡通信,為了更靈活地實現容器間網絡的共享與隔離,也為了在管理成千上萬個容器時可以更加自動化地進行網絡配置,我們需要來了解更高級的網絡方案。

本文及后期的文章將通過一些工具和額外的操作來突破Docker網絡原有的限制,實現一些更高級的功能來滿足實際運用中的復雜需求。

把Linux network namespace玩起來

在上一篇文章中已經介紹過了linux network namespace,在本文中我們將從實踐的角度來了解如何在linux系統下操作linux network namespace。

ip是linux系統下一個強大的網絡配置工具,它不僅可以替代一些傳統的網絡管理工具,如ifconfig、route等,還可以實現更豐富的功能。下面將介紹如何使用ip命令來管理network namespace。

使用ip netns來操作network namespace

ip netns命令是用來操作network namespace的指令,具體使用方法如下。

  • 創建一個network namespace:

    #創建一個名為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
    
  • 在network namespace中執行一條命令:

命令格式

ip netns exec <network nameapce name> <command>

比如顯示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為network namespace配置網卡

當使用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網卡:


Docker高級網絡實踐之 玩轉Linux network namespace & pipework

從net-test的veth-2網卡ping主機的veth-1網卡:


Docker高級網絡實踐之 玩轉Linux network namespace & pipework

將兩個network namespace連接起來

很多時候,想搭建一個復雜的網絡環境來測試數據,往往受困于沒有足夠的資源來創建虛擬機。我們掌握了配置network namespace后,便可以解決這個問題。可以在一臺普通的機器上,以簡單的方式創建多個相互隔離的network namespace,然后通過網卡、網橋等虛擬設備將它們連接起來,組成想要的網絡拓撲。

下面我們來演示一個簡單的例子,將兩個network namespace通過veth pair設備連起來。過程如下:

1、創建兩個network namespace ns1、ns2,名稱可自行定義:

[root@ganbing ~]# ip netns add ns1
[root@ganbing ~]# ip netns add ns2
[root@ganbing ~]# ip netns ls
ns2
ns1

2、創建veth pair設備veth-a,veth-b:

[root@ganbing ~]# ip link add veth-a type veth peer name veth-b

3、將網卡分別放到兩個network namespace中:

[root@ganbing ~]# ip link set veth-a netns ns1
[root@ganbing ~]# ip link set veth-b netns ns2

4、啟動這兩個網張:

[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

5、分配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

6、驗證連通


Docker高級網絡實踐之 玩轉Linux network namespace & pipework

通過veth pair設備連接起來的兩個network namespace就好像直接通過網線連接起來的兩臺機器,它的拓撲圖如下所示:


Docker高級網絡實踐之 玩轉Linux network namespace & pipework

大家想一下,如果有更多的network namespace需要連接怎么辦?是不是就需要引入虛擬網橋了,就如同Docker網絡一樣。

使用ip命令配置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(ns1、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命令操作了,步驟方法如下:

1、用docker inspect查看test1容器的PID

[root@ganbing ~]# docker inspect  --format '{{.State.Pid}}' test1
17037

2、如果/var/run/netns目錄不存在,就要手工創建(一般都有),然后在/var/run/netns目錄下創建軟鏈接,指向test1容器的network namespace

[root@ganbing ~]# ln -s /proc/17037/ns/net  /var/run/netns/test1

3、測試是否成功

[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。但需要額外的安裝這個工具。

把pipework玩起來

Docker現有的網絡比較簡單,擴展性和靈活性都不能滿足很多復雜的應用場景。很多時候都需要自定義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參數來啟動容器。操作如下:

1、啟動一個test容器

[root@docker ~]# docker run -itd --name test01 --network none busybox
39ea5fac5ebb8bd25372d04efb6b662a18cd6fdf85105c22df0796087d776280

2、創建一個供容器連接的網橋br0

[root@docker ~]# brctl addbr br0
[root@docker ~]# ip link set dev br0

3、將主機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

4、找到test01的PID

[root@docker ~]# docker inspect  --format '{{.State.Pid}}' test01
4557

5、將容器的network namespace添加到/var/run/netns/目錄下

[root@docker ~]# mkdir /var/run/netns
[root@docker netns]# ln -s /proc/4557/ns/net /var/run/netns/test01

6、創建用于連接網橋和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

7、查看一下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容器和宿主機連接的網絡圖如下所示:


Docker高級網絡實踐之 玩轉Linux network namespace & pipework

現在test01容器可以與本地主機相互訪問,并且test01容器可以通過本地網絡的網關172.18.18.1訪問外部網絡。

來解析一下pipework

從上面的過程可以發現,配置Docker容器的網絡是相當繁瑣的。如果需要經常自定義Docker網絡,可以把上面的步驟編寫成shell腳本,這樣方便操作。事實上,目前已有了一個這樣的工具解脫我們繁瑣的步驟,就是由Docker公司工程師Jerome Petazzoni在Githu上發布的pipework的工具。pipwork號稱是容器的SDN解決方案,可以在復場景下將容器連接起來。其實隨著Docker網絡的不斷改進,piipwork工具的很多功能會被Docker原生支持,因此pipework當初只是過渡方案之一而以,大家只要知道了解就行了。下面來看一下pipework的功能。

*** 支持linux網橋連接到容器并配置容器IP**
1、下載pipework

[root@docker ~]# git clone https://github.com/jpetazzo/pipework

2、將pipework腳本放處指定的目錄,/usr/local/bin

[root@docker ~]# cp ./pipework/pipework /usr/local/bin/

3、對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

上面配置命令操作如下:

  • 查看主機是否存在br0網橋,不存在就創建;
  • 向test01中加入一塊名為eth1的網卡,并配置IP172.18.18.36/24;
  • 若test01中已有默認路由,就刪除,把172.18.18.1設為默認路由l
  • 將test01容器連接到之前創建的網橋上br0;

這個過程和之前采用ip命令配置的過程類似,pipework其實就是用shell寫的代碼。

pipework其實還有其它的很多功能,比如還支持open vswitch、支持dhcp獲取容器的IP等等,本文只要是和大家了解一下它的作用和功能,其它詳細的功能就不作介紹了。

看到這里的朋友應該對network namespace和pipework有了更好的理解

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,117評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,860評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,128評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,291評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,025評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,421評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,477評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,642評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,177評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,970評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,157評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,717評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,410評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,821評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,053評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,896評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,157評論 2 375

推薦閱讀更多精彩內容

  • docker之容器通信 這節屬于了解學習,算是爛尾,最后我也沒找到合適的方式去固定容器ip,然后作為正式環境去跑,...
    道無虛閱讀 5,511評論 1 7
  • 轉載自 http://blog.opskumu.com/docker.html 一、Docker 簡介 Docke...
    極客圈閱讀 10,520評論 0 120
  • 概述 自從docker容器出現以來,容器的網絡通信就一直是大家關注的焦點,也是生產環境的迫切需求。而容器的網絡通信...
    糙老爺們兒吃什么櫻桃閱讀 3,639評論 1 5
  • 昨夜繁星月下 我嗅到了都快已淡忘的芳香 依然是那么迷人與醉人心魂 回頭望去空無一物 突然覺得還是自己太敏感 望著空...
    窮奇閱讀 195評論 0 0
  • 沒有什么能夠阻擋你對自由的向往 即便是死亡也是你另一種重生 ——現在開頭 你的心里住著一個國王 生活在不眠的都市 ...
    郁衡子閱讀 420評論 7 2