Я наконец-то дошел до того, чтобы научиться использовать Docker после того, как узнал, что это такое и что делает, даже не используя его. Это первая публикация, в которой я попытался использовать Docker, и, вероятно, именно к ней я обращаюсь всякий раз, когда начинаю новый проект (в любом случае для Java или Kotlin).
Это будет короткая публикация, которая берет существующий проект (из одной из моих других публикаций) и изменяет его, чтобы он мог работать внутри контейнеров. Я сомневаюсь, что этот пост будет содержать что-то впечатляющее, но я знаю, что это поможет мне в будущем и, возможно, поможет вам сейчас.
Прежде чем мы начнем, давайте посмотрим на существующий проект. Вот ссылки на код и соответствующий пост в блоге . Сообщение в блоге охватывает всю информацию о коде. Вот краткое изложение, чтобы мы могли продолжить этот пост. Старый проект представляет собой приложение Spring Boot с базой данных MongoDB и очередью сообщений ActiveMQ. Все эти компоненты являются основным кормом для контейнеризации.
Последний комментарий к содержанию этого поста: я предполагаю, что вы уже установили Docker или можете сами выяснить, как это сделать.
Преобразование приложения Spring
Прежде всего, приложение Spring Boot.
Это единственная часть проекта, которая содержит наш код. Остальные — просто изображения, загруженные из чужого репозитория. Чтобы начать движение этого приложения к запуску в контейнере, нам нужно создать Dockerfile
который определяет содержимое изображения:
1
2
3
4
5
|
FROM openjdk: 8 -jdk-alpine LABEL maintainer= "Dan Newton" ADD target/spring-boot-jms-tutorial- 1.0 . 0 .jar app.jar EXPOSE 8080 ENTRYPOINT [ "java" , "-jar" , "/app.jar" ] |
Это берет базовый образ openjdk:8-jdk-alpine
который является хорошей отправной точкой для приложения, добавляет Jar openjdk:8-jdk-alpine
созданный из кода приложения (с именем app.jar
) и предоставляет порт для связи между контейнерами. Последняя строка определяет команду, которая выполняется при запуске образа в контейнере. Это то, что запускает приложение Spring.
Чтобы создать образ из Dockerfile
выполните команду ниже (при условии, что вы уже создали код приложения):
1
|
docker build -t spring-boot-jms-tutorial . |
Теперь есть образ с именем spring-boot-jms-tutorial
( -t
позволяет нам определить имя). Теперь это можно использовать для создания контейнера, который выполняет код, который упакован в Jar образа:
1
|
docker run --name application -p 4000 : 8080 spring-boot-jms-tutorial |
Это создаст и запустит контейнер, созданный из образа spring-boot-jms-tutorial
. Он называет application
контейнер, а свойство -p
позволяет сопоставить порт с локальной машины порту внутри контейнера. Чтобы получить доступ к порту 8080
контейнера, нам просто нужно использовать порт 4000
на нашей собственной машине.
Если мы остановили этот контейнер и хотели запустить его снова, мы должны использовать команду:
1
|
docker start application |
Где application
— это имя контейнера, который мы создали ранее. Если снова docker run
он создаст другой новый контейнер, а не повторно использует существующий. На самом деле, поскольку мы предоставили имя контейнеру, выполнение той же команды run
ранее, приведет к ошибке.
Теперь приложение Spring успешно выполняется в контейнере, но журналы выглядят не очень хорошо. Давайте кратко рассмотрим, чтобы мы знали, что нам нужно делать дальше.
Ошибка подключения к MongoDB:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
Exception in monitor thread while connecting to server mongocontainer: 27017 com.mongodb.MongoSocketException: mongocontainer: Name does not resolve at com.mongodb.ServerAddress.getSocketAddress(ServerAddress.java: 188 ) ~[mongodb-driver-core- 3.6 . 4 .jar!/:na] at com.mongodb.connection.SocketStreamHelper.initialize(SocketStreamHelper.java: 59 ) ~[mongodb-driver-core- 3.6 . 4 .jar!/:na] at com.mongodb.connection.SocketStream.open(SocketStream.java: 57 ) ~[mongodb-driver-core- 3.6 . 4 .jar!/:na] at com.mongodb.connection.InternalStreamConnection.open(InternalStreamConnection.java: 126 ) ~[mongodb-driver-core- 3.6 . 4 .jar!/:na] at com.mongodb.connection.DefaultServerMonitor$ServerMonitorRunnable.run(DefaultServerMonitor.java: 114 ) ~[mongodb-driver-core- 3.6 . 4 .jar!/:na] at java.lang.Thread.run(Thread.java: 748 ) [na: 1.8 .0_171] Caused by: java.net.UnknownHostException: mongocontainer: Name does not resolve at java.net.Inet4AddressImpl.lookupAllHostAddr(Native Method) ~[na: 1.8 .0_171] at java.net.InetAddress$ 2 .lookupAllHostAddr(InetAddress.java: 928 ) ~[na: 1.8 .0_171] at java.net.InetAddress.getAddressesFromNameService(InetAddress.java: 1323 ) ~[na: 1.8 .0_171] at java.net.InetAddress.getAllByName0(InetAddress.java: 1276 ) ~[na: 1.8 .0_171] at java.net.InetAddress.getAllByName(InetAddress.java: 1192 ) ~[na: 1.8 .0_171] at java.net.InetAddress.getAllByName(InetAddress.java: 1126 ) ~[na: 1.8 .0_171] at java.net.InetAddress.getByName(InetAddress.java: 1076 ) ~[na: 1.8 .0_171] at com.mongodb.ServerAddress.getSocketAddress(ServerAddress.java: 186 ) ~[mongodb-driver-core- 3.6 . 4 .jar!/:na] ... 5 common frames omitted |
ActiveMQ также не существует:
1
2
3
|
Could not refresh JMS Connection for destination 'OrderTransactionQueue' - retrying using FixedBackOff{interval= 5000 , currentAttempts= 1 , maxAttempts=unlimited}. Cause: Could not connect to broker URL: tcp: //activemqcontainer:61616. Reason: java.net.UnknownHostException: activemqcontainer |
Мы разберем их в следующих разделах, чтобы приложение могло работать в полном объеме.
И последнее, прежде чем мы перейдем к рассмотрению Mongo и ActiveMQ.
Также можно использовать dockerfile-maven-plugin
чтобы помочь с вышеизложенным, который собирает контейнер как часть запуска mvn install
. Я решил не использовать его, так как не мог заставить его работать должным образом с docker-compose
. Ниже приведен краткий пример использования плагина:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
<build> <plugins> <plugin> <groupId>com.spotify</groupId> <artifactId>dockerfile-maven-plugin</artifactId> <version> 1.4 . 4 </version> <executions> <execution> <id> default </id> <goals> <goal>build</goal> <goal>push</goal> </goals> </execution> </executions> <configuration> <!-- Names the image: spring-boot-jms-tutorial --> <repository>${project.artifactId}</repository> <buildArgs> <JAR_FILE>${project.build.finalName}.jar</JAR_FILE> </buildArgs> </configuration> </plugin> </plugins> </build> |
Это позволяет нам заменить несколько строк в Dockerfile
:
1
2
3
4
5
6
|
FROM openjdk:8-jdk-alpine LABEL maintainer= "Dan Newton" ARG JAR_FILE ADD target/${JAR_FILE} app.jar EXPOSE 8080 ENTRYPOINT [ "java" , "-jar" , "/app.jar" ] |
Здесь была добавлена одна строка и изменена одна существующая. Аргумент JAR_FILE
заменяет исходное имя Jar, которое вводится плагином из pom.xml
. Сделайте эти изменения и запустите mvn install
и bam, ваш контейнер собран со всем необходимым кодом.
Использование изображения MongoDB
Существует изображение MongoDB, готовое к использованию. Он идеально назван mongo
… Что еще ты ожидал? Все, что нам нужно сделать, это run
изображение и дать ему имя контейнера:
1
|
docker run -d --name mongocontainer mongo |
Добавление -d
запустит контейнер в фоновом режиме. Имя контейнера не только для удобства, так как приложению Spring оно понадобится позже для подключения к Mongo.
На образ ActiveMQ
Настроить ActiveMQ так же просто, как Mongo. Запустите команду ниже:
1
|
docker run -d --name activemqcontainer -p 8161:8161 rmohr /activemq |
Здесь порты 8161
отображаются из контейнера в машину, на которой он работает, что позволяет получить доступ к консоли администратора из-за пределов контейнера.
Связывая все это вместе
Если бы вы выполняли все эти команды, читая пост, вы бы заметили, что контейнер application
самом деле не смог увидеть mongocontainer
и activemqcontainer
. Это потому, что они не работают в одной сети. Заставить их общаться не сложно и требует всего несколько дополнительных шагов.
По умолчанию Docker создает сеть Bridge при настройке без дополнительной настройки. Ниже описано, как это сделать:
1
|
docker network create network |
Теперь, когда сеть (названная network
) создана, команды, которые выполнялись ранее, необходимо изменить, чтобы создать контейнеры, которые будут подключаться к сети. Ниже приведены 3 команды, использованные для создания контейнеров в предыдущих разделах, каждая из которых была изменена для присоединения к сети.
1
2
3
|
docker run -d --name mongocontainer --network=network mongo docker run -d --name activemqcontainer -p 8161 : 8161 --network=network rmohr/activemq docker run --name application -p 4000 : 8080 --network=network spring-boot-jms-tutorial |
После того как все они будут запущены, приложение в целом будет работать. Каждый контейнер может видеть друг друга. Разрешение контейнера application
для подключения к MongoDB и ActiveMQ в их соответствующих контейнерах.
На данный момент все работает. Он работает так же, как я помню, он работал, когда я все настроил на своем ноутбуке. Но на этот раз ничего не настроено локально… За исключением Docker!
Докер сочиняет это
Мы могли бы остановиться на этом, но в следующем разделе все станет еще проще. Docker Compose позволяет нам эффективно объединять все команды, которые мы запускали ранее, и запускать все контейнеры и их сеть в одну команду. Очевидно, что есть еще некоторые настройки, но, в конце концов, я думаю, что количество набирается на самом деле уменьшается.
Для этого нам нужно создать файл docker-compose.yml
:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
version: '3' services: appcontainer: build: context: . args: JAR_FILE: /spring-boot-jms-tutorial-1 .0.0.jar ports: - "4000:8080" activemqcontainer: image: "rmohr/activemq" ports: - "8161:8161" mongocontainer: image: "mongo" |
Используйте с этой версией Dockerfile
:
1
2
3
4
5
6
|
FROM openjdk:8-jdk-alpine LABEL maintainer= "Dan Newton" ARG JAR_FILE ADD target/${JAR_FILE} app.jar EXPOSE 8080 ENTRYPOINT [ "java" , "-jar" , "/app.jar" ] |
Это почти все, что нужно сделать.
appcontainer
— это контейнер приложений Spring, созданный из кода проекта. Свойство build
контейнера указывает Docker создавать образ на основе Dockerfile
проектов, найденного в корневом каталоге проекта. Он передает аргумент JAR_FILE
в Dockerfile
перемещая часть конфигурации в этот файл.
Два других контейнера не требуют особой настройки. Как и в случае с предыдущими командами, определяются образы, из которых они построены, и activemqcontainer
добавляет конфигурацию вокруг своих сопоставленных портов.
Последний фрагмент конфигурации происходит в фоновом режиме. Сеть создана и все контейнеры добавлены в нее. Это избавляет от необходимости создавать сеть вручную.
Все, что осталось сделать, это запустить команду up
:
1
|
docker-compose up |
Это соберет и запустит все контейнеры. Изображение кода приложения создается при необходимости. Выполнение этой точной команды выведет все журналы контейнеров на консоль, для этого в фоновом режиме добавьте флаг -d
:
1
|
docker-compose up -d |
После этого мы можем взглянуть на созданные контейнеры и сеть. Бег:
1
|
docker ps -a -- format "table {{.Image}}\t{{.Names}}" |
Показывает нам:
1
2
3
4
|
IMAGE NAMES mongo spring-boot-jms_mongocontainer_1 spring-boot-jms_appcontainer spring-boot-jms_appcontainer_1 rmohr /activemq spring-boot-jms_activemqcontainer_1 |
И сеть:
1
|
docker network ls |
Производит:
1
2
|
NETWORK ID NAME DRIVER SCOPE 163edcfe5ada spring-boot-jms_default bridge local |
К именам контейнеров и сети добавляется имя проекта.
Вывод
Вот и все, что нужно … В любом случае, для простой настройки я уверен, что боги Докера могут сделать гораздо больше, но я не один из них … Пока.
В заключение мы взяли существующее приложение, которое я написал, для локальной работы на машине и поместили все в несколько контейнеров. Это означало, что мы пошли с машины, на которой нужно было все настроить, с установленными MongoDB и ActiveMQ. Вместо этого — машина, которая может пропустить все это, используя контейнеры и требуя только установки Docker. Затем Docker управляет всеми зависимостями для нас.
Мы рассмотрели, как переместить каждую часть приложения в контейнер, а затем связали все это вместе с Docker Compose. В конце всего этого мы оставляем одну команду, которая может переместить нас из абсолютно ничего во все необходимое для запуска приложения.
Код, используемый в этом посте, можно найти на моем GitHub .
Если вы нашли этот пост полезным, вы можете подписаться на меня в Twitter на @LankyDanDev, чтобы не отставать от моих новых сообщений.
Опубликовано на Java Code Geeks с разрешения Дэна Ньютона, партнера нашей программы JCG . См. Оригинальную статью здесь: Использование Docker для помещения существующего приложения в некоторые контейнеры.
Мнения, высказанные участниками Java Code Geeks, являются их собственными. |