BACK
Featured image of post Docker Compose 鏈接外部容器的幾種方式

Docker Compose 鏈接外部容器的幾種方式

在 Docker 中,容器之間的鏈接是一種很常見的操作,它提供了訪問其中的某個容器的網絡服務而不需要將所需的端口暴露給 Docker Host 主機的功能。Docker Compose 中對該特性的支持同樣是很方便的。然而,如果需要鏈接的容器沒有定義在同一個 docker-compose.yml 中的時候,這個時候就稍微麻煩複雜了點。

參考網站

在 Docker 中,容器之間的鏈接是一種很常見的操作,它提供了訪問其中的某個容器的網絡服務而不需要將所需的端口暴露給 Docker Host 主機的功能。Docker Compose 中對該特性的支持同樣是很方便的。然而,如果需要鏈接的容器沒有定義在同一個 docker-compose.yml 中的時候,這個時候就稍微麻煩複雜了點。


不使用 Docker Compose

在不使用Docker Compose的時候,將兩個容器鏈接起來使用—link參數,相對來說比較簡單,以nginx鏡像為例子:

1
2
docker run --rm --name test1 -d nginx # 開啟一個實例test1 
docker run --rm --name test2 --link test1 -d nginx # 開啟一個實例test2並與test1建立鏈接

這樣,test2 與 test1 便建立了鏈接,就可以在 test2 中使用訪問 test1 中的服務了。


使用Docker Compose

如果使用 Docker Compose,那麼這個事情就更簡單了,還是以上面的 nginx 鏡像為例子,編輯 docker-compose.yml 文件為:

docker-compose.yml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
version: "3"
services:
  test2:
    image: nginx
    depends_on:
      - test1
    links:
      - test1
  test1:
    image: nginx

最終效果與使用普通的 Docker 命令 docker run xxxx 建立的鏈接並無區別。這只是一種最為理想的情況。


問題

  1. 如果容器沒有定義在同一個 docker-compose.yml 文件中,應該如何鏈接它們呢?
  2. 如果定義在 docker-compose.yml 文件中的容器需要與 docker run xxx 啟動的容器鏈接,需要如何處理?

方法一:讓需要鏈接的容器同屬一個外部網絡

我們還是使用 nginx 鏡像來模擬這樣的一個情景:假設我們需要將兩個使用 Docker Compose 管理的 nignx 容器(test1和test2)鏈接起來,使得 test2 能夠訪問 test1 中提供的服務,這裡我們以能 ping 通為準。

首先,我們定義容器 test1 的 docker-compose.yml 文件內容為:

docker-compose.yml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
version: "3"
services:
  test2:
    image: nginx
    container_name: test1
    networks:
      - default
      - app_net
networks:
  app_net:
    external: true

容器 test2 內容與 test1 基本一樣,只是多了一個 external_links

需要特別說明的是:最近發布的 Docker 版本已經不需要使用 external_links 來鏈接容器,容器的 DNS 服務可以正確的作出判斷,因此如果你你需要兼容較老版本的 Docker 的話,那麼容器 test2 的 docker-compose.yml 文件內容為:

docker-compose.yml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
version: "3"
services:
  test2:
    image: nginx
    networks:
      - default
      - app_net
    external_links:
      - test1
    container_name: test2
networks:
  app_net:
    external: true

否則的話,test2 的 docker-compose.yml 和 test1 的定義完全一致,不需要額外多指定一個 external_links。相關的問題請參見 stackoverflow 上的相關問題:docker-compose + external container

正如你看到的那樣,這裡兩個容器的定義裡都使用了同一個外部網絡 app_net,因此,我們需要在啟動這兩個容器之前通過以下命令再創建外部網絡:

1
docker network create app_net

之後,通過 docker-compose up -d 命令啟動這兩個容器,然後執行 docker exec -it test2 ping test1,你將會看到如下的輸出:

1
2
3
4
5
6
7
8
$ docker exec -it test2 ping test1 
PING test1 (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: icmp_seq=0 ttl=64 time=0.091 ms
64 bytes from 172.18.0.2: icmp_seq=1 ttl=64 time=0.146 ms
64 bytes from 172.18.0.2: icmp_seq=2 ttl=64 time=0.150 ms
64 bytes from 172.18.0.2: icmp_seq=3 ttl=64 time=0.145 ms
64 bytes from 172.18.0.2: icmp_seq=4 ttl=64 time=0.126 ms
64 bytes from 172.18.0.2: icmp_seq=5 ttl=64 time=0.147 ms

證明這兩個容器是成功鏈接了,反過來在 test1 中 ping test2 也是能夠正常 ping 通的。

如果我們通過 docker run --rm --name test3 -d nginx 這種方式來先啟動了一個容器(test3),並且沒有指定它所屬的外部網絡,而需要將其與 test1 或者 test2 鏈接的話,這個時候手動鏈接外部網絡即可:

1
docker network connect app_net test3

這樣,三個容器都可以相互訪問了。


方法二:更改需要鏈接的容器的網絡模式

通過更改你想要相互鏈接的容器的網絡模式為 bridge,並指定需要鏈接的外部容器(external_links)即可。

與同屬外部網絡的容器可以相互訪問的鏈接方式一不同,這種方式的訪問是單向的。

還是以 nginx 容器鏡像為例子,如果容器實例 nginx1 需要訪問容器實例 nginx2,那麼 nginx2 的 docker-compose.yml 定義為:

docker-compose.yml
1
2
3
4
5
6
version: "3"
services:
  nginx2:
    image: nginx
    container_name: nginx2
    network_mode: bridge

與其對應的,nginx1 的 docker-compose.yml 定義為:

docker-compose.yml
1
2
3
4
5
6
7
8
version: "3"
services:
  nginx1:
    image: nginx
    external_links:
      - nginx2
    container_name: nginx1
    network_mode: bridge

需要特別說明的是,這裡的 external_links 是不能省略的,而且 nginx1 的啟動必須要在 nginx2 之後,否則可能會報找不到容器 nginx2 的錯誤。

接著我們使用 ping 來測試下連通性:

1
2
3
4
5
6
7
8
$ docker exec -it nginx1 ping nginx2  # nginx1 to nginx2
PING nginx2 (172.17.0.4): 56 data bytes
64 bytes from 172.17.0.4: icmp_seq=0 ttl=64 time=0.141 ms
64 bytes from 172.17.0.4: icmp_seq=1 ttl=64 time=0.139 ms
64 bytes from 172.17.0.4: icmp_seq=2 ttl=64 time=0.145 ms

$ docker exec -it nginx2 ping nginx1 #nginx2 to nginx1
ping: unknown host

以上也能充分證明這種方式是屬於單向聯通的。

在實際應用中根據自己的需要靈活的選擇這兩種鏈接方式,如果想偷懶的話,大可選擇第二種。不過我更推薦第一種,不難看出無論是聯通性還是靈活性,較為更改網絡模式的第二種都更為友好。


comments powered by Disqus