如何在主机上使用容器主机名访问Docker 1.10中的容器

43
我有一个带嵌入式DNS服务的Docker版本1.10。
我在docker-compose文件中创建了两个服务容器,它们可以通过主机名和IP相互访问,但是当我想要从主机机器访问它们之一时,只有IP可用而无法使用主机名。
所以,在Docker 1.10中是否可能通过容器名称从主机机器访问Docker容器?
更新:
docker-compose.yml
version: '2'
services:
    service_a:
        image: nginx
        container_name: docker_a
        ports:
           - 8080:80
    service_b:
        image: nginx
        container_name: docker_b
        ports:
            - 8081:80

然后我通过命令启动它:docker-compose up --force-recreate

当我运行以下命令时:

  • docker exec -i -t docker_a ping -c4 docker_b - 它可以正常工作
  • docker exec -i -t docker_b ping -c4 docker_a - 它可以正常工作
  • ping 172.19.0.2 - 它可以正常工作(172.19.0.2docker_b 的 IP 地址)
  • ping docker_a - 失败

docker network inspect test_default 的结果为:

[
    {
        "Name": "test_default",
        "Id":   "f6436ef4a2cd4c09ffdee82b0d0b47f96dd5aee3e1bde068376dd26f81e79712",
        "Scope": "local",
        "Driver": "bridge",
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.19.0.0/16",
                    "Gateway": "172.19.0.1/16"
                }
            ]
        },
        "Containers": {
             "a9f13f023761123115fcb2b454d3fd21666b8e1e0637f134026c44a7a84f1b0b": {
                "Name": "docker_a",
                "EndpointID":     "a5c8e08feda96d0de8f7c6203f2707dd3f9f6c3a64666126055b16a3908fafed",
                "MacAddress": "02:42:ac:13:00:03",
                "IPv4Address": "172.19.0.3/16",
                "IPv6Address": ""
            },
                "c6532af99f691659b452c1cbf1693731a75cdfab9ea50428d9c99dd09c3e9a40": {
                "Name": "docker_b",
                "EndpointID":     "28a1877a0fdbaeb8d33a290e5a5768edc737d069d23ef9bbcc1d64cfe5fbe312",
                "MacAddress": "02:42:ac:13:00:02",
                "IPv4Address": "172.19.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {}
    }
]

你能详细说明一下吗?你使用什么命令,得到了什么输出? - user2915097
当然可以,谢谢你。docker-compose.yml - Adam Bernau
2
@warmoverflow:是的,可以用这种方式来做,但是IP地址是动态分配给容器的。这样每次更改都需要手动更新hosts文件,有点麻烦。所以我想问一下,是否有可能在不修改hosts文件或使用额外的发现服务的情况下解决这个问题呢? - Adam Bernau
我搜索了一下,似乎没有第三方工具或脚本是不可能的。为什么你需要在没有任何端口映射的情况下直接从主机访问容器呢? - Xiongbing Jin
@warmoverflow:我不需要它用于生产,而是用于开发。我需要它来进行调试和开发工具集成。 - Adam Bernau
显示剩余3条评论
5个回答

16

正如在这里回答的那样,有一个名为DNS代理服务器的软件解决方案可供使用。

$ sudo ./dns-proxy-server

$ docker run --rm --hostname nginx.dev nginx
$ ping nginx.dev
PING nginx.dev (172.17.0.4) 56(84) bytes of data.
64 bytes from 172.17.0.4 (172.17.0.4): icmp_seq=1 ttl=64 time=0.043 ms
64 bytes from 172.17.0.4 (172.17.0.4): icmp_seq=2 ttl=64 time=0.022 ms

3
除非我使用--hostname创建容器,否则它无法工作。我不希望修改正在运行的容器的设置。它应该使用默认主机名,即容器名称,就像从容器内部一样工作。有什么办法吗? - Dojo

14

为了特别解决这个问题,我创建了一个简单的"etc/hosts"域注入工具,用于解析主机上本地Docker容器的名称。只需运行:

docker run -d \
    -v /var/run/docker.sock:/tmp/docker.sock \
    -v /etc/hosts:/tmp/hosts \
    --name docker-hoster \
    dvdarias/docker-hoster
您可以通过容器名称主机名容器ID以及每个网络所声明的网络别名来访问容器。
当容器启动时,它们会自动注册,并在暂停、死亡或停止时被移除。

很棒的产品!不过如果它是一个无根容器,并且只使用docker-socket-proxy来访问套接字API(或者脚本运行在docker-socket-proxy本身上),那就更好了。 - Shōgun8

13

这是我的做法。

我编写了一个名为 dnsthing 的 Python 脚本,它监听 Docker 事件 API 以获取启动或停止的容器。脚本维护了一个类似 hosts 的文件,其中包含容器的名称和地址。容器的命名方式为 <container_name>.<network>.docker,例如,如果我运行以下命令:

docker run --rm --name mysql -e MYSQL_ROOT_PASSWORD=secret  mysql

我得到了这个:

172.17.0.2 mysql.bridge.docker

我随后运行一个指向这个hosts文件的dnsmasq进程。具体来说,我使用以下配置运行一个dnsmasq实例:

listen-address=172.31.255.253
bind-interfaces
addn-hosts=/run/dnsmasq/docker.hosts
local=/docker/
no-hosts
no-resolv

我这样运行dnsthing脚本:

dnsthing -c "systemctl restart dnsmasq_docker" \
  -H /run/dnsmasq/docker.hosts --verbose

因此:

  • dnsthing 在容器停止/启动时更新/run/dnsmasq/docker.hosts
  • 更新后, dnsthing 运行systemctl restart dnsmasq_docker
  • dnsmasq_docker 使用上述配置运行dnsmasq,绑定到具有地址172.31.255.253的本地桥接接口。
  • 我的系统上“主”dnsmasq进程由NetworkManager维护,并使用来自/etc/NetworkManager/dnsmasq.d/dockerdns的配置:

  • server=/docker/172.31.255.253
    

    这意味着dnsmasq会将所有以.docker域名结尾的主机请求传递给docker_dnsmasq服务。

显然,这需要进行一些设置才能将所有内容整合在一起,但完成后似乎就可以正常工作了:

$ ping -c1  mysql.bridge.docker
PING mysql.bridge.docker (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.087 ms

--- mysql.bridge.docker ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.087/0.087/0.087/0.000 ms

1
这看起来很有趣,谢谢。但是我还有一些理解上的漏洞…… dnsmasq_docker 是怎么回事? - sjmeverett

4

最简单的方法是通过向hosts文件中添加条目来完成此操作

  • 对于Linux:在/etc/hosts文件中添加127.0.0.1 docker_a docker_b
  • 对于Mac:类似于Linux但使用虚拟机的IP地址docker-machine ip default

1
类似于@larsks,我也写了一个Python脚本,但将其实现为服务。这是它:https://github.com/nicolai-budico/dockerhosts 它使用参数--hostsdir=/var/run/docker-hosts启动dnsmasq,并在每次运行容器列表发生更改时更新文件/var/run/docker-hosts/hosts。 一旦文件/var/run/docker-hosts/hosts被更改,dnsmasq会自动更新其映射,容器在一秒钟内就可以通过主机名访问。
$ docker run -d --hostname=myapp.local.com --rm -it ubuntu:17.10
9af0b6a89feee747151007214b4e24b8ec7c9b2858badff6d584110bed45b740

$ nslookup myapp.local.com
Server:         127.0.0.53
Address:        127.0.0.53#53

Non-authoritative answer:
Name:   myapp.local.com
Address: 172.17.0.2

有安装和卸载脚本。您需要允许系统与此dnsmasq实例进行交互。我在systemd-resolved中注册了它:

$ cat /etc/systemd/resolved.conf

[Resolve]
DNS=127.0.0.54
#FallbackDNS=
#Domains=
#LLMNR=yes
#MulticastDNS=yes
#DNSSEC=no
#Cache=yes
#DNSStubListener=udp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接