传统容器链接
警告
--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 绑定到主机上的 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
参数中列出的每个目标容器设置一个 <别名>_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