Dockerizing Spring Boot Application
1. обзор
В этой статье мы сосредоточимся на том, как докеризоватьSpring Boot Application, чтобы запускать его в изолированной среде, также известной как «dockerize». container.
Кроме того, мы покажем, как создать композицию контейнеров, которые зависят друг от друга и связаны друг с другом в виртуальной частной сети. Мы также увидим, как ими можно управлять вместе с помощью отдельных команд.
Начнем с создания облегченного базового образа с поддержкой Java, выполняющегоhttps://hub.docker.com//alpine/[_Alpine Linux].
2. Общее базовое изображение
Мы собираемся использовать собственный формат файла сборкиDocker’s: aDockerfile.
Dockerfile - это, в принципе, линейный пакетный файл, содержащий команды для создания образа. Не обязательно помещать эти команды в файл, потому что мы можем передать их и в командную строку - файл просто удобнее.
Итак, напишем наш первыйDockerfile:
FROM alpine:edge
MAINTAINER example.com
RUN apk add --no-cache openjdk8
COPY files/UnlimitedJCEPolicyJDK8/* \
/usr/lib/jvm/java-1.8-openjdk/jre/lib/security/
-
FROM: ключевое словоFROM сообщаетDocker использовать данное изображение с его тегом в качестве основы для сборки. Если этого образа нет в локальной библиотеке, выполняется онлайн-поиск вDockerHub или в любом другом настроенном удаленном реестре.
-
MAINTAINER:MAINTAINER обычно является адресом электронной почты, идентифицирующим автора изображения.
-
RUN: с помощью командыRUN мы выполняем командную строку оболочки в целевой системе. Здесь мы используем менеджер пакетовAlpine Linux’sapk для установкиJava 8 OpenJDK
-
COPY: последняя команда сообщаетDockerCOPY несколько файлов из локальной файловой системы, в частности подпапку в каталоге сборки, в образ по заданному пути
REQUIREMENTS: Чтобы успешно запустить руководство, вы должны загрузитьJava Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files изOracle. Просто распакуйте загруженный архив в локальную папку с именем‘files'..
Чтобы наконец создать образ и сохранить его в локальной библиотеке, нам нужно выполнить:
docker build --tag=alpine-java:base --rm=true .
NOTICE: Параметр–tag даст образу его имя, а–rm=true удалит промежуточные изображения после того, как оно было успешно построено. Последний символ в этой команде оболочки - точка, выступающая в качестве аргумента директории сборки.
3. Dockerize автономное загрузочное приложение Spring
В качестве примера приложения, которое мы можем докерировать, мы возьмемspring-cloud-config/server изspring cloud configuration tutorial. В качестве подготовительного этапа мы должны собрать работающий jar-файл и скопировать его в наш каталог сборкиDocker:
tutorials $> cd spring-cloud-config/server
server $> mvn package spring-boot:repackage
server $> cp target/server-0.0.1-SNAPSHOT.jar \
../../spring-boot-docker/files/config-server.jar
server $> cd ../../spring-boot-docker
Теперь мы создадимDockerfile с именемDockerfile.server со следующим содержимым:
FROM alpine-java:base
MAINTAINER example.com
COPY files/spring-cloud-config-server.jar /opt/spring-cloud/lib/
COPY files/spring-cloud-config-server-entrypoint.sh /opt/spring-cloud/bin/
ENV SPRING_APPLICATION_JSON= \
'{"spring": {"cloud": {"config": {"server": \
{"git": {"uri": "/var/lib/spring-cloud/config-repo", \
"clone-on-start": true}}}}}}'
ENTRYPOINT ["/usr/bin/java"]
CMD ["-jar", "/opt/spring-cloud/lib/spring-cloud-config-server.jar"]
VOLUME /var/lib/spring-cloud/config-repo
EXPOSE 8888
-
FROM: В качестве основы для нашего изображения мы возьмемJava-enabledAlpine Linux, созданный в предыдущем разделе.
-
COPY: мы позволяемDocker скопировать наш файл jar в изображение
-
ENV: эта команда позволяет нам определить некоторые переменные среды, которые будут учитываться приложением, запущенным в контейнере. Здесь мы определяем настроенныйSpring Boot Application configuration, чтобы позже передать его исполняемому файлу jar
-
ENTRYPOINT/CMD: This will be the executable to start when the container is booting. Мы должны определить их какJSON-Array, потому что мы будем использоватьENTRYPOINT в сочетании сCMD для некоторых аргументов приложения.
-
VOLUME: поскольку наш контейнер будет работать в изолированной среде без прямого доступа к сети, мы должны определить точку монтирования-заполнитель для нашего репозитория конфигурации
-
EXPOSE: Здесь мы сообщаемDocker, на каком порту отображается наше приложение. Этот порт будет опубликован на хосте при загрузке контейнера
Чтобы создать изображение из нашегоDockerfile, мы должны запустить‘docker build', как раньше:
$> docker build --file=Dockerfile.server \
--tag=config-server:latest --rm=true .
Но прежде чем мы собираемся запускать контейнер из нашего образа, нам нужно создать том для монтирования:
$> docker volume create --name=spring-cloud-config-repo
NOTICE: Хотя контейнер является неизменяемым, если после выхода из приложения он не зафиксирован в образе, данные, хранящиеся в томе, будут постоянными в нескольких контейнерах.
Наконец мы можем запустить контейнер из нашего изображения:
$> docker run --name=config-server --publish=8888:8888 \
--volume=spring-cloud-config-repo:/var/lib/spring-cloud/config-repo \
config-server:latest
-
Во-первых, нам нужно–name наш контейнер. Если нет, один будет выбран автоматически
-
Затем мы должны–publish наш открытый порт (см.Dockerfile) к порту на нашем хосте. Значение указывается в виде‘host-port:container-port'. Если указан только контейнерный порт, будет использоваться случайно выбранный хост-порт. Если мы оставим эту опцию, контейнер будет полностью изолирован
-
Параметр–volume дает доступ либо к каталогу на хосте (при использовании с абсолютным путем), либо к ранее созданному томуDocker (при использовании сvolume-name). Путь после двоеточия указываетmountpoint внутри контейнера
-
В качестве аргумента мы должны указатьDocker, какое изображение использовать. Здесь мы должны указатьimage-name из предыдущего шага «docker build»
-
Еще несколько полезных опций:
-
-it - включить интерактивный режим и выделитьpseudo-tty
-
-d - отсоединить от контейнера после загрузки
-
Если мы запустили контейнер в отдельном режиме, мы можем проверить его детали, остановить его и удалить с помощью следующих команд:
$> docker inspect config-server
$> docker stop config-server
$> docker rm config-server
4. Dockerize зависимые приложения в составе
КомандыDocker иDockerfiles особенно подходят для создания индивидуальных контейнеров. Но если вы хотитеoperate on a network of isolated applications, управление контейнером быстро становится загроможденным.
To solve that, Docker provides a tool named Docker Compose. Он поставляется с собственным файлом сборки в форматеYAML и лучше подходит для управления несколькими контейнерами. Например: он может запускать или останавливать совокупность сервисов одной командой или объединять вывод журнала нескольких сервисов вместе в одинpseudo-tty.
Давайте создадим пример двух приложений, работающих в разных контейнерах докеров. Они будут общаться друг с другом и представляться как «единое целое» для хост-системы. Мы создадим и скопируем примерspring-cloud-config/client, описанный вspring cloud configuration tutorial, в нашу папкуfiles, как мы делали раньше сconfig-server.
Это будет нашdocker-compose.yml:
version: '2'
services:
config-server:
container_name: config-server
build:
context: .
dockerfile: Dockerfile.server
image: config-server:latest
expose:
- 8888
networks:
- spring-cloud-network
volumes:
- spring-cloud-config-repo:/var/lib/spring-cloud/config-repo
logging:
driver: json-file
config-client:
container_name: config-client
build:
context: .
dockerfile: Dockerfile.client
image: config-client:latest
entrypoint: /opt/spring-cloud/bin/config-client-entrypoint.sh
environment:
SPRING_APPLICATION_JSON: \
'{"spring": {"cloud": \
{"config": {"uri": "http://config-server:8888"}}}}'
expose:
- 8080
ports:
- 8080:8080
networks:
- spring-cloud-network
links:
- config-server:config-server
depends_on:
- config-server
logging:
driver: json-file
networks:
spring-cloud-network:
driver: bridge
volumes:
spring-cloud-config-repo:
external: true
-
version: указывает, какую версию формата следует использовать. Это обязательное поле. Здесь мы используем более новую версию, аlegacy format - «1».
-
services: каждый объект в этом ключе определяет контейнерservice, также известный как контейнер. Этот раздел является обязательным
-
build: если задано,docker-compose может построить изображение изDockerfile
-
context: если указано, он указывает каталог сборки, в котором выполняется поискDockerfile
-
dockerfile: если задано, устанавливает альтернативное имя дляDockerfile
-
-
image: сообщаетDocker, какое имя он должен дать образу при использовании функций сборки. В противном случае он ищет это изображение в библиотеке илиremote-registry
-
networks: это идентификатор названных сетей для использования. Данныйname-value должен быть указан в разделеnetworks
-
volumes: определяет именованные тома для использования и точки монтирования для монтирования томов, разделенные двоеточием. Аналогично, в разделеnetworks,volume-name должен быть определен в отдельном разделеvolumes
-
links: это создаст внутреннее сетевое соединение между услугойthis и указанной службой. СлужбаThis сможет подключиться к указанной службе, при этом часть перед двоеточием указываетservice-name из разделаservices, а часть после двоеточия указывает имя хоста, на котором служба прослушивает открытый порт
-
depends_on: указываетDocker запускать службу, только если перечисленные службы были запущены успешно. NOTICE: Это работает только на уровне контейнера! Для обходного пути, чтобы сначала запустить зависимыйapplication, см.config-client-entrypoint.sh
-
logging: здесь мы используем драйвер‘json-file', который используется по умолчанию. В качестве альтернативы можно использовать‘syslog' с заданной опцией адреса или‘none'
-
-
networks: В этом разделе мы указываемnetworks, доступные для наших служб. В этом примере мы позволяемdocker-compose создать именованныйnetwork типа‘bridge' для нас. Если параметрexternal установлен наtrue, он будет использовать существующий с заданным именем.
-
volumes: это очень похоже на разделnetworks
Прежде чем мы продолжим, мы проверим наш файл сборки на наличие синтаксических ошибок:
$> docker-compose config
Это будет нашDockerfile.client, из которого будет построен образconfig-client. Он отличается отDockerfile.server тем, что мы дополнительно устанавливаемOpenBSD netcat (который понадобится на следующем шаге) и делаемentrypoint исполняемым:
FROM alpine-java:base
MAINTAINER example.com
RUN apk --no-cache add netcat-openbsd
COPY files/config-client.jar /opt/spring-cloud/lib/
COPY files/config-client-entrypoint.sh /opt/spring-cloud/bin/
RUN chmod 755 /opt/spring-cloud/bin/config-client-entrypoint.sh
И это будет настроенныйentrypoint для нашегоconfig-client service. Здесь мы используемnetcat в цикле, чтобы проверить, готов ли нашconfig-server. Обратите внимание, что мы можем получить доступ кconfig-server по егоlink-name, вместо IP-адреса:
#!/bin/sh
while ! nc -z config-server 8888 ; do
echo "Waiting for upcoming Config Server"
sleep 2
done
java -jar /opt/spring-cloud/lib/config-client.jar
Наконец, мы можем построить наши изображения, создать определенные контейнеры и запустить его одной командой:
$> docker-compose up --build
Чтобы остановить контейнеры, удалить его изDocker и удалить из него подключенныеnetworks иvolumes, мы можем использовать противоположную команду:
$> docker-compose down
Приятной особенностьюdocker-compose являетсяability to scale services. Например, мы можем указатьDocker запустить один контейнер дляconfig-server и три контейнера дляconfig-client.
Но для того, чтобы это работало правильно, мы должны удалитьcontainer_name из нашегоdocker-compose.yml, чтобы позволитьDocker выбрать один, и мы должны изменитьexposed port configuration, чтобы избежать конфликтов .
После этого мы можем масштабировать наши сервисы следующим образом:
$> docker-compose build
$> docker-compose up -d
$> docker-compose scale config-server=1 config-client=3
5. Заключение
Как мы видели, теперь мы можем создавать собственные изображенияDocker, запускатьSpring Boot Application как контейнерDocker и создавать зависимые контейнеры сdocker-compose.
Для дальнейшего чтения о файлах сборки мы обратимся к официальнымDockerfile reference иdocker-compose.yml reference.
Как обычно, исходные коды этого руководства находятся вon Github.