В чем разница между «выставлять» и «публиковать» в Docker?

517

Я экспериментирую с Dockerfiles и думаю, что понимаю большую часть логики. Однако я не вижу разницы между «выставлением» и «публикацией» порта в этом контексте.

Все учебники, которые я видел, сначала включают EXPOSEкоманду в Dockerfile:

...
EXPOSE 8080
...

Затем они создают образ из этого Dockerfile:

$ docker build -t an_image - < Dockerfile

А затем опубликуйте тот же порт, что и выше, при запуске образа:

$ docker run -d -p 8080 an_image

или опубликовать все порты, используя

$ docker run -d -P an_image

Какой смысл выставлять порт в Dockerfile, если он все равно будет опубликован? Возникает ли когда-нибудь необходимость выставлять порт, а не публиковать его позже? По сути, я хотел бы указать все порты, которые я буду использовать в Dockerfile при создании образа, а затем не беспокоиться о них снова, запустив их просто с помощью:

$ docker run -d an_image

Это возможно?

user1496984
источник

Ответы:

733

По сути, у вас есть три варианта:

  1. Ни указать, EXPOSEни-p
  2. Только указать EXPOSE
  3. Укажите EXPOSEи-p

1) Если вы не укажете ни то, EXPOSEни другое -p, услуга в контейнере будет доступна только внутри самого контейнера.

2) Если у вас EXPOSEпорт, служба в контейнере не доступна снаружи Docker, но из других контейнеров Docker. Так что это хорошо для межконтейнерного общения.

3) Если вы EXPOSEи -pпорт, служба в контейнере доступна из любой точки мира, даже за пределами Docker.

Причина, по которой оба разделены, ИМХО, потому что:

  • выбор порта хоста зависит от хоста и, следовательно, не принадлежит Dockerfile (иначе это будет зависеть от хоста),
  • и часто достаточно, чтобы служба в контейнере была доступна из других контейнеров.

В документации прямо говорится:

EXPOSEИнструкция предоставляет порты для использования в ссылки.

Он также указывает на то, как связать контейнеры , что в основном является межконтейнерным взаимодействием, о котором я говорил.

PS: если вы делаете -p, но не делаете EXPOSE, Docker делает неявное EXPOSE. Это связано с тем, что если порт открыт для общего доступа, он автоматически также открывается для других контейнеров Docker. Следовательно -pвключает EXPOSE. Вот почему я не перечислил это как четвертый случай.

Голо Роден
источник
57
Я думаю, что вы не правы с EXPOSE. Из других контейнеров вы можете получить доступ ко всем портам контейнера, не открывая их. Я попробовал это. Подвох в том, что IP-адрес контейнера непредсказуем. Я считаю, что ссылка используется, чтобы указать, к какому контейнеру вы хотите подключиться (чтобы вы связались с конкретным IP-адресом контейнера), а не для подключения.
Иржи
7
«Если вы не указали какой - либо из тех , кто», было бы полезно , если бы вы уточнить , что с «теми» вы имеете в виду , EXPOSEи -pа не три пули точки прецедентов. Я немного запутался там.
Питикос,
4
Чтобы быть полностью завершена, этот ответ должен также обратиться четвертый возможный случай: вы не указали EXPOSE, но вы же указать -p. Насколько я понимаю, если вы всегда используете -pи запускаете отдельные контейнеры, то EXPOSEэто можно пропустить, но это становится полезным / необходимым при использовании -Pили --link. (И поскольку вы не знаете, как другие люди будут использовать ваше изображение, EXPOSEследует указать это в любых общедоступных изображениях.)
GrandOpener
6
В документах больше не указывается «Инструкция EXPOSE предоставляет порты для использования в ссылках».
Лорин Хохштайн
11
Даунвот, потому что это существенно неверно. Expose - это в основном документация, и ее использование не ограничивает доступ. Это опасное недоразумение, если кто-то полагается на него, чтобы ограничить доступ.
mc0e
167

Короткий ответ:

  • EXPOSEэто способ документирования
  • --publish(или -p) является способом отображения на хост - порт к включенному контейнерного порта

Обратите внимание, что ниже:

  • EXPOSEсвязано с Dockerfiles( документирование )
  • --publishсвязано с docker run ...( выполнение / время выполнения )

Разоблачение и публикация портов

В сети Docker есть два разных механизма, которые напрямую задействуют сетевые порты: открывающие и публикующие порты. Это относится к мостовой сети по умолчанию и пользовательским мостовым сетям.

  • Вы EXPOSEоткрываете порты, используя ключевое слово в Dockerfile или --exposeфлаг для запуска Docker . Предоставление портов - это способ документировать, какие порты используются, но на самом деле не отображает и не открывает какие-либо порты . Разоблачение портов не является обязательным.

  • Вы публикуете порты, используя --publishили--publish-all флагаdocker run . Это сообщает Docker, какие порты открывать в сетевом интерфейсе контейнера. Когда порт публикуется, он сопоставляется с доступным портом высокого порядка (выше, чем 30000) на хост-компьютере, если только вы не укажете порт, который будет отображаться на хост-компьютере во время выполнения. Вы не можете указать порт для сопоставления на хост-машине при создании образа (в Dockerfile), потому что нет способа гарантировать, что порт будет доступен на хост-машине, на которой вы запускаете образ .

