Docker容器間通訊方法

幾天前,爲了解決平常在本地進行平常工做和開發測試之間的矛盾,利用docker在Windows系統中搭建了基於Linux的測試環境:藉助Docker,在win10下編碼,一鍵在Linux下測試。在這邊文章裏主要介紹瞭如何在本地經過docker構建與生產環境基本一致的環境並一鍵運行、測試咱們的代碼。Docker官方建議每一個容器中只運行一個服務[1],可是咱們的項目多是由多個服務組成,在服務中可能會須要mysql、redis等中間件的支持,因此一般咱們將一個項目的不一樣服務劃分到不一樣容器中。這種作法雖然具備低耦合、高隔離性等優勢,可是也增長了服務之間通訊的複雜度。mysql

Docker容器間的通訊方式根據媒介能夠分爲:volume共享通訊網絡通訊等;根據通訊範圍也能夠分爲:同主機通訊跨主機通訊等。而本文主要針對容器間的網絡通訊方法進行討論。redis

1. Docker的網絡驅動模型

Docker的網絡驅動模型分類:sql

  • bridge:Docker中默認的網絡驅動模型,在啓動容器時若是不指定則默認爲此驅動類型;
  • host:打破Docker容器與宿主機之間的網絡隔離,直接使用宿主機的網絡環境,該模型僅適用於Docker17.6及以上版本;
  • overlay:能夠鏈接多個docker守護進程或者知足集羣服務之間的通訊;適用於不一樣宿主機上的docker容器之間的通訊;
  • macvlan:能夠爲docker容器分配MAC地址,使其像真實的物理機同樣運行;
  • none:即禁用了網絡驅動,須要本身手動自定義網絡驅動配置;
  • plugins:使用第三方網絡驅動插件;

以上是Docker支持的幾種網絡驅動模型,它們都具備獨特的特色和應用範圍,爲了更加詳細地瞭解Docker的網絡運行原理,下面挑選幾種較爲重要的網絡模型進行研究。docker

2. bridge

2.1 docker的默認網橋

bridge原理示意圖

如上圖所示爲Docker中bridge驅動模式的示意圖,其中藍色的模塊表示主機上的網卡。當Docker啓動時會自動在主機上建立一個虛擬網橋docker0,使用默認網絡模式建立docker容器時會自動建立一對兒veth pair接口,一端鏈接在docker容器中(如圖容器中的eth0),一端鏈接在虛擬網橋docker0上(如圖veth)。這種veth pair是一種虛擬網絡設備,主要用於不一樣namespace中(意味着網絡隔離)的網絡通訊,它老是成對存在的。在這裏能夠把它想象成一對兒靠虛擬網線鏈接起來的兩個虛擬網卡,一端鏈接着docker容器,一端鏈接着虛擬網橋docker0shell

經過這種方式,不一樣docker容器之間能夠經過ip地址互相通訊,也能夠經過虛擬網橋訪問主機上的網絡eth0(添加iptables規則,將docker容器對目標地址發出的訪問經過地址假裝的方式修改成主機對目標地址進行訪問)。後端

若是想要外界網絡訪問docker容器時,須要在docker容器啓動時加上參數'-p [主機端口]:[容器端口]'進行端口映射,原理也是經過修改iptables規則將訪問[主機端口]的數據轉發到docker容器的[容器端口]中,可是這種作法也存在着佔用主機有限的端口資源的缺點。安全

在主機上經過命令docker network ls能夠查看docker中存在的網絡:bash

docker network ls
# 輸出結果:
NETWORK ID          NAME                DRIVER              SCOPE
e79b7548b225        bridge              bridge              local
666d5f1f459d        host                host                local
d0d785cf4794        none                null                local
複製代碼

而後經過命令docker network inspect bridge查看bridge網絡的詳細配置:服務器

