Как оркестровать микросервисы с помощью Docker Compose

Большинство современных программных систем имеют микросервисную архитектуру. Для стека микросервисных технологий Spring Cloud характерно содержание в прикладной системе нескольких прикладных микросервисов.

Перед запуском приложения необходимо запустить шлюз, центр регистрации, центр конфигурации, базу данных (даже при наличии внедренного в систему межплатформенного ПО, такого как Redis, RabbitMQ, системы управления логами ELK, системы визуализации Grafana и других сервисов).

При развертывании приложения с микросервисной архитектурой необходимо упаковать разработанный сервис Spring Boot в образ Docker, импортировать его в Docker, а затем запустить развертывание.

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

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


Что такое оркестрация контейнеров?

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

Всем известный Kubernetes (K8s)  —  продвинутый инструмент оркестрации контейнеров. Кроме него, широко используемыми инструментами оркестрации являются Docker Swarm и Docker Compose. Обе эти платформы  —  проекты официального инструмента оркестрации контейнеров Docker, однако роли их различны.

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

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

Предназначение K8s собственно такое же, как и у Docker Swarm. Это платформа для эксплуатации и обслуживания контейнеров, разработанная компанией Google. Она стала основным инструментом оркестрации контейнеров.

Dockerfile

Чтобы понимать, как работает Docker Compose, необходимо разбираться в Dockerfile. Сервис Spring Boot после компиляции представляет собой JAR-пакет. Перед развертыванием сервиса в контейнере Docker нужно собрать JAR-пакет в Docker-образ с помощью Dockerfile.

Dockerfile  —  это текстовый файл, используемый для создания образа. Содержимое файла  —  инструкции и указания по сборке образа.

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

FROM

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

# Формат
FROM [--platform=<platform>] <image> [AS <name>]  
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>] 
FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]

# Использование
FROM centos
FROM openjdk:8-jre
FROM node:12.18.4-alpine@sha256:757574c5a2...

Описание параметра:

  • [ — platform=<platform>]  —  необязательный параметр, с помощью которого можно указать платформу образа, например Linux/amd64, Linux/arm64 и windows/amd64, обычно по умолчанию.
  • <image>  —  это имя образа, за которым следует номер версии :tag, используемый для указания образа, и контекстно-адресуемый идентификатор @digest (подробности об этих параметрах читайте на официальном сайте). Как правило, используются только теги. Если ни один из них не указан, будет получена последняя версия.
  • [as <name>]  —  имя текущего этапа сборки, поскольку в Dockerfile можно использовать несколько FROM для создания нескольких образов. Поэтому as <name> можно применять в следующей команде COPY. С помощью  — from=<name> можно ссылаться на образ, собранный ранее, например для копирования файлов, созданных предыдущим образом, и т. д.

ARG

Эта директива используется для указания переменной, которая передается во время выполнения сборки. Она может задавать значение по умолчанию. При сборке контейнера с помощью команды docker build используйте  — build-arg <varname> = <value> для передачи параметров.

# Формат
ARG <name>[=<default value>]

# Использование
ARG CODE_VERSION=laster
ARG testArg=123

# Параметр
FROM centos:7
ARG parameter=123
RUN echo $parameter

docker build --build-arg parameter=234 -t test:1.0

# Примечание
# ARG можно использовать до FROM, вне фазы сборки FROM, поэтому данную директиву нельзя использовать ни в одной инструкции после FROM. 
ARG CENTOS_VERSION=laster
FROM centos:${CENTOS_VERSION}

ENV

Эта директива используется для установки переменных среды контейнера как в процессе сборки, так и в запущенном контейнере.

# Формат
ENV <key>=<value> ...

# Примечание
# Можно задать более одного или использовать другой синтаксис ENV <ключ> <значение>. На официальном сайте рекомендуется использовать первый вариант, а второй может быть удален в будущих версиях. 
# Переменные ENV переопределяют переменные ARG

# Использование
ENV APP_VERSION=1.1.0
ENV WEB_HOME /opt/webapps

Copy

Эта директива используется для копирования файлов и каталогов из пути в контейнер.

# Формат
COPY <src>... <dest>
COPY "<src>",... "<dest>"

# Использование
COPY test.jar /opt/web/

ADD

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

# Формат
ADD <src>... <dest>
ADD "<src>",... "<dest>"

