So greifen Sie vom Docker-Container auf den Host-Port zu

277

Ich habe einen Docker-Container mit Jenkins. Als Teil des Erstellungsprozesses muss ich auf einen Webserver zugreifen, der lokal auf dem Hostcomputer ausgeführt wird. Gibt es eine Möglichkeit, den Host-Webserver (der für die Ausführung an einem Port konfiguriert werden kann) für den Jenkins-Container verfügbar zu machen?

EDIT: Ich verwende Docker nativ auf einem Linux-Computer.

AKTUALISIEREN:

Zusätzlich zur Antwort von @larsks unten, um die IP-Adresse der Host-IP vom Host-Computer zu erhalten, gehe ich wie folgt vor:

ip addr show docker0 | grep -Po 'inet \K[\d.]+'
Tri Nguyen
quelle
Die Verwendung eines Kommentars, da dies eine schreckliche Antwort ist, aber ich glaube, Sie können im Allgemeinen unter 172.17.1.78 darauf zugreifen - es sei denn, dies ist ein boot2docker-Setup.
CashIsClay
@CashIsClay Ich habe das versucht und habe diesen Fehler immer noch bekommencurl: (7) Failed to connect to 172.17.1.78 port 7000: No route to host
Tri Nguyen
Sie haben nicht angegeben; Führen Sie boot2docker aus oder führen Sie Docker nativ unter Linux aus?
Larsks
@larsks Entschuldigung, ich habe gerade die Frage aktualisiert - ich führe sie nativ unter Linux aus.
Tri Nguyen

Antworten:

206

Wenn Sie Docker nativ unter Linux ausführen, können Sie über die IP-Adresse der docker0Schnittstelle auf Hostdienste zugreifen . Innerhalb des Containers ist dies Ihre Standardroute.

Zum Beispiel auf meinem System:

$ ip addr show docker0
7: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 00:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::f4d2:49ff:fedd:28a0/64 scope link 
       valid_lft forever preferred_lft forever

Und in einem Container:

# ip route show
default via 172.17.0.1 dev eth0 
172.17.0.0/16 dev eth0  src 172.17.0.4 

Es ist ziemlich einfach, diese IP-Adresse mit einem einfachen Shell-Skript zu extrahieren:

#!/bin/sh

hostip=$(ip route show | awk '/default/ {print $3}')
echo $hostip

Möglicherweise müssen Sie die iptablesRegeln auf Ihrem Host ändern , um Verbindungen von Docker-Containern zuzulassen. So etwas reicht aus:

# iptables -A INPUT -i docker0 -j ACCEPT

Dies würde den Zugriff auf alle Ports auf dem Host von Docker-Containern aus ermöglichen. Beachten Sie, dass:

  • iptables-Regeln sind geordnet, und diese Regel kann das Richtige tun oder auch nicht, je nachdem, welche anderen Regeln davor stehen.

  • Sie können nur auf Hostdienste zugreifen, die entweder (a) abhören INADDR_ANY(auch bekannt als 0.0.0.0) oder die docker0Schnittstelle explizit abhören .

Larsks
quelle
1
Vielen Dank. Das Erhalten der IP-Adresse war alles, was für mich benötigt wurde, ohne die iptables zu ändern :)
Tri Nguyen
7
Wie wäre es mit Docker für MAC? AFAIK Es gibt kein Docker0-Netzwerk für "Docker for MAC". Wie kann ich in diesem Fall vom Container aus eine Verbindung zum Host herstellen?
Vijay
17
Wenn Sie Docker für MAC v 17.06 oder höher verwenden, verwenden Sie einfach docker.for.mac.localhostanstelle von localhostoder 127.0.0.1. Hier ist doc .
Merito
1
Ich habe den Hostnamen meines Hosts verwendet, anstatt die IP-Adresse zu erhalten (Hostname-Befehl auf dem Host)
Marek F
311

Für MacOS und Windows

Docker v 18.03 und höher (seit dem 21. März 2018)

Verwenden Sie Ihre interne IP-Adresse oder stellen Sie eine Verbindung mit dem speziellen DNS-Namen her, host.docker.internalder in die vom Host verwendete interne IP-Adresse aufgelöst wird.

Linux-Unterstützung ausstehend https://github.com/docker/for-linux/issues/264

MacOS mit früheren Versionen von Docker

Docker für Mac v 17.12 bis v 18.02

Wie oben, aber docker.for.mac.host.internalstattdessen verwenden.

Docker für Mac v 17.06 bis v 17.11

Wie oben, aber docker.for.mac.localhoststattdessen verwenden.

Docker für Mac 17.05 und niedriger

Um vom Docker-Container aus auf den Host-Computer zuzugreifen, müssen Sie einen IP-Alias ​​an Ihre Netzwerkschnittstelle anhängen. Sie können jede gewünschte IP-Adresse binden. Stellen Sie jedoch sicher, dass Sie sie nicht für andere Zwecke verwenden.

sudo ifconfig lo0 alias 123.123.123.123/24

Stellen Sie dann sicher, dass Ihr Server die oben genannte IP abhört oder 0.0.0.0. Wenn localhost abgehört 127.0.0.1wird, wird die Verbindung nicht akzeptiert.

Dann zeigen Sie einfach Ihren Docker-Container auf diese IP und Sie können auf den Host-Computer zugreifen!

Zum Testen können Sie so etwas wie curl -X GET 123.123.123.123:3000im Container ausführen .

Der Alias ​​wird bei jedem Neustart zurückgesetzt. Erstellen Sie daher bei Bedarf ein Startskript.

Lösung und weitere Dokumentation hier: https://docs.docker.com/docker-for-mac/networking/#use-cases-and-workarounds

Janne Annala
quelle
Ich habe das erfolgreich getestet. Keine Notwendigkeit, die Firewall
auszuschalten
15
Ab dem 17.06., Veröffentlichung im Juni 2017, wird empfohlen, eine Verbindung zu dem speziellen DNS-Namen nur für Macs docker.for.mac.localhostherzustellen, der in die vom Host verwendete interne IP-Adresse aufgelöst wird! ... ich habe es getestet und es funktioniert tatsächlich! :)
Kyr
Genau das Richtige für Mac-Benutzer wie mich. Vielen Dank. Eine Sache, die ich jedoch nicht verstehe, ist, warum ich ip route show | awk '/default/ {print $3}'eine IP gebe und docker.for.mac.localhosteine andere.
dmmd
8
geändert zu host.docker.internal docs.docker.com/docker-for-mac/networking/…
Schneeball
1
Empfehlen Sie die Verwendung host.docker.internalim Docker-Container und die Konfiguration 127.0.0.1 host.docker.internalin der Hosts- Datei.
Junlin
82

Verwenden Sie --net="host"in Ihrem docker runBefehl, dann zeigt localhostin Ihrem Docker-Container auf Ihren Docker-Host.

samthebest
quelle
15
Funktioniert nicht für Benutzer von Dpcker für Windows / Docker für Mac, soweit ich weiß, da Container in virtualisierten Umgebungen ausgeführt werden: Hyper-V (Windows) / xhyve (Mac)
Ninjaboy
6
DIES! Das ist die Antwort!
user3751385
12
Nur zur Veranschaulichung: innerhalb von Docker Componse, network_mode: "host"
jbarros
Dies funktioniert bei Docker für Mac Version 19.03.1 auf Client und Server nicht. Ich wünschte, es würde funktionieren, aber es ist nicht so.
Ton
1
Selbst wenn ich auf einem Mac bin, funktioniert es, wenn Sie die Kommunikation zwischen zwei Docker-Containern wünschen, und da ich alle Anwendungen in Docker-Images
umwandle,
25

Lösung mit Docker-Compose: Für den Zugriff auf den hostbasierten Dienst können Sie den network_modeParameter https://docs.docker.com/compose/compose-file/#network_mode verwenden

version: '3'
services:
  jenkins:
    network_mode: host

EDIT 2020-04-27: Empfohlen nur zur Verwendung in lokalen Entwicklungsumgebungen.

