【问题标题】:Access docker container from host using containers name使用容器名称从主机访问 docker 容器
【发布时间】:2016-09-11 13:15:15
【问题描述】:

我正在开发一项服务,并使用 docker compose 来旋转诸如 postgres、redis、elasticsearch 之类的服务。我有一个基于 RubyOnRails 的 Web 应用程序,可以读写所有这些服务。

这是我的docker-compose.yml

version: '2'

services:
  redis:
    image: redis:2.8
    networks:
      - frontapp

  elasticsearch:
    image: elasticsearch:2.2
    networks:
      - frontapp

  postgres:  
    image: postgres:9.5
    environment:
      POSTGRES_USER: elephant
      POSTGRES_PASSWORD: smarty_pants
      POSTGRES_DB: elephant
    volumes:
      - /var/lib/postgresql/data
    networks:
      - frontapp

networks:
  frontapp:
    driver: bridge

我可以在这个网络中 ping 容器

$ docker-compose run redis /bin/bash
root@777501e06c03:/data# ping postgres
PING postgres (172.20.0.2): 56 data bytes
64 bytes from 172.20.0.2: icmp_seq=0 ttl=64 time=0.346 ms
64 bytes from 172.20.0.2: icmp_seq=1 ttl=64 time=0.047 ms
...

到目前为止一切顺利。现在我想在我的主机上运行 ruby​​ on rails 应用程序,但能够使用postgresql://username:password@postgres/database 之类的 url 访问 postgres 实例,目前这是不可能的

$ ping postgres
ping: unknown host postgres

我可以在 docker 中看到我的网络

$ docker network ls
NETWORK ID          NAME                DRIVER
ac394b85ce09        bridge              bridge              
0189d7e86b33        elephant_default    bridge              
7e00c70bde3b        elephant_frontapp   bridge              
a648554a72fa        host                host                
4ad9f0f41b36        none                null 

我可以看到它的接口

$ ifconfig 
br-0189d7e86b33 Link encap:Ethernet  HWaddr 02:42:76:72:bb:c2  
          inet addr:172.18.0.1  Bcast:0.0.0.0  Mask:255.255.0.0
          inet6 addr: fe80::42:76ff:fe72:bbc2/64 Scope:Link
          UP BROADCAST MULTICAST  MTU:1500  Metric:1
          RX packets:36 errors:0 dropped:0 overruns:0 frame:0
          TX packets:60 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:2000 (2.0 KB)  TX bytes:8792 (8.7 KB)

br-7e00c70bde3b Link encap:Ethernet  HWaddr 02:42:e7:d1:fe:29  
          inet addr:172.20.0.1  Bcast:0.0.0.0  Mask:255.255.0.0
          inet6 addr: fe80::42:e7ff:fed1:fe29/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:1584 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1597 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:407137 (407.1 KB)  TX bytes:292299 (292.2 KB)
...

但我不确定下一步该怎么做。我试着玩了一下/etc/resolv.conf,主要是nameserver指令,但是没有效果。

对于如何正确配置此设置的任何建议,我将不胜感激。

更新

浏览互联网资源后,我设法为盒子分配了静态 IP 地址。就目前而言,这足以让我继续开发。这是我目前的docker-compose.yml

version: '2'

services:
  redis:
    image: redis:2.8
    networks:
      frontapp:
        ipv4_address: 172.25.0.11

  elasticsearch:
    image: elasticsearch:2.2
    networks:
      frontapp:
        ipv4_address: 172.25.0.12

  postgres:  
    image: postgres:9.5
    environment:
      POSTGRES_USER: elephant
      POSTGRES_PASSWORD: smarty_pants
      POSTGRES_DB: elephant
    volumes:
      - /var/lib/postgresql/data
    networks:
      frontapp:
        ipv4_address: 172.25.0.10

networks:
  frontapp:
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: 172.25.0.0/16
          gateway: 172.25.0.1