# Использование
ADD test.txt /tmp/test

WORKDIR

Эта директива устанавливает рабочий каталог для последующих директив. Если каталог не существует, он будет создан автоматически.

# Формат
WORKDIR /path/to/workdir

# Использование
WORKDIR /build

LABEL

Эта директива используется для указания информации тега метаданных образа.

# Формат
LABEL <key>=<value> <key>=<value> <key>=<value> ...

# Использование
LABEL version="1.0"
LABEL description="This text illustrates"

CMD

Эта инструкция задает команду, которая будет выполняться при запуске контейнера после сборки образа.

# Формат
CMD ["executable","param1","param2"]
CMD ["param1","param2"]
CMD command param1 param2

# Использование
CMD sleep 40; java -jar secondkill-order.jar
CMD ["java", "-jar", "secondkill-order.jar"]

RUN

Эта инструкция используется для указания команды, которая должна быть выполнена при сборке образа. Основное различие между RUN и CMD заключается в том, что CMD выполняется при запуске контейнера, а RUN  —  во время построения контейнера.

# Формат
RUN <command>
RUN ["executable", "param1", "param2"]

# Использование
RUN yum install -y net-tools
RUN ["/bin/bash", "-c", "echo hello"]

EXPOSE

Эта команда указывает на то, что контейнер запущен на определенном порте. Вы можете указать протокол прослушивания. Если он не указан, по умолчанию используется протокол TCP.

# Формат
EXPOSE <port> [<port>/<protocol>...]

# Использование
EXPOSE 80/udp
EXPOSE 80 443

VOLUME

Эта директива используется для создания точки монтирования с указанным именем, которая может сопоставлять каталог за пределами контейнера.

# Формат
VOLUME ["/data"]

# Использование
VOLUME /myvol

Здесь приведены лишь наиболее часто используемые команды Dockerfile. Чтобы ознакомиться с ними более детально, обратитесь к документации на официальном сайте.


Создание приложения Spring Boot с помощью Dockerfile

Использовать Dockerfile для упаковки Spring Boot достаточно просто. Для этого нужно использовать образ Jdk8, скопировать локально упакованный JAR-пакет в образ, открыть порт сервиса и запустить сервис.

# Зеркало, использующее openjdk8
FROM openjdk:8-jre

# Установка рабочего каталога
WORKDIR /build

# Скопируйте пакет JAR в контейнер
ADD ./test-springboot-docker-1.0.jar ./test.jar

# Откройте порт 8080
EXPOSE 8080

# Запустите пакет JAR
CMD java -jar test.jar

Как обычно помещаем Dockerfile в каталог проекта.

Фото автора

Затем развернем пакет maven для упаковки JAR-пакета. Поместим JAR-пакет и Dockerfile в один каталог на сервере со средой Docker и с помощью команды docker build превратим микросервис Spring Boot в образ.

Фото автора

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

Фото автора

Поскольку порт 8080 используется другими сервисами, выполним маппинг на порт 80. Если получить доступ непосредственно к этому порту прямо сейчас, можно увидеть, что контейнер успешно запущен.

Фото автора

Docker Compose

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

Docker Compose использует файл docker-compose.yml для определения нескольких сервисов. Формат файла  —  YAML. Для начала нужно определить структуру всего файла.

Файловая структура docker-compose.yml

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

Официальная документация довольно подробна и представлена в формате docker-compose.yml.

version: указание версии сборки, соответствующей текущему файлу; в основном это 1, 2.x и 3.x
service: список сервисов
<service-name>: имя сервиса
image: указание запущенного образа; можно напрямую извлекать существующий образ для обработки
build: установка папки, где находится Dockerfile, способный обрабатывать образы, которые должны быть собраны с помощью Dockerfile.
content: путь для хранения Dockerfile
dockerfile: указывает имя файла Dockerfile для сборки
args: аргументы сборки, доступные только в процессе сборки
container_name: установка имени контейнера
restart: стратегия перезапуска; есть такие опции, как no, always, no-failure и unless-stopped
ports: открытие порта контейнера, формат: порт хоста: порт контейнера
- 8080:8080
hostname: установка имени хоста контейнера
volumes: установка точки монтирования контейнера, который может быть смонтирован на хосте; основной формат: путь к хосту: путь к контейнеру [: режим доступа]
- /opt/data:/opt/data
- /var/lib/mysql:/var/lib/mysql:rw
volumes_from: монтирование объемов информации другого сервиса или контейнера
- service_name
- container_name
environment: установка переменных среды
- RACK_ENV=development
networks: настройка сетей
app_netwotk:

Команды Docker compose

Ниже перечислены часто используемые команды с их общими описаниями (подробные описания команд и используемых параметров представлены на официальном сайте).

  • build: построить контейнер.
  • ps: вывести список всех контейнеров текущего проекта.
  • up: построить и запустить контейнер, общие параметры: -d : фоновый старт, -: указать конфигурационный файл.
  • exec: войти в указанный контейнер.
  • top: просмотреть текущее состояние каждого контейнера в проекте.
  • logs: просмотреть выходные данные контейнера.
  • down: остановить и удалить все контейнеры и соответствующую сеть.
  • rm: удалить все остановленные контейнеры.
  • start/stop/restart: запустить контейнер/остановить контейнер/перезапустить контейнер.

Развертывание микросервисов Docker Compose

В качестве примера воспользуемся проектом торгового центра с разделением на фронтенд и бэкенд. Он включает в себя 5 сервисов:

  • шлюз;
  • сервис аутентификации;
  • пользовательский сервис;
  • сервис товаров;
  • сервис заказов.

Кроме того, есть реестр Nacos, а также проект использует Rabbitmq, Redis и базу данных Mysql. Таким образом, всего необходимо развернуть 8 контейнеров.

Установка Docker Compose

Хотя Docker Compose является инструментом оркестрации на официальном сайте, он не включен по умолчанию и должен быть установлен вручную.

Метод установки относительно прост: достаточно загрузить бинарный файл Docker Compose и установить разрешение на запуск исполняемого файла.

  1. Скачайте файл Docker Compose.
sudo curl -L https://github.com/docker/compose/releases/download/1.16.1/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose

2. Добавьте к файлу разрешение на выполнение.

sudo chmod +x /usr/local/bin/docker-compose

3. Протестируйте результаты установки.

$ docker-compose --version
docker-compose version 1.18.0, build 8dd22a96

Общая структура файла

Общая структура файла docker-compose.yml выглядит следующим образом: указывается версия файла (здесь используется версия 3), далее следуют сервисы (названия сервисов соответствуют нижеуказанным).

Составная часть сетевой конфигурации  —  это создание network. Иначе по умолчанию будет создано имя сети с текущей папкой + _default.

Например, если имя папки проекта  —  second kill, а суффикс сети указан как app, будет создана сеть secondkill_app.

version: '3'
services:
secondkill-register:
...
secondkill-mysql:
...
secondkill-redis:
...
secondkill-rabbitmq:
...
secondkill-zuul:
...
secondkill-auth:
...
secondkill-goods:
...
secondkill-order:
...
secondkill-user:
...
networks:
app:

Развертывание реестра

Развертывание реестра  —  относительно простой процесс. Извлеките образ nacos/nacos-server:1.4.2, но обратите внимание на установку режима запуска nacos.

Поскольку по умолчанию Nacos запускается в кластере, необходимо увеличить MODE среды до автономного режима.

Кроме того, следует отметить, что здесь nacos используется только как центр регистрации и соединение с базой данных не настроено.

Если понадобится сохранить данные Nacos в базе данных, обратите внимание на настройку информации о базе данных.

secondkill-register:
# Извлечение образа
image: nacos/nacos-server:1.4.2
# Метод перезапуска (Restart): always
restart: always
# Маппинг портов
ports:
- 8848:8848
# Имя контейнера
container_name: secondkill-register
# Имя хоста, которое требует от других контейнеров доступа к сети через вторичное имя
hostname: secondkill-register
# Переменная среды, установка режима запуска на автономный запуск
environment:
- MODE: standalone
# конфигурация информации mysql
# - SPRING_DATASOURCE_PLATFORM=mysql
# - MYSQL_MASTER_SERVICE_HOST=127.0.0.1
# - MYSQL_MASTER_SERVICE_PORT=3306
# - MYSQL_MASTER_SERVICE_USER=root
# - MYSQL_MASTER_SERVICE_PASSWORD=123456
# - MYSQL_MASTER_SERVICE_DB_NAME=nacos
# - MYSQL_SLAVE_SERVICE_HOST=122.112.152.188
# - MYSQL_SLAVE_SERVICE_PORT=3306
# Добавление в сеть
networks:
- app

