Предоставление порта в реальном контейнере Docker

Я пытаюсь создать контейнер Docker, который действует как полнофункциональная виртуальная машина. Я знаю, что я могу использовать инструкцию EXPOSE внутри файла Docker, чтобы открыть порт, и я могу использовать флаг -p с docker run для назначения портов, но как только контейнер фактически запущен, есть ли команда открывать/отображать дополнительные порты живут?

Например, скажем, у меня есть контейнер Docker, который запускает sshd. Кто-то еще использует контейнер ssh и устанавливает httpd. Есть ли способ открыть порт 80 на контейнере и отобразить его на порт 8080 на хосте, чтобы пользователи могли посещать веб-сервер, запущенный в контейнере, без перезапуска?

Ответ 1

Вы не можете сделать это через Docker, но вы можете получить доступ к контейнеру, который не был открыт, с главной машины.

Если у вас есть контейнер с чем-то, запущенным на его порту 8000, вы можете запустить

wget http://container_ip:8000

Чтобы получить IP-адрес контейнера, запустите 2 команды:

docker ps

docker inspect container_name | grep IPAddress

Внутренне, Docker запускает вызов iptables при запуске изображения, поэтому, возможно, некоторые изменения будут работать.

чтобы открыть контейнерный порт 8000 на вашем порту 8001 localhosts:

 iptables -t nat -A  DOCKER -p tcp --dport 8001 -j DNAT --to-destination 172.17.0.19:8000

Один из способов, которым вы можете это исправить, - это настроить другой контейнер с соответствующим отображением портов и сравнить результат команды iptables-save (хотя мне пришлось удалить некоторые из другие параметры, которые заставляют трафик проходить через прокси-сервер докеры).

ПРИМЕЧАНИЕ: это подрывный докер, поэтому следует сделать это с осознанием того, что он может создать синий дым

ИЛИ

Другой альтернативой является просмотр опции (new? post 0.6.6?) -P, которая будет использовать произвольные хост-порты, а затем подключить их.

ИЛИ

с 0.6.5, вы можете использовать функцию LINKs, чтобы открыть новый контейнер, который говорит с существующим, с некоторой дополнительной ретрансляцией на флаги -p этого контейнера? (Я еще не использовал LINK)

ИЛИ

с докером 0.11? вы можете использовать docker run --net host .. для прикрепления вашего контейнера непосредственно к сетевым интерфейсам хоста (т.е. сеть не находится на расстоянии) и, таким образом, открываются порты all, которые вы открываете в контейнере.

Ответ 2

Вот что я буду делать:

  • Заключить живой контейнер.
  • Запустите контейнер снова с новым изображением с открытыми портами (я бы рекомендовал установить общий том и открыть порт ssh)
sudo docker ps 
sudo docker commit <containerid> <foo/live>
sudo docker run -i -p 22 -p 8000:80 -m /data:/data -t <foo/live> /bin/bash

Ответ 3

Вот еще одна идея. Используйте SSH для пересылки портов; это также полезно для работы в OS X (и, возможно, Windows), когда ваш хост Docker является виртуальной машиной.

docker exec -it <containterid> ssh -R5432:localhost:5432 <user>@<hostip>

Ответ 4

IPtables-хаки не работают, по крайней мере, на Docker 1.4.1.

Лучшим способом было бы запустить другой контейнер с открытым портом и реле с помощью socat. Это то, что я сделал (временно) для подключения к базе данных с помощью SQLPlus:

docker run -d --name sqlplus --link db:db -p 1521:1521 sqlplus

Dockerfile:

FROM debian:7

RUN apt-get update && \
    apt-get -y install socat && \
    apt-get clean

USER nobody

CMD socat -dddd TCP-LISTEN:1521,reuseaddr,fork TCP:db:1521

Ответ 5

Пока вы не можете открыть новый порт существующего контейнера, вы можете запустить новый контейнер в той же сети Docker и перенести его в исходный контейнер.

