Docker Compose 容器連線
在Docker中,容器之間的連結是一種很常見的操作:它提供了訪問其中的某個容器的網路服務而不需要將所需的埠暴露給Docker Host主機的功能。Docker Compose中對該特性的支援同樣是很方便的。然而,如果需要連結的容器沒有定義在同一個docker-compose.yml
中的時候,這個時候就稍微麻煩複雜了點。
在不使用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,那麼這個事情就更簡單了,還是以上面的nginx
映象為例子,編輯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
建立的連結並無區別。這只是一種最為理想的情況。
- 如果容器沒有定義在同一個
docker-compose.yml
檔案中,應該如何連結它們呢? - 又如果定義在
docker-compose.yml
檔案中的容器需要與docker run xxx
啟動的容器連結,需要如何處理?
針對這兩種典型的情況,下面給出我個人測試可行的辦法:
方式一:讓需要連結的容器同屬一個外部網路
我們還是使用nginx映象來模擬這樣的一個情景:假設我們需要將兩個使用Docker Compose管理的nignx容器(test1
test2
)連結起來,使得test2
能夠訪問test1
中提供的服務,這裡我們以能ping通為準。
首先,我們定義容器test1
的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
檔案內容為:
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
中pingtest2
也是能夠正常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
的doker-compose.yml
定義為:
1 2 3 4 5 6 |
version: "3" services: nginx2: image: nginx container_name: nginx2 network_mode: bridge |
與其對應的,nginx1
的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 |
以上也能充分證明這種方式是屬於單向聯通的。
在實際應用中根據自己的需要靈活的選擇這兩種連結方式,如果想偷懶的話,大可選擇第二種。不過我更推薦第一種,不難看出無論是聯通性還是靈活性,較為更改網路模式的第二