如何在 Docker 容器之间建立链接,以便重新启动不会破坏它?

我有几个码头集装箱运行如下:

  • Nginx
  • 网络应用程序1
  • 网络应用程序2
  • PostgreSQL

因为 Nginx 需要连接到 web app 1和2中的 web 应用服务器,而 web 应用需要与 PostgreSQL 交谈,所以我有这样的链接:

  • Nginx ——-link ——-> Web app 1
  • Nginx ——-link ——-> Web app 2
  • Web 应用程序1——-link ——-> PostgreSQL
  • Web 应用程序2——-link ——-> PostgreSQL

一开始效果不错。然而,当我开发一个新版本的 Web 应用程序1和 Web 应用程序2时,我需要替换它们。我所做的就是移除 web 应用程序容器,设置新的容器并启动它们。

对于 web 应用程序容器,它们的 IP 地址一开始应该是这样的:

  • 172.17.0.2
  • 172.17.0.3

在我替换他们之后,他们会有新的 IP 地址:

  • 172.17.0.5
  • 172.17.0.6

现在,Nginx 容器中暴露的环境变量仍然指向旧的 IP 地址。问题来了。如何在不破坏容器之间连接的情况下更换容器?同样的问题也会发生在 PostgreSQL 上。如果我想升级 PostgreSQL 映像版本,我当然需要删除它并运行新的映像版本,但是我需要重新构建整个容器图,所以这对于实际的服务器操作来说并不理想。

24515 次浏览

您可以使用带名称的 码头连接器来解决这个问题。

最基本的设置是首先创建一个 名字数据库容器:

$ sudo docker run -d --name db training/postgres

然后创建一个连接到 db 的 web 容器:

$ sudo docker run -d -P --name web --link db:db training/webapp python app.py

有了这个,您就不需要手动将容器与它们的 IP 地址连接起来。

您可以将映像的连接端口绑定到主机上的固定端口,然后配置服务来使用它们。

这也有它的缺点,但它可能在你的情况下工作。

使用 OpenSVC 方法,您可以通过以下方法解决:

  • 使用具有自己的 ip 地址/dns 名称的服务(最终用户将连接到的服务)
  • 告诉 docker 将端口公开到这个特定的 ip 地址(“—— ip”docker 选项)
  • 将应用程序配置为连接到服务 ip 地址

每次更换容器时,都要确保它连接到正确的 ip 地址。

教程在此 = > 基于 OpenSVC 的 Docker 多容器

不要错过 tuto 结尾的“复杂编排”部分,它可以帮助您按照正确的顺序启动/停止容器(1 postgreql 子集 + 1 webapp 子集 + 1 nginx 子集)

主要缺点是您将 webapp 和 PostgreSQL 端口暴露给公共地址,实际上只有 nginx tcp 端口需要暴露给公共地址。

您还可以尝试使用大使方法,使用一个中间容器来保持链接完整... (参见 https://docs.docker.com/articles/ambassador_pattern_linking/)以获得更多信息

你可以使用 大使集装箱。但是不要将大使容器链接到您的客户机,因为这会产生与上面相同的问题。相反,在 docker 主机上使用大使容器的公开端口(通常为172.17.42.1)。例如:

海报数量:

$ docker run --name PGDATA -v /data/pgdata/data:/data -v /data/pgdata/log:/var/log/postgresql phusion/baseimage:0.9.10 true

Postgres-容器:

$ docker run -d --name postgres --volumes-from PGDATA -e USER=postgres -e PASS='postgres' paintedfox/postgresql

邮递用大使-容器:

$ docker run -d --name pg_ambassador --link postgres:postgres -p 5432:5432 ctlc/ambassador

现在,您可以在不链接大使容器和访问网关主机(通常是172.17.42.1)上的 postgreql 客户机容器的情况下启动 postgreql:

$ docker run --rm -t -i paintedfox/postgresql /bin/bash
root@b94251eac8be:/# PGHOST=$(netstat -nr | grep '^0\.0\.0\.0 ' | awk '{print $2}')
root@b94251eac8be:/# echo $PGHOST
172.17.42.1
root@b94251eac8be:/#
root@b94251eac8be:/# psql -h $PGHOST --user postgres
Password for user postgres:
psql (9.3.4)
SSL connection (cipher: DHE-RSA-AES256-SHA, bits: 256)
Type "help" for help.


postgres=#
postgres=# select 6*7 as answer;
answer
--------
42
(1 row)


bpostgres=#

现在您可以重新启动大使容器,而无需重新启动客户端。

--link的效果是静态的,因此它不适用于您的场景(目前没有重新链接,尽管您可以 删除链接)。

我们在 dockerize.it 中使用了两种不同的方法来解决这个问题,没有链接或大使(尽管也可以添加大使)。

1)使用动态域名解析系统

通常的想法是为数据库(或任何其他服务)指定一个名称,并在启动和停止容器时使用实际的 IP 更新短命的 DNS 服务器。

