傳統容器連結
警告
--link旗標是 Docker 的舊版功能。它最終可能會被移除。除非您絕對需要繼續使用它,否則我們建議您使用使用者定義網路來促進兩個容器之間的通訊,而不是使用--link。使用者定義網路不支援但您可以使用--link達成的一個功能是容器之間共用環境變數。然而,您可以使用其他機制,例如資料卷 (volumes),以更受控的方式在容器之間共用環境變數。請參閱 使用者定義橋接器與預設橋接器之間的差異,以瞭解使用
--link的替代方案。
本節資訊說明了在 Docker 預設 bridge 網路中舊版容器連結,該網路會在您安裝 Docker 時自動建立。
在 Docker 網路功能 出現之前,您可以使用 Docker 連結功能,讓容器彼此探索並安全地將一個容器的資訊傳輸到另一個容器。隨著 Docker 網路功能的引入,您仍然可以建立連結,但它們在預設的 bridge 網路和 使用者定義網路 之間行為不同。
本節簡要討論了透過網路連接埠進行連接,然後詳細介紹了預設 bridge 網路中的容器連結。
使用網路連接埠映射進行連接
假設您使用此指令執行一個簡單的 Python Flask 應用程式
$ docker run -d -P training/webapp python app.py
注意容器具有內部網路和 IP 位址。Docker 可以有多種網路配置。您可以在此處查看更多關於 Docker 網路的資訊。
建立該容器時,使用了 -P 旗標自動將其內部任何網路連接埠映射到 Docker 主機上「臨時連接埠範圍」(ephemeral port range) 內的一個隨機高連接埠。接下來,當執行 docker ps 時,您看到容器中的 5000 連接埠綁定到主機上的 49155 連接埠。
$ docker ps nostalgic_morse
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
bc533791f3f5 training/webapp:latest python app.py 5 seconds ago Up 2 seconds 0.0.0.0:49155->5000/tcp nostalgic_morse
您也看到了如何使用 -p 旗標將容器的連接埠綁定到特定連接埠。這裡將主機的 80 連接埠映射到容器的 5000 連接埠
$ docker run -d -p 80:5000 training/webapp python app.py
您也了解到為何這不是一個好主意,因為它會限制您只能在該特定連接埠上執行一個容器。
相反地,您可以指定一個主機連接埠範圍來綁定容器連接埠,這個範圍不同於預設的「臨時連接埠範圍」(ephemeral port range)
$ docker run -d -p 8000-9000:5000 training/webapp python app.py
這會將容器中的 5000 連接埠綁定到主機上 8000 到 9000 之間的一個隨機可用連接埠。
還有其他幾種配置 -p 旗標的方式。預設情況下,-p 旗標會將指定的連接埠綁定到主機上的所有介面。但您也可以指定綁定到特定介面,例如只綁定到 localhost。
$ docker run -d -p 127.0.0.1:80:5000 training/webapp python app.py
這會將容器內部的 5000 連接埠綁定到主機上的 localhost 或 127.0.0.1 介面上的 80 連接埠。
或者,若要將容器的 5000 連接埠綁定到一個動態連接埠,但僅限於 localhost,您可以使用
$ docker run -d -p 127.0.0.1::5000 training/webapp python app.py
您也可以透過新增尾隨的 /udp 或 /sctp 來綁定 UDP 和 SCTP (通常由電信協定使用,例如 SIGTRAN、Diameter 和 S1AP/X2AP) 連接埠。例如
$ docker run -d -p 127.0.0.1:80:5000/udp training/webapp python app.py
您還了解了有用的 docker port 捷徑,它顯示了目前的連接埠綁定。這對於顯示特定連接埠配置也很有用。例如,如果您已將容器連接埠綁定到主機上的 localhost,則 docker port 的輸出會反映這一點。
$ docker port nostalgic_morse 5000
127.0.0.1:49155
注意
-p旗標可以多次使用,以配置多個連接埠。
透過連結系統進行連接
注意本節介紹預設
bridge網路中的舊版連結功能。有關使用者定義網路中連結的更多資訊,請參閱 使用者定義橋接器與預設橋接器之間的差異。
網路連接埠映射並非 Docker 容器彼此連接的唯一方式。Docker 還擁有一個連結系統,可讓您將多個容器連結在一起,並將連接資訊從一個容器傳送到另一個容器。當容器連結時,有關來源容器的資訊可以傳送到接收容器。這允許接收者查看描述來源容器某些方面的選定資料。
命名的重要性
為了建立連結,Docker 依賴於您的容器名稱。您已經看到,您建立的每個容器都有一個自動建立的名稱;事實上,您在本指南中已經熟悉了我們老朋友 nostalgic_morse。您也可以自己為容器命名。這種命名提供了兩個有用的功能
以更容易記住的方式為執行特定功能的容器命名會很有用,例如將包含 Web 應用程式的容器命名為
web。它為 Docker 提供了一個參考點,允許其引用其他容器,例如,您可以指定將容器
web連結到容器db。
您可以透過使用 --name 旗標來命名您的容器,例如
$ docker run -d -P --name web training/webapp python app.py
這會啟動一個新容器,並使用 --name 旗標將容器命名為 web。您可以使用 docker ps 指令查看容器的名稱。
$ docker ps -l
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
aed84ee21bde training/webapp:latest python app.py 12 hours ago Up 2 seconds 0.0.0.0:49154->5000/tcp web
您也可以使用 docker inspect 來回傳容器的名稱。
注意容器名稱必須是唯一的。這表示您只能將一個容器命名為
web。如果您想重複使用容器名稱,必須先刪除舊容器 (使用docker container rm),然後才能建立一個同名的新容器。作為替代方案,您可以在docker run指令中使用--rm旗標。這會在容器停止後立即將其刪除。
跨連結通訊
連結允許容器彼此探索並安全地將一個容器的資訊傳輸到另一個容器。當您建立連結時,您會在來源容器和接收容器之間建立一個通道。然後,接收者可以存取有關來源的選定資料。若要建立連結,請使用 --link 旗標。首先,建立一個新容器,這次是包含資料庫的容器。
$ docker run -d --name db training/postgres
這會從 training/postgres 映像檔建立一個名為 db 的新容器,該映像檔包含一個 PostgreSQL 資料庫。
現在,您需要刪除之前建立的 web 容器,以便將其替換為一個連結的容器
$ docker container rm -f web
現在,建立一個新的 web 容器並將其與您的 db 容器連結。
$ docker run -d -P --name web --link db:db training/webapp python app.py
這會將新的 web 容器與您之前建立的 db 容器連結。--link 旗標的格式為
--link <name or id>:alias
其中 name 是我們連結到的容器名稱,而 alias 是連結的別名。這個別名將很快被使用。--link 旗標也可以採用以下格式
--link <name or id>
在這種情況下,別名與名稱相符。您可以將前面的範例寫為
$ docker run -d -P --name web --link db training/webapp python app.py
接下來,使用 docker inspect 檢查您連結的容器
$ docker inspect -f "{{ .HostConfig.Links }}" web
[/db:/web/db]
您可以看到 web 容器現在已連結到 db 容器 web/db。這允許它存取關於 db 容器的資訊。
那麼連結容器實際上做了什麼?您已經了解到,連結允許來源容器向接收容器提供關於自身的資訊。在我們的範例中,接收者 web 可以存取關於來源 db 的資訊。為此,Docker 在容器之間建立了一個安全通道,而無需在容器上外部公開任何連接埠;當我們啟動 db 容器時,我們沒有使用 -P 或 -p 旗標。這是連結的一個重要優點:我們不需要將來源容器(這裡指 PostgreSQL 資料庫)公開到網路。
Docker 以兩種方式將來源容器的連接資訊公開給接收容器
- 環境變數,
- 更新
/etc/hosts檔案。
環境變數
當您連結容器時,Docker 會建立多個環境變數。Docker 會根據 --link 參數自動在目標容器中建立環境變數。它還會公開所有來自來源容器的 Docker 環境變數。這些包括來自以下方面的變數:
- 來源容器 Dockerfile 中的
ENV指令 - 當來源容器啟動時,
docker run指令上的-e、--env和--env-file選項
這些環境變數使目標容器能夠透過程式設計方式,探索與來源容器相關的資訊。
警告請務必了解,容器內所有源自 Docker 的環境變數都會提供給任何連結到該容器的其他容器。如果其中儲存了敏感資料,這可能會產生嚴重的安全隱患。
Docker 會為 --link 參數中列出的每個目標容器設定一個 <alias>_NAME 環境變數。例如,如果一個名為 web 的新容器透過 --link db:webdb 連結到一個名為 db 的資料庫容器,那麼 Docker 會在 web 容器中建立一個 WEBDB_NAME=/web/webdb 變數。
Docker 也為來源容器公開的每個連接埠定義了一組環境變數。每個變數都有一個獨特的字首,格式為 <name>_PORT_<port>_<protocol>
此字首中的組成部分為
- 在
--link參數中指定的別名<name>(例如,webdb) - 公開的
<port>編號 - 一個
<protocol>,可以是 TCP 或 UDP
Docker 使用此字首格式定義三個不同的環境變數
prefix_ADDR變數包含來自 URL 的 IP 位址,例如WEBDB_PORT_5432_TCP_ADDR=172.17.0.82。prefix_PORT變數僅包含來自 URL 的連接埠號碼,例如WEBDB_PORT_5432_TCP_PORT=5432。prefix_PROTO變數僅包含來自 URL 的協定,例如WEBDB_PORT_5432_TCP_PROTO=tcp。
如果容器公開多個連接埠,則會為每個連接埠定義一組環境變數。這意味著,例如,如果一個容器公開 4 個連接埠,Docker 會建立 12 個環境變數,每個連接埠 3 個。
此外,Docker 還會建立一個名為 <alias>_PORT 的環境變數。此變數包含來源容器第一個公開連接埠的 URL。'第一個'連接埠定義為編號最低的公開連接埠。例如,考慮 WEBDB_PORT=tcp://172.17.0.82:5432 變數。如果該連接埠同時用於 tcp 和 udp,則會指定 tcp。
最後,Docker 還會將來源容器中每個源自 Docker 的環境變數作為目標容器中的環境變數公開。對於每個變數,Docker 會在目標容器中建立一個 <alias>_ENV_<name> 變數。該變數的值設定為 Docker 啟動來源容器時所使用的值。
回到我們的資料庫範例,您可以執行 env 指令來列出指定容器的環境變數。
$ docker run --rm --name web2 --link db:db training/webapp env
<...>
DB_NAME=/web2/db
DB_PORT=tcp://172.17.0.5:5432
DB_PORT_5432_TCP=tcp://172.17.0.5:5432
DB_PORT_5432_TCP_PROTO=tcp
DB_PORT_5432_TCP_PORT=5432
DB_PORT_5432_TCP_ADDR=172.17.0.5
<...>
您可以看到 Docker 已經建立了一系列環境變數,其中包含有關來源 db 容器的有用資訊。每個變數都以 DB_ 作為字首,這是從您上面指定的 alias 填充的。如果 alias 是 db1,則變數將以 DB1_ 作為字首。您可以使用這些環境變數來配置您的應用程式以連接到 db 容器上的資料庫。連接是安全且私密的;只有連結的 web 容器可以與 db 容器通訊。
關於 Docker 環境變數的重要注意事項
與 /etc/hosts 檔案 中的主機條目不同,如果來源容器重新啟動,儲存在環境變數中的 IP 位址不會自動更新。我們建議使用 /etc/hosts 中的主機條目來解析連結容器的 IP 位址。
這些環境變數僅為容器中的第一個程序設定。某些守護進程 (daemon),例如 sshd,在為連接生成 shell 時會清除它們。
更新 /etc/hosts 檔案
除了環境變數之外,Docker 還會為來源容器在 /etc/hosts 檔案中新增一個主機條目。這是 web 容器的條目
$ docker run -t -i --rm --link db:webdb training/webapp /bin/bash
root@aed84ee21bde:/opt/webapp# cat /etc/hosts
172.17.0.7 aed84ee21bde
<...>
172.17.0.5 webdb 6e5cdeb2d300 db
您可以看到兩個相關的主機條目。第一個是 web 容器的條目,它使用容器 ID 作為主機名稱。第二個條目使用連結別名來引用 db 容器的 IP 位址。除了您提供的別名之外,如果連結容器的名稱與提供給 --link 參數的別名不同,連結容器的名稱和主機名稱也會新增到 /etc/hosts 中,用於連結容器的 IP 位址。您可以透過這些條目中的任何一個 ping 該主機
root@aed84ee21bde:/opt/webapp# apt-get install -yqq inetutils-ping
root@aed84ee21bde:/opt/webapp# ping webdb
PING webdb (172.17.0.5): 48 data bytes
56 bytes from 172.17.0.5: icmp_seq=0 ttl=64 time=0.267 ms
56 bytes from 172.17.0.5: icmp_seq=1 ttl=64 time=0.250 ms
56 bytes from 172.17.0.5: icmp_seq=2 ttl=64 time=0.256 ms
注意在此範例中,您必須安裝
ping,因為它最初未包含在容器中。
在這裡,您使用 ping 指令透過其主機條目 ping 了 db 容器,該主機條目解析為 172.17.0.5。您可以使用此主機條目來配置應用程式以使用您的 db 容器。
注意您可以將多個接收容器連結到單一來源。例如,您可以將多個 (名稱不同) web 容器連接到您的
db容器。
如果您重新啟動來源容器,連結容器上的 /etc/hosts 檔案會自動更新為來源容器的新 IP 位址,從而允許連結通訊繼續進行。
$ docker restart db
db
$ docker run -t -i --rm --link db:db training/webapp /bin/bash
root@aed84ee21bde:/opt/webapp# cat /etc/hosts
172.17.0.7 aed84ee21bde
<...>
172.17.0.9 db