от: Docker контейнерные сети

Обновление за октябрь 2019 года : вышеуказанный фрагмент текста больше не находится в документации, но архивная версия находится здесь: docs.docker.com/v17.09/engine/userguide/networking/#exposing-and-publishing-ports

Возможно текущая документация ниже:

Опубликованные порты

По умолчанию при создании контейнера он не публикует свои порты во внешнем мире. Чтобы сделать порт доступным для служб вне Docker или для контейнеров Docker, которые не подключены к сети контейнера, используйте флаг --publishили -p. Это создает правило брандмауэра, которое сопоставляет порт контейнера с портом на хосте Docker.

и можно найти здесь: docs.docker.com/config/containers/container-networking/#published-ports

Также,

ПОДВЕРГАТЬ

... EXPOSEИнструкция фактически не публикует порт . Он функционирует как тип документации между человеком, который создает образ, и человеком, который запускает контейнер, о том, какие порты предназначены для публикации.

от: ссылка на Dockerfile






Доступ к услуге, когда EXPOSE/ --publishне определены:

В ответе @Golo Roden говорится, что:

«Если вы не укажете ни один из них, служба в контейнере не будет доступна нигде, кроме как внутри самого контейнера».

Возможно, именно так и было во время написания ответа, но теперь кажется, что даже если вы не используете EXPOSEили --publish, hostи другие containersпользователи той же сети смогут получить доступ к службе, которую вы можете запустить внутри этого контейнера.

Как проверить это:

Я использовал следующее Dockerfile. По сути, я начинаю с Ubuntu и устанавливаю крошечный веб-сервер:

FROM ubuntu
RUN apt-get update && apt-get install -y mini-httpd

Я buildизображение как «testexpose» и runновый контейнер с:

docker run --rm -it testexpose bash

Внутри контейнера я запускаю несколько экземпляров mini-httpd:

root@fb8f7dd1322d:/# mini_httpd -p 80
root@fb8f7dd1322d:/# mini_httpd -p 8080
root@fb8f7dd1322d:/# mini_httpd -p 8090

Затем я могу использовать curlхост или другие контейнеры для загрузки домашней страницы mini-httpd.

tgogos
источник
16
теперь это правильный ответ. принятый ответ основан на предыдущих версиях, кажется.
Люк W
какой порт хоста вы использовали для curl?
мозговой штурм
Я использовал IP контейнера (что-то вроде 172.17.0.2) и все порты, которые я упоминаю. Если вы используете Docker для Mac / Windows, сеть отличается. Там нет docker0моста.
tgogos
при публикации всех портов EXPOSEd с флагом "-P", как я могу сказать, какой порт используется на хосте ??
шестьдесят четыре бита
1
@ sixty4bit взгляните на этот вопрос: как узнать, какой случайный порт выбрал Docker? ,
tgogos
9

См. Официальную ссылку на документацию: https://docs.docker.com/engine/reference/builder/#expose.

EXPOSEПозволяют определить частный (контейнер) и общественных (хост) портов , чтобы выставить на изображение время сборки, когда контейнер работает , если вы запустите контейнер -P.

$ docker help run
...
  -P, --publish-all                    Publish all exposed ports to random ports
...

Публичный порт и протокол являются необязательными, если не указан общедоступный порт, на хосте docker будет выбран случайный порт, чтобы открыть указанный порт контейнера в Dockerfile.

Хорошая практика - не указывать публичный порт, поскольку он ограничивает только один контейнер на хост (второй контейнер выбрасывает порт, который уже используется).

Вы можете использовать -pв docker runконтролировать то , что общественный порт открытые контейнерные порты будут соединяться.

Во всяком случае, если вы не используете EXPOSE-Pвключенным Docker), ни -pодин порт не будет открыт.

Если вы всегда использовать -pна которые docker runвам не нужны , EXPOSEно если вы используете EXPOSEвашу docker runкоманду может быть более простым, EXPOSEможет быть полезно , если вы не все равно , что порт будет экспонировать на хосте, или , если вы уверены , что только один контейнер будет загружен.

тонна
источник
это правильно. Если у вас есть EXPOSE portNumber в Dockerfile, не забудьте вызвать Docker, запущенный с -P.
КунЮ Цай
6

Вы открываете порты, используя ключевое слово EXPOSE в Dockerfile или флаг --expose для запуска Docker. Предоставление портов - это способ документировать, какие порты используются, но на самом деле не отображает и не открывает какие-либо порты. Разоблачение портов не является обязательным.

Источник: github commit

mzalazar
источник
3

Большинство людей используют docker compose с сетями. В документации говорится:

Сетевая функция Docker поддерживает создание сетей без необходимости выставлять порты внутри сети, подробную информацию см. В обзоре этой функции).

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

гермафродит
источник
-5

EXPOSE используется для сопоставления локального порта с контейнерным портом, т. Е. Если вы указываете expose в файле Docker, например

EXPOSE 8090

Что он сделает, это отобразит локальный хост-порт 8090 на контейнерный порт 8090

Мансур Али
источник