如何设置docker集装箱之间的联系,以便重新启动不会打破它?
我有几个docker集装箱运行
- Nginx的
- networking应用程序1
- networking应用程序2
- PostgreSQL的
由于Nginx需要连接Web应用程序1和2内的Web应用程序服务器,并且Web应用程序需要与postgresql对话,所以我有这样的链接
- Nginx —链接—> Web应用程序1
- Nginx —链接—> Web应用程序2
- Web应用程序1 —链接—> PostgreSQL
- Web应用程序2 —链接—> PostgreSQL
这在开始时效果相当不错,但是,当我开发新版本的Web应用程序1和Web应用程序2时,我需要replace它们。 我所做的是删除networking应用程序容器,设置新的容器,并启动它们。
对于networking应用程序容器,他们的IP地址在第一个将是类似的
- 172.17.0.2
- 172.17.0.3
而且在我replace之后,他们现在有了新的IP地址
- 172.17.0.5
- 172.17.0.6
此时,Nginx容器中暴露的环境variables仍旧指向旧的IP地址。 问题在于如何在不破坏其他容器之间的连接的情况下更换容器? 同样的问题也会发生在PostgreSQL上,如果我想升级PostgreSQL映像版本,我当然需要删除它并运行新的,但是之后我需要重build整个容器图,这不是一个好主意生活服务器操作。
– 链接的效果是静态的,所以它不适用于你的场景(目前没有重新链接,尽pipe你可以删除链接 )。
我们一直在dockerize.it使用两种不同的方法解决这个问题,没有链接或大使(虽然你也可以添加大使)。
1)使用dynamicDNS
一般的想法是,你为数据库(或任何其他服务)指定一个单一名称,并在启动和停止容器时使用实际的IP更新一个短期的DNS服务器。
我们从SkyDock开始。 它适用于两个docker集装箱,DNS服务器和一个自动更新的监视器。 后来,我们转而使用Consul更加自定义(也使用dockerized版本:docker -consul )。
这个(我们还没有尝试过)的发展将是设置etcd或类似的,并使用其自定义API来了解IP和端口。 该软件也应该支持dynamic重新configuration。
2)使用docker bridge ip
在公开容器端口时,您可以将它们绑定到docker0
桥,它具有(或可以具有)众所周知的地址。
使用新版本replace容器时,只需使新容器在同一IP上发布相同的端口即可。
这更简单但也更有限。 如果您运行类似的软件(例如,两个容器无法在docker0
桥上的3306端口上docker0
),etcétera等,您可能会有端口冲突…所以我们目前的最爱是选项1。
链接是针对特定的容器,而不是基于容器的名称。 所以,当你删除一个容器,链接断开,新的容器(即使具有相同的名称)不会自动取代它。
新的networkingfunction允许您通过名称连接到容器,因此如果您创build新的networking,则连接到该networking的任何容器都可以通过名称到达其他容器。 例:
1)创build新的networking
$ docker network create <network-name>
2)将容器连接到networking
$ docker run --net=<network-name> ...
要么
$ docker network connect <network-name> <container-name>
3)按名称Ping容器
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
不同,新的networking不会创build环境variables,也不会与其他容器共享环境variables。
此function目前不支持别名
你可以使用大使容器 。 但是不要把大使容器连接到你的客户端,因为这会产生与上面相同的问题。 相反,使用Docker主机上的大使容器的暴露端口(通常为172.17.42.1)。 例:
postgres数量:
$ 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
大使 – postgres容器:
$ docker run -d --name pg_ambassador --link postgres:postgres -p 5432:5432 ctlc/ambassador
现在,您可以启动postgresql客户端容器,而无需链接大使容器,并在网关主机(通常为172.17.42.1)上访问postgresql:
$ 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=#
现在,您可以重新启动大使容器,而不必重新启动客户端。
如果有人仍然好奇,则必须使用每个Docker容器的/ etc / hosts文件中的主机条目,并且不应依赖ENVvariables,因为它们不会自动更新。
每个链接的容器都有一个主机文件条目,格式为LINKEDCONTAINERNAME_PORT_PORTNUMBER_TCP等。
以下是来自Docker 文档
有关Docker环境variables的重要说明
与/ etc / hosts文件中的主机条目不同,如果源容器重新启动,则存储在环境variables中的IP地址不会自动更新。 我们build议使用/ etc / hosts中的主机条目来parsing链接的容器的IP地址。
这些环境variables仅为容器中的第一个进程设置。 一些守护进程(如sshd)会在产生shell连接时擦除它们。
这是在3周前docker的实验版本中包含的,引入了服务: https : //github.com/docker/docker/blob/master/experimental/networking.md
您应该能够通过使用--publish-service <name>
参数运行--publish-service <name>
容器来获得dynamic链接。 这个名字将通过DNS访问。 这在容器重启时是持久的(只要你用相同的服务名称重启容器)
您可以使用名称的dockerlinks来解决这个问题。
最基本的设置是首先创build一个命名的数据库容器:
$ sudo docker run -d --name db training/postgres
然后创build一个连接到数据库的Web容器:
$ sudo docker run -d -P --name web --link db:db training/webapp python app.py
有了这个,你不需要手动连接容器与他们的IP地址。
使用OpenSVC方法,您可以通过以下方法解决:
- 使用具有自己的IP地址/ DNS名称的服务(最终用户将连接到的服务)
- 告诉docker公开端口到这个特定的IP地址(“–ip”docker选项)
- configuration您的应用程序连接到服务的IP地址
每次更换容器时,都确定它将连接到正确的IP地址。
这里的教程=> Docker多容器与OpenSVC
不要错过tuto结尾处的“复杂编排”部分,它可以帮助您以正确的顺序启动/停止容器(1个postgresql子集+ 1个webapp子集+ 1个nginx子集)
主要的缺点是将webapp和PostgreSQL端口暴露给公共地址,实际上只有nginx tcp端口需要公开。
您也可以尝试使用中间容器的大使方法,以保持链接完好无损(请参阅https://docs.docker.com/articles/ambassador_pattern_linking/ )以获取更多信息
您可以将图像的连接端口绑定到主机上的固定端口,并将服务configuration为使用它们。
这也有它的缺点,但它可能适用于你的情况。
另一种select是使用--net container:$CONTAINER_ID
选项。
第1步:创build“networking”容器
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步:将服务注入“networking”容器
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
只要你不接触“networking”容器,链接的IP地址不应该改变。
networking范围的别名是你需要的是这种情况。 这是一个相当新的特性,可以用来“发布”一个为整个networking提供服务的容器,而不像只能从一个容器访问的链接别名。
它不会在容器之间添加任何依赖关系 – 只要两者都在运行,它们就可以进行通信,无论重新启动,更换和启动顺序如何。 它在内部使用DNS,我相信,而不是/ etc / hosts
像这样使用它: docker run --net=some_user_definied_nw --net-alias postgres ...
并且您可以使用来自同一networking上的任何容器的别名连接到它。
不能在默认networking上工作,不幸的是,你必须创build一个与docker network create <network>
,然后使用它与--net=<network>
为每个容器( 撰写支持它以及 )。
除了容器被closures,因此别名不可用,多个容器也可以共享一个别名,在这种情况下,不能保证它将被parsing到正确的别名。 但在某些情况下,可能有助于无缝升级。
到目前为止,还没有很好的文档logging,通过阅读手册页很难弄清楚。