1. 程式人生 > >Docker 容器的通訊(十二)

Docker 容器的通訊(十二)

目錄

一、容器間通訊

1、IP 通訊

兩個容器要能通訊,必須要有屬於同一個網路的網絡卡。滿足這個條件後,容器就可以通過 IP 互動了。具體做法是在容器建立時通過--network指定相應的網路,或者通過docker network connect

將現有容器加入到指定網路。

2、Docker DNS Server

通過 IP 訪問容器雖然滿足了通訊的需求,但還是不夠靈活。因為我們在部署應用之前可能無法確定 IP,部署之後再指定要訪問的 IP 會比較麻煩。對於這個問題,可以通過 docker 自帶的 DNS 服務解決。

從 Docker 1.10 版本開始,docker daemon 實現了一個內嵌的 DNS server,使容器可以直接通過“容器名”通訊。方法很簡單,只要在啟動時用 --name 為容器命名就可以了。

下面啟動兩個容器 bbox1 和 bbox2:

docker run -it --network=my_bridge2 --name=bbox1 busybox
docker run -it --network=my_bridge2 --name=bbox2 busybox

然後,bbox2 就可以直接 ping 到 bbox1 了:

使用 docker DNS 有個限制:只能在 user-defined 網路中使用。也就是說,預設的 bridge 網路是無法使用 DNS 的。

3、joined 容器

joined 容器是另一種實現容器間通訊的方式。

joined 容器非常特別,它可以使兩個或多個容器共享一個網路棧,共享網絡卡和配置資訊,joined 容器之間可以通過 127.0.0.1 直接通訊。請看下面的例子:

先建立一個 httpd 容器,名字為 web1。

docker run -d -it --name=web1 httpd

然後建立 busybox 容器並通過 --network=container:web1 指定 jointed 容器為 web1:

[email protected]:~# docker run -it --network=container:web1 busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue 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
60: [email protected]: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue 
    link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.3/16 scope global eth0
       valid_lft forever preferred_lft forever

注意 busybox 容器中的網路配置資訊,下面我們檢視一下 web1 的網路:

[email protected]:~# docker exec -it web1 /bin/bash
[email protected]:/usr/local/apache2# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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
60: [email protected]: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.3/16 scope global eth0
       valid_lft forever preferred_lft forever

busybox 和 web1 的網絡卡 mac 地址與 IP 完全一樣,它們共享了相同的網路棧。busybox 可以直接用 127.0.0.1 訪問 web1 的 http 服務。

joined 容器非常適合以下場景:

  • 不同容器中的程式希望通過 loopback 高效快速地通訊,比如 web server 與 app server。
  • 希望監控其他容器的網路流量,比如執行在獨立容器中的網路監控程式。

二、容器訪問外部網路

從之前容器的構建,在容器內安裝軟體等等,我們可以看出容器是可以訪問外部網際網路的。

docker run -ti busybox

現象很簡單,但更重要的:我們應該理解現象下的本質。

在上面的例子中,busybox 位於 docker0 這個私有 bridge 網路中(172.17.0.0/16),當 busybox 從容器向外 ping 時,資料包是怎樣到達 www.wzlinux.com 的呢?

這裡的關鍵就是 NAT。我們檢視一下 docker host 上的 iptables 規則:

[email protected]:~# iptables -t nat -S
-P PREROUTING ACCEPT
-P INPUT ACCEPT
-P OUTPUT ACCEPT
-P POSTROUTING ACCEPT
-N DOCKER
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
-A POSTROUTING -s 192.168.31.0/24 ! -o br-43043f6bbc1a -j MASQUERADE
-A POSTROUTING -s 172.18.0.0/16 ! -o br-423b660fbdbd -j MASQUERADE
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
-A DOCKER -i br-43043f6bbc1a -j RETURN
-A DOCKER -i br-423b660fbdbd -j RETURN
-A DOCKER -i docker0 -j RETURN

在 NAT 表中,有這麼一條規則:

-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE

其含義是:如果網橋 docker0 收到來自 172.17.0.0/16 網段的外出包,把它交給 MASQUERADE 處理。而 MASQUERADE 的處理方式是將包的源地址替換成 host 的地址傳送出去,即做了一次網路地址轉換(NAT)。

下面我們通過 tcpdump 檢視地址是如何轉換的。先檢視 docker host 的路由表:

[email protected]:~# ip r
default via 192.168.2.1 dev enp5s0 onlink 
172.17.0.0/16 dev docker0  proto kernel  scope link  src 172.17.0.1 
172.18.0.0/16 dev br-423b660fbdbd  proto kernel  scope link  src 172.18.0.1 linkdown 
192.168.2.0/24 dev enp5s0  proto kernel  scope link  src 192.168.2.206 
192.168.31.0/24 dev br-43043f6bbc1a  proto kernel  scope link  src 192.168.31.1 

