Любое сетевое приложение стартует на каком-то интерфейсе с указанием порта. Часто, по умолчанию, это 127.0.0.1:3000.
# Fastify по умолчанию стартует на 127.0.0.1
npx fastify start server/plugin.js -l info -P
[17:54:05.528] INFO (12575): Server listening at http://127.0.0.1:3000
По идее этого достаточно для разработки и такой адрес можно открыть в браузере, но с Docker этот фокус не пройдет. Сеть внутри контейнера полностью своя и localhost внутри контейнера, это не тоже самое что и localhost вне его. Поэтому сервер запущенный на localhost внутри контейнера, не будет доступен снаружи
curl localhost:3000
curl: (7) Failed to connect to localhost port 3000 after 5 ms: Couldn't connect to server
Эта ошибка одна из самых частых у людей, которые только начинают работать с Docker. Они стартуют приложение внутри контейнера и долго не могут понять, почему оно не доступно снаружи.
Для взаимодействия с внешним миром, запуск любых серверов внутри контейнера должен выполняться на адресе 0.0.0.0. Это специальная маска, которая указывает операционной системе подключиться к каждому доступному интерфейсу в системе. А работает это потому, что Docker создает специальный интерфейс со своим ip-адресом внутри каждого контейнера, именно через него реализуется выход наружу.
# -a 0.0.0.0 меняет адрес
npx fastify start server/plugin.js -l info -P -a 0.0.0.0
[17:57:08.680] INFO (16523): Server listening at http://0.0.0.0:3000
Но это только первая часть задачи, есть и вторая. Даже когда сервер слушает правильный интерфейс, Docker не дает к нему обратиться просто так. Он требует явного указания порта, который нужно открыть наружу. Это сделано не случайно, так как внутри контейнера может запускаться много всего, но это не значит, что мы хотим дать внешний доступ ко всем запущенным сервисам. Самый распространенный способ указывать порты выглядит так:
docker run -p 8080:3000 hexletcomponents/devops-example-app
# -p <external port>:<internal port>
Здесь мы говорим Docker, что внешний порт 8080, нужно связать с портом 3000 внутри контейнера. К каким ip-адресам привязаны эти порты? Внутренний это тот, который мы указали веб-серверу, когда запустили его на 0.0.0.0, для нашего приложения это 0.0.0.0:3000. Внешний автоматически подставляет 0.0.0.0, поэтому любой сервис запущенный в контейнере таким образом, будет автоматически доступен снаружи, если хотя бы один из интерфейсов машины доступен извне. Поэтому возможен запуск с явным указанием адреса:
docker run -p 127.0.0.1:8080:3000 hexletcomponents/devops-example-app
При таком запуске, веб-сервер станет доступным на хост-машине только для 127.0.0.1. Такое бывает нужно, если у хост-машины есть ip-адрес, который доступен снаружи, и мы не хотим случайно дать доступ до контейнера внешним клиентам.
В примере выше мы указали разные номера для внешнего и внутреннего портов для наглядности, но на практике порты могут быть одинаковыми и так делают часто:
docker run -p 3000:3000 hexletcomponents/devops-example-app
Docker позволяет пробрасывать столько портов, сколько нужно. Например, в случае nginx часто требуется использовать и 80
порт, и 443
для HTTPS. Сделать это можно так:
docker run -p 80:80 -p 443:443 nginx
Самостоятельная работа
В этой самостоятельной работе вам нужно будет запустить контейнер hexletcomponents/js-fastify-blog. Приложение запускается внутри контейнера на 3000 порту. Запустите его таким образом, чтобы блог был доступен снаружи по адресу: http://localhost:8080
Дополнительные материалы
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
- Статья «Как учиться и справляться с негативными мыслями»
- Статья «Ловушки обучения»
- Статья «Сложные простые задачи по программированию»
- Вебинар «Как самостоятельно учиться»
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.