Развертывание Rabbitmq и Redis

Развернуть очереди сообщений и Redis относительно легко. Однако следует отметить, что для удобства собственного развертывания для временной демонстрации я не перенаправлял постоянные данные и конфигурационные файлы Redis и Rabbitmq, что крайне не рекомендуется. Следует развертывать сервисы с фиксацией состояния и не хранить данные.

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

secondkill-redis:
  # Образ redis
  image: redis:3.2
  # Маппинг портов
  ports:
    - 6379:6379
  # Метод перезапуска (Restart): always
  restart: always
  # Имя контейнера
  container_name: secondkill-redis
  # Имя хоста
  hostname: secondkill-redis
  # Присоединение к сети приложения 
  networks:
    - app

secondkill-rabbitmq:
  # Образ rabbitmq 
  image: rabbitmq:3.8.4
  # Маппинг портов
  ports:
    - 5672:5672
    - 15672:15672
  # Метод перезапуска (Restart): always
  restart: always 
  # Имя контейнера
  container_name: secondkill-rabbitmq
  # Имя хоста
  hostname: secondkill-rabbitmq
  # Присоединение к сети приложения 
  networks:
    - app

Развертывание базы данных

Как и сервис Redis, развертывание базы данных не монтирует конфигурационные файлы и данные (не рекомендуется, обратите на это внимание).

Поскольку при запуске базы данных здесь необходимо установить пароль базы данных, а также импортировать скрипт базы данных в базу данных, для создания образа здесь используется Dockerfile.

# Извлечение образа myql8 
FROM mysql:8.0.14
# Установка переменной часового пояса
ENV TZ=Asia/Shanghai
# Установка часового пояса
RUN ln -sf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# Импорт скрипта базы данных
COPY ./choy_ms.sql /docker-entrypoint-initdb.d

Поместим скрипт базы данных и Dockerfile в один и тот же каталог, затем укажем каталог через build.content в docker-compose.yml.

Фото автора
secondkill-mysql:
# Установка каталога для скрипта сборки dockerfile
build:
context: ./db
environment:
# Установка пароля базы данных
MYSQL_ROOT_PASSWORD: 86598659yu
restart: always
container_name: secondkill-mysql
image: secondkill-mysql
ports:
- 3306:3306
networks:
- app

Развертывание микросервисов

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

Поэтому в конфигурационном файле application.yml необходимо заменить IP базы данных, очередь, реестра и Redis на имя хоста вышеуказанного контейнера, то есть на определенное имя хоста.

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

spring:
cloud:
nacos:
discovery:
# secondkill-register - это имя хоста реестра
server-addr: secondkill-register:8848
datasource:
druid:
# Имя хоста базы данных: secondkill-mysql
url: jdbc:mysql://secondkill-mysql:3306/choy_ms...
...
redis:
# Имя хоста redis: secondkill-redis
host: secondkill-redis:127.0.0.1
...
rabbitmq:
# Имя хоста rabbitmq: secondkill-rabbitmq
host: secondkill-rabbitmq
port: 5672

При работе с Dockerfile необходимо обращать внимание на связь между местом хранения Dockerfile и целевым каталогом.

Поскольку весь проект извлекается, упаковывается и разворачивается на сервере через git, поместим Dockerfile под каждый микросервис, а JAR-пакет, в соответствии с командой ADD, должен предоставить целевой каталог.

Важно также при запуске микросервисов дождаться запуска сервисов центра регистрации, базы данных, Redis и очереди. Иначе запуск микросервисов будет неудачным. Для отсрочки запуска следует использовать команду sleep или конфигурацию шаблона docker-compose.yml: depends_on.

Фото автора
# Извлечение образа java8 
FROM openjdk:8-jre
# Установка рабочего каталога
WORKDIR /build
# Копирование JAR-пакета
ADD ./target/secondkill-order-1.1.0.jar ./secondkill-order.jar
# Открытый порт
EXPOSE 8010
# Чтобы запустить пакет JAR, необходимо отложить запуск и подождать, пока запустятся другие сервисы
CMD sleep 40; java -jar secondkill-order.jar

Следующим будет файл docker-compose.yml. docker-compose.yml хранится в корневом каталоге всего проекта.