預設路由通過 enp5s0 發出去,所以我們要同時監控 enp5s0 和 docker0 上的 icmp(ping)資料包。

當 busybox ping wzlinux.com 時,tcpdump 輸出如下:

tcpdump -i docker0 -n icmp

docker0 收到 busybox 的 ping 包,源地址為容器 IP 172.17.0.4,這沒問題,交給 MASQUERADE 處理。這時,在 enp5s0 上我們看到了變化:

ping 包的源地址變成了 enp5s0 的 IP 192.168.2.206。

這就是 iptable NAT 規則處理的結果,從而保證資料包能夠到達外網。下面用一張圖來說明這個過程:

  1. busybox 傳送 ping 包:172.17.0.4 > www.wzlinux.com 。
  2. docker0 收到包,發現是傳送到外網的,交給 NAT 處理。
  3. NAT 將源地址換成 enp5s0 的 IP:192.168.2.206 > www.wzlinux.com 。
  4. ping 包從 enp5s0 傳送出去,到達 www.wzlinux.com 。

通過 NAT,docker 實現了容器對外網的訪問。

三、外部網路訪問容器

1、隨機埠

通過埠對映的方法可以使外部網路訪問容器。

docker 可將容器對外提供服務的埠對映到 host 的某個埠,外網通過該埠訪問容器。容器啟動時通過 -p 引數對映埠:

[email protected]:~# docker run -d -p 80 httpd
1893fd9dbe36e2c9fcc47fcef91e5439e802d5fc6f5c56c9842b090133266b17
[email protected]:~# docker ps
CONTAINER ID        IMAGE               COMMAND              CREATED             STATUS              PORTS                   NAMES
1893fd9dbe36        httpd               "httpd-foreground"   5 seconds ago       Up 3 seconds        0.0.0.0:32770->80/tcp   friendly_bardeen
[email protected]:~# docker port 1893fd9dbe36
80/tcp -> 0.0.0.0:32770

容器啟動後,可通過 docker ps 或者 docker port 檢視到 host 對映的埠。在上面的例子中,httpd 容器的 80 埠被對映到 host 32770 上,這樣就可以通過

2、指定埠

除了對映動態埠,也可在 -p 中指定對映到 host 某個特定埠,例如可將 80 埠對映到 host 的 8080 埠:

[email protected]:~# docker run -d -p 8080:80 httpd
3c83e2c7106cd0f467e98902d8a81d87eebe79493913852c345892bafa5c4535
[email protected]:~# curl localhost:8080
<html><body><h1>It works!</h1></body></html>

每一個對映的埠,host 都會啟動一個 docker-proxy 程序來處理訪問容器的流量:

以 0.0.0.0:32770->80/tcp 為例分析整個過程:

  1. docker-proxy 監聽 host 的 32770 埠。
  2. 當 curl 訪問 192.168.2.206:32770 時,docker-proxy 轉發給容器 172.17.0.4:80。
  3. httpd 容器響應請求並返回結果。

上圖之所以會出現兩個 172.17.0.4 是因為之前的 busybox 容器停止了,IP 被收回了,當新的容器啟動時,會按照數字大小進行 IP 的分配,所以恰巧分配到了之前容器使用過的,當我們把之前的 busybox 容器再啟動起來,他會按照數大小分配新的 IP。

3、不指定任何埠。

docker run -d -P httpd

4、指定一個埠範圍

比如我們可能在一個機器上面部署很多應用,我們不必去一次次修改埠,設定一個範圍,容器會自動使用這個範圍裡面沒有使用的埠,從小到大。

docker run -d -p 8000-9000:80 httpd
docker run -d -p 8000-9000:80 httpd
[email protected]:~# netstat -tlnp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      1131/sshd       
tcp6       0      0 :::22                   :::*                    LISTEN      1131/sshd       
tcp6       0      0 :::8000                 :::*                    LISTEN      18652/docker-proxy
tcp6       0      0 :::8001                 :::*                    LISTEN      18796/docker-proxy

5、指定 IP 對映

docker run -d -p 127.0.0.1:80:80 httpd

6、指定協議

比如我們對映 udp 埠。

docker run -d -p 127.0.0.1:80:80/udp httpd

參考文件:https://docs.docker.com/engine/userguide/networking/configure-dns/
https://docs.docker.com/engine/userguide/networking/default_network/configure-dns/
https://docs.docker.com/engine/userguide/networking/default_network/dockerlinks/