Яка різниця між "експонувати" та "опублікувати" в Докер?


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

Чи можливо це?

Відповіді:


731

В основному у вас є три варіанти:

  1. Ні вказувати, EXPOSEні-p
  2. Тільки вкажіть EXPOSE
  3. Вкажіть EXPOSEі-p

1) Якщо ви не вказуєте ні EXPOSEнорму -p, послуга в контейнері буде доступна лише зсередини самого контейнера.

2) Якщо ви EXPOSEкористуєтесь портом, послуга в контейнері доступна не ззовні Docker, а з інших контейнерів Docker. Тож це добре для міжконтейнерного спілкування.

3) Якщо ви EXPOSEта -pпорт, послуга в контейнері доступна з будь-якого місця, навіть поза Docker.

Причиною, чому обидва розділені, є ІМХО, оскільки:

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

У документації прямо зазначено:

EXPOSEІнструкція надає порти для використання в посилання.

Він також вказує на те, як зв’язати контейнери , що в основному є міжконтейнерним спілкуванням, про яке я говорив.

PS: Якщо ви робите -p, але цього не робите EXPOSE, Докер робить неявне EXPOSE. Це тому, що якщо порт відкритий для загального користування, він автоматично відкриється і для інших контейнерів Docker. Звідси -pвключає EXPOSE. Тому я не перераховував це вище як четвертий випадок.


57
Я думаю, що ви неправі з EXPOSE. З інших контейнерів ви можете отримати доступ до всіх портів контейнерів, не піддаючи їх дії. Я спробував це. Тут є те, що IP-адреса контейнера непередбачувана. Я вважаю, що посилання використовується для визначення того, який контейнер ви хочете підключити (тому ви посилаєтесь на конкретний IP-контейнер), а не для включення з'єднання.
Іррі

7
«Якщо ви не вказали який - або з тих , хто", було б корисно , якби ви уточнити , що з «тими» ви маєте в виду , EXPOSEі -pа не три кулі точки прецедентів. Мене там трохи заплутало.
Пітікос

4
Щоб бути повністю завершена, ця відповідь має також звернутися четвертий можливий випадок: ви не вказали EXPOSE, але ви ж вказати -p. Наскільки я розумію, що якщо ви завжди використовуєте -pта запускаєте окремі контейнери, тоді EXPOSEдобре опускати, але це стає корисним / необхідним при використанні -Pабо --link. (А оскільки ви не знаєте, як інші люди будуть використовувати ваше зображення, його EXPOSEслід вказати на будь-яких публічних зображеннях.)
GrandOpener

6
Документи більше не заявляють "Інструкція EXPOSE відкриває порти для використання у посиланнях".
Лорін Хохштайн

11
Downvote тому, що це суттєво неправильно. Експозиція - це в основному документація, а не використання не обмежує доступ. Це нерозуміння дангеросу, якщо хтось покладається на нього, щоб обмежити доступ.
mc0e

166

Коротка відповідь:

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

Зауважте нижче:

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

Розкриття та публікація портів

У мережах Docker є два різних механізми, які безпосередньо включають мережеві порти: відкриття та публікація портів. Це стосується мостової мережі за замовчуванням та визначених користувачем мостових мереж.

  • Ви відкриваєте порти, використовуючи EXPOSEключове слово в Dockerfile або --exposeпрапор для запуску докера . Розкриття портів - це спосіб документувати, які порти використовуються, але насправді не відображає та не відкриває жодних портів . Розкривати порти необов’язково.

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

від: Мережа контейнерів Docker

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

Можливо, поточна документація наведена нижче:

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

За замовчуванням, коли ви створюєте контейнер, він не публікує жодного зі своїх портів у зовнішній світ. Щоб зробити порт доступним для служб за межами Docker або контейнерів Docker, які не підключені до мережі контейнера, використовуйте прапор --publishабо -p. Це створює правило брандмауера, яке відображає порт контейнера на порт хоста Docker.

і їх можна знайти тут: docs.docker.com/config/containers/container-networking/#publish-ports

Також,

ВИДАЧА

... EXPOSEІнструкція фактично не публікує порт . Він функціонує як тип документації між людиною, яка створює зображення, і особою, яка управляє контейнером, про які порти призначені для публікації.

від: Довідкова довідка






Доступ до послуг, коли 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.


16
тепер це правильна відповідь. прийнята відповідь заснована на попередніх версіях, здається.
Лука Ш

що таке хост-порт, який ви використовували для 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
...

Загальнодоступний порт і протокол необов’язкові, якщо не вказаний загальнодоступний порт, докер буде обраний на хості докером, щоб виставити вказаний порт контейнера на Dockerfile.

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

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

У будь-якому випадку, якщо ви не використовуєте EXPOSE-Pрежимом докерного запуску) -p, жодні порти не будуть відкриті.

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


це вірно. Коли у вас є порт EXPOSE portNumber у Dockerfile, не забудьте викликати докер-запуск із -P.
КуньЮ Цай

6

Ви відкриваєте порти, використовуючи ключове слово EXPOSE в Dockerfile або прапор --expose to docker run. Розкриття портів - це спосіб документувати, які порти використовуються, але насправді не відображає та не відкриває жодних портів. Розкривати порти необов’язково.

Джерело: github commit


3

Більшість людей використовують докерні композиції з мережами. У документації зазначено:

Функція мережі Docker підтримує створення мереж без необхідності виставляти порти всередині мережі, детальну інформацію див. В огляді цієї функції).

Це означає, що якщо ви використовуєте мережі для зв'язку між контейнерами, вам не потрібно буде турбуватися про відкриття портів.


-5

EXPOSE використовується для відображення локального порту контейнера портів, тобто: якщо ви вказали експонацію у файлі docker, як

EXPOSE 8090

Що це зробить, буде покласти localhost порт 8090 на контейнерний порт 8090

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