В build.content необходимо задать относительный путь к Dockerfile под каждым сервисом.

Фото автора
secondkill-order:
# Установка относительного пути к сервису, соответствующему Dockerfile
build:
context: ./secondkill-service/secondkill-order
ports:
- 8010:8010
restart: always
container_name: secondkill-order
hostname: secondkill-order
networks:
- app

Установим относительный путь к сервису, соответствующему Dockerfile.

# Указание номера версии 
version: '3'
services:
# Сервис реестра
secondkill-register:
# Образ nacos-1.4.2
image: nacos/nacos-server:1.4.2
# Метод перезапуска (Restart): always
restart: always
# Маппинг портов
ports:
- 8848:8848
# Имя контейнера
container_name: secondkill-register
# Имя хоста
hostname: secondkill-register
# Переменная среды, установка режима запуска на автономный запуск
environment:
MODE: standalone
# Добавление в веб-приложение
networks:
- app

# Сервис базы данных
secondkill-mysql:
# База данных соответствует каталогу Dockerfile
build:
context: ./db
# Установка пароля базы данных
environment:
MYSQL_ROOT_PASSWORD: 86598659yu
restart: always
container_name: secondkill-mysql
image: secondkill-mysql
ports:
- 3306:3306
networks:
- app

# Сервис redis
secondkill-redis:
# Образ redis-3.2
image: redis:3.2
ports:
- 6379:6379
restart: always
container_name: secondkill-redis
hostname: secondkill-redis
networks:
- app

# Сервис rabbitmq
secondkill-rabbitmq:
# Образ rabbitmq-3.8.4
image: rabbitmq:3.8.4
ports:
- 5672:5672
- 15672:15672
restart: always
container_name: secondkill-rabbitmq
hostname: secondkill-rabbitmq
networks:
- app

# Далее следуют микросервисы

# Сервис шлюза
secondkill-zuul:
# Сервис шлюза соответствует пути Dockerfile
build:
context: ./secondkill-zuul
ports:
- 8000:8000
restart: always
container_name: secondkill-zuul
hostname: secondkill-zuul
networks:
- app

# Сервис аутентификации
secondkill-auth:
# Сервис аутентификации соответствует пути Dockerfile
build:
context: ./secondkill-auth
ports:
- 8002:8002
restart: always
container_name: secondkill-auth
hostname: secondkill-auth
networks:
- app

# Сервис продукта
secondkill-goods:
# Сервис продукта соответствует пути Dockerfile
build:
context: ./secondkill-service/secondkill-goods
ports:
- 8021:8021
restart: always
container_name: secondkill-goods
hostname: secondkill-goods
networks:
- app

# Сервис заказов
secondkill-order:
# Сервис заказов соответствует пути Dockerfile
build:
context: ./secondkill-service/secondkill-order
ports:
- 8010:8010
restart: always
container_name: secondkill-order
hostname: secondkill-order
networks:
- app

# Пользовательский сервис
secondkill-user:
# Пользовательский сервис соответствует пути Dockerfile
build:
context: ./secondkill-service/secondkill-user
ports:
- 8001:8001
restart: always
container_name: secondkill-user
hostname: secondkill-user
networks:
- app

# Установка app в качестве сети
networks:
app:

Развертывание сервиса

Сначала нужно извлечь проект на сервер. В данном случае берем его прямо из репозитория Github, затем входим в проект и выполняем mvn clean && mvn package для упаковки и компиляции проекта.

Фото автора

Затем выполняем docker-compose up -d для оркестрации и запуска контейнера.

Фото автора

На этом сервере установлен portainer. Можно напрямую войти в portainer для просмотра состояния контейнеров. Нажмите на имя каждого контейнера, чтобы посмотреть вывод запуска сервера.

Фото автора
Фото автора

Можно также напрямую войти в центр регистрации Nacos, чтобы просмотреть статус регистрации сервиса.

Если же потребуется остановить все сервисы и удалить контейнер, используйте команду docker-compose down.

Фото автора

Читайте также:

Читайте нас в TelegramVK и Дзен


Перевод статьи Dwen: How to Orchestrate Microservices With Docker-Compose

Предыдущая статьяКак защитить текст от комбинации Ctrl+F в браузере
Следующая статьяКак профессионально использовать сопоставимые типы TypeScript