Вопрос или проблема
Вот базовый образ Docker NGINX:
FROM nginx:alpine
COPY src /usr/share/nginx/html
EXPOSE 80
Метаданные образа включают открытый порт:
> docker inspect --format '{{json .Config.ExposedPorts}}' ...
{"80/tcp":{}}
Но при запуске с docker run --publish-all
сервер недоступен.
Его нужно запускать с docker run --publish 80:80
.
Но разве не в этом заключается смысл EXPOSE
— документировать порт образа, чтобы их можно было публиковать без необходимости явно указывать благодаря --publish-all
?
--publish-all
флаг публикует “открытые порты на временные порты”.
Таким образом, вы получите что-то вроде:
docker container inspect nginx --format=json \
| jq -r '.[0].NetworkSettings.Ports'
{
"80/tcp": [
{
"HostIp": "0.0.0.0",
"HostPort": "32769"
}
]
}
Где, в этом случае, порт контейнера 80 сопоставлен с портом хоста 32769 (не 80).
Ответ или решение
Когда вы работаете с Docker, важно понимать механизм работы с портами, особенно в контексте флагов командной строки, таких как --publish
и --publish-all
. Давайте подробнее разберём проблему, с которой вы столкнулись при запуске контейнера с использованием --publish-all
, и почему это может не давать ожидаемых результатов.
Объяснение проблемы
При использовании команды:
docker run --publish-all <image-name>
Docker автоматически открывает все порты, которые были объявлены с помощью директивы EXPOSE
в Dockerfile. Однако, важно понимать, что при этом порты не публикуются на стандартных портах хоста. Вместо этого, каждый открытый порт сопоставляется с произвольным портом (эпhemeral port) на вашем хосте. Это значит, что ваш контейнерный порт 80 может быть сопоставлен, например, с портом 32769 на хосте (как показано в примере вашего вывода).
Почему EXPOSE
не подразумевает публикацию на стандартных портах
Директива EXPOSE
служит лишь для документирования того, какие порты используются приложением внутри контейнера. При этом, команда --publish-all
сопоставляет эти порты с произвольными портами хоста, что часто вызывает путаницу. Однако это не означает, что пользователи всегда смогут получить доступ к контейнеру через стандартные порты, такие как 80.
Когда вы хотите, чтобы ваш сервер был доступен через стандартный HTTP-порт 80 на хосте, необходимо использовать команду:
docker run --publish 80:80 <image-name>
Эта команда явно указывает на то, что порт 80 вашего контейнера будет доступен через порт 80 вашего хоста.
Преимущества явной публикации портов
-
Контроль: Указание конкретных портов предоставляет пользователю явный контроль над тем, где и как доступно приложение. Вы всегда знаете, какой порт будет использован на хосте.
-
Предсказуемость: С явной публикацией вы устраняете неопределенность, связанную с произвольным сопоставлением портов. Это особенно важно, если у вас работают несколько приложений, использующих один и тот же контейнерный порт.
-
Простота отладки: Если возникли проблемы с доступом к приложению, проще диагностировать их, когда вы точно знаете, каким образом порты сопоставлены.
Заключение
В итоге, использование директивы EXPOSE
в вашем Dockerfile не гарантирует, что ваши сервисы будут доступны через стандартные сетевые порты на вашем хосте. Для обеспечения должного уровня доступности всегда рекомендуется явно публиковать необходимые порты с помощью флага --publish
. Это не только повышает уровни контроля и предсказуемости, но и упрощает администрирование и отладку вашей контейнерной инфраструктуры.
Если у вас остались вопросы или требуется дополнительная информация, не стесняйтесь обращаться за разъяснениями!