Jak uzyskać dostęp do portu hosta z kontenera dokera

278

Mam kontener dokerów z jedynkami. W ramach procesu kompilacji muszę uzyskać dostęp do serwera WWW, który jest uruchomiony lokalnie na komputerze-hoście. Czy istnieje sposób, aby serwer sieciowy hosta (który można skonfigurować do działania na porcie) mógł być narażony na kontener Jenkins?

EDYCJA: Uruchamiam dokera natywnie na komputerze z systemem Linux.

AKTUALIZACJA:

Oprócz odpowiedzi @larsk poniżej, aby uzyskać adres IP hosta IP z hosta, wykonuję następujące czynności:

ip addr show docker0 | grep -Po 'inet \K[\d.]+'
Tri Nguyen
źródło
Używanie komentarza, ponieważ jest to okropna odpowiedź, ale uważam, że ogólnie można uzyskać do niego dostęp w 172.17.1.78 - chyba że jest to konfiguracja boot2docker.
CashIsClay
@CashIsClay Próbowałem tego i nadal mam ten błądcurl: (7) Failed to connect to 172.17.1.78 port 7000: No route to host
Tri Nguyen
Nie określiłeś; czy korzystasz z boot2docker, czy raczej używasz Dockera w systemie Linux?
larsks
@larsks przepraszam, właśnie zaktualizowałem pytanie - uruchamiam je natywnie w systemie Linux.
Tri Nguyen,

Odpowiedzi:

206

Podczas uruchamiania Dockera natywnie w systemie Linux, można uzyskać dostęp do usług hosta za pomocą adresu IP docker0interfejsu. Z wnętrza kontenera będzie to domyślna trasa.

Na przykład w moim systemie:

$ 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

I w pojemniku:

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

Wyodrębnienie tego adresu IP jest dość łatwe przy użyciu prostego skryptu powłoki:

#!/bin/sh

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

Konieczne może być zmodyfikowanie iptablesreguł na hoście, aby zezwolić na połączenia z kontenerów Docker. Coś takiego może załatwić sprawę:

# iptables -A INPUT -i docker0 -j ACCEPT

Umożliwiłoby to dostęp do dowolnych portów na hoście z kontenerów Docker. Uwaga:

  • Reguły iptables są uporządkowane, a ta reguła może, ale nie musi, postępować właściwie, w zależności od tego, jakie inne reguły ją poprzedzają.

  • będziesz mieć dostęp do usług hosta, które albo (a) nasłuchują INADDR_ANY(aka 0.0.0.0), albo jawnie nasłuchują na docker0interfejsie.

Larsks
źródło
1
Dzięki. Zdobycie adresu IP było wszystkim, czego potrzebowałem, bez modyfikowania iptables :)
Tri Nguyen
7
Co powiesz na Docker na MAC? AFAIK nie ma dostępnej sieci docker0 dla „Docker for MAC”. W takim przypadku jak mogę połączyć się z hostem z kontenera?
Vijay,
17
Jeśli używasz Dockera dla MAC v 17.06 lub nowszego, po prostu użyj docker.for.mac.localhostzamiast localhostlub 127.0.0.1. Oto dokument .
merito
1
Użyłem nazwy hosta mojego hosta zamiast adresu IP (polecenie hosta na hoście)
Marek F
312

Dla macOS i Windows

Docker przeciwko 18.03 i nowszym (od 21 marca 2018)

Użyj wewnętrznego adresu IP lub połącz się ze specjalną nazwą DNS, host.docker.internalktóra rozwiąże z wewnętrznym adresem IP używanym przez hosta.

Wsparcie dla systemu Linux w oczekiwaniu na https://github.com/docker/for-linux/issues/264

MacOS z wcześniejszymi wersjami Dockera

Docker dla komputerów Mac w wersji od 17.12 do 18.02

To samo co powyżej, ale użyj docker.for.mac.host.internalzamiast tego.

Docker dla komputerów Mac w wersji od 17.06 do 17.11

To samo co powyżej, ale użyj docker.for.mac.localhostzamiast tego.

Docker na komputery Mac 17.05 i starsze

Aby uzyskać dostęp do komputera hosta z kontenera dokowanego, musisz dołączyć alias IP do interfejsu sieciowego. Możesz powiązać dowolny adres IP, po prostu upewnij się, że nie używasz go do niczego innego.

sudo ifconfig lo0 alias 123.123.123.123/24

Następnie upewnij się, że serwer nasłuchuje adresu IP wspomnianego powyżej lub 0.0.0.0. Jeśli nasłuchuje na localhost 127.0.0.1, nie zaakceptuje połączenia.

Następnie po prostu skieruj kontener dokujący na ten adres IP, aby uzyskać dostęp do komputera hosta!

Aby przetestować, możesz uruchomić coś takiego jak curl -X GET 123.123.123.123:3000wewnątrz kontenera.

Alias ​​zostanie zresetowany przy każdym ponownym uruchomieniu, więc w razie potrzeby utwórz skrypt startowy.

Rozwiązanie i więcej dokumentacji tutaj: https://docs.docker.com/docker-for-mac/networking/#use-cases-and-workarounds

Janne Annala
źródło
Z powodzeniem to przetestowałem. Nie trzeba wyłączać zapory
alvaro g
15
Od 17.06 r., Wydana w czerwcu 2017 r., Ich zaleceniem jest połączenie się ze specjalną nazwą DNS tylko dla komputerów Mac, docker.for.mac.localhostktóra zostanie rozpoznana na wewnętrznym adresie IP używanym przez hosta! ... Przetestowałem to i faktycznie działa! :)
Kyr
Natychmiastowe odpowiedzi dla użytkowników komputerów Mac takich jak ja. Dzięki. Jednej rzeczy, której nie rozumiem, to dlaczego ip route show | awk '/default/ {print $3}'jeden adres IP docker.for.mac.localhostjest inny, a inny.
dmmd
8
zmieniono na host.docker.internal docs.docker.com/docker-for-mac/networking/…
Snowball
1
Zalecane użycie host.docker.internalw kontenerze dokowanym i konfiguracja 127.0.0.1 host.docker.internalw pliku hosts .
Junlin
84

Użyj --net="host"w swoim docker runpoleceniu, a następnie localhostw kontenerze dokera wskaże hosta dokera.

samthebest
źródło
15
O ile rozumiem, nie będą działać dla osób używających Dpcker dla Windows / Docker dla komputerów Mac ze względu na fakt, że kontenery działają w środowiskach zwirtualizowanych: Hyper-V (Windows) / xhyve (Mac)
ninjaboy
6
TO! Oto odpowiedź!
user3751385,
14
Dla przypomnienia: w ramach Docker Componse, network_mode: "host"
jbarros
To nie działa dla mnie w Docker dla komputerów Mac w wersji 19.03.1 na kliencie i serwerze. Chciałbym, żeby to działało, ale tak nie jest.
glina
1
Nawet jeśli jestem na komputerze Mac, działa to, gdy chcesz komunikację między dwoma kontenerami dokerów, a ponieważ przekształcam wszystkie aplikacje w obraz
dokera, to
26

Rozwiązanie z docker-compose: Aby uzyskać dostęp do usługi opartej na hoście, możesz użyć network_modeparametru https://docs.docker.com/compose/compose-file/#network_mode

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

EDIT 2020-04-27: zalecany do stosowania tylko w lokalnym środowisku programistycznym.

Vovan
źródło
Jak uzyskać dostęp do Jenkins? Wygląda na to, że przekierowanie portów nie działa, jeśli używasz trybu sieci hosta
Jeff Tian
1
jest to bardzo ryzykowne rozwiązanie i wcale nie jest zalecane. NIE powinniśmy otwierać naszej sieci hostów na kontenery, chyba że jest to wyraźnie potrzebne
Fatemeh Majd
12

Stworzyłem kontener dokerów do robienia dokładnie tego https://github.com/qoomon/docker-host

Następnie możesz po prostu użyć nazwy kontenera dns, aby uzyskać dostęp do systemu hosta, np curl http://dockerhost:9200

qoomon
źródło
To sprytne rozwiązanie. Czy zdajesz sobie sprawę z tego, że używasz tego przy dużym ruchu? Prokurentowanie całego ruchu przez ten kontener może być narzutem.
bcoughlan
3
Tak, działa całkiem nieźle,
praktycznie
11

Odkryliśmy, że prostszym rozwiązaniem wszystkich tych śmieci sieciowych jest po prostu użycie gniazda domeny dla usługi. Jeśli mimo to próbujesz połączyć się z hostem, po prostu zamontuj gniazdo jako wolumin i jesteś na dobrej drodze. W przypadku postgresql było to tak proste, jak:

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

Następnie właśnie skonfigurowaliśmy nasze połączenie z bazą danych, aby używało gniazda zamiast sieci. Dosłownie takie łatwe.

mlissner
źródło
To świetne rozwiązanie. Dobra robota!
opłacony frajer
2
Do Twojej wiadomości napotkaliśmy duży problem: Docker dla komputerów Mac nie obsługuje gniazd jako zamontowanych woluminów. Trwało to płynnie, dopóki ktoś Mac nie spróbował. :(
mlissner,
Dzięki! Działa jak urok w systemie Linux!
Marcelo Cardoso
7

Zbadałem różne rozwiązania i uważam to za najmniej hackujące rozwiązanie:

  1. Zdefiniuj statyczny adres IP dla adresu IP bramy mostu.
  2. Dodaj adres IP bramy jako dodatkowy wpis w extra_hostsdyrektywie.

Jedynym minusem jest to, że jeśli robi to wiele sieci lub projektów, musisz upewnić się, że ich zakres adresów IP nie powoduje konfliktu.

Oto przykład Docker Compose:

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

Następnie możesz uzyskać dostęp do portów na hoście z kontenera, używając nazwy hosta „dockerhost”.

bcoughlan
źródło
3

Możesz uzyskać dostęp do lokalnego serwera WWW, który działa na twoim hoście na dwa sposoby.

  1. Podejście 1 z publicznym adresem IP

    Użyj publicznego adresu IP komputera hosta, aby uzyskać dostęp do serwera WWW w kontenerze dokowanym Jenkins.

  2. Podejdź do 2 z siecią hosta

    Użyj „--net host”, aby dodać kontener dokujący Jenkins do stosu sieciowego hosta. Kontenery rozmieszczone na stosie hosta mają pełny dostęp do interfejsu hosta. Możesz uzyskać dostęp do lokalnego serwera WWW w kontenerze dokowanym z prywatnym adresem IP komputera-hosta.

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

Uruchom kontener z siecią hosta Eg: docker run --net host -it ubuntui uruchom, ifconfigaby wyświetlić listę wszystkich dostępnych adresów IP sieci, które są dostępne z kontenera dokera.

Np .: Uruchomiłem serwer nginx na moim lokalnym komputerze hosta i jestem w stanie uzyskać dostęp do adresów URL stron internetowych nginx z kontenera dokera Ubuntu.

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

Dostęp do serwera internetowego Nginx (działającego na lokalnym komputerze hosta) z kontenera dokującego Ubuntu z prywatnym adresem IP sieci.

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
źródło
3

W przypadku docker-composeużycia sieci mostowej do utworzenia sieci prywatnej między kontenerami przyjęte rozwiązanie docker0nie działa, ponieważ interfejs wyjściowy z kontenerów nie jest, docker0ale jest to losowo generowany identyfikator interfejsu, taki jak:

$ 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

Niestety losowy identyfikator nie jest przewidywalny i będzie się zmieniał za każdym razem, gdy komponowanie musi odtworzyć sieć (np. Przy ponownym uruchomieniu hosta). Moim rozwiązaniem jest utworzenie sieci prywatnej w znanej podsieci i skonfigurowanie iptablesakceptacji tego zakresu:

Utwórz fragment pliku:

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"

Możesz zmienić podsieć, jeśli wymaga tego twoje środowisko. Dowolnie wybrałem 192.168.32.0/20za pomocą, docker network inspectaby zobaczyć, co było tworzone domyślnie.

Skonfiguruj iptablesna hoście, aby zezwalał na prywatną podsieć jako źródło:

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

To najprostsza możliwa iptablesreguła. Możesz dodać inne ograniczenia, na przykład według portu docelowego. Nie zapomnij zachować reguł iptables, gdy będziesz zadowolony, że działają.

Zaletą tego podejścia jest powtarzalność, a zatem automatyzacja. Korzystam z templatemodułu ansible, aby wdrożyć mój plik tworzenia z podstawieniem zmiennych, a następnie używam modułów iptablesi shell, aby odpowiednio skonfigurować i zachować reguły zapory.

Andy Brown
źródło
Widziałem wiele sugerujących odpowiedzi iptables -A INPUT -i docker0 -j ACCEPT, ale to mi nie pomogło, podczas gdy iptables -I INPUT 1 -s 192.168.32.0/20 -j ACCEPTsugerowane tutaj rozwiązało mój problem.
Terry Brown,
1

To stare pytanie i zawierało wiele odpowiedzi, ale żadne z nich nie pasowało wystarczająco dobrze do mojego kontekstu. W moim przypadku kontenery są bardzo ubogie i nie zawierają narzędzi sieciowych niezbędnych do wyodrębnienia adresu IP hosta z kontenera.

Ponadto, --net="host"podejście to jest bardzo surowym podejściem, które nie ma zastosowania, gdy chce się mieć dobrze izolowaną konfigurację sieci z kilkoma kontenerami.

Zatem moim podejściem jest wyodrębnienie adresu hosta po stronie hosta, a następnie przekazanie go do kontenera z --add-hostparametrem:

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

lub zapisz adres IP hosta w zmiennej środowiskowej i użyj tej zmiennej później:

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

Następnie docker-hostjest dodawany do pliku hosts kontenera i można go używać w ciągach połączeń z bazą danych lub adresach URL API.

Passiday
źródło
0

Gdy masz dwa obrazy dokerów „już” utworzone i chcesz umieścić dwa kontenery, aby komunikować się ze sobą.

W tym celu możesz wygodnie uruchomić każdy kontener z własną nazwą - i użyć flagi --link, aby umożliwić komunikację między nimi. Nie dostajesz tego jednak podczas kompilacji dokera.

Kiedy jesteś w scenariuszu takim jak ja i to jest twoje

docker build -t "centos7/someApp" someApp/ 

To się psuje, kiedy próbujesz

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

i utkniesz na „curl / wget”, nie zwracając „trasy do hosta”.

Powodem są zabezpieczenia ustawione przez dokera, które domyślnie zabraniają komunikacji z kontenera do hosta lub innych kontenerów działających na hoście. To było dla mnie dość zaskakujące, muszę przyznać, że można oczekiwać, że echosfery dokerów działających na lokalnej maszynie po prostu bezbłędnie będą mogły się ze sobą połączyć bez zbytniej przeszkody.

Wyjaśnienie tego zostało szczegółowo opisane w następującej dokumentacji.

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

Dostępne są dwa szybkie obejścia, które pomagają w poruszaniu się, obniżając bezpieczeństwo sieci.

Najprostszą alternatywą jest wyłączenie zapory ogniowej - lub zezwolenie na wszystkie. Oznacza to uruchomienie niezbędnej komendy, którą może być systemctl stop firewalld, iptables -F lub równoważny.

Mam nadzieję, że te informacje ci pomogą.

99Sono
źródło
3
Dla przypomnienia, --linkjest teraz przestarzały
Mr.Budris
0

Dla mnie (Windows 10, Docker Engine v19.03.8) była to kombinacja https://stackoverflow.com/a/43541732/7924573 i https://stackoverflow.com/a/50866007/7924573 .

  1. zmień host / ip na host.docker.internal
    np .: LOGGER_URL = " http: //host.docker.internal: 8085 / log "
  2. ustaw tryb_sieci na mostek (jeśli chcesz utrzymać przekierowanie portów; jeśli nie używasz hosta ):
    version: '3.7' services: server: build: . ports: - "5000:5000" network_mode: bridge lub alternatywnie: użyj, --net="bridge"jeśli nie używasz komponentu dokującego (podobny do https://stackoverflow.com/a/48806927/7924573 )
    Jak wskazano w poprzednich odpowiedziach: należy tego używać tylko w lokalnym środowisku programistycznym .
    Aby uzyskać więcej informacji, przeczytaj: https://docs.docker.com/compose/compose-file/#network_mode i https://docs.docker.com/docker-for-windows/networking/#use-cases-and-workarounds
tschomacker
źródło