傳統容器連結

警告

--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 引數中列出的每個目標容器設定一個 <別名>_NAME 環境變數。例如,如果一個名為 web 的新容器透過 --link db:webdb 連結到一個名為 db 的資料庫容器,則 Docker 在 web 容器中建立一個 WEBDB_NAME=/web/webdb 變數。

Docker 還為源容器公開的每個埠定義了一組環境變數。每個變數都具有一個唯一的格式為 <名稱>_PORT_<埠>_<協議> 的字首。

此字首中的元件是

  • --link 引數中指定的別名 <名稱>(例如,webdb
  • 公開的 <埠> 號碼
  • 一個 <協議>,它可以是 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 建立一個名為 <別名>_PORT 的環境變數。此變數包含源容器第一個公開埠的 URL。第一個埠被定義為暴露的埠,其埠號最低。例如,考慮 WEBDB_PORT=tcp://172.17.0.82:5432 變數。如果該埠用於 tcp 和 udp,則指定 tcp 的那個。

最後,Docker 還將源容器中的每個來自 Docker 的環境變數作為目標中的環境變數公開。對於每個變數,Docker 在目標容器中建立一個 <別名>_ENV_<名稱> 變數。該變數的值設定為 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_ 為字首,該字首來自您在上面指定的 別名。如果 別名db1,則變數將以 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