Докеризация Spring Boot Application

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.