docker network inspect bridge
# 輸出結果:
[
    {
        "Name": "bridge",
        "Id": "e79b7548b225c3c80d0f70d0de0b5911ed70a7f39ac20f75a8ae71c5cef05b3a",
        "Created": "2019-05-17T13:01:27.6581642Z",
        "Scope": "local",
        "Driver": "bridge",         # bridge驅動模式
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.17.0.0/16",          # 子網,docker容器的IP範圍
                    "Gateway": "172.17.0.1"             # 網關,即docker0
                }
            ]
        },
        ...
        省略
        ...
        # 兩個docker容器(一個mysql容器,一個Web服務容器)的網絡配置
        "Containers": {
            # mysql容器
            "d6f33e9bbd60e10d02dd2eebea424a7fc129d9646c96742ec3fe467833017679": {
                "Name": "mysqld5.7",
                "EndpointID": "32e900f33367e3570c416c43a5618bd7a742cf94f36799e92895951ed1784736",
                "MacAddress": "02:42:ac:11:00:02",
                "IPv4Address": "172.17.0.2/16",         # 容器的IP
                "IPv6Address": ""
            },
            # 基於tornado的Web服務容器
            "dd292d535d16bbfe5382e29486756f4dddfea8e9b10af769db61618d739c5c4e": {
                "Name": "test_demo",
                "EndpointID": "eaf8e294f7b54aa50c6e6b30ac91f63b1a0ccbc5b56d6fbdcfeacd0471b15eb3",
                "MacAddress": "02:42:ac:11:00:03",
                "IPv4Address": "172.17.0.3/16",         # 容器的IP
                "IPv6Address": ""
            }
        },
        "Options": {
            "com.docker.network.bridge.default_bridge": "true",
            "com.docker.network.bridge.enable_icc": "true",
            "com.docker.network.bridge.enable_ip_masquerade": "true",
            "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
            "com.docker.network.bridge.name": "docker0",
            "com.docker.network.driver.mtu": "1500"
        },
        "Labels": {}
    }
]
複製代碼

由上述配置信息能夠看出,docker網橋的網關的IP爲"172.17.0.1",也即docker0,而docker的子網爲"172.17.0.0/16",docker將會爲容器在"172.17.0.0/16"中分配IP,如其中的mysql容器的IP爲"172.17.0.2/16"、test_demo容器的IP爲"172.17.0.3/16"。因爲不一樣容器經過veth pair鏈接在虛擬網橋docker0上,因此容器之間能夠經過IP互相通訊,可是沒法經過容器名進行通訊:網絡

# 在test_demo容器中訪問mysqld5.7容器(經過IP)
ping 172.17.0.2 -c 3
# 輸出結果:
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.147 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.185 ms
64 bytes from 172.17.0.2: icmp_seq=3 ttl=64 time=0.209 ms

--- 172.17.0.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2057ms
rtt min/avg/max/mdev = 0.147/0.180/0.209/0.027 ms
 # 在test_demo容器中訪問mysqld5.7容器(經過容器名)
ping mysqld5.7
# 輸出結果:
ping: mysqld5.7: Name or service not known
複製代碼

因此,默認的網橋bridge上的容器只能經過IP互連,沒法經過DNS解析名稱或別名。假如咱們在container1中部署了Web服務,在container2中部署了mysql,container1中的Web服務每每須要鏈接container2的mysql,這是隻能靠IP進行鏈接,可是docker也沒法保證容器重啓後的IP地址不變,因此更好的方式是經過別名進行互聯,在網絡中加入DNS服務器,將容器名與IP地址進行匹配,省去了手動修改Web服務中鏈接mysql的IP的過程。

爲了實現不一樣容器經過容器名或別名的互連,docker提供瞭如下幾種:

  1. 在啓動docker容器時加入--link參數,可是目前已經被廢棄,廢棄的主要緣由是須要在鏈接的兩個容器上都建立--link選項,當互連的容器數量較多時,操做的複雜度會顯著增長;

  2. 啓動docker容器後進入容器並修改/etc/host配置文件,缺點是手動配置較爲繁雜;

  3. 用戶自定義bridge網橋,這是目前解決此類問題的主要方法;

2.2 用戶自定義bridge

用戶自定義bridge相對於使用默認bridge的主要優點:

  • 用戶自定義bridge能夠在容器化的應用程序提供更好的隔離效果和更好的互通性: 這段話看似有些矛盾,可是其中更好的隔離效果是針對外界網絡,而更好的互通性則是指同一bridge下的不一樣容器之間。仍是以以前的分別部署了Web服務和mysql服務的兩個容器container一、container2爲例,container1只須要對外界網絡暴露Web服務的80端口,而負責後端的container2只需與container1互連,不須要對外暴露,有效地保護了後端容器的安全性,提升了容器對外的隔離效果。而同屬於用戶自定義bridge的容器container一、container2之間自動將全部端口暴露,方便容器間進行無障礙的通訊,而不會遭受到外界的意外訪問。
  • 用戶自定義bridge在容器之間提供了自動DNS解析: 這也是本文討論的重點,不一樣於默認bridge只能經過IP互連的限制,用戶自定義的bridge自動提供了容器間的DNS解析功能,容器間能夠經過容器名或別名進行通訊。
