使用容器名从主机访问 Docker 容器

我正在开发一个服务,并使用那里码头组成旋转服务,如 postgres,reis,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

我可以定位这个网络里的集装箱

$ 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 ails 应用程序,但是能够使用像 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
112076 次浏览

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

或者

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

Aditya 是正确的。在您的例子中,最简单的方法是在 /etc/hosts中硬编码您的主机名/IP 映射

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

如果这是一个问题,我建议你阅读这篇解释如何强制容器获得特定 IP 地址的博客文章:

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

如果您只是在本地使用 docker-compose 设置,那么您可以使用

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

然后使用本地主机: 9300(或9200取决于协议)从您的 Web 应用程序访问 Elasticsearch。

一个更复杂的解决方案是运行您自己的解析容器名称的 dns。我觉得这个解决方案更接近你的要求。我以前在本地运行库伯内特时使用过 skydns。

有很多选择。看看 https://github.com/gliderlabs/registratorhttps://github.com/jderusse/docker-dns-gen。我没有尝试,但是您可以使用与前面示例中的弹性端口相同的方式将 dns 端口映射到您的主机,然后将 localhost 添加到 Resolv.conf,以便能够从您的主机解析您的容器名称。

有一个开源应用程序可以解决这个问题,它的名字叫做 代理服务器这里有一些例子,来自官方存储库

这是一个解决容器主机名的 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 (并恢复到原来的时候,它停止)

为测试创建一些容器

检查码头作业文件

$ 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

有两种解决方案(除了 /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 实例交互。我在系统里登记了-解决了:

$ cat /etc/systemd/resolved.conf


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

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

我知道,这是一个微不足道的解决方案,但让我解释一下:

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

码头运行 rm-p 8085:8085 chadmoon/gtk3-docker

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

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

  • 简短的脚本,易于查看(不想让一些未经查看的应用程序有很多依赖项访问 Docker 套接字(这意味着根访问))
  • 它使用 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