如何通过“主机名”在 Docker 容器之间进行通信

Posted

技术标签:

【中文标题】如何通过“主机名”在 Docker 容器之间进行通信【英文标题】:How to communicate between Docker containers via "hostname" 【发布时间】:2015-08-13 05:33:45 【问题描述】:

我计划将我的单体服务器拆分为许多小型 docker 容器,但尚未找到“容器间通信”的良好解决方案。这是我的目标场景:

我知道如何将容器链接在一起以及如何公开端口,但这些解决方案都没有让我满意。

是否有任何解决方案可以像在传统服务器网络中那样通过主机名(容器名)在容器之间进行通信?

【问题讨论】:

我最近写了一个document,完全符合您的要求。它基本上记录了如何安装多个容器(每个进程一个)并使它们集成。 “容器间通信”是part of the game。 我刚刚找到了Tumtum Blog,偶然发现了this paragraph in the official Docker documentation。我不知道我是否一直错过这一段,或者它是否是新添加的,但这应该正是我需要的:) docker 1.10 已经发布,docker connect 很棒 (github.com/docker/docker/blob/…)。见my edited answer below 我认为你应该试试docker-compose。效果很好。 【参考方案1】:

新的网络功能允许您通过以下方式连接到容器 他们的名字,所以如果你创建一个新网络,任何连接到的容器 该网络可以通过其名称访问其他容器。示例:

1) 创建新网络

$ docker network create <network-name>       

2) 将容器连接到网络

$ 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

参见文档的this 部分;

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

此功能目前不支持别名

【讨论】:

效果很好。为什么默认网络默认不启用这个?? 不太明显的部分是,您需要重新启动在不同容器中运行的应用程序。那么容器A如何让运行在容器B中的应用重启呢?显然,似乎需要某种通信总线。我的首要任务是使用 Redis 进行信令和容器间通信。所以所有容器都订阅了 redis chanel,它们会在那里进行对话...... docker-compose 中已发布端口的变化怎么样.yml 文件需要完整的docker-compose down,up,restart ? 这正是我一整天都在寻找的东西!不知道您可以通过容器名称/ID 来引用网络节点。谢谢! @Stéphane 由于向后兼容性,它在默认的bridge 网络中被禁用,但是是的,我同意,它应该默认启用!【参考方案2】:

