传统容器链接
警告
--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 参数中列出的每个目标容器设置一个 <alias>_NAME 环境变量。例如,如果一个名为 web 的新容器通过 --link db:webdb 链接到名为 db 的数据库容器,那么 Docker 会在 web 容器中创建一个 WEBDB_NAME=/web/webdb 变量。
Docker 还为源容器暴露的每个端口定义了一组环境变量。每个变量都有一个唯一的前缀,格式为 <name>_PORT_<port>_<protocol>
此前缀中的组件包括:
- 在
--link参数中指定的别名<name>(例如,webdb) - 暴露的
<port>号 - a
<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 地址。
这些环境变量仅为容器中的第一个进程设置。某些守护进程(如 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