Przekaż port hosta do kontenera Dockera

166

Czy możliwe jest otwarcie portów dostępu do kontenera Docker przez hosta? Konkretnie mam MongoDB i RabbitMQ uruchomione na hoście i chciałbym uruchomić proces w kontenerze Docker, aby nasłuchiwać kolejki i (opcjonalnie) pisać do bazy danych.

Wiem, że mogę przekierować port z kontenera do hosta (przez opcję -p) i mam połączenie ze światem zewnętrznym (tj. Internetem) z kontenera Docker, ale nie chciałbym ujawniać portów RabbitMQ i MongoDB od gospodarza do świata zewnętrznego.

EDYCJA: kilka wyjaśnień:

Starting Nmap 5.21 ( http://nmap.org ) at 2013-07-22 22:39 CEST
Nmap scan report for localhost (127.0.0.1)
Host is up (0.00027s latency).
PORT     STATE SERVICE
6311/tcp open  unknown

joelkuiper@vps20528 ~ % docker run -i -t base /bin/bash
root@f043b4b235a7:/# apt-get install nmap
root@f043b4b235a7:/# nmap 172.16.42.1 -p 6311 # IP found via docker inspect -> gateway

Starting Nmap 6.00 ( http://nmap.org ) at 2013-07-22 20:43 UTC
Nmap scan report for 172.16.42.1
Host is up (0.000060s latency).
PORT     STATE    SERVICE
6311/tcp filtered unknown
MAC Address: E2:69:9C:11:42:65 (Unknown)

Nmap done: 1 IP address (1 host up) scanned in 13.31 seconds

Musiałem zrobić tę sztuczkę, aby uzyskać jakiekolwiek połączenie internetowe z kontenerem: Moja zapora sieciowa blokuje połączenia sieciowe z kontenera Dockera na zewnątrz

EDYCJA : Ostatecznie zdecydowałem się na stworzenie niestandardowego mostu za pomocą rurociągów i nasłuchiwanie usług na adresach IP mostka. Wybrałem to podejście zamiast nasłuchiwania MongoDB i RabbitMQ na mostku docker, ponieważ zapewnia to większą elastyczność.

JoelKuiper
źródło

Odpowiedzi:

53

Twój host Dockera udostępnia adapter do wszystkich kontenerów. Zakładając, że jesteś na ostatnim ubuntu, możesz uruchomić

ip addr

Spowoduje to wyświetlenie listy kart sieciowych, z których jedna będzie wyglądać mniej więcej tak

3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP
link/ether 22:23:6b:28:6b:e0 brd ff:ff:ff:ff:ff:ff
inet 172.17.42.1/16 scope global docker0
inet6 fe80::a402:65ff:fe86:bba6/64 scope link
   valid_lft forever preferred_lft forever

Będziesz musiał powiedzieć królikowi / mongo powiązanie z tym adresem IP (172.17.42.1). Następnie powinno być możliwe otwieranie połączeń do 172.17.42.1 z poziomu kontenerów.

Seldo
źródło
35
Skąd kontener wie, na jaki adres IP ma wysyłać żądania? Mogę na stałe zakodować wartość (172.17.42.1 tutaj i na moim urządzeniu testowym, ale czy to zawsze prawda?), Ale wydaje się, że jest to sprzeczne z zasadami dockera dotyczącymi pracy z dowolnym hostem!
JP.
2
@Seldo: Czy ten interfejs wymaga konfiguracji, aby się pojawił? Używam dockera 1.7.1 i mam tylko loi eth0.
mknecht
8
Czy można to jakoś zrobić, jeśli host nasłuchuje tylko na 127.0.0.1?
HansHarhoff
5
„Będziesz musiał powiedzieć królikowi / mongo, aby połączył się z tym adresem IP (172.17.42.1). Następnie powinieneś być w stanie otwierać połączenia do 172.17.42.1 z poziomu kontenerów”. Byłoby miło, gdybyś wyjaśnił, jak to zrobić
Novaterata
1
Jak wspomniał @Novaterata, czy ktoś mógłby wyjaśnić ten proces?
keskinsaf
121

Prostym, ale stosunkowo niebezpiecznym sposobem byłoby skorzystanie z --net=hostopcji docker run.

Ta opcja sprawia, że ​​kontener używa stosu sieciowego hosta. Następnie możesz połączyć się z usługami działającymi na hoście, używając po prostu „localhost” jako nazwy hosta.

Jest to łatwiejsze do skonfigurowania, ponieważ nie będziesz musiał konfigurować usługi tak, aby akceptowała połączenia z adresu IP kontenera Dockera i nie będziesz musiał podawać kontenerowi Dockera konkretnego adresu IP lub nazwy hosta, z którym ma się połączyć, po prostu port.

Na przykład możesz to przetestować, uruchamiając następujące polecenie, które zakłada, że ​​twój obraz jest wywoływany my_image, twój obraz zawiera telnetnarzędzie, a usługa, z którą chcesz się połączyć, znajduje się na porcie 25:

docker run --rm -i -t --net=host my_image telnet localhost 25

Jeśli myślisz o zrobieniu tego w ten sposób, zapoznaj się z ostrzeżeniem dotyczącym bezpieczeństwa na tej stronie:

https://docs.docker.com/articles/networking/

To mówi:

--net = host - Nakazuje Dockerowi pominięcie umieszczania kontenera w oddzielnym stosie sieciowym. Zasadniczo ten wybór mówi Dockerowi, aby nie umieszczał kontenerów w sieci! Podczas gdy procesy kontenerów będą nadal ograniczone do własnego systemu plików i listy procesów oraz limitów zasobów, szybkie polecenie ip addr pokaże Ci, że z punktu widzenia sieci żyją „na zewnątrz” głównego hosta Dockera i mają pełny dostęp do jego interfejsów sieciowych . Zauważ, że nie pozwala to kontenerowi na rekonfigurację stosu sieciowego hosta - co wymagałoby --privileged = true - ale pozwala procesom kontenera otwierać porty o niskiej liczbie, jak każdy inny proces główny. Umożliwia również kontenerowi dostęp do lokalnych usług sieciowych, takich jak D-bus. Może to prowadzić do tego, że procesy w kontenerze mogą wykonywać nieoczekiwane czynności, takie jak ponowne uruchamianie komputera.

David Grayson
źródło
12
Dla każdego, kto nie używa dockera w Linuksie (np. Używa wirtualizacji) to nie zadziała, ponieważ hostem będzie zawierająca VM, a nie rzeczywisty system operacyjny hosta.
Sebastian Graf,
13
W szczególności na MacOS nie jest to możliwe (bez pewnych obejść): docs.docker.com/docker-for-mac/networking/ ...
pje
15
W systemie MacOS --net=hostnie działa, jeśli zezwalasz procesowi kontenera na łączenie się z komputerem głównym za pomocą localhost. Zamiast tego połącz kontener ze specjalną nazwą hosta tylko dla systemu MacOS docker.for.mac.host.internalzamiast localhost. Aby docker runto działało, nie są potrzebne żadne dodatkowe parametry . Możesz przekazać to jako zmienną env, używając, -ejeśli chcesz, aby platforma kontenerowa była agnostyczna. W ten sposób możesz połączyć się z hostem wymienionym w env var i przekazać docker.for.mac.host.internalna MacOS i localhostLinux.
tul
18
Najnowsza nazwa hosta dla mac to host.docker.internal, zobacz dokumentację
xysun
Sama dla Windows docker run --rm -it --net=host postgres bashnastępniepsql -h host.docker.internal -U postgres
Leo Cavalcante
12

Możesz także stworzyć tunel ssh.

docker-compose.yml:

---

version: '2'

services:
  kibana:
    image: "kibana:4.5.1"
    links:
      - elasticsearch
    volumes:
      - ./config/kibana:/opt/kibana/config:ro

  elasticsearch:
    build:
      context: .
      dockerfile: ./docker/Dockerfile.tunnel
    entrypoint: ssh
    command: "-N elasticsearch -L 0.0.0.0:9200:localhost:9200"

docker/Dockerfile.tunnel:

FROM buildpack-deps:jessie

RUN apt-get update && \
    DEBIAN_FRONTEND=noninteractive \
    apt-get -y install ssh && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

COPY ./config/ssh/id_rsa /root/.ssh/id_rsa
COPY ./config/ssh/config /root/.ssh/config
COPY ./config/ssh/known_hosts /root/.ssh/known_hosts
RUN chmod 600 /root/.ssh/id_rsa && \
    chmod 600 /root/.ssh/config && \
    chown $USER:$USER -R /root/.ssh

config/ssh/config:

# Elasticsearch Server
Host elasticsearch
    HostName jump.host.czerasz.com
    User czerasz
    ForwardAgent yes
    IdentityFile ~/.ssh/id_rsa

W ten sposób elasticsearchma tunel do serwera z uruchomioną usługą (Elasticsearch, MongoDB, PostgreSQL) i udostępnia port 9200 z tą usługą.

czerasz
źródło
8
Zasadniczo umieszczasz klucz prywatny w obrazie platformy Docker. Sekrety nigdy nie powinny dostać się do obrazu Dockera.
Teoh Han Hui
2
Jak dotąd jest to jedyne rozsądne rozwiązanie.
helvete
5

Miałem podobny problem z dostępem do serwera LDAP z kontenera dockera. Ustawiłem stały adres IP dla kontenera i dodałem regułę zapory.

docker-compose.yml:

version: '2'
services:
  containerName:
    image: dockerImageName:latest
    extra_hosts:
      - "dockerhost:192.168.50.1"
    networks:
      my_net:
        ipv4_address: 192.168.50.2
networks:
  my_net:
    ipam:
      config:
      - subnet: 192.168.50.0/24

reguła iptables:

iptables -A INPUT -j ACCEPT -p tcp -s 192.168.50.2 -d $192.168.50.1 --dport portnumberOnHost

Dostęp do wnętrza kontenera dockerhost:portnumberOnHost

Arigion
źródło
3

Jeśli MongoDB i RabbitMQ są uruchomione na hoście, port powinien już zostać ujawniony, ponieważ nie znajduje się w Dockerze.

Nie potrzebujesz tej -popcji, aby udostępnić porty z kontenera do hosta. Domyślnie wszystkie porty są widoczne. Ta -popcja umożliwia wystawienie portu z kontenera na zewnątrz hosta.

Domyślam się, że nie potrzebujesz -pw ogóle i powinno działać dobrze :)

trzeszczenie
źródło
1
Wiedziałem o tym, ale wydaje mi się, że brakuje mi trochę informacji: zobacz ostatnią edycję, ponieważ nie mogę dotrzeć do portów na hoście.
JoelKuiper,
2
Musisz skonfigurować rabbitmq i mongodb, aby nasłuchiwać na mostku, a nie tylko na głównym interfejsie sieciowym.
trzeszczenie
13
@creack. Jak zmusić rabbitmq i mongodb do słuchania na moście?
Ryan Walls