编辑:在 Docker 1.9 之后,docker network 命令(见下文https://***.com/a/35184695/977939)是实现此目的的推荐方法。


我的解决方案是在主机上设置一个 dnsmasq 以自动更新 DNS 记录:“A”记录具有容器的名称并自动指向容器的 IP 地址(每 10 秒)。 automatic updating script 粘贴在这里:

#!/bin/bash

# 10 seconds interval time by default
INTERVAL=$INTERVAL:-10

# dnsmasq config directory
DNSMASQ_CONFIG=$DNSMASQ_CONFIG:-.

# commands used in this script
DOCKER=$DOCKER:-docker
SLEEP=$SLEEP:-sleep
TAIL=$TAIL:-tail

declare -A service_map

while true
do
    changed=false
    while read line
    do
        name=$line##* 
        ip=$($DOCKER inspect --format '.NetworkSettings.IPAddress' $name)
        if [ -z $service_map[$name] ] || [ $service_map[$name] != $ip ] # IP addr changed
        then
            service_map[$name]=$ip
            # write to file
            echo $name has a new IP Address $ip >&2
            echo "host-record=$name,$ip"  > "$DNSMASQ_CONFIG/docker-$name"
            changed=true
        fi
    done < <($DOCKER ps | $TAIL -n +2)

    # a change of IP address occured, restart dnsmasq
    if [ $changed = true ]
    then
        systemctl restart dnsmasq
    fi

    $SLEEP $INTERVAL
done

确保您的 dnsmasq 服务在 docker0 上可用。然后,使用--dns HOST_ADDRESS 启动您的容器以使用此迷你 dns 服务。

参考:http://docs.blowb.org/setup-host/dnsmasq.html

【讨论】:

这看起来很有趣,比我的 --link 答案更有弹性。 +1 @VonC 看起来新的 libnetwork 可能会取代此解决方法。让我们看看吧。 @xuhdev 我在docs.blowb.org/setup-host/dnsmasq.html 中设置了dnsmasq。但是我在使用 docker 容器中的 dig 时遇到问题,它会超时。但是 ping 主机 docker0 接口 ip 有效。还可以使用来自 docker host 的相同 docker0 ip 进行挖掘。你有什么建议吗? @Satheesh 也许是您的防火墙设置阻止了您的容器从主机查询 DNS? @xuhdev 谢谢它是我主机中的防火墙,导致了这个问题。一旦我弯下防火墙,我的容器就会与主机上的 dnsmasq 通信【参考方案3】:

应该是what --link is for,至少对于主机名部分来说是这样。 使用docker 1.10, and PR 19242,那就是:

docker network create --net-alias=[]: Add network-scoped alias for the container

(见下文最后一节)

这就是Updating the /etc/hosts file详情

除了环境变量之外,Docker 还将源容器的主机条目添加到 /etc/hosts 文件中。

例如,启动一个 LDAP 服务器:

docker run -t  --name openldap -d -p 389:389 larrycai/openldap

并定义一个图像来测试该 LDAP 服务器:

FROM ubuntu
RUN apt-get -y install ldap-utils
RUN touch /root/.bash_aliases
RUN echo "alias lds='ldapsearch -H ldap://internalopenldap -LL -b
ou=Users,dc=openstack,dc=org -D cn=admin,dc=openstack,dc=org -w
password'" > /root/.bash_aliases
ENTRYPOINT bash

您可以使用 --link 在测试映像中将“openldap”容器公开为“internalopenldap”:

 docker run -it --rm --name ldp --link openldap:internalopenldap ldaptest

然后,如果您键入“lds”,则该别名将起作用:

ldapsearch -H ldap://internalopenldap ...

那会返回人。意思是internalopenldap 可以从ldaptest 图像正确到达。


当然,docker 1.7 会添加libnetwork,它提供了用于连接容器的原生 Go 实现。请参阅blog post。 它引入了更完整的架构,带有容器网络模型 (CNM)

这将使用新的“network”命令更新 Docker CLI,并记录如何使用“-net”标志将容器分配给网络。


docker 1.10 有一个新部分 Network-scoped alias,现在是 officially documented in network connect

虽然链接提供了在容器内本地化的私有名称解析,但网络范围的别名为特定网络范围内的任何其他容器通过备用名称发现容器提供了一种方法。 与由服务的消费者定义的链接别名不同,网络范围的别名由向网络提供服务的容器定义。

继续上面的示例,使用网络别名在 isolated_nw 中创建另一个容器。

$ docker run --net=isolated_nw -itd --name=container6 -alias app busybox
8ebe6767c1e0361f27433090060b33200aac054a68476c3be87ef4005eb1df17

--alias=[]         

为容器添加网络范围的别名

您可以使用--link 选项将另一个容器链接到首选别名

您可以暂停、重新启动和停止连接到网络的容器。暂停的容器保持连接,并且可以通过网络检查来显示。当容器停止时,它不会出现在网络上,直到你重新启动它。

如果指定,则在重新启动停止的容器时重新应用容器的 IP 地址。如果 IP 地址不再可用,则容器无法启动。

保证 IP 地址可用的一种方法是在创建网络时指定 --ip-range,并从该范围之外选择静态 IP 地址。这样可以确保当该容器不在网络上时,不会将 IP 地址提供给另一个容器。

$ docker network create --subnet 172.20.0.0/16 --ip-range 172.20.240.0/20 multi-host-network

$ docker network connect --ip 172.20.128.2 multi-host-network container2
$ docker network connect --link container1:c1 multi-host-network container2

【讨论】:

--link 的问题是,如果不重新启动链接的容器,您将无法重新启动容器。当您查看我的图形时,重新启动 mysql 容器将导致其他容器的级联重新启动。【参考方案4】:

编辑:它不再是前沿:http://blog.docker.com/2016/02/docker-1-10/

原答案 我和它战斗了一整夜。 如果你不怕流血,最新版本的Docker engine 和Docker compose 都实现了libnetwork。

使用正确的配置文件(需要放在版本 2 中),您将创建可以互相看到的服务。而且,奖励,你也可以使用 docker-compose 来扩展它们(你可以扩展任何你想要的不绑定主机端口的服务)

这是一个例子file

version: "2"
services:
  router:
    build: services/router/
    ports:
      - "8080:8080"
  auth:
    build: services/auth/
  todo:
    build: services/todo/
  data:
    build: services/data/

还有这个新版 compose 文件的参考: https://github.com/docker/compose/blob/1.6.0-rc1/docs/networking.md

【讨论】:

【参考方案5】:

据我所知,仅使用 Docker 是不可能的。您需要一些 DNS 将容器 ip:s 映射到主机名。

如果您想要开箱即用的解决方案。一种解决方案是使用例如Kontena。它带有 Weave 的网络覆盖技术,该技术用于为每个服务创建虚拟专用 LAN 网络,每个服务都可以通过 service_name.kontena.local-address 访问。

这是 Wordpress 应用程序的 YAML 文件的简单示例,其中 Wordpress 服务使用 wordpress-mysql.kontena.local 地址连接到 MySQL 服务器:

wordpress:                                                                         
  image: wordpress:4.1                                                             
  stateful: true                                                                   
  ports:                                                                           
    - 80:80                                                                      
  links:                                                                           
    - mysql:wordpress-mysql                                                        
  environment:                                                                     
    - WORDPRESS_DB_HOST=wordpress-mysql.kontena.local                              
    - WORDPRESS_DB_PASSWORD=secret                                                 
mysql:                                                                             
  image: mariadb:5.5                                                               
  stateful: true                                                                   
  environment:                                                                     
    - MYSQL_ROOT_PASSWORD=secret

【讨论】:

以上是关于如何通过“主机名”在 Docker 容器之间进行通信的主要内容,如果未能解决你的问题,请参考以下文章

如何设置 docker 容器以通过主机名相互引用?

使用 Gitlab CI 时如何设置 docker 容器的主机名

Docker 1.10 通过主机名访问容器

Docker网络

在 Elastic Beanstalk 上设置 Docker 容器主机名

为啥 PHP docker 容器无法查找 MYSQL 容器的主机名?