我们从 天空码头开始。它使用两个 Docker 容器,DNS 服务器和一个监视器来自动更新。后来,我们使用 执政官(也使用了一个修改过的版本: 码头领事)进行了更多的定制。

这种方法的一个改进(我们还没有尝试过)是设置 etcd 或类似的方法,并使用其自定义 API 来学习 IP 和端口。该软件也应该支持动态重构。

2)使用 Docker Bridge ip

当公开容器端口时,您只需将它们绑定到 docker0桥,该桥具有(或可能具有)一个众所周知的地址。

当用新版本替换容器时,只需让新容器在相同的 IP 上发布相同的端口。

这更为简单,但也更为有限。如果您运行类似的软件,您可能会有端口冲突(例如,两个容器无法监听 docker0桥上的3306端口) ,等等... ... 所以我们目前最喜欢的是选项1。

如果还有人感到好奇,那么您必须使用每个 Docker 容器的/etc/hosts 文件中的主机条目,并且不应该依赖于 ENV 变量,因为它们不会自动更新。

每个链接容器都有一个主机文件条目,格式为 LINKEDCONTAINERNAME _ PORT _ PORTNUMBER _ TCP 等。

以下内容来自 Docker 医生

关于 Docker 环境变量的重要说明

与/etc/hosts 文件中的主机条目不同,存储在 环境变量不会自动更新 中的主机条目 Etc/hosts 解析链接容器的 IP 地址。

中的第一个进程设置这些环境变量 一些守护进程(如 sshd)在产生时会清除它们 连接壳体。

3周前,随着服务 https://github.com/docker/docker/blob/master/experimental/networking.md的引入,在 Docker 的实验构建中包含了这个功能

通过运行带有 --publish-service <name>参数的 docker 容器,您应该能够获得一个适当的动态链接。此名称将可通过 DNS 访问。这在容器重新启动时是持久的(只要您使用当然相同的服务名重新启动容器)

另一种选择是使用 --net container:$CONTAINER_ID选项。

步骤1: 创建“网络”容器

docker run --name db_net ubuntu:14.04 sleep infinity
docker run --name app1_net --link db_net:db ubuntu:14.04 sleep infinity
docker run --name app2_net --link db_net:db ubuntu:14.04 sleep infinity
docker run -p 80 -p 443 --name nginx_net --link app1_net:app1 --link app2_net:app2 ubuntu:14.04 sleep infinity

步骤2: 将服务注入到“网络”容器中

docker run --name db --net container:db_net pgsql
docker run --name app1 --net container:app1_net app1
docker run --name app2 --net container:app1_net app2
docker run --name nginx --net container:app1_net nginx

只要你不触摸“网络”容器,你的链接的 IP 地址不应该改变。

链接 用于特定的容器,而不是基于容器的名称。因此,当您删除一个容器时,链接就断开了,新的容器(即使名称相同)也不会自动取代它的位置。

新的网络特性允许您通过以下方式连接到容器 因此,如果创建一个新网络,任何连接到 例如:

1)建立新的网络

$ docker network create <network-name>

2)将容器连接到网络

$ docker run --net=<network-name> ...

或者

$ docker network connect <network-name> <container-name>

3)按名称命名的乒乓球容器

docker exec -ti <container-name-A> ping <container-name-B>


64 bytes from c1 (172.18.0.4): icmp_seq=1 ttl=64 time=0.137 ms
64 bytes from c1 (172.18.0.4): icmp_seq=2 ttl=64 time=0.073 ms
64 bytes from c1 (172.18.0.4): icmp_seq=3 ttl=64 time=0.074 ms
64 bytes from c1 (172.18.0.4): icmp_seq=4 ttl=64 time=0.074 ms

请参阅 这个部分的文档;

注意: 与传统的 links不同,新的网络 不会创建环境变量,也不与其他容器共享环境变量。

这个特性目前不支持别名

网络作用域的别名 就是您需要的这种情况。这是一个相当新的特性,可用于“发布”为整个网络提供服务的容器,不像链接别名只能从一个容器访问。

它不会在容器之间添加任何依赖关系ーー只要两个容器都在运行,它们就可以进行通信,而无需考虑重新启动、替换和启动顺序。我相信,它在内部使用 DNS,而不是/etc/hosts

像这样使用它: docker run --net=some_user_definied_nw --net-alias postgres ...,您可以使用该别名从同一网络上的任何容器连接到它。

不能在默认网络上工作,不幸的是,您必须用 docker network create <network>创建一个,然后用 --net=<network>为每个容器(作曲也支持它)创建一个。

除了容器被关闭,因此无法通过别名访问外,多个容器还可以共享一个别名,在这种情况下,不能保证它会被解析到正确的别名。但在某些情况下,这可能有助于无缝升级。

到目前为止,所有这些都没有得到很好的记录,仅仅通过阅读手册页就很难搞清楚。