舊版容器連結

警告

--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 引數的別名不同)和連結容器的主機名也會被新增到 /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