舊版容器連結

警告

--link 標誌是 Docker 的舊版功能。它最終可能會被移除。除非您絕對需要繼續使用它,否則我們建議您使用使用者定義網路來促進兩個容器之間的通訊,而不是使用 --link。使用者定義網路不支援但您可以使用 --link 執行的其中一項功能是在容器之間共用環境變數。但是,您可以使用其他機制(例如磁碟區)以更受控的方式在容器之間共用環境變數。

請參閱 使用者定義橋接器與預設橋接器之間的差異,以瞭解一些使用 --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 主機上 _臨時埠範圍_ 內的隨機高埠。接下來,執行 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

而且您也看到了為什麼這不是一個好主意,因為它將您限制在該特定埠上只有一個容器。

相反,您可以指定一個主機埠範圍來將容器埠繫結到與預設 _臨時埠範圍_ 不同的範圍

$ 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 繫結到主機上 localhost127.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。您也可以自行命名容器。此命名提供了兩個有用的功能

  1. 將執行特定功能的容器命名成易於記憶的方式會很有用,例如將包含 Web 應用程式的容器命名為 web

  2. 它為 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 填入的。如果 aliasdb1,則變數的前綴將是 DB1_。您可以使用這些環境變數來配置您的應用程式,以連接到 db 容器上的資料庫。連接是安全且私密的;只有已連結的 web 容器可以與 db 容器通訊。

關於 Docker 環境變數的重要注意事項

/etc/hosts 檔案中的主機項目不同,如果來源容器重新啟動,儲存在環境變數中的 IP 地址不會自動更新。我們建議使用 /etc/hosts 中的主機項目來解析已連結容器的 IP 地址。

這些環境變數僅針對容器中的第一個行程設定。某些守護行程,例如 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 參數的別名不同)和已連結容器的主機名稱也會新增到已連結容器 IP 地址的 /etc/hosts 中。您可以透過任何這些項目 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