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

前言

 在上一篇文章中 《「深刻淺出」來解讀Docker網絡核心原理》 你們瞭解了Docker中libnetwrok提供的4種驅動,它們各有千秋,但實際上每一種方式都有必定的侷限性。假設須要運營一個數據中心的網絡,咱們有許多的宿主機,每臺宿主機上運行了成千上萬個Docker容器,若是使用4種網絡驅動的話會是怎麼樣的呢,咱們來分析一下:linux

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

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

 本文及後期的文章將經過一些工具和額外的操做來突破Docker網絡原有的限制,實現一些更高級的功能來知足實際運用中的複雜需求。github

把Linux network namespace玩起來

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

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

使用ip netns來操做network namespace

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
  • 在network namespace中執行一條命令:

#命令格式
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爲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設備連起來。過程以下:

一、建立兩個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

六、驗證連通
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(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。但須要額外的安裝這個工具。

把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參數來啓動容器。操做以下:

一、啓動一個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容器和宿主機鏈接的網絡圖以下所示:
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
一、下載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

上面配置命令操做以下:

  • 查看主機是否存在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有了更好的理解

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

相關文章
相關標籤/搜索