【问题讨论】:

    标签: docker docker-compose


    【解决方案1】:

    从外部无法看到 docker 容器的主机名。您可以做的是为容器分配一个名称,并通过该名称访问容器。如果您链接 2 个容器,例如 container1 和 container2,则 docker 会负责将容器 2 的 IP 和主机名写入容器 1。但是,在您的情况下,您的应用程序正在主机中运行。

    您知道容器的 IP。因此,在主机的 /etc/hosts 中,您可以添加 $IP $hostanameof 容器

    【讨论】:

      【解决方案2】:

      阿迪亚是正确的。在您的情况下,最简单的方法是在/etc/hosts 中硬编码您的主机名/IP 映射

      然而,这种方法的问题在于您无法控制您的 postgres 机器将拥有的私有 IP 地址。每次启动新容器时 IP 地址都会更改,因此您需要更新 /etc/hosts 文件。

      如果这是个问题,我建议您阅读这篇博文,其中解释了如何强制容器获取特定 IP 地址:

      https://xand.es/2016/05/09/docker-with-known-ip/

      【讨论】:

      • 我想到了这种方法,但长期用于开发很麻烦。
      • 容器重启后IP地址可能随时变化,因此该方案不可扩展。
      【解决方案3】:

      如果您仅在本地使用 docker-compose 设置,则可以使用以下命令将端口从容器映射到主机

      elasticsearch:
        image: elasticsearch:2.2
        ports:
          - 9300:9300
          - 9200:9200
      

      然后使用您的网络应用程序中的 localhost:9300(或 9200,具体取决于协议)访问 Elasticsearch。

      更复杂的解决方案是运行您自己的 dns 来解析容器名称。我认为这个解决方案更接近你的要求。我以前在本地运行 kubernetes 时使用过 skydns。

      有几个选项。看看https://github.com/gliderlabs/registratorhttps://github.com/jderusse/docker-dns-gen。我没有尝试过,但是您可能会以与上一个示例中的弹性端口相同的方式将 dns 端口映射到您的主机,然后将 localhost 添加到您的 resolv.conf 以便能够从您的主持人。

      【讨论】:

        【解决方案4】:

        有一个开源应用程序可以解决这个问题,它被称为DNS Proxy Serverhere some examples 来自官方存储库

        这是一个解析容器主机名的 DNS 服务器,如果找不到匹配的主机名,也可以从互联网上解决它

        启动 DNS 服务器

        $ docker run --hostname dns.mageddo --restart=unless-stopped -p 5380:5380 \
        -v /var/run/docker.sock:/var/run/docker.sock \
        -v /etc/resolv.conf:/etc/resolv.conf \
        defreitas/dns-proxy-server
        

        它将自动设置为您的默认 DNS(并在停止时恢复为原始 DNS)

        为测试创建一些容器

        检查 docker-compose 文件

        $ cat docker-compose.yml
        version: '3'
        services:
          nginx-1:
            image: nginx
            hostname: nginx-1.docker
            network_mode: bridge 
          linux-1:
            image: alpine
            hostname: linux-1.docker
            command: sh -c 'apk add --update bind-tools && tail -f /dev/null'
            network_mode: bridge # that way he can solve others containers names even inside, solve nginx-2, for example
        

        启动容器

        $ docker-compose up
        

        解决容器

        来自主机

        nslookup nginx-1.docker
        Server:     13.0.0.5
        Address:    13.0.0.5#53
        Non-authoritative answer:
        Name:   nginx-1.docker
        Address: 13.0.0.6
        

        来自另一个容器

        $ docker-compose exec linux-1 ping nginx-1.docker
        PING nginx-1.docker (13.0.0.6): 56 data bytes
        64 bytes from 13.0.0.6: seq=0 ttl=64 time=0.034 ms
        

        它还可以解决互联网主机名问题

        $ nslookup google.com
        Server:     13.0.0.5
        Address:    13.0.0.5#53
        
        Non-authoritative answer:
        Name:   google.com
        Address: 216.58.202.78
        

        【讨论】:

        • 看起来正是我当时正在寻找的东西。一旦我再次进入这个项目,我一定会尝试你的解决方案!
        • 应该注意这个解决方案不适用于 OSX。很遗憾,resolv.conf 文件不用于 DNS 解析。
        • 如果您的开发团队中有 Windows 用户,也不是一个选择;)
        • 在将它与 Docker for Windows 和 WSL2 一起使用时,还有其他人在使用该库时遇到问题吗?即使按照 Windows ping 到 docker 容器的说明失败
        【解决方案5】:

        herehere描述了两种解决方案(/etc/hosts除外)

        我用 Python 编写了自己的解决方案,并将其实现为服务,以提供从容器主机名到其 IP 的映射。这里是: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
        

        【讨论】:

          【解决方案6】:

          您可以将 RoR 应用程序/或任何其他需要访问容器的应用程序 Dockerize。

          我知道,这是一个简单的解决方案,但让我解释一下:

          我想要类似的东西,但出于不同的原因。我正在通过 SAML 实现 SSO,并希望创建一个开发环境,我可以在其中测试解决方案。最初我想在主机上运行浏览器,但由于我在从客户端上的主机访问任意端口时遇到问题,而且 deFreitas 的基于 DNS 的解决方案在 Mac 上不起作用,我意识到,我可以对浏览器进行 docker 化:

          docker run --rm -p 8085:8085 chadmoon/gtk3-docker

          详情请见:https://github.com/moondev/gtk3-docker

          【讨论】:

            【解决方案7】:

            我正在使用 bash 脚本来更新 /etc/hosts。为什么是这个解决方案?

            • 脚本短,易于审查(不想让一些未经审查的应用程序具有大量依赖项访问 Docker 套接字(这意味着 root 访问权限))
            • 每次启动或停止容器时,它都会使用docker events 运行(此处发布的其他解决方案每秒循环运行一次,效率较低)
            • 更新 /etc/hosts,无需单独的 DNS 服务器。
            • 只有 bashmktempgrepxargssedjqdocker 的依赖项,我已经安装了所有这些。

            只需将脚本放在某个地方,例如/usr/local/bin/docker-update-hosts:

            #!/usr/bin/env bash
            set -e -u -o pipefail
            
            hosts_file=/etc/hosts
            begin_block="# BEGIN DOCKER CONTAINERS"
            end_block="# END DOCKER CONTAINERS"
            
            if ! grep -Fxq "$begin_block" "$hosts_file"; then
                echo -e "\n${begin_block}\n${end_block}\n" >> "$hosts_file"
            fi
            
            (echo "| container start |" && docker events) | \
            while read event; do
                if [[ "$event" == *" container start "* ]] || [[ "$event" == *" network disconnect "* ]]; then
                    hosts_file_tmp="$(mktemp)"
                    docker container ls -q | xargs -r docker container inspect | \
                    jq -r '.[]|"\(.NetworkSettings.Networks[].IPAddress|select(length > 0) // "# no ip address:") \(.Name|sub("^/"; "")|sub("_1$"; ""))"' | \
                    sed -ne "/^${begin_block}$/ {p; r /dev/stdin" -e ":a; n; /^${end_block}$/ {p; b}; ba}; p" "$hosts_file" \
                    > "$hosts_file_tmp"
                    chmod 644 "$hosts_file_tmp"
                    mv "$hosts_file_tmp" "$hosts_file"
                fi
            done
            

            注意:该脚本会从容器名称中删除 docker-compose 添加的 _1 后缀。如果您不想这样做,只需从脚本中删除 |sub("_1$"; "")

            您可以使用 systemd 服务与 Docker 同步运行:/etc/systemd/system/docker-update-hosts.service:

            [Unit]
            Description=Update Docker containers in /etc/hosts
            Requires=docker.service
            After=docker.service
            PartOf=docker.service
            
            [Service]
            ExecStart=/usr/local/bin/docker-update-hosts
            
            [Install]
            WantedBy=docker.service
            

            要激活,运行:

            systemctl daemon-reload
            systemctl enable docker-update-hosts.service
            systemctl start docker-update-hosts.service
            

            【讨论】:

            • 注意添加执行权限到文件/usr/local/bin/docker-update-hosts:chmod +x /usr/local/bin/docker-update-hosts
            • 注意为自 compose 3.x 以来的新自动名称添加 |sub("-1$"; "")
            猜你喜欢
            • 2020-02-17
            • 2017-02-18
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-06-20
            • 1970-01-01
            相关资源
            最近更新 更多