# docker run \
  --rm \
  -p $PORT:1234 \
  verb/socat \
    TCP-LISTEN:1234,fork \
    TCP-CONNECT:$TARGET_CONTAINER_IP:$TARGET_CONTAINER_PORT

Работающий пример

Запустите веб-сервис, который прослушивает порт 80, но не выставляет свой внутренний порт 80 (oops!):

# docker run -ti mkodockx/docker-pastebin   # Forgot to expose PORT 80!

Найти свою IP-сеть Docker:

# docker inspect 63256f72142a | grep IPAddress
                    "IPAddress": "172.17.0.2",

Запустите verb/socat с открытым портом 8080 и попросите его переслать TCP-трафик на этот IP-порт 80:

# docker run --rm -p 8080:1234 verb/socat TCP-LISTEN:1234,fork TCP-CONNECT:172.17.0.2:80

Теперь вы можете получить доступ к pastebin на http://localhost:8080/, и ваши запросы перейдут на socat:1234, который переводит его на pastebin:80, а ответ перемещается по тому же пути в обратном направлении.

Ответ 6

Мне пришлось иметь дело с этой же проблемой и смог решить ее, не останавливая ни один из моих запущенных контейнеров. Это решение обновлено по состоянию на февраль 2016 года, используя Docker 1.9.1. Во всяком случае, этот ответ представляет собой подробную версию ответа @ricardo-branco, но более подробно для новых пользователей.

В моем сценарии я хотел временно подключиться к MySQL, работающему в контейнере, и поскольку к нему привязаны другие контейнеры приложений, остановка, переконфигурация и повторный запуск контейнера базы данных была не стартовой.

Поскольку я хотел бы получить доступ к базе данных MySQL извне (из Sequel Pro через SSH-туннелирование), я собираюсь использовать порт 33306 на главной машине. (Не 3306, на всякий случай, если работает внешний экземпляр MySQL.)

Около часа настройки iptables оказался бесплодным, хотя:

Шаг за шагом, вот что я сделал:

mkdir db-expose-33306
cd db-expose-33306
vim Dockerfile

Измените dockerfile, разместив его внутри:

# Exposes port 3306 on linked "db" container, to be accessible at host:33306
FROM ubuntu:latest # (Recommended to use the same base as the DB container)

RUN apt-get update && \
    apt-get -y install socat && \
    apt-get clean

USER nobody
EXPOSE 33306

CMD socat -dddd TCP-LISTEN:33306,reuseaddr,fork TCP:db:3306

Затем создайте изображение:

docker build -t your-namespace/db-expose-33306 .

Затем запустите его, привязав к вашему запущенному контейнеру. (Используйте -d вместо -rm, чтобы сохранить его в фоновом режиме, пока явным образом не остановится и не удаляется. Я хочу, чтобы он выполнялся временно в этом случае.)

docker run -it --rm --name=db-33306 --link the_live_db_container:db -p 33306:33306  your-namespace/db-expose-33306

Ответ 7

Чтобы добавить к принятому ответу iptables решение, мне пришлось запустить еще две команды на хосте, чтобы открыть его для внешнего мира.

HOST> iptables -t nat -A DOCKER -p tcp --dport 443 -j DNAT --to-destination 172.17.0.2:443
HOST> iptables -t nat -A POSTROUTING -j MASQUERADE -p tcp --source 172.17.0.2 --destination 172.17.0.2 --dport https
HOST> iptables -A DOCKER -j ACCEPT -p tcp --destination 172.17.0.2 --dport https

Примечание. Я открывал порт https (443), мой внутренний IP-адрес докера был 172.17.0.2

Примечание 2: Эти правила и временные и будут продолжаться только до перезапуска контейнера.

Ответ 8

Вы можете использовать оверлейную сеть, например Weave Net, которая назначит уникальный IP-адрес для каждого контейнера и неявно выставляет все порты каждой контейнерной части сети.

Weave также обеспечивает сетевую интеграцию хоста. По умолчанию он отключен, но если вы хотите также получить доступ к IP-адресам контейнера (и всем его портам) с хоста, вы можете запустить просто запустить weave expose.

