Перенаправить порт хоста в Docker-контейнер

167

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

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

РЕДАКТИРОВАТЬ: некоторые уточнения:

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

Я должен был сделать этот трюк, чтобы получить любое интернет-соединение с контейнером: мой брандмауэр блокирует сетевые подключения от док-контейнера к внешней стороне.

РЕДАКТИРОВАТЬ : В конце концов я пошел с созданием настраиваемого моста с использованием конвейерной связи и заставить службы прослушивать IP-адреса моста. Я пошел с этим подходом вместо того, чтобы MongoDB и RabbitMQ слушали на док-мосте, потому что он дает больше гибкости.

JoelKuiper
источник

Ответы:

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 из ваших контейнеров.

Seldo
источник
36
Как контейнер узнает, на какой IP отправлять запросы? Я могу жестко закодировать значение (172.17.42.1 здесь и на моем тестовом стенде, но всегда ли это так?), Но, похоже, это противоречит принципам докера работы с любым хостом!
JP.
2
@Seldo: Нужно ли настраивать этот интерфейс? Я использую Docker 1.7.1, и у меня есть только loи eth0.
mknecht
8
Можно ли это как-то сделать, если хост только слушает 127.0.0.1?
ХансХархофф
5
«Вам нужно будет сказать rabbit / mongo привязаться к этому IP (172.17.42.1). После этого вы сможете открывать соединения с 172.17.42.1 из своих контейнеров». Было бы хорошо, если бы вы объяснили, как это сделать
Novaterata
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 = host - Указывает Docker пропустить размещение контейнера внутри отдельного сетевого стека. По сути, этот выбор говорит Docker не контейнировать сети контейнера! В то время как контейнерные процессы будут по-прежнему ограничиваться собственной файловой системой, списком процессов и ограничениями ресурсов, быстрая команда ip addr покажет вам, что в сети они живут «снаружи» на главном хосте Docker и имеют полный доступ к его сетевым интерфейсам. , Обратите внимание, что это не позволяет контейнеру реконфигурировать стек сети хоста - для этого потребуется --privileged = true - но позволяет процессам контейнера открывать порты с низким номером, как любой другой корневой процесс. Это также позволяет контейнеру получать доступ к локальным сетевым сервисам, таким как D-bus. Это может привести к тому, что процессы в контейнере смогут выполнять неожиданные действия, такие как перезагрузка компьютера.

Дэвид Грейсон
источник
12
Для тех, кто не использует Docker в Linux (например, использует некоторую виртуализацию), это не сработает, поскольку хост будет содержать виртуальную машину, а не фактическую хост-операционную систему.
Себастьян Граф
13
В частности, в MacOS это невозможно (без некоторых обходных путей): docs.docker.com/docker-for-mac/networking/…
pje
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 с этой службой.

czerasz
источник
8
Вы в основном помещаете закрытый ключ в образ Docker. Секреты никогда не должны попадать в образ Docker.
Теох Хан Хуэй,
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

Arigion
источник
3

Если MongoDB и RabbitMQ работают на хосте, то порт должен быть уже открыт, поскольку он не находится в Docker.

Вам не нужна -pопция для предоставления портов от контейнера к хосту. По умолчанию все порты выставлены. -pОпция позволяет выставить порт из контейнера с внешней стороны хозяина.

Итак, я думаю, что вам не нужно -pвообще, и это должно работать нормально :)

creack
источник
1
Я знал это, но, похоже, мне не хватает информации: посмотрите последние изменения, так как я не могу добраться до портов на хосте.
JoelKuiper
2
Вам необходимо настроить rabbitmq и mongodb так, чтобы они слушали на мосту, а не только на основном сетевом интерфейсе.
Creack
13
@creack, как ты заставляешь rabbitmq и mongodb слушать на мосту?
Райан Уоллс