2.2.1 如何使用用戶自定義bridge
  1. 建立用戶自定義bridge:

    docker network create my-net        # 建立了一個名爲"my-net"的網絡
    複製代碼
  2. 將Web服務容器和mysql服務容器加入到"my-net"中,並觀察變化:

    docker network connect my-net test_demo         # 將Web服務加入my-net網絡中
    docker network connect my-net mysqld5.7         # 將mysql服務加入my-net網絡中
    複製代碼
    # 查看my-net的網絡配置
    docker network inspect my-net
    # 輸出結果(省略部份內容):
    [
        {
            "Name": "my-net",
            "Scope": "local",
            "Driver": "bridge",
            "EnableIPv6": false,
            "IPAM": {
                "Config": [
                    {
                        "Subnet": "172.18.0.0/16",      # my-net的子網
                        "Gateway": "172.18.0.1"         # my-net的網關
                    }
                ]
            },
            "Containers": {
                "d6f33e9bbd60e10d02dd2eebea424a7fc129d9646c96742ec3fe467833017679": {
                    "Name": "mysqld5.7",
                    "EndpointID": "7d0e8d70bb523cceb4d2d8d4e3f8231fc68332c70f7f9b4e5d4abccce2b31a65",
                    "MacAddress": "02:42:ac:12:00:03",
                    "IPv4Address": "172.18.0.3/16",         # mysql服務容器的IP,與以前不一樣
                    "IPv6Address": ""
                },
                "dd292d535d16bbfe5382e29486756f4dddfea8e9b10af769db61618d739c5c4e": {
                    "Name": "test_demo",
                    "EndpointID": "f7802f1af81e258f77e227609dfdcdf66c49f19776381eb8b0dca6d9e794ccad",
                    "MacAddress": "02:42:ac:12:00:02",
                    "IPv4Address": "172.18.0.2/16",         # Web服務容器的IP,與以前不一樣
                    "IPv6Address": ""
                }
            },
        }
    ]
    複製代碼

    如上所示,用戶自定義網絡my-net的子網爲"172.18.0.0/16",因此兩docker容器在my-net網絡中的IP分別爲:"172.18.0.3/16"、"172.18.0.2/16",與以前的"172.17.0.2/16"、"172.17.0.3/16"不一樣。

  3. 經過容器名或別名互連: 進入到Web服務器container1中鏈接container2:

    ping mysqld5.7 -c 3
    # 輸出結果:
    PING mysqld5.7 (172.18.0.3) 56(84) bytes of data.
    64 bytes from mysqld5.7.my-net (172.18.0.3): icmp_seq=1 ttl=64 time=0.066 ms
    64 bytes from mysqld5.7.my-net (172.18.0.3): icmp_seq=2 ttl=64 time=0.145 ms
    64 bytes from mysqld5.7.my-net (172.18.0.3): icmp_seq=3 ttl=64 time=0.143 ms
    
    --- mysqld5.7 ping statistics ---
    3 packets transmitted, 3 received, 0% packet loss, time 2048ms
    rtt min/avg/max/mdev = 0.066/0.118/0.145/0.036 ms
    複製代碼

    如上所示,咱們已經能夠經過容器名"mysqld5.7"鏈接mysql服務器了。

  4. 斷開網絡: 因爲咱們的容器仍然鏈接着默認bridgedocker0,而如今咱們已經不須要它,因此應該將容器與docker0的鏈接斷開,執行如下操做:

    # 斷開容器與docker0的鏈接
    docker network disconnect bridge test_demo
    docker network disconnect bridge mysqld5.7
    複製代碼

3. overlay

關鍵字:跨主機通訊、集羣

【待續。。】


  1. Run multiple services in a container. docs.docker.com/config/cont… ↩︎

相關文章
相關標籤/搜索