Перейти к основному содержимому

Откройте более одного порта с помощью Docker

· 5 мин. чтения

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. Мы увидели, что на самом деле означает экспонирование, а также как получить полный контроль над публикацией портов между контейнером и хост-компьютером.