1. 程式人生 > >Docker筆記(九):網路管理

Docker筆記(九):網路管理

Docker的應用執行在容器中,其相互之間或與外部之間是如何通訊的,涉及到哪些知識點,本文對相關內容進行整理。因網路這塊牽涉的面較多,因此只從日常使用或理解的角度出發,過於專業的就不深入探討了。

1. Docker預設的網路拓撲

在Docker筆記(二):Docker管理的物件中,介紹了Docker通過一些驅動程式來實現容器之間或容器與外部的互聯,包括bridge(預設的虛擬網橋形式),host(與主機共享網路棧),overlay(跨Docker Daemon容器間的互聯),macvlan(為容器分配mac地址),none(禁用所有網路)等。

預設情況下,Docker啟動時會建立一個虛擬網橋 docker0,可以理解為一個軟體交換機。當建立一個 Docker 容器的時候,會建立一對 veth pair 介面(當資料包傳送到一個介面時,另外一個介面也可以收到相同的資料包)。這對介面一端在容器內,即 eth0 ;另一端在宿主機本地並被掛載到 docker0 網橋,名稱以veth 開頭,如 veth340c305,docker0會在掛載到它上面的網口之間進行轉發,從而實現主機與容器之間及容器與容器之間的相互通訊。Docker預設的網路拓撲圖如下:

我們可以在宿主機上通過ifconfig檢視相關的網路介面,

~$ ifconfig
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.17.0.1 netmask 255.255.0.0 broadcast 172.17.255.255
inet6 fe80::42:46ff:fe26:ce0b prefixlen 64 scopeid 0x20<link>
ether 02:42:46:26:ce:0b txqueuelen 0 (Ethernet)
RX packets 16868344 bytes 127838098551 (127.8 GB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 17929275 bytes 137867853738 (137.8 GB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

veth340c305: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet6 fe80::50f7:7ff:fe8f:6e72 prefixlen 64 scopeid 0x20<link>
ether 52:f7:07:8f:6e:72 txqueuelen 0 (Ethernet)
RX packets 8093606 bytes 126893792744 (126.8 GB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 8795102 bytes 10834735399 (10.8 GB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

veth6c803b7: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet6 fe80::1045:4cff:fe66:7f5a prefixlen 64 scopeid 0x20<link>
ether 12:45:4c:66:7f:5a txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 140 bytes 9832 (9.8 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

 

通過brctl show可檢視網路介面的掛載情況,

~$ brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.02424626ce0b no veth340c305
veth6c803b7

 

由上可看出網路介面veth340c305,veth6c803b7都掛在虛擬網橋docker0上。

2. 容器與外部的互聯

我們前面的許多容器啟動命令都有新增類似 -p 8080:8080 的引數,以指定將宿主機埠對映到容器埠,從而通過訪問 宿主機IP:宿主機埠 的地址來訪問對應埠的容器服務。
埠對映的完整格式為 宿主機IP:宿主機埠:容器埠

,其中前兩個是可以兩者都取,或只取其一

  • 宿主機IP:宿主機埠:容器埠:將指定宿主機IP的一個指定埠對映到容器埠,如192.168.40.205:8090:8080
  • 宿主機IP::容器埠:將指定宿主機IP的一個隨機埠對映到容器埠上,如果宿主機有多個IP,則可以通過這種格式指定繫結其中一個宿主機IP,隨機埠範圍為49000~49900
  • 宿主機埠:容器埠:將宿主機所有網路介面IP的指定埠對映到容器埠上,8090:8080等效於0.0.0.0:8090:80800.0.0.0即表示所有網路介面地址)

可以使用 docker port 容器ID或名稱 容器埠docker ps命令來檢視埠對映情況,如

~$ docker port test-dev 8080
0.0.0.0:32768

~$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
696a76944e72 cnbots:dev "/bin/sh -c '/usr/lo…" 23 minutes ago Up 23 minutes 0.0.0.0:32768->8080/tcp test-dev

 

在容器啟動時,可以多次使用 -p 來指定對映多個埠。

如果不指定具體的宿主機埠,則可以使用 -P(大寫)來分配一個宿主機的隨機埠(範圍為49000~49900), 如docker run -d -P --name test-dev test:dev,然後通過docker port 容器ID或名稱 容器埠docker ps命令來檢視具體對映到了哪個埠。

3. 容器之間的互聯

同一個Docker Daemon下的容器,彼此之間是可以通過容器IP互相訪問的(如何檢視容器IP?用docker inspect 容器ID或名稱命令),如果要實現兩個容器之間可以通過容器名直接訪問,則可以通過自建一個docker網路。

# 建立一個自定義網路,-d 表示網路型別,可以為bridge(網橋,軟體交換機),或overlay(跨Docker Daemon容器間的互聯)
~$ docker network create -d bridge my-net
0c97fc265ed1cab67d84b9376d6914c9558419c73bb5abc040e75c945cd99f0a

# 啟動一個centos容器centos1,通過 --network 指定自定義網路
~$ docker run -it --name centos1 --network my-net centos:7.3.1611 bash
[root@3dcf507bd12a /]#

# 再啟動一個centos容器centos2(開啟另一個視窗),指定同一個自定義網路
~$ docker run -it --name centos2 --network my-net centos:7.3.1611 bash
[root@16dcce660a89 /]#

# 在centos1容器中直接ping centos2
[root@3dcf507bd12a /]# ping centos2
PING centos2 (172.19.0.2) 56(84) bytes of data.
64 bytes from centos2.my-net (172.19.0.2): icmp_seq=1 ttl=64 time=0.111 ms
64 bytes from centos2.my-net (172.19.0.2): icmp_seq=2 ttl=64 time=0.058 ms

# 在centos2容器中直接ping centos1
[root@16dcce660a89 /]# ping centos1
PING centos1 (172.19.0.3) 56(84) bytes of data.
64 bytes from centos1.my-net (172.19.0.3): icmp_seq=1 ttl=64 time=0.061 ms
64 bytes from centos1.my-net (172.19.0.3): icmp_seq=2 ttl=64 time=0.054 ms

 

由上可見通過自定義網橋連線的容器可以通過容器名稱互相訪問。如果需要多個容器之間互聯,則可以使用Docker Compose。

4. 配置容器的DNS

如果要自定義所有容器的DNS,則可以在 /etc/docker/daemon.json 中增加

{
"dns" : [
"114.114.114.114",
"8.8.8.8"
]
}

 

也可以在啟動容器時通過引數指定單個容器的DNS配置,--dns=IP_ADDRESS,這會將指定DNS的地址新增到容器的 /etc/resolv.conf 檔案中,讓容器用這個DNS伺服器來解析所有不在 /etc/hosts 中的主機名。

5. Docker網路的底層實現

容器的網路訪問控制,主要是通過Linux上的iptables防火牆來實現與管理的。

1. 容器訪問外部網路
容器訪問外部網路,需要通過本地系統的轉發,可以通過如下命令檢視轉發是否開啟

$sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1

# 為1為開啟,為0則未開啟,可通過如下命令開啟,也可以在Docker服務啟動時通過引數--ip-forward=true開啟
$sysctl -w net.ipv4.ip_forward=1

容器所有到外部網路的訪問,源地址都會被 NAT 成本地系統的 IP 地址。這是使用 iptables 的源地址偽裝操作實現的,

~# iptables -t nat -nL
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
MASQUERADE all -- 172.17.0.0/16 0.0.0.0/0

上述規則將所有源地址在 172.17.0.0/16 的網段(容器IP所在網段),目標地址為任意網段(包括外部網路)的流量動態偽裝為從系統網絡卡發出。MASQUERADE 跟傳統 SNAT 的好處是它能動態從網絡卡獲取地址。

2. 外部訪問容器

通過 -p 或 -P 指定埠對映,允許外部訪問容器埠,實質也是在本地的 iptable 的 nat 表中新增相應的規則,如

~# iptables -t nat -nL
Chain DOCKER (2 references)
target prot opt source destination
DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:3306 to:172.17.0.2:3306
DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:11090 to:172.17.0.3:11090

 

這裡的規則映射了 0.0.0.0 ,意味著將接受主機來自所有網路介面的流量。

3. 容器之間的訪問
容器之間能互相訪問,需要滿足兩個條件:1)容器的網路拓撲是否已經互聯,預設情況下容器都連線到docker0網橋上,預設是互聯的。2)本地系統的防火牆iptables是否允許通過。當容器啟動時通過–link互聯時,也是在iptables中建立對應規則來實現。

6. 總結

本文整理了Docker網路相關知識,對容器之間及容器與外部之間的通訊機制應該有了一定的瞭解。除了預設的網路實現,Docker還提供了網路的配置及自定義網路,出於篇幅,本文介紹到這,後續再補充。

 



相關閱讀

Docker筆記(一):什麼是Docker

Docker筆記(二):Docker管理的物件
Docker筆記(三):Docker安裝與配置
Docker筆記(四):Docker映象管理
Docker筆記(五):整一個自己的映象
Docker筆記(六):容器管理

Docker筆記(七):常用服務安裝——Nginx、MySql、Redis

Docker筆記(八):資料管理


我的微信公眾號:jboost-ksxy (一個不只有技術乾貨的公眾號,歡迎關注,及時獲取更新內容)

&n