Vovan
quelle
Wie kann man dann auf Jenkins zugreifen? Scheint, dass die Portweiterleitung nicht funktioniert, wenn der Host-Netzwerkmodus verwendet wird
Jeff Tian
1
Es ist eine sehr riskante Lösung und überhaupt nicht zu empfehlen. Wir sollten unser Host-Netzwerk NICHT für Container öffnen, es sei denn, dies wird ausdrücklich benötigt
Fatemeh Majd,
12

Ich habe einen Docker-Container erstellt, um genau das zu tun: https://github.com/qoomon/docker-host

Sie können dann einfach den Containernamen dns verwenden, um auf das Hostsystem zuzugreifen, z curl http://dockerhost:9200

Qoomon
quelle
Das ist eine clevere Lösung. Ist Ihnen etwas bekannt, das dies mit viel Verkehr nutzt? Das Proxying des gesamten Datenverkehrs durch diesen Container kann zu einem Mehraufwand führen.
Bcoughlan
3
Ja, es funktioniert ganz gut, kaum Overhead, weil es nur über ein Loopback-Gerät
funktioniert
11

Wir haben festgestellt, dass eine einfachere Lösung für all diesen Netzwerk-Junk darin besteht, nur den Domain-Socket für den Dienst zu verwenden. Wenn Sie trotzdem versuchen, eine Verbindung zum Host herzustellen, stellen Sie den Socket einfach als Volume bereit, und schon können Sie loslegen. Für postgresql war dies so einfach wie:

docker run -v /var/run/postgresql:/var/run/postgresql

Dann richten wir einfach unsere Datenbankverbindung ein, um den Socket anstelle des Netzwerks zu verwenden. Im wahrsten Sinne des Wortes so einfach.

