Все статьи | Код

Как настроить маппинг портов между хостом и Docker-контейнером

Как настроить маппинг портов между хостом и Docker-контейнером главное изображение

Для взаимодействия с Docker-контейнером с хоста нужно настроить маппинг портов. Разберемся, почему это важно, и рассмотрим несколько способов добавления нового маппинга после запуска Docker-контейнера.

Это адаптированный перевод статьи Assigning a Port Mapping to a Running Docker Container из блога образовательного проекта Baeldung.

Прежде чем начать, уточним — текст подойдет тем, кто знает Docker хотя бы на базовом уровне. Если вам интересно разобраться с технологией, на Хекслете есть курс по Docker.

Зачем нужен маппинг

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

Маппинг нужен для того, чтобы все запросы, проходящие через порт хоста, перенаправлялись в Docker-контейнер. Другими словами, сопоставление портов делает процессы внутри контейнера доступными извне.

При запуске нового Docker-контейнера с помощью команды docker run можно сопоставить порты опцией --publish или -p:

docker run -d -p 81:80 --name httpd-container httpd

Эта команда запускает Docker-контейнер httpd (HTTP-сервер Apache) и маппинг 81 порта хоста с 80 портом внутри Docker-контейнера. Стоит отметить, что по умолчанию сервер httpd прослушивает порт 80.

Теперь доступ к приложению можно получить, используя порт 81 на хосте:

curl http://localhost:81
<html><body><h1>It works!</h1></body></html>

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

Как назначить маппинг работающему Docker-контейнеру

Рассмотрим ситуацию, в которой маппинг не был задан при запуске Docker-контейнера или было задан с ошибкой. Получить доступ к сервису через TCP/IP-соединение на хосте не получится, но есть три способа решить эту проблему:

  • Остановить работающий Docker-контейнер, а затем создать и запустить новый c оригинальным docker images
  • Сделать docker commit работающего Docker-контейнера, создать новый и запустите его через docker images, сохранив состояние
  • Добавить новый маппинг, используя файлы конфигурации Docker

Разберем каждый способ подробнее.

Перезапуск Docker-контейнера

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

Хотя это самый простой способ, он подходит не во всех ситуациях. Например, решение не подойдет в ситуации, когда внутри Docker-контейнера уже выполнены некоторые операции: установились зависимости, запустились процессы или обновились файлы. Если восстановить такой Docker-контейнер из образа, все эти изменения будут потеряны.

Стоит отметить, что важные данные лучше хранить в Docker volumes, чтобы упростить замену Docker-контейнеров.

Повторный запуск из docker commit

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

Поскольку мы фиксируем состояние существующего Docker-контейнера оно становится доступно в новом образе.

Попробуем реализовать этот способ. Сначала остановим Docker-контейнер и создадим его образ с помощью команды docker commit:

# Останавливаем контейнер
docker stop httpd-container
httpd-container
# Создаем образ на основа существующего контейнера
docker commit httpd-container httpd-image
# Хэш образа
sha256:33da33fcad051c90ac9b7dea9b2dbda442767e05ddebd8d6db8ac6893ef4ef40

Затем удалим Docker-контейнер и заново запустим его из образа. На этом этапе важно убедиться, что маппинг настроен верно:

# Удаляем контейнер
docker rm httpd-container
# Команда напечатает на экране название удалённого контейнера
httpd-container
# Запускаем контейнер
docker run -d -p 83:80 --name httpd-container httpd-image
# На экране выведится хеш запущенного контейнера
dd2535c477ad74e80b3642abca9055efacb89eaf14572b91f91bf20cd3f0cbf3

Перенастройка Docker-контейнера без его удаления

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

Рассмотрим способ, как настроить маппинг в уже существующем Docker-контейнере. Сначала запустим новый его без сопоставления портов:

docker run -d --name httpd-container httpd 
a0ed1c9fc60c358d53400dc244e94ef0db4d1347e70bd4be725a890b062ebbe7
# Команда выведет запущенные контейнеры
docker ps
CONTAINER ID   IMAGE     COMMAND              CREATED             STATUS          PORTS       NAMES
a0ed1c9fc60c   httpd     "httpd-foreground"   1 second ago        Up 1 second     80/tcp      httpd-container

Команда docker run возвращает полный идентификатор Docker длиной 64 символа. Другой способ получить его — команда docker inspect:

$ docker inspect --format="{{.Id}}" httpd-container
a0ed1c9fc60c358d53400dc244e94ef0db4d1347e70bd4be725a890b062ebbe7

Идентификатор пригодится для поиска файла конфигурации Docker.

Остановка Docker-контейнера и службы Docker

Первый шаг по перенастройке работающего Docker-контейнера — его остановка. Выполнить ее можно командой docker stop:

docker stop httpd-container
httpd-container

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

# Останавливаем службу Docker
systemctl stop docker

Читайте также: Как сохранять фокус на протяжении всего обучения: советы от Хекслета

Как найти конфигурационные файлы

Все конфигурационные файлы, которые относятся к Docker-контейнерам, образам, томам и сетям Docker, можно найти в каталоге /var/lib/docker. Стоит отметить, что на Windows и MacOS путь к каталогу отличается.

