1. Обзор
Когда мы докеризовываем наши приложения, нам обычно нужно открыть один порт. Приложение использует этот порт для взаимодействия с другими контейнерами или внешним миром. Иногда одного порта недостаточно. Для других целей может потребоваться один или несколько дополнительных портов. Например, в приложении Spring Boot нам нужен отдельный порт для публикации конечных точек управления для мониторинга приложения с помощью привода.
В этой статье мы увидим, как объявить несколько открытых портов и как связать открытые порты с портами хост-компьютера , чтобы добиться вышеуказанного.
2. Объявление портов
Во-первых, нам нужно объявить порты открытыми. Мы можем сделать это при создании образа докера. Также возможно объявлять порты при запуске контейнера на основе образа. Давайте посмотрим, как мы это делаем.
Мы начнем с примера приложения Spring Boot — my-app
. На протяжении всей статьи мы будем использовать один и тот же пример для понимания концепций. Наше приложение имеет только одну конечную точку GET
, которая возвращает « Hello buddy
». Он также имеет активированный пружинный привод. Приложение работает через порт 8080
, а конечные точки управления — через порт 8081
. Итак, когда приложение запущено на локальном компьютере, работают такие команды:
$ curl http://localhost:8080
Hello buddy
$ curl http://localhost:8081/actuator/health
{"status":"UP"}
2.1. Декларация в Dockerfile
Поскольку наше приложение my-app
публикует свои конечные точки на двух портах, 8080
и 8081
, нам нужно открыть оба порта в нашем Dockerfile
. Глагол EXPOSE
в Dockerfile
предоставляет порты :
FROM openjdk:8-jdk-alpine
EXPOSE 8080
EXPOSE 8081
ARG JAR_FILE=target/my-app-0.1.jar
ADD ${JAR_FILE} my-app.jar
ENTRYPOINT ["java","-jar","/my-app.jar"]
Однако, когда мы собираем образ с помощью этого Dockerfile
:
$ docker build -t my-app:latest .
На самом деле это не открывает порты, потому что автор Dockerfile
не имеет контроля над сетью, в которой будет работать контейнер. Скорее, команда EXPOSE
действует как документация. При этом человек, который запускает контейнер, понимает, какие порты контейнера необходимо опубликовать на главном компьютере для связи с приложением.
Мы также можем указать протокол — TCP
или UDP
— для связи через этот порт:
EXPOSE 8080/tcp
EXPOSE 8081/udp
Если мы ничего не указываем, по умолчанию используется TCP .
Команда также поддерживает объявление порта в диапазоне:
EXPOSE 8000-8009
Приведенная выше команда сообщает, что приложению необходимо открыть 10 портов, начиная с 8000
до 8009
, для связи.
2.2. Объявление в команде запуска докеров
Предположим, у нас уже есть образ docker для my-app
, который предоставляет только один порт 8080
с помощью команды EXPOSE
в своем Dockerfile
. Теперь, если мы хотим открыть другой порт, 8081
, мы должны использовать параметр –expose
вместе с командой запуска
:
$ docker run --name myapp -d --expose=8081 my-app:latest
Приведенная выше команда запускает контейнер с именем myapp
из образа my-app
и предоставляет 8081
вместе с портом 8080
. Мы можем проверить это, используя:
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2debb3c5345b my-app:latest "java -jar /my-app.j…" 5 seconds ago Up 3 seconds 8080-8081/tcp myapp
Важно понимать, что этот параметр только раскрывает, но не публикует порты на хост-компьютере. Чтобы понять это более четко, давайте выполним:
$ docker port myapp
Это ничего не печатает, потому что на хост-компьютере не были открыты и сопоставлены порты. Поэтому мы не можем получить доступ к приложению, даже если оно работает внутри контейнера:
$ curl http://localhost:8080
curl: (7) Failed to connect to localhost port 8080: Connection refused
Мы также можем выбрать доступ к диапазону портов таким же образом:
$ docker run --name myapp -d --expose=8000-8009 my-app:latest
3. Публикация портов
Мы уже научились открывать порты для докеризованного приложения. Теперь пришло время их опубликовать.
3.1. Публикация в команде «Выполнить»
Давайте повторно используем пример изображения my-app
из предыдущего раздела. В его Dockerfile
есть два порта — 8080
и 8081
. Запустив контейнер на основе этого образа, мы можем опубликовать сразу все открытые порты, используя аргумент -P
:
$ docker run --name myapp -d -P myApp:latest
Приведенная выше команда открывает два случайных порта на главном компьютере и сопоставляет их с портами 8080
и 8081
контейнера Docker. Он ведет себя так же, если мы выставляем диапазон портов.
Для проверки отображаемых портов мы используем:
$ docker port myapp
8080/tcp -> 0.0.0.0:32773
8081/tcp -> 0.0.0.0:32772
Теперь приложение доступно через порт 32773,
а конечные точки управления доступны через 32772
:
$ curl http://localhost:32773
Hello buddy
$ curl http://localhost:32772/actuator/health
{"status":"UP"}
Вместо случайного выделения портов мы можем выбрать определенные порты на главном компьютере с помощью параметра -p
:
$ docker run --name myapp -d -p 80:8080 my-app:latest
Приведенная выше команда публикует только порт 8080
и сопоставляется с портом 80
на хост-сервере. Это не делает конечные точки привода доступными снаружи контейнера:
$ curl http://localhost:80
Hello buddy
$ curl http://localhost:8081/actuator/health
curl: (7) Failed to connect to localhost port 8081: Connection refused
Чтобы опубликовать несколько сопоставлений портов, мы используем параметр -p
несколько раз :
$ docker run --name myapp -d -p 80:8080 -p 81:8081 my-app:latest
Таким образом, мы также контролируем, какие порты контейнера открываются наружу.
3.2. Публикация в docker-compose
Если мы используем наше приложение в docker-compose
, мы можем предоставить список портов, которые необходимо опубликовать в файле docker-compose.yml
:
version: "3.7"
services:
myapp:
image: my-app:latest
ports:
- 8080
- 8081
Если мы запустим эту настройку, она назначает случайные порты хост-сервера с заданными портами:
$ docker-compose up -d
Starting my-app_myapp_1 ... done
$ docker port my-app_myapp_1
8080/tcp -> 0.0.0.0:32785
8081/tcp -> 0.0.0.0:32784
Однако можно предоставить конкретный выбор портов:
version: "3.7"
services:
myapp:
image: my-app:latest
ports:
- 80:8080
- 81:8081
Здесь 80
и 81
— это порты хост-компьютера, а 8080
и 8081
— порты контейнера.
4. Вывод
В этой статье мы обсудили различные способы предоставления и публикации нескольких портов в контейнере Docker. Мы увидели, что на самом деле означает экспонирование, а также как получить полный контроль над публикацией портов между контейнером и хост-компьютером.