Полное раскрытие: я работаю в Weaveworks.

Ответ 9

Вы можете использовать SSH для создания туннеля и выставить свой контейнер на своем хосте.

Вы можете сделать это в обоих направлениях: от контейнера до хоста и от хоста к контейнеру. Но вам нужен SSH-инструмент, такой как OpenSSH, как (клиент на одном, так и сервер в другом).

Например, в контейнере вы можете сделать

$ yum install -y openssh openssh-server.x86_64
service sshd restart
Stopping sshd:                                             [FAILED]
Generating SSH2 RSA host key:                              [  OK  ]
Generating SSH1 RSA host key:                              [  OK  ]
Generating SSH2 DSA host key:                              [  OK  ]
Starting sshd:                                             [  OK  ]
$ passwd # You need to set a root password..

Вы можете найти IP-адрес контейнера из этой строки (в контейнере):

$ ifconfig eth0 | grep "inet addr" | sed 's/^[^:]*:\([^ ]*\).*/\1/g'
172.17.0.2

Затем в хосте вы можете просто сделать:

sudo ssh -NfL 80:0.0.0.0:80 [email protected]

Ответ 10

Существует удобная оболочка HAProxy.

docker run -it -p LOCALPORT:PROXYPORT --rm --link TARGET_CONTAINER:EZNAME -e "BACKEND_HOST=EZNAME" -e "BACKEND_PORT=PROXYPORT" demandbase/docker-tcp-proxy

Это создает HAProxy для целевого контейнера. легкий peasy.

Ответ 11

Сначала прочитайте ответ Рикарду. Это сработало для меня.

Однако существует сценарий, когда это не будет работать, если запускаемый контейнер был запущен с помощью компоновки докеров. Это связано с тем, что docker-compose (я запускаю docker 1.17) создает новую сеть. Способ решения этого сценария был бы

docker network ls

Затем добавьте следующее docker run -d --name sqlplus --link db:db -p 1521:1521 sqlplus --net network_name

Ответ 12

Невозможно выполнить сопоставление живого порта, но есть несколько способов предоставить контейнер Docker, что будет иметь реальный интерфейс, такой как виртуальная машина.

Интерфейсы Macvlan

Docker теперь включает сетевой драйвер Macvlan. Это придает сети Docker интерфейс "реального мира" и позволяет назначать эти сетевые адреса непосредственно контейнеру (например, режим мостов с виртуальными машинами).

docker network create \
    -d macvlan \
    --subnet=172.16.86.0/24 \
    --gateway=172.16.86.1  \
    -o parent=eth0 pub_net

pipework также может отобразить реальный интерфейс в контейнер или настроить дополнительный интерфейс в более старых версиях Docker.

IP-адрес маршрутизации

Если у вас есть контроль над сетью, вы можете маршрутизировать дополнительные сети на хост Docker для использования в контейнерах.

Затем вы назначаете эту сеть контейнерам и настраиваете хост Docker для маршрутизации пакетов через сеть докеров.

Общий интерфейс хоста

Опция --net host позволяет разделять общий интерфейс хоста в контейнере, но это, вероятно, не очень хорошая настройка для запуска нескольких контейнеров на одном хосте из-за общей природы.

Ответ 14

Для запуска контейнера используйте следующую команду:

docker run -d -p <host_port>:<container_exposed_port> -t <image_name>
  • - ваш номер порта localhost.
  • - номер порта, выставленный в файле докера для этого контейнера.

Ответ 15

Существует также флаг -P, который является совершенно новым и сообщает Docker о отображении любых необходимых сетевых портов внутри нашего контейнера на наш хост. Это поможет вам просмотреть веб-приложение из внешнего мира.

Когда вы привязываетесь к порту, Docker перенаправляет его для вас. Используя команду порта Docker, вы увидите что-то вроде этого:

docker port 5019f8e8f902
5000/tcp -> 0.0.0.0:49162
5555/tcp -> 0.0.0.0:49163
8080/tcp -> 0.0.0.0:49164