1. Обзор
В этом руководстве мы сосредоточимся на том, как докеризировать приложение Spring Boot
, чтобы запустить его в изолированной среде, также известной как контейнер
.
Мы узнаем, как создать композицию контейнеров, которые зависят друг от друга и связаны друг с другом в виртуальной частной сети. Мы также увидим, как ими можно управлять вместе с помощью отдельных команд.
Давайте начнем с создания простого приложения Spring Boot, которое мы затем запустим в облегченном базовом образе под управлением Alpine Linux
.
2. Dockerize отдельное приложение Spring Boot
В качестве примера приложения, которое мы можем докеризовать, мы создадим простое приложение Spring Boot, docker-message-server,
которое предоставляет одну конечную точку и возвращает статическое сообщение:
@RestController
public class DockerMessageController {
@GetMapping("/messages")
public String getMessage() {
return "Hello from Docker!";
}
}
С правильно настроенным файлом Maven мы можем создать исполняемый файл jar:
$> mvn clean package
Далее мы запустим приложение Spring Boot:
$> java -jar target/docker-message-server-1.0.0.jar
Теперь у нас есть работающее приложение Spring Boot, к которому мы можем получить доступ по адресу localhost:8888/messages.
Чтобы докеризировать приложение, мы сначала создаем файл с именем Dockerfile
со следующим содержимым:
FROM openjdk:8-jdk-alpine
MAINTAINER foreach.com
COPY target/docker-message-server-1.0.0.jar message-server-1.0.0.jar
ENTRYPOINT ["java","-jar","/message-server-1.0.0.jar"]
Этот файл содержит следующую информацию:
- FROM : В качестве основы для нашего образа мы возьмем
Alpine Linux
с поддержкойJava
, созданный в предыдущем разделе. `` - MAINTAINER : хранитель образа.
- COPY : мы позволяем
Docker
скопировать наш jar-файл в образ. - ENTRYPOINT : это будет исполняемый файл, запускаемый при загрузке контейнера. Мы должны определить их как
JSON-Array
, потому что мы будем использоватьENTRYPOINT
в сочетании сCMD
для некоторых аргументов приложения.
Чтобы создать образ из нашего Dockerfile
, нам нужно запустить «docker build»,
как и раньше:
$> docker build --tag=message-server:latest .
Наконец, мы можем запустить контейнер из нашего образа:
$> docker run -p8887:8888 message-server:latest
Это запустит наше приложение в Docker, и мы сможем получить к нему доступ с хост-компьютера по адресу localhost:8887/messages
. Здесь важно определить сопоставление портов, которое сопоставляет порт на хосте (8887) с портом внутри Docker ( 8888
). Это порт, который мы определили в свойствах приложения Spring Boot.
Примечание. Порт 8887 может быть недоступен на машине, на которой мы запускаем контейнер. В этом случае сопоставление может не работать, и нам нужно выбрать порт, который все еще доступен.
Если мы запустим контейнер в автономном режиме, мы можем проверить его детали, остановить его и удалить с помощью следующих команд:
$> docker inspect message-server
$> docker stop message-server
$> docker rm message-server
2.1. Изменение основного изображения
Мы можем легко изменить базовый образ, чтобы использовать другую версию Java. Например, если мы хотим использовать дистрибутив Corretto от Amazon, мы можем просто изменить Dockerfile
:
FROM amazoncorretto:11-alpine-jdk
MAINTAINER foreach.com
COPY target/docker-message-server-1.0.0.jar message-server-1.0.0.jar
ENTRYPOINT ["java","-jar","/message-server-1.0.0.jar"]
Кроме того, мы можем использовать собственное базовое изображение. Мы рассмотрим, как это сделать позже в этом уроке.
3. Dockerize приложения в композите
Команды Docker
и Dockerfiles
особенно подходят для создания отдельных контейнеров. Однако, если мы хотим работать в сети изолированных приложений
, управление контейнером быстро становится загроможденным.
Для решения этой проблемы Docker
предоставляет инструмент Docker Compose
. Этот инструмент поставляется с собственным файлом сборки в формате YAML
и лучше подходит для управления несколькими контейнерами. Например, он может запускать или останавливать набор служб одной командой или объединять выходные данные журнала нескольких служб в один псевдотерминал
.
3.1. Второе загрузочное приложение Spring
Давайте создадим пример двух приложений, работающих в разных контейнерах Docker. Они будут взаимодействовать друг с другом и представляться хост-системе как «единое целое». В качестве простого примера создадим второе приложение Spring Boot docker-product-server
:
@RestController
public class DockerProductController {
@GetMapping("/products")
public String getMessage() {
return "A brand new product";
}
}
Мы можем собрать и запустить приложение так же, как и наш сервер сообщений
.
3.2. Файл компоновки Docker
Мы можем объединить конфигурацию обоих сервисов в один файл с именем docker-compose.yml
:
version: '2'
services:
message-server:
container_name: message-server
build:
context: docker-message-server
dockerfile: Dockerfile
image: message-server:latest
ports:
- 18888:8888
networks:
- spring-cloud-network
product-server:
container_name: product-server
build:
context: docker-product-server
dockerfile: Dockerfile
image: product-server:latest
ports:
- 19999:9999
networks:
- spring-cloud-network
networks:
spring-cloud-network:
driver: bridge
версия : указывает, какую версию формата следует использовать. Это обязательное поле. Здесь мы используем более новую версию, тогда как
устаревший формат
— «1».services : каждый объект в этом ключе определяет
службу
, также известную как контейнер. Этот раздел является обязательным.build : если указано,
docker-compose
может создать образ изDockerfile.
context : если указано, указывает каталог сборки, в котором
просматривается Dockerfile
.dockerfile : если указано, задает альтернативное имя для
Dockerfile.
image : сообщает
Docker
, какое имя он должен дать образу, когда используются функции сборки. В противном случае он ищет этот образ в библиотеке илиудаленном реестре.
network : это идентификатор именованных сетей для использования. Данное
имя-значение
должно быть указано в разделесетей
.сети : в этом разделе мы указываем
сети,
доступные для наших сервисов.
В этом примере мы позволяемdocker-compose
создать для нас именованнуюсеть
типа«мост»
. Если для параметраexternal
установлено значениеtrue
, он будет использовать существующий с заданным именем.
Прежде чем мы продолжим, мы проверим наш файл сборки на наличие синтаксических ошибок:
$> docker-compose config
Затем мы можем собрать наши образы, создать определенные контейнеры и запустить их одной командой:
$> docker-compose up --build
Это запустит сервер сообщений
и сервер продукта
одновременно.
Чтобы остановить контейнеры, удалите их из Docker
и удалите из него подключенные сети
. Для этого мы можем использовать противоположную команду:
$> docker-compose down
Более подробное знакомство с docker-compose
можно прочитать в нашей статье Introduction to Docker Compose .
3.3. Услуги масштабирования
Приятной особенностью docker-compose
является возможность масштабирования сервисов . Например, мы можем указать Docker
запустить три контейнера для сервера сообщений
и два контейнера для продукта-сервера
.
Однако, чтобы это работало правильно, мы должны удалить container_name
из нашего docker-compose.yml
, чтобы Docker
мог выбирать имена и изменять конфигурацию открытого порта,
чтобы избежать конфликтов.
Что касается портов, мы можем указать Docker сопоставить диапазон портов на хосте с одним конкретным портом внутри Docker:
ports:
- 18800-18888:8888
После этого мы можем масштабировать наши сервисы следующим образом (обратите внимание, что мы используем модифицированный yml-файл
):
$> docker-compose --file docker-compose-scale.yml up -d --build --scale message-server=1 product-server=1
Эта команда запустит один сервер сообщений
и один сервер продукта
.
Чтобы масштабировать наши сервисы, мы можем запустить следующую команду:
$> docker-compose --file docker-compose-scale.yml up -d --build --scale message-server=3 product-server=2
Эта команда запустит два дополнительных сервера сообщений и один дополнительный сервер продукта. Работающие контейнеры не будут остановлены.
4. Пользовательское базовое изображение
Базовый образ ( openjdk:8-jdk-alpine
), который мы использовали до сих пор, содержал дистрибутив операционной системы Alpine с уже установленным JDK 8. В качестве альтернативы мы можем создать собственный базовый образ (на основе Alpine или любой другой операционной системы).
Для этого мы можем использовать Dockerfile
с Alpine в качестве базового образа и установить JDK по нашему выбору:
FROM alpine:edge
MAINTAINER foreach.com
RUN apk add --no-cache openjdk8
- FROM : ключевое слово
FROM
указываетDocker
использовать данное изображение с его тегом в качестве базы сборки. Если этого образа нет в локальной библиотеке, выполняется онлайн-поиск наDockerHub
или в любом другом настроенном удаленном реестре. - MAINTAINER :
MAINTAINER
обычно представляет собой адрес электронной почты, идентифицирующий автора изображения. - RUN : С помощью команды
RUN
мы выполняем командную строку оболочки в целевой системе. Здесь мы используем менеджер пакетовAlpine Linux ,
apk,
для установкиJava 8 OpenJDK.
Чтобы окончательно собрать образ и сохранить его в локальной библиотеке, нам нужно запустить:
docker build --tag=alpine-java:base --rm=true .
ВНИМАНИЕ: Опция –tag
задает имя образа, а –rm=true
удаляет промежуточные образы после его успешной сборки. Последний символ в этой команде оболочки — точка, действующая как аргумент каталога сборки.
Теперь мы можем использовать созданный образ вместо openjdk:8-jdk-alpine
.
5. Поддержка пакетов сборки в Spring Boot 2.3
В Spring Boot 2.3 добавлена поддержка пакетов сборки . Проще говоря, вместо того, чтобы создавать собственный Dockerfile и собирать его с помощью чего-то вроде docker build
, все, что нам нужно сделать, это выполнить следующую команду:
$ mvn spring-boot:build-image
Точно так же в Gradle:
$ ./gradlew bootBuildImage
Чтобы это работало, нам нужно установить и запустить Docker.
Основная мотивация сборочных пакетов — создать такой же опыт развертывания, который уже некоторое время предоставляют некоторые известные облачные сервисы, такие как Heroku или Cloud Foundry. Мы просто запускаем цель build-image
, а затем платформа сама позаботится о сборке и развертывании артефакта.
Кроме того, это может помочь нам изменить способ создания образов Docker более эффективно . Вместо того, чтобы применять одно и то же изменение ко множеству файлов Docker в разных проектах, все, что нам нужно сделать, это изменить или настроить сборщик образов buildpacks.
Помимо простоты использования и улучшения общего опыта разработчиков, он также может быть более эффективным. Например, подход buildpacks создаст многоуровневый образ Docker и использует развернутую версию файла Jar.
Давайте посмотрим, что произойдет после того, как мы запустим указанную выше команду.
Когда мы перечисляем доступные образы докеров:
docker image ls -a
Мы видим строку для изображения, которое мы только что создали:
docker-message-server 1.0.0 b535b0cc0079
Здесь имя и версия образа соответствуют имени и версии, которые мы определили в файле конфигурации Maven или Gradle. Хэш-код — это короткая версия хеша изображения.
Затем, чтобы запустить наш контейнер, мы можем просто запустить:
docker run -it -p9099:8888 docker-message-server:1.0.0
Как и в случае с нашим встроенным образом, нам нужно сопоставить порт, чтобы сделать наше приложение Spring Boot доступным из-за пределов Docker.
6. Заключение
В этой статье мы узнали, как создавать собственные образы Docker
, запускать приложение Spring Boot
в качестве контейнера Docker
и создавать контейнеры с помощью docker-compose
.
Для дальнейшего чтения о файлах сборки мы обращаемся к официальному справочнику Dockerfile
и справочнику docker-compose.yml
.
Как обычно, исходники этой статьи можно найти на Github
.