mlissner
quelle
Dies ist eine großartige Lösung. Gut gemacht!
Ein bezahlter Nerd
2
Zu Ihrer Information, wir sind auf ein großes Problem gestoßen: Docker für Mac unterstützt keine Sockets als gemountete Volumes. Dies ging schwimmend, bis eine Mac-Person es versuchte. :(
mlissner
Vielen Dank! Arbeitete wie ein Zauber unter Linux!
Marcelo Cardoso
7

Ich habe die verschiedenen Lösungen untersucht und finde, dass dies die am wenigsten hackige Lösung ist:

  1. Definieren Sie eine statische IP-Adresse für die Bridge-Gateway-IP.
  2. Fügen Sie die Gateway-IP als zusätzlichen Eintrag in die extra_hostsDirektive ein.

Der einzige Nachteil ist, dass Sie bei mehreren Netzwerken oder Projekten sicherstellen müssen, dass deren IP-Adressbereich nicht in Konflikt steht.

Hier ist ein Docker Compose-Beispiel:

version: '2.3'

services:
  redis:
    image: "redis"
    extra_hosts:
      - "dockerhost:172.20.0.1"

networks:
  default:
    ipam:
      driver: default
      config:
      - subnet: 172.20.0.0/16
        gateway: 172.20.0.1

Sie können dann über den Hostnamen "dockerhost" aus dem Container heraus auf Ports auf dem Host zugreifen.

bcoughlan
quelle
3

Sie können auf zwei Arten auf den lokalen Webserver zugreifen, der auf Ihrem Host-Computer ausgeführt wird.

  1. Ansatz 1 mit öffentlicher IP

    Verwenden Sie die öffentliche IP-Adresse des Hostcomputers, um auf den Webserver im Docker-Container von Jenkins zuzugreifen.

  2. Ansatz 2 mit dem Host-Netzwerk

    Verwenden Sie "--net host", um den Jenkins-Docker-Container zum Netzwerkstapel des Hosts hinzuzufügen. Container, die auf dem Host-Stack bereitgestellt werden, haben vollständigen Zugriff auf die Host-Schnittstelle. Sie können im Docker-Container mit einer privaten IP-Adresse des Host-Computers auf den lokalen Webserver zugreifen.

NETWORK ID          NAME                      DRIVER              SCOPE
b3554ea51ca3        bridge                    bridge              local
2f0d6d6fdd88        host                      host                local
b9c2a4bc23b2        none                      null                local

Starten Sie einen Container mit dem Host-Netzwerk Eg: docker run --net host -it ubuntuund führen Sie ihn aus, ifconfigum alle verfügbaren Netzwerk-IP-Adressen aufzulisten, die über den Docker-Container erreichbar sind.

Beispiel: Ich habe einen Nginx-Server auf meinem lokalen Host-Computer gestartet und kann über den Ubuntu-Docker-Container auf die URLs der Nginx-Website zugreifen.

docker run --net host -it ubuntu

$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
a604f7af5e36        ubuntu              "/bin/bash"         22 seconds ago      Up 20 seconds                           ubuntu_server

Zugriff auf den Nginx-Webserver (der auf dem lokalen Host-Computer ausgeführt wird) über den Ubuntu-Docker-Container mit der IP-Adresse des privaten Netzwerks.

root@linuxkit-025000000001:/# curl 192.168.x.x -I
HTTP/1.1 200 OK
Server: nginx/1.15.10
Date: Tue, 09 Apr 2019 05:12:12 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 26 Mar 2019 14:04:38 GMT
Connection: keep-alive
ETag: "5c9a3176-264"
Accept-Ranges: bytes
Sreenivasa Reddy
quelle
3

Für die docker-composeVerwendung von Bridge-Netzwerken zum Erstellen eines privaten Netzwerks zwischen Containern docker0funktioniert die akzeptierte Lösung nicht, da die Ausgangsschnittstelle aus den Containern nicht, docker0sondern eine zufällig generierte Schnittstellen-ID ist, z.

$ ifconfig

br-02d7f5ba5a51: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.32.1  netmask 255.255.240.0  broadcast 192.168.47.255

Leider ist diese zufällige ID nicht vorhersehbar und ändert sich jedes Mal, wenn compose das Netzwerk neu erstellen muss (z. B. bei einem Neustart des Hosts). Meine Lösung hierfür besteht darin, das private Netzwerk in einem bekannten Subnetz zu erstellen und so zu konfigurieren iptables, dass dieser Bereich akzeptiert wird:

Datei-Snippet verfassen:

version: "3.7"

services:
  mongodb:
    image: mongo:4.2.2
    networks:
    - mynet
    # rest of service config and other services removed for clarity

networks:
  mynet:
    name: mynet
    ipam:
      driver: default
      config:
      - subnet: "192.168.32.0/20"

Sie können das Subnetz ändern, wenn Ihre Umgebung dies erfordert. Ich habe willkürlich ausgewählt, 192.168.32.0/20indem ich verwendet habe, um docker network inspectzu sehen, was standardmäßig erstellt wurde.

iptablesAuf dem Host so konfigurieren , dass das private Subnetz als Quelle zugelassen wird:

$ iptables -I INPUT 1 -s 192.168.32.0/20 -j ACCEPT

Dies ist die einfachste mögliche iptablesRegel. Möglicherweise möchten Sie weitere Einschränkungen hinzufügen, z. B. nach Zielport. Vergessen Sie nicht, Ihre iptables-Regeln beizubehalten, wenn Sie froh sind, dass sie funktionieren.

Dieser Ansatz hat den Vorteil, dass er wiederholbar und daher automatisierbar ist. Ich benutze ansible das templateModul meine compose Datei mit Variablensubstitution zu implementieren und dann die Verwendung iptablesund shellModule zu konfigurieren und die Firewall - Regeln bestehen, respectively.

Andy Brown
quelle
Ich habe mehrere Antworten vorgeschlagen iptables -A INPUT -i docker0 -j ACCEPT, aber das hat mir nicht geholfen, während die iptables -I INPUT 1 -s 192.168.32.0/20 -j ACCEPThier vorgeschlagenen mein Problem gelöst haben.
Terry Brown
1

Dies ist eine alte Frage und hatte viele Antworten, aber keine davon passt gut genug zu meinem Kontext. In meinem Fall sind die Container sehr schlank und enthalten keine der Netzwerk-Tools, die zum Extrahieren der IP-Adresse des Hosts aus dem Container erforderlich sind.

Die Verwendung des --net="host"Ansatzes ist auch ein sehr grober Ansatz, der nicht anwendbar ist, wenn eine gut isolierte Netzwerkkonfiguration mit mehreren Containern gewünscht wird.

Mein Ansatz besteht also darin, die Hostadresse auf der Hostseite zu extrahieren und sie dann mit dem folgenden --add-hostParameter an den Container zu übergeben :

$ docker run --add-host=docker-host:`ip addr show docker0 | grep -Po 'inet \K[\d.]+'` image_name

Oder speichern Sie die IP-Adresse des Hosts in einer Umgebungsvariablen und verwenden Sie die Variable später:

$ DOCKERIP=`ip addr show docker0 | grep -Po 'inet \K[\d.]+'`
$ docker run --add-host=docker-host:$DOCKERIP image_name

Anschließend docker-hostwird das zur Hosts-Datei des Containers hinzugefügt, und Sie können es in Ihren Datenbankverbindungszeichenfolgen oder API-URLs verwenden.

Passiday
quelle
0

Wenn Sie zwei Docker-Images "bereits" erstellt haben und zwei Container für die Kommunikation untereinander platzieren möchten.

Dazu können Sie bequem jeden Container mit seinem eigenen Namen ausführen und das Flag --link verwenden, um die Kommunikation zwischen ihnen zu ermöglichen. Sie erhalten dies jedoch nicht während des Docker-Builds.

Wenn Sie sich in einem Szenario wie mir befinden und es Ihnen gehört

docker build -t "centos7/someApp" someApp/ 

Das bricht, wenn Sie es versuchen

curl http://172.17.0.1:localPort/fileIWouldLikeToDownload.tar.gz > dump.tar.gz

und Sie bleiben bei "curl / wget" hängen und geben keine "Route zum Host" zurück.

Der Grund ist die vom Docker eingerichtete Sicherheit, die standardmäßig die Kommunikation von einem Container zum Host oder zu anderen auf Ihrem Host ausgeführten Containern verbietet. Das war ziemlich überraschend für mich, ich muss sagen, Sie würden erwarten, dass das Echosystem von Docker-Maschinen, die auf einem lokalen Computer laufen, ohne allzu große Hürden fehlerfrei aufeinander zugreifen kann.

Die Erklärung hierfür wird in der folgenden Dokumentation ausführlich beschrieben.

http://www.dedoimedo.com/computers/docker-networking.html

Es werden zwei schnelle Problemumgehungen angegeben, die Ihnen helfen, sich zu bewegen, indem Sie die Netzwerksicherheit verringern.

Die einfachste Alternative besteht darin, die Firewall auszuschalten - oder alle zuzulassen. Dies bedeutet, dass der erforderliche Befehl ausgeführt wird. Dies kann systemctl stop firewalld, iptables -F oder ein gleichwertiger Befehl sein.

Hoffe, diese Informationen helfen Ihnen.

99Sono
quelle
3
Nur als Hinweis, --linkist jetzt veraltet
Mr.Budris
0

Für mich (Windows 10, Docker Engine v19.03.8) war es eine Mischung aus https://stackoverflow.com/a/43541732/7924573 und https://stackoverflow.com/a/50866007/7924573 .

  1. Ändern Sie den Host / die IP in host.docker.internal,
    z. B.: LOGGER_URL = " http: //host.docker.internal: 8085 / log "
  2. Setzen Sie den Netzwerkmodus auf Bridge (wenn Sie die Portweiterleitung beibehalten möchten; wenn Sie keinen Host verwenden ):
    version: '3.7' services: server: build: . ports: - "5000:5000" network_mode: bridge oder alternativ: Verwenden --net="bridge"Sie, wenn Sie Docker-Compose nicht verwenden (ähnlich wie https://stackoverflow.com/a/48806927/7924573 ).
    Wie in früheren Antworten ausgeführt: Dies sollte nur in einer lokalen Entwicklungsumgebung verwendet werden .
    Weitere Informationen finden Sie unter: https://docs.docker.com/compose/compose-file/#network_mode und https://docs.docker.com/docker-for-windows/networking/#use-cases-and-workarounds
tschomacker
quelle