В данном случае интерес представляют два файла: hostconfig.json и config.v2.json. Найти их можно в следующем каталоге:

/var/lib/docker/containers/<ID>/

В данном случае ID — полный идентификатор Docker-контейнера, который мы получили ранее:

a0ed1c9fc60c358d53400dc244e94ef0db4d1347e70bd4be725a890b062ebbe7

Конфигурационные файлы можно найти в следующем каталоге:

$ ls /var/lib/docker/containers/a0ed1c9fc60c358d53400dc244e94ef0db4d1347e70bd4be725a890b062ebbe7/
a0ed1c9fc60c358d53400dc244e94ef0db4d1347e70bd4be725a890b062ebbe7-json.log  checkpoints  config.v2.json  hostconfig.json  hostname  hosts  mounts  resolv.conf  resolv.conf.hash

Обновление конфигурационных файлов

В первую очередь стоит обновить файл hostconfig.json. Для этого найдем ключ привязки портов в этом файле. Поскольку при запуске Docker-контейнера маппинг не был настроен, поле ключа PortBindings Docker-контейнера будет пустым:

{
  ...
  ...
  "PortBindings": {},
  ...
  ...
}

Следующий шаг — назначение 82 порта хоста 80 порту Docker-контейнера httpd-container. Для этого обновим привязки портов в файле JSON:

{
  ...
  ...
  "PortBindings": {"80/tcp":[{"HostIp":"","HostPort":"82"}]},
  ...
  ...
}

Как только маппинг выполнен, нужно открыть порт 80 Docker-контейнера в файле config.v2.json. Для этого добавим поле открытых портов (если его еще нет) в ключ конфигурации:

{
  ...
  "Config":
  {
    ...
    "ExposedPorts":
    {
      "80/tcp":{}
    },
    ...
  }
}

Таким образом мы сообщаем демону Docker, что Docker-контейнер прослушивает 80 порт в этом сетевом интерфейсе.

Можно открыть несколько портов, передав значения в виде пар ключ-значение, разделенных запятыми:

{
  ...
  "Config":
  {
    ...
    "ExposedPorts":
    {
      "80/tcp":{},
      "82/tcp":{},
      "8080/tcp":{}
    },
    ...
  }
}

Проверка изменений

Проверка изменений состоит из нескольких шагов. Сначала запустим службу Docker:

systemctl start docker

Теперь запустим httpd-контейнер и проверим маппинг портов с помощью команды docker ps:

docker start httpd-container
httpd-container
docker ps
CONTAINER ID   IMAGE     COMMAND              CREATED             STATUS          PORTS                               NAMES
a0ed1c9fc60c   httpd     "httpd-foreground"   1 hours ago         Up 1 seconds    0.0.0.0:82->80/tcp, :::82->80/tcp   httpd-container

Из результата выполнения команды видно, что порты Docker-контейнера теперь сопоставлены с портами хоста. После этого доступ к службе httpd Docker-контейнера можно получить извне — через 82 порт:

curl http://localhost:82
<html><body><h1>It works!</h1></body></html>

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

Изменить существующие маппинги любого Docker-контейнера можно используя процедуру, описанную выше. Единственное отличие будет заключаться в том, что ключ привязки портов в файле hostconfig.json не будет пустым. В этом случае достаточно просто обновить номер используемого порта.

При обновлении TCP-порта Docker-контейнера в маппинге необходимо указать тот же порт в файле config.v2.json.

Теперь все проверки прошли — можно запустить службу Docker и Docker-контейнер, чтобы изменения вступили в силу.

Никогда не останавливайтесь: В программировании говорят, что нужно постоянно учиться даже для того, чтобы просто находиться на месте. Развивайтесь с нами — на Хекслете есть сотни курсов по разработке на разных языках и технологиях

Аватар пользователя Oleg Sabitov
Oleg Sabitov 18 мая 2022
Рекомендуемые программы

С нуля до разработчика. Возвращаем деньги, если не удалось найти работу.

Иконка программы Фронтенд-разработчик
Профессия
Разработка фронтенд-компонентов веб-приложений
7 июля 10 месяцев
Иконка программы Python-разработчик
Профессия
Разработка веб-приложений на Django
7 июля 10 месяцев
Иконка программы PHP-разработчик
Профессия
Разработка веб-приложений на Laravel
7 июля 10 месяцев
Иконка программы Node.js-разработчик
Профессия
Разработка бэкенд-компонентов веб-приложений
7 июля 10 месяцев
Иконка программы Fullstack-разработчик
Профессия
Новый
Разработка фронтенд и бэкенд компонентов веб-приложений
7 июля 16 месяцев
Иконка программы Верстальщик
Профессия
Вёрстка с использованием последних стандартов CSS
в любое время 5 месяцев
Иконка программы Java-разработчик
Профессия
Разработка приложений на языке Java
7 июля 10 месяцев
Иконка программы Разработчик на Ruby on Rails
Профессия
Создает веб-приложения со скоростью света
7 июля 5 месяцев