Лучший способ создания нескольких окружений для Spring Boot приложения с помощью Docker Compose

от автора

В новой статье от команды Amplicode я расскажу, как можно создать несколько Docker Compose файлов для разных нужд. Например, для продакшена и разработки, и при этом не утонуть в копипасте.

Статья также доступна в формате видео, так что можно и смотреть, и читать — как вам удобнее!

Смотреть на Rutube || Смотреть на YouTube


Итак, сегодня мы хотим получить два Docker Compose файла. Один будет полезен для продакшена и будет содержать три сервиса: наше Spring Boot приложение, PostgreSQL и Kafka, а второй будет использоваться во время разработки и точно так же будет включать в себя PostgreSQL и Kafka, а также инструменты для работы с ними, а именно pgAdmin и Kafka UI.

Docker Compose для продакшена!?

Отметим, что касательно использования Docker Compose в продакшене однозначного мнения нет. Одни считают, что Docker Compose не подходит для продакшена, и лучше использовать продвинутые системы оркестрации, такие как Kubernetes. Другие считают, что это вполне допустимо при определенных условиях. В общем, как часто бывает в программировании, ответ зависит от контекста.

Способы решения поставленной задачи 

Возвращаясь к поставленной задаче: как вы можете заметить, Kafka и PostgreSQL должны присутствовать в обоих Docker Compose файлах, и есть несколько способов решить эту задачу.

Использование одного Docker Compose файла с профилями

Первый способ, которым мы можем решить поставленную задачу — это вообще отказаться от двух Docker Compose файлов, а создать только один, в котором описать все сервисы и использовать профили, чтобы указать, какие сервисы нужны во время разработки, а какие пригодятся только в продакшене.

#многие свойства сервисов не указаны для простоты восприятия  services:    spring-petclinic:      image: spring-petclinic:latest      profiles:        - prod    postgres:      image: postgres:16.3      profiles:        - prod        - dev    kafka:      image: confluentinc/cp-kafka:7.6.1      profiles:        - prod        - dev    kafkaui:      image: provectuslabs/kafka-ui:v0.7.2      profiles:        - dev    pgadmin:      image: dpage/pgadmin4:8.12.0      profiles:        - dev 

Однако, этот вариант немного ограничен с точки зрения использования. Например, я не смогу указать для разных профилей, что для них должны использоваться разные порты или скрипты инициализации.

Создание отдельных Docker Compose файлов для каждого окружения 

Второй способ, который я могу выбрать для решения этой задачи — это просто скопировать нужные мне сервисы в разные Docker Compose файлы. Однако, если придерживаться этого варианта, можно наткнуться на все типичные проблемы, которые связаны с копированием и вставкой содержимого, например, изменить версию сервиса в одном Docker Compose файле, но забыть сделать это в другом. И если первоначально у меня было всего лишь два Docker Compose файла, в дальнейшем их может неожиданно стать четыре, потом восемь, и так далее. Поддерживать все это в рабочем состоянии будет все сложнее и сложнее.

Использование include и extends для переиспользования сервисов 

Наконец, есть третий способ, про который, возможно, не все знают. Этот способ подразумевает использование конструкций include и extends в Docker Compose для переиспользования сервисов.

Конструкция include позволяет включить один Docker Compose файл в другой, по сути предоставляя аналог запуска сразу нескольких Docker Compose файлов из терминала.

Файл services.yaml:

#многие свойства сервисов не указаны для простоты восприятия  services:    postgres:      image: postgres:16.3    kafka:      image: confluentinc/cp-kafka:7.6.1 

 Файл app-compose.yaml:

#многие свойства сервисов не указаны для простоты восприятия  include:    - services.yaml    services:    spring-petclinic:      image: spring-petclinic:latest

Этот подход действительно довольно удобен, если вам нужно просто переиспользовать одни и те же сервисы для разных Docker Compose файлов без какой‑либо дополнительной настройки.

И этот подход в том числе поддерживается Amplicode с точки зрения визуального отображения:

Но если вам нужно произвести тонкую настройку сервиса под свои нужды, то здесь больше подойдет ключевое слово extends.

