Greifen Sie vom Host aus über den Containernamen auf den Docker-Container zu

75

Ich entwickle einen Dienst und verwende dort Docker Compose, um Dienste wie Postgres, Redis, Elasticsearch zu spinnen. Ich habe eine Webanwendung, die auf RubyOnRails basiert und von all diesen Diensten schreibt und liest.

Hier ist mein 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

Und ich kann Container innerhalb dieses Netzwerks pingen

$ 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
...

So weit, ist es gut. Jetzt möchte ich die Ruby on Rails-Anwendung auf meinem Host-Computer ausführen, aber auf die Postgres-Instanz mit einer URL zugreifen können, wie dies postgresql://username:password@postgres/databasederzeit nicht möglich ist

$ ping postgres
ping: unknown host postgres

Ich kann mein Netzwerk im Docker sehen

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

Und ich kann eine Schnittstelle dazu sehen

$ 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)
...

Aber ich bin mir nicht sicher, was ich als nächstes tun soll. Ich habe versucht, ein bisschen damit zu spielen /etc/resolv.conf, hauptsächlich mit nameserverDirektiven, aber das hatte keine Wirkung.

Ich würde mich über Anregungen zur korrekten Konfiguration dieses Setups freuen.

AKTUALISIEREN

Nach dem Durchsuchen der Internetressourcen gelang es mir, Boxen statische IP-Adressen zuzuweisen. Im Moment reicht es mir, mich weiterzuentwickeln. Hier ist meine aktuelledocker-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
Max
quelle

Antworten:

49

Es gibt eine OpenSource-Anwendung, die dieses Problem löst. Sie heißt DNS-Proxyserver und hier einige Beispiele aus dem offiziellen Repository

Es ist ein DNS-Server, der Container-Hostnamen löst. Wenn kein passender Hostname gefunden werden konnte, lösen Sie ihn auch aus dem Internet

Starten Sie den DNS-Server

$ 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

Es wird automatisch als Standard-DNS festgelegt (und stellt das Original wieder her, wenn es stoppt).

Erstellen einiger Container zum Testen

Überprüfen der Docker-Compose-Datei

$ 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

Startbehälter

$ docker-compose up

Behälter lösen

vom Host

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

aus einem anderen Behälter

$ 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

Außerdem werden Internet-Hostnamen gelöst

$ 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
deFreitas
quelle
Sieht genau so aus, wie ich damals gesucht habe. Sobald ich wieder zu diesem Projekt komme, werde ich auf jeden Fall Ihre Lösung ausprobieren!
Max
3
Es ist zu beachten, dass diese Lösung unter OSX nicht funktioniert. Die resolv.confDatei wird leider nicht für die DNS-Auflösung verwendet.
Xaero Degreaz
1
Auch keine Option, wenn Sie einen Windows-Benutzer in Ihrem
Entwicklerteam haben
@Riscie Scheint Windows zu unterstützen: mageddo.github.io/dns-proxy-server/latest/en/1-getting-started/…
Exic
6

Wenn Sie Ihr Docker-Compose-Setup nur lokal verwenden, können Sie die Ports von Ihren Containern Ihrem Host mit zuordnen

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

Verwenden Sie dann localhost: 9300 (oder 9200, je nach Protokoll) aus Ihrer Web-App, um auf Elasticsearch zuzugreifen.

Eine komplexere Lösung besteht darin, eigene DNS auszuführen, die Containernamen auflösen. Ich denke, dass diese Lösung viel näher an dem ist, was Sie verlangen. Ich habe zuvor skydns verwendet, wenn ich Kubernetes lokal ausgeführt habe.

Es gibt einige Möglichkeiten. Besuchen Sie https://github.com/gliderlabs/registrator und https://github.com/jderusse/docker-dns-gen . Ich habe es nicht ausprobiert, aber Sie können den DNS-Port möglicherweise auf die gleiche Weise wie die elastischen Ports im vorherigen Beispiel Ihrem Host zuordnen und dann localhost zu Ihrer resolv.conf hinzufügen, um Ihre Containernamen aus Ihrem auflösen zu können Gastgeber.

Johan
quelle
6

Hier und hier werden (außer /etc/hosts) zwei Lösungen beschrieben

Ich habe meine eigene Lösung in Python geschrieben und sie als Dienst implementiert, um die Zuordnung vom Hostnamen des Containers zu seiner IP bereitzustellen. Hier ist es: https://github.com/nicolai-budico/dockerhosts

Es startet dnsmasq mit Parametern --hostsdir=/var/run/docker-hostsund aktualisiert die Datei /var/run/docker-hosts/hostsjedes Mal, wenn eine Liste der ausgeführten Container geändert wurde. Sobald die Datei /var/run/docker-hosts/hostsgeändert wird, aktualisiert dnsmasq automatisch die Zuordnung und der Container wird in Sekundenschnelle nach Hostname verfügbar.

$ 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

Es gibt Installations- und Deinstallationsskripte. Sie müssen lediglich zulassen, dass Ihr System mit dieser dnsmasq-Instanz interagiert. Ich habe mich in systemd-aufgelöst registriert:

$ cat /etc/systemd/resolved.conf

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

Der Hostname des Docker-Containers ist von außen nicht sichtbar. Sie können dem Container einen Namen zuweisen und über den Namen auf den Container zugreifen. Wenn Sie zwei Container verknüpfen, z. B. Container1 und Container2, übernimmt Docker das Schreiben der IP-Adresse und des Hostnamens von Container2 in Container1. In Ihrem Fall wird Ihre Anwendung jedoch auf der Hostmaschine ausgeführt.

ODER

Sie kennen die IP des Containers. Sie können also in / etc / hosts Ihres Host-Computers $ IP $ hostanameof container hinzufügen

Aditya CS
quelle
2

Ich verwende ein Bash-Skript zum Aktualisieren /etc/hosts. Warum diese Lösung?

  • Kurzes Skript, einfach zu überprüfen (wollte einer nicht überprüften Anwendung mit vielen Abhängigkeiten keinen Zugriff auf den Docker-Socket gewähren (was Root-Zugriff bedeutet))
  • Es wird docker eventsjedes Mal ausgeführt, wenn ein Container gestartet oder gestoppt wird (andere hier veröffentlichte Lösungen werden jede Sekunde in einer Schleife ausgeführt, was weitaus weniger effizient ist).
  • Updates /etc/hosts, kein separater DNS-Server erforderlich.
  • Nur Abhängigkeiten bash, mktemp, grep, xargs, sed, jqund docker, von denen alle hatte ich bereits installiert.

Platzieren Sie das Skript einfach irgendwo, zB /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

Hinweis: Das Skript entfernt das _1von Docker-Compose hinzugefügte Suffix aus den Containernamen. Wenn Sie das nicht möchten, entfernen Sie es einfach |sub("_1$"; "")aus dem Skript.

Sie können einen systemd Dienst nutzen diese synchron mit Docker auszuführen: /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

Führen Sie zum Aktivieren Folgendes aus:

systemctl daemon-reload
systemctl enable docker-update-hosts.service
systemctl start docker-update-hosts.service
fnkr
quelle
0

Aditya ist richtig. In Ihrem Fall ist es am einfachsten, Ihren Hostnamen / Ihre IP-Zuordnung fest zu codieren/etc/hosts

Das Problem bei diesem Ansatz ist jedoch, dass Sie die private IP-Adresse Ihres Postgres-Computers nicht steuern. Die IP-Adresse ändert sich jedes Mal, wenn Sie einen neuen Container starten. Daher müssen Sie Ihre Datei / etc / hosts aktualisieren.

Wenn dies ein Problem ist, würde ich empfehlen, dass Sie diesen Blog-Beitrag lesen, in dem erläutert wird, wie erzwungen wird, dass ein Container eine bestimmte IP-Adresse erhält:

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

Christophe Schmitz
quelle
Ich habe über diesen Ansatz nachgedacht, aber das ist ein Weg, um langfristig für die Entwicklung eingesetzt zu werden.
Max
Die IP-Adresse kann sich bei jedem Neustart des Containers ändern, daher ist diese Lösung nicht skalierbar.
Octavian
0

Sie können die RoR-App / oder jede andere App, die Zugriff auf die Container benötigt, andocken.

Ich weiß, das ist eine triviale Lösung, aber lassen Sie mich erklären:

Ich wollte etwas Ähnliches, aber aus einem anderen Grund. Ich implementiere SSO über SAML und wollte eine Entwicklungsumgebung erstellen, in der ich die Lösung testen kann. Ursprünglich wollte ich den Browser auf dem Host-Computer ausführen, aber da ich Probleme beim Zugriff auf beliebige Ports vom Host auf Clients hatte und auch die DNS-basierte Lösung von deFreitas auf dem Mac nicht funktioniert, wurde mir klar, dass ich einen Browser docken kann:

Docker-Lauf --rm -p 8085: 8085 chadmoon / gtk3-docker

Weitere Informationen finden Sie unter: https://github.com/moondev/gtk3-docker .

Vajk Hermecz
quelle