Переслати порт хоста на контейнер докера


167

Чи можливо відкрити хост до портів контейнерів Docker? Конкретно у мене MongoDB та RabbitMQ працюють на хості, і я хотів би запустити процес у контейнері Docker, щоб прослухати чергу та (необов'язково) записати в базу даних.

Я знаю, що можу переслати порт від контейнера до хоста (через опцію -p) і мати зв’язок із зовнішнім світом (тобто Інтернетом) зсередини контейнера Docker, але я хотів би не виставляти порти RabbitMQ та MongoDB від господаря до зовнішнього світу.

EDIT: деяке уточнення:

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

Мені довелося зробити цей трюк, щоб отримати будь-яке з'єднання з Інтернетом під час контейнера: Мій брандмауер блокує мережеві з'єднання від контейнера докера до зовні

EDIT : Врешті-решт я пішов зі створенням власного мосту, використовуючи трубопроводи та надаючи послуги прослуховуванню IP-адрес мосту. Я пішов із таким підходом замість того, щоб MongoDB та RabbitMQ слухали на докерському мосту, оскільки це дає більшу гнучкість.

Відповіді:


54

Ваш хокер-докер виставляє адаптер для всіх контейнерів. Якщо припустити, що ви перебуваєте на останньому ubuntu, ви можете запустити

ip addr

Це дасть вам список мережевих адаптерів, один з яких буде виглядати приблизно так

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

Вам потрібно буде сказати кролику / монго, щоб прив’язатись до цього IP (172.17.42.1). Після цього ви зможете відкрити з'єднання до 172.17.42.1 зсередини контейнерів.


36
Як контейнер знає, до якого IP-адреси надсилати запити? Я можу жорстко кодувати значення (172.17.42.1 тут і на моїй тестовій установці, але це завжди правда?), Але це, здається, суперечить принципам роботи докера з будь-яким хостом!
JP.

2
@Seldo: Чи потрібен цей інтерфейс для налаштування? Я використовую docker 1.7.1, а у мене є тільки loі eth0.
mknecht

8
Чи можливо це зробити якось, якщо хост слухає лише 127.0.0.1?
HansHarhoff

5
"Вам потрібно буде сказати кролику / монго, щоб прив'язатись до цього IP (172.17.42.1). Після цього ви маєте змогу відкривати з'єднання до 172.17.42.1 зсередини своїх контейнерів." Було б добре, якби ви пояснили, як це зробити
Новатерата

1
Як згадував @Novaterata, чи може хтось пояснити цей процес?
keskinsaf

122

Простим, але відносно небезпечним способом було б використовувати --net=hostваріант для docker run.

Цей параметр робить це таким чином, що контейнер використовує мережевий стек хоста. Потім ви можете підключитися до служб, що працюють на хості, просто використовуючи "localhost" як ім'я хоста.

Це простіше налаштувати, оскільки вам не доведеться конфігурувати службу, щоб приймати з'єднання з IP-адреси контейнера докера, і вам не доведеться повідомляти контейнеру докера конкретну IP-адресу або ім'я хоста, до якого потрібно підключитися, просто порт.

Наприклад, ви можете перевірити це, виконавши таку команду, яка передбачає, що ваше зображення викликається my_image, ваше зображення включає telnetутиліту, а послуга, до якої потрібно підключитися, знаходиться на порту 25:

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

Якщо ви вирішили зробити це таким чином, будь ласка, зверніть увагу на безпеку на цій сторінці:

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

Він говорить:

--net = хост - повідомляє Docker пропустити розміщення контейнера всередині окремого мережного стеку. По суті, цей вибір підказує Docker не використовувати контейнерну мережу! Хоча контейнерні процеси все ще будуть обмежені власною файловою системою та списком процесів та обмеженнями ресурсів, швидка команда ip addr покаже вам, що в мережевому режимі вони живуть «поза» у головному хості Докера та мають повний доступ до його мережевих інтерфейсів. . Зауважте, що це не дозволяє контейнеру перенастроювати стек хост-мережі - що вимагатиме --privileged = true - але це дозволяє контейнерним процесам відкривати порти з невеликим числом, як і будь-який інший кореневий процес. Він також дозволяє контейнеру отримати доступ до таких послуг локальної мережі, як D-bus. Це може призвести до того, що процеси в контейнері можуть робити несподівані речі, такі як перезавантажити комп'ютер.


12
Для тих, хто не використовує docker в Linux (наприклад, використовуючи деяку віртуалізацію), це не буде працювати, оскільки хост буде містити VM, а не власне ОС хоста.
Себастьян Граф

13
Зокрема, на MacOS, це НЕ представляється можливим (без будь - яких обхідних шляхів): docs.docker.com/docker-for-mac/networking / ...
ПРД

15
У MacOS --net=hostне працює, оскільки дозволяє контейнерові підключитися до хост-машини за допомогою localhost. Натомість підкажіть ваш контейнер до спеціального імені хоста MacOS, docker.for.mac.host.internalа не localhost. Для цього не потрібні додаткові параметри docker run. Ви можете передати це як env var, використовуючи, -eякщо хочете зберегти свою платформу контейнерів агностичною. Таким чином ви зможете підключитися до хоста, названого в env var, і перейти docker.for.mac.host.internalна MacOS та localhostLinux.
тул

18
Останнє ім'я хоста для mac - host.docker.internalдивіться doc
xysun

Те саме для Windows docker run --rm -it --net=host postgres bashтодіpsql -h host.docker.internal -U postgres
Лео Кавальканте

12

Ви також можете створити 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

Таким чином у elasticsearchтунелю є сервер із запущеною службою (Elasticsearch, MongoDB, PostgreSQL) і відкриває порт 9200 з цією службою.


8
Ви в основному вводите приватний ключ у зображення Докера. Секрети ніколи не повинні потрапляти в зображення Докера.
Teoh Han Hui

2
Це єдине досі корисне рішення.
helvete

5

У мене була подібна проблема з доступом до LDAP-сервера з докерного контейнера. Я встановив фіксовану IP-адресу для контейнера і додав правило брандмауера.

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

Правило iptables:

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

Всередині контейнера доступ dockerhost:portnumberOnHost


3

Якщо MongoDB і RabbitMQ працюють на хості, порт повинен уже відкриватися, оскільки його немає в Docker.

Вам не потрібна -pопція, щоб виставити порти з контейнера до хоста. За замовчуванням відкриваються всі порти. Цей -pпараметр дозволяє виставити порт від контейнера до зовнішньої сторони хоста.

Отже, я здогадуюсь, що вам це зовсім не потрібно, -pі це повинно працювати нормально :)


1
Я це знав, але, здається, мені бракує трохи інформації: дивіться недавню редакцію, оскільки я не в змозі дістатися до портів хосту.
JoelKuiper

2
Потрібно налаштувати rabbitmq і mongodb, щоб вони також слухали на мосту, а не лише на вашому головному мережевому інтерфейсі.
скрип

13
@creack Як змусити кролика і mongodb слухати на мосту?
Ryan Walls
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.