С его помощью, точно так же, как и с помощью include, можно включить сервисы из другого Docker Compose файла в текущий. При этом у нас появляется возможность конфигурировать его свойства.

Файл services.yaml:

#многие свойства сервисов не указаны для простоты восприятия  services:    postgres:      image: postgres:16.3    kafka:      image: confluentinc/cp-kafka:7.6.1 

Файл app-compose.yaml:

services:    spring-petclinic:      image: spring-petclinic:latest    postgres:      extends:        service: postgres        file: services.yaml     kafka:      extends:        service: kafka        file: services.yaml       #так как это расширение указанного сервиса,      #мы можем доконфигурировать его свойства       ports:        - "9092:9092" 

В данной ситуации вариант с extends подойдет лучше всего. Используя этот вариант, можно избежать дублирования кода и при этом сохранить гибкость конфигурирования. Кстати, схожий подход использует и JHipster, генератор Spring Boot приложений. Отличный доклад про этот генератор сделал Илья Кучмин на последнем JPoint. Рекомендую его посмотреть, доклад получился очень интересный

Решим задачу с extends и include

По легенде мы занимаемся доработкой существующего приложения, в котором уже был реализован следующий Docker Compose файл:

services:    spring-petclinic:      image: spring-petclinic:latest      build:        context: .        args:          DOCKER_BUILDKIT: 1      restart: always      ports:        - "8080:8080"      environment:        POSTGRES_HOST: postgres        POSTGRES_DB: spring-petclinic        POSTGRES_USER: root        POSTGRES_PASSWORD: root        KAFKA_BOOTSTRAP_SERVERS: kafka:29092      healthcheck:        test: wget --no-verbose --tries=1 --spider http://localhost:8080/actuator/health || exit 1        interval: 30s        timeout: 5s        start_period: 30s        retries: 5      depends_on:        - postgres    postgres:      image: postgres:16.3      restart: always      ports:        - "5432:5432"      volumes:        - postgres_data:/var/lib/postgresql/data      environment:        POSTGRES_USER: root        POSTGRES_PASSWORD: root        POSTGRES_DB: spring-petclinic      healthcheck:        test: pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB        interval: 10s        timeout: 5s        start_period: 10s        retries: 5    kafka:      image: confluentinc/cp-kafka:7.6.1      restart: always      ports:        - "29092:29092"        - "9092:9092"      volumes:        - kafka_data:/var/lib/kafka/data      environment:        KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092        KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT,CONTROLLER:PLAINTEXT        KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1        KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT        KAFKA_NODE_ID: 1        CLUSTER_ID: 8GyRIS62T8aMSkDJs-AH5Q        KAFKA_PROCESS_ROLES: controller,broker        KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka:9093        KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER        KAFKA_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://0.0.0.0:9092,CONTROLLER://kafka:9093      healthcheck:        test: kafka-topics --bootstrap-server localhost:9092 --list        interval: 10s        timeout: 5s        start_period: 30s        retries: 5    volumes:    postgres_data:    kafka_data:

Docker Compose файл для переиспользуемых сервисов

По сути, этот файл мы можем использовать для продакшена, с небольшими модификациями. Давайте их выполним. Сначала переименуем его в compose.prod.yaml:

Далее, скопируем сервисы Kafka и PostgreSQL в новый Docker Compose файл с сервисами. Именно этот файл мы и будем в дальнейшем переиспользовать в других docker compose файлах, предназначенных для разных целей (продакшена, разработки, тестового окружения и т. д.). Для этого в панели Amplicode Explorer веберем DockerNewDocker Compose File и зададим ему название services.yaml.

Так как все настройки, которые я укажу в этом файле, будут использоваться и в других местах, в которых я буду расширять сервисы из этого Docker Compose файла, те значения, которые, вероятнее всего, будут отличаться, отсюда лучше удалить. Поэтому для PostgreSQL удалим переменные окружения:

Также удалим открытый для внешнего подключения порт, так как если оставить его в этом сервисе и затем переопределять его в других Docker Compose файлах, то он все равно не будет перезатерт. Значения номеров портов будут объединяться из двух Docker Compose файлов, и в результате PostgreSQL будет доступен на двух портах.

А в случае с продакшеном я бы вообще не хотел, чтобы какой‑либо сервис, помимо Spring Boot приложения, открывал для внешнего подключения свои порты.

Для Kafka удалю только порты, переменные окружения оставлю, так как они будут одинаковые для двух окружений.

Файл services.yaml готов:

services:    postgres:      image: postgres:16.3      restart: always      volumes:        - postgres_data:/var/lib/postgresql/data      healthcheck:        test: pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB        interval: 10s        timeout: 5s        start_period: 10s        retries: 5    kafka:      image: confluentinc/cp-kafka:7.6.1      restart: always      volumes:        - kafka_data:/var/lib/kafka/data      environment:        KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092        KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT,CONTROLLER:PLAINTEXT        KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1        KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT        KAFKA_NODE_ID: 1        CLUSTER_ID: zNOJ9oWQQWCJqtCat68MLQ        KAFKA_PROCESS_ROLES: controller,broker        KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka:9093        KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER        KAFKA_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://0.0.0.0:9092,CONTROLLER://kafka:9093      healthcheck:        test: kafka-topics --bootstrap-server localhost:9092 --list        interval: 10s        timeout: 5s        start_period: 30s        retries: 5  volumes:    kafka_data:    postgres_data:

Docker Compose файл для продакшена

Теперь мне нужно вернуть файл для продакшена в то состояние, в котором он был. А именно, добавить сервисы для PostgreSQL и Kafka. Для этого вызову меню Generate и в нем найду действие Extend Existing Service от Amplicode:

Выберу сервис, имя оставлю без изменений.

В результате сервис в файле compose.prod.yaml будет выглядеть следующим образом:

Для корректной работы приложения сразу после запуска мне нужны некоторые таблицы в базе данных и записи в них. Я могу проинициализировать базу, указав путь к директории, в которой лежат скрипты инициализации. Чтобы сделать это не покидая IDE и не ошибиться с написанием пути можно воспользоваться панелью Amplicode Designer и секцией Init scripts. Используя ее, укажу путь до директории со скриптами в строке Source:

Так как указывать чувствительную информацию в открытом виде в Docker Compose файлах – это довольно грубое нарушение общепринятых политик безопасности, давайте воспользуемся .env файлом. Начнём набирать env_file и Amplicode предложит нам code completion не только для атрибута сервиса:

 Но и для названия файла, расположенного в проекте, с расширением .env:

Что самое удобное, так это то, что Amplicode также отобразит все данные из него в соответствующих секциях в панели Amplicode Designer:

Также не забудем удалить переменные окружения из сервиса с нашим Spring Boot приложением:

 И укажем .env файл:

Остается добавить Kafka, для которой никаких дополнительных телодвижений тут делать не придется. Аналогичным образом вызовем меню Generate и расширим сервис Kafka:

В итоге файл compose.prod.yaml теперь выглядит следующим образом:

services:    spring-petclinic:      image: spring-petclinic:latest      build:        context: .        args:          DOCKER_BUILDKIT: 1      restart: always      ports:        - "8080:8080"      env_file:        - prod.env      healthcheck:        test: wget --no-verbose --tries=1 --spider http://localhost:8080/actuator/health || exit 1        interval: 30s        timeout: 5s        start_period: 30s        retries: 5      depends_on:        - postgres    postgres:      extends:        service: postgres        file: services.yaml      env_file:        - prod.env      volumes:        - ./src/main/resources/db/postgres:/docker-entrypoint-initdb.d:ro    kafka:      extends:        service: kafka        file: services.yaml  volumes:    postgres_data:    kafka_data:

Docker Compose файл для разработки

Теперь провернём аналогичные действия и для Docker Compose файла, который понадобиться нам для разработки.

Сначала расширим сервисы PostgreSQL и Kafka описанные в файле services.yaml. В итоге у нас получится файл compose.dev.yaml со следующим содержимым:

services:    postgres:      extends:        service: postgres        file: services.yaml      ports:        - "5432:5432"      volumes:        - ./src/main/resources/db/postgres:/docker-entrypoint-initdb.d:ro      environment:        POSTGRES_DB: postgres        POSTGRES_USER: root        POSTGRES_PASSWORD: root    kafka:      extends:        service: kafka        file: services.yaml    volumes:    postgres_data:    kafka_data:

Отмечу, что для PostgreSQL я открыл порт 5432 для внешнего подключения, а для Kafka порт 9092. Если бы я этого не сделал, то я бы не смог достучаться до базы данных и брокера сообщений извне Docker сети. А, следовательно, если бы я запустил приложение в режиме отладки, подключиться к PostgreSQL или Kafka оно бы не смогло.

Теперь мне остается только добавить удобные инструменты для взаимодействия с нашими сервисами во время разработки. Amplicode подозревает, что я именно этого и хочу, и предлагает их сгенерировать.

Что интересно, для PostgreSQL он даже автоматически настроит подключение в pgAdmin, так что мне ничего не нужно будет настраивать после того, как я его запущу и подключусь. Можно будет просто сразу начать пользоваться.

Жмём ОК и pgAdmin сервис готов:

Повторю то же самое и для Kafka UI.

Вот и все:

В итоге, после всех наших манипуляций файл compose.dev.yaml выглядит следующим образом:

services:    postgres:      extends:        service: postgres        file: services.yaml      ports:        - "5432:5432"      volumes:        - ./src/main/resources/db/postgres:/docker-entrypoint-initdb.d:ro      environment:        POSTGRES_DB: postgres        POSTGRES_USER: root        POSTGRES_PASSWORD: root    kafka:      extends:        service: kafka        file: services.yaml    pgadmin:      image: dpage/pgadmin4:8.12.0      restart: "no"      ports:        - "5050:80"      volumes:        - pgadmin_data:/var/lib/pgadmin        - ./docker/pgadmin/servers.json:/pgadmin4/servers.json        - ./docker/pgadmin/pgpass:/pgadmin4/pgpass      environment:        PGADMIN_DEFAULT_EMAIL: admin@admin.com        PGADMIN_DEFAULT_PASSWORD: root        PGADMIN_CONFIG_SERVER_MODE: "False"        PGADMIN_CONFIG_MASTER_PASSWORD_REQUIRED: "False"      healthcheck:        test: wget --no-verbose --tries=1 --spider http://localhost:80/misc/ping || exit -1        interval: 10s        timeout: 5s        start_period: 10s        retries: 5      entrypoint: /bin/sh -c "chmod 600 /pgadmin4/pgpass; /entrypoint.sh;"    kafkaui:      image: provectuslabs/kafka-ui:v0.7.2      restart: "no"      ports:        - "8989:8080"      environment:        DYNAMIC_CONFIG_ENABLED: "true"        KAFKA_CLUSTERS_0_NAME: 8GyRIS62T8aMSkDJs-AH5Q        KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: kafka:29092      healthcheck:        test: wget --no-verbose --tries=1 --spider http://localhost:8080/actuator/health || exit -1        interval: 10s        timeout: 5s        start_period: 60s        retries: 5  volumes:    postgres_data:    kafka_data:    pgadmin_data:   

Заключение

Теперь любой разработчик без каких-либо проблем сможет в пару кликов запустить все необходимые для разработки сервисы. И у него не будет никаких проблем, связанных с локальным окружением.

Сегодня мы узнали про два очень полезных ключевых слова Docker Compose — include и extends — а также научились ими пользоваться для решения конкретных задач.

Подписывайтесь на наши Telegram и YouTube, чтобы не пропустить новые материалы про Amplicode, Spring и связанные с ним технологии!

А если вы хотите попробовать Amplicode в действии – то можете установить его абсолютно бесплатно уже сейчас, как в IntelliJ IDEA/GigaIDE, так и в VS Code.


ссылка на оригинал статьи https://habr.com/ru/articles/848696/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *