13 April 2016
Даже самый простой современный сервис состоит из нескольких приложений: как минимум, реверс-прокси, СУБД и самого веб приложения. В последнее время к ним часто стали добавлять Redis для сессий, Solr/Sphinx для поиска и многое другое. Да и само монолитное бизнес-приложение всё чаще разбивают на отдельные сервисы, микро и не очень. Но с большой силой приходит и большая ответственность: каждый новый сервис всё сложнее поддерживать, сложнее развернуть в тестовом окружении.
Для разворачивания приложений используем Docker - удобное средство для управления контейнерами. С помощью контейнеров больше не придется изучать длинные инструкции по установке СУБД или nginx, можно легко использовать утилиты, написанные на Ruby или Pyhton (и даже не придется использовать virtualenv).
Разворачивать в Docker отдельные контейнеры очень просто, но чем больше контейнеров нам нужно для полноценного функционирования нашего приложения, тем сложнее становится процесс запуска системы. Для этого создан Docker Compose - утилита, позволяющая строить новые образы, запускать их в нужном порядке, подключать хранилища, создавать и подключатся к сетям и многое другое.
Итак, пускай у нас есть простой Java-сервис. Сейчас я изучаю messaging, поэтому будем использовать Compose для запуска двух приложений: Publisher и Subscriber, а также сам брокер очередей RabbitMQ.
Схема нашего приложения:
gradle/
- wrapper/
- gradle-wrapper.jar
- gradle-wrapper.properties
publisher/
- src/main/java/...
- build.gradle
- Dockerfile
subscriber/
- src/main/java/...
- build.gradle
- Dockerfile
build.gradle
docker-compose.yml
gradlew
gradlew.bat
Не буду подробно описывать структуру градлового билда. Там используется application plugin который генерирует tar-архив, со всеми необходимыми библиотеками, а также стартовые скрипты для запуска. Результаты работы этого плагина идеально подходят нам для создания образа нашего приложения.
Вот пример Dockerfile для просто java-приложения:
FROM saladinkzn/debian-java
RUN mkdir /workdir
ADD build/distributions/app1.tar /workdir/
WORKDIR /workdir
CMD /bin/bash app1/bin/app1
EXPOSE 8080
Абсолютно аналогично делаем Dockerfile для второго нашего приложения и переходим к самому главному - файлу настройки Docker Composer: docker-compose.yml
.
version: '2'
services:
# RabbitMQ, используем стандартный библиотечный образ, вытаскиваем наружу стандартный порт 5672, хотя это и не обязательно.
rabbitmq:
image: rabbitmq
ports:
- "5672:5672"
networks:
- inner
# Наше приложение, строим его с использованием результатов Gradle билда.
app1:
# Имя образа, которое будет присвоено построенному образу
image: app1
# Контекст для docker build
build: ./app1
# Пробрасываем порты из контейнера наружу
ports:
- "8081:8080"
# Присоединяемся к внутренней сети
networks:
- inner
# Говорим Compose, что наш контейнер нужно запускать после образа с брокером очередей.
depends_on:
- rabbitmq
app2:
image: app2
build: ./app2
ports:
- "8082:8080"
networks:
- inner
depends_on:
- rabbitmq
networks:
inner:
Неплохо было бы создать отдельный режим для запуска приложения в режиме для разработки (с запущенным дебаг режимом и проброшенным портом для удаленной отладки).