遗留容器链接

警告

--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

这将从包含PostgreSQL数据库的training/postgres镜像中创建一个名为db的新容器。

现在,您需要删除之前创建的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_为前缀,这是从你上面指定的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

你可以看到两个相关的主机条目。第一个是使用容器ID作为主机名的web容器的条目。第二个条目使用链接别名来引用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命令来pingdb容器,使用其主机条目,该条目解析为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