Docker compose і об'єднання проектів з допомогою mixer-a

Одна з проблем, з якими доводиться столкиваться, займаючись налаштуванням оточення для розробників, з використанням Docker і Docker-compose, це питання про те, як зводити разом кілька різних проектів. За умови, що всі проекти, звичайно ж, мають
docker-compose.yml
файл.



Причин, за якими стає необхідно робити це, може бути кілька:
  • Розробка низько пов'язаних компонентів величезної системи. Де кожен проект, по суті, може бути окремим самостійним додатком
  • З'єднання окремих компонентів для тестування. Винесення
    mock
    -сервісів і тестів в окремі контейнери зі своєю логікою лінкування і взаємодії
  • Зовнішні, по відношенню до проекту, системи, які тим не менш 'живуть' в docker середовищі

Зміст


Проблема
Власне, комбінування
N
файлів в один і запуск їх як цілісної програми, і стало основною проблемою. Що ж стало другорядними проблемами, які не дозволили просто на просто поєднати один за одним існуючі файли?

  • Конфлікти імен контейнерів c оновленням всього дерева конфігурації
  • Конфлікти портів пробрасываемых на хост-машину
  • Перевизначення властивостей сервісу
  • Дозвіл відносних шляхів
  • Видалення зайвих або дубльованих сервісів


Рішення
Таким чином витративши на розум прийшов витратити пару вихідних і написати простий
python
препроцесор, який і буде робити всю брудну рутинну роботу по об'єднанню
docker-compose
файлів.
Як наслідок, вийшов невеликий (на 600 рядків коду) скрипт в цілому і повністю вирішує проблеми виникли колись переді мною.

Однак, як бонус, повз позначених вище проблем, скрипт допоміг вирішити ще одну цікаву задачу. А саме після невеликих умовиводів, вдалося паралельно запускати кілька екземплярів програми з одним
docker-compose
файлом.

Приклади використання
Кілька кроків, які дозволять швидко використовувати дане рішення

Установка
Необхідно завантажити бінарний файл, який є упакованим
python
модулем
Установка
sudo wget https://github.com/paunin/docker-compose-mixer/blob/master/dist/dc-mixer?raw=true -O /usr/local/bin/dc-mixer
sudo chmod +x /usr/local/bin/dc-mixer


Налаштування
Зміст конфігураційного
docker-compose-mixer.yml
file по своїй суті є невеликим конфіг, який описує як саме два або більше проектів будуть стартувати разом.

Скажімо у нас є 2 проекту
project A
та
project B
c двома
docker-compose.yml
файлами.
Проекти, будучи різними версіями системи, являють собою додаток з набором сервісів:
java application, redis, rabbitmq, mail
, проект
project A
(нова версія) містить
mysql
сервіс.
project A
$ cat ../proj_a/docker-compose.yml
sources:
build: images/sources
volumes:
- .:/var/application.host

application:
build: images/java
dockerfile: application.yml
dns:
- 8.8.8.8
- 9.9.9.9
hostname: application.host
working_dir: /var/application.host
cgroup_parent: m-executor-abcd
links:
- redis:redis
- rabbitmq
- mail
- mysql:db
volumes_from:
- sources
command: "/start.sh"
env_file:
- ./env_files/application.env
- ./env_files/rabbit.env
environment:
- DB_DRIVER: mysql
- DB_PORT: 3306
ports:
- "80:80" #http
- "1098:1098" #jmx
external_links:
- redis_1
- project_db_1:mysql
extra_hosts:
- "somehost:162.242.195.82"
labels:
com.example.description: "Accounting webapp"
com.example.department: "Finance"

redis:
labels:
- "com.example.description=Accounting webapp"
- "com.example.department=Finance"
extends:
file: ../redis.yml
service: redisbase
expose:
ports:
- "6379:6379"
- "127.0.0.1:6370:6370"
log_driver: "syslog"
log_opt:
syslog-address: "tcp://192.168.0.42:123"
net: "bridge"

rabbitmq:
build: images/rabbitmq
ports:
- "15672:15672"
volumes_from:
- sources
command: /start.sh
env_file:
- ./env_files/rabbit.env

mail:
build: images/mail
hostname: mail
domainname: application.host
expose:
- 25
- 143
ports:
- "25:25"
- "143:143"
volumes:
- ./images/mail/spamassassin:/tmp/spamassassin/
- ./images/mail/postfix:/tmp/postfix/
- ./images/mail/mail:/tmp/mail/

mysql:
build: images/mysql
ports:
- 3602

project B
$ cat ./proj_b/docker-compose.yml
sources:
build: images/sources
volumes:
- .:/var/application.host

application:
build: images/java
dockerfile: application.yml
dns:
- 8.8.8.8
- 9.9.9.9
hostname: application.host
working_dir: /var/application.host
cgroup_parent: m-executor-abcd
links:
- redis:redis
- rabbitmq
- mail
volumes_from:
- sources
command: "/start.sh"
env_file:
- ./env_files/application.env
- ./env_files/rabbit.env
environment:
- VARIABLE: value
ports:
- "80:80" #http
- "1098:1098" #jmx
external_links:
- redis_1
- project_db_1:mysql
extra_hosts:
- "somehost:162.242.195.82"
labels:
com.example.description: "Accounting webapp"
com.example.department: "Finance"

redis:
labels:
- "com.example.description=Accounting webapp"
- "com.example.department=Finance"
extends:
file: ../redis.yml
service: redisbase
expose:
ports:
- "6379:6379"
- "127.0.0.1:6370:6373"
log_driver: "syslog"
log_opt:
syslog-address: "tcp://192.168.0.42:123"
net: "bridge"

rabbitmq:
build: images/rabbitmq
ports:
- "15672:15672"
volumes_from:
- sources
command: /start.sh
env_file:
- ./env_files/rabbit.env

mail:
build: images/mail
hostname: mail
domainname: application.host
expose:
- 25
- 143
ports:
- "25:25"
- "143:143"
volumes:
- ./images/mail/spamassassin:/tmp/spamassassin/
- ./images/mail/postfix:/tmp/postfix/
- ./images/mail/mail:/tmp/mail/


Базове завдання — об'єднати два оточення в одну з використанням лише одного
rabbitmq
сервісу. До того ж необхідно створити новий сервіс
pgsql
і підключити його до першого проекту (замінивши mysql)
Конфігурація для об'єднання файлів повинна лежати в файлі
./docker-compose-mixer.yml
(опція
-i, --input-file
може визначити інше ім'я для файлу)
docker-compose-mixer.yml
$ cat ./docker-compose-mixer.yml
includes:
proja: ../proj_a/docker-compose.yml
projb: ./proj_b/docker-compose.yml
ignores:
- projbrabbitmq
overrides:
projbapplication:
links:
- projaredis:redis
- projarabbitmq:rabbitmq
- projamail:mail
projaapplication:
links:
- projaredis:redis
- projarabbitmq:rabbitmq
- projamail:mail
- pgsql:db
environment:
- DB_DRIVER: pgsql
- DB_PORT: 5432
master_services:
pgsql:
image:pgsql:latest
expose: 
- 5432
ports:
5432:5432


Запуск
Команда `
dc-mixer -v
` запускає препроцесор у вербальному режимі і зберігає результат у файл. Опції запуску допомагають керувати поведінкою.
Опції запуску
$ dc-mixer --help
Compile docker-compose from several docker-compose.yml files

Usage:
dc-mixer [options]

Options:
-h, --help Print help information
-i, --input-file Input file (default `docker-compose-mixer.yml` in current directory)
-o, --output-file Output file (default `docker-compose.yml` in current directory)
-h, --help Print help information
-v, --verbose Enable verbose mode

For more information read documentation: https://github.com/paunin/docker-compose-mixer


Результат
По замовчуванню, результат об'єднання файлів буде збережений у файл
./docker-compose.yml
. (опція
-o, --output-file
може визначити інше ім'я для файлу)
Результат роботи скрипта
$ dc-mixer -v -o docker-compose.yml 
DEBUG:root:Start compiling compose file...
DEBUG:root:Input file: /Users/paunin/Sites/dc-mixer.local/examples/example2/proj/docker-compose-mixer.yml; output file: docker-compose.yml
DEBUG:root:Mixer config is below:
{'overrides': {'projbapplication': {'links': ['projaredis:redis', 'projarabbitmq:rabbitmq', 'projamail:mail']}, 'projaapplication': {'environment': {'DB_DRIVER': 'pgsql', 'DB_PORT': 5432}, 'links': ['projaredis:redis', 'projarabbitmq:rabbitmq', 'projamail:mail', 'pgsql:db']}}, 'master_services': {'pgsql': {'image': 'pgsql:latest', 'expose': [5432], 'ports': '5432:5432'}}, 'ignores': ['projbrabbitmq'], 'includes': {'projb': './proj_b/docker-compose.yml', 'proja': '../proj_a/docker-compose.yml'}}
DEBUG:root:Creating scope for file: /Users/paunin/Sites/dc-mixer.local/examples/example2/proj/proj_b/docker-compose.yml and prefix: projb
DEBUG:root:Creating scope for file: /Users/paunin/Sites/dc-mixer.local/examples/example2/proj_a/docker-compose.yml and prefix: proja
DEBUG:root:Resolving services names
DEBUG:root:Resolving services with paths
DEBUG:root:Resolving services ports
DEBUG:root:Redefined ports:
{'projaapplication': {80: 81, 1098: 1099}, 'projaredis': {6370: 6371, 6379: 6380}, 'projamail': {25: 26, 143: 144}}
DEBUG:root:Result scope is:
{'projasources': {'build': '../proj_a/images/sources', 'volumes': ['./../proj_a:/var/application.host']}, 'projaredis': {'log_opt': {'syslog-address': 'tcp://192.168.0.42:123'}, 'log_driver': 'syslog', 'expose': None, 'labels': ['com.example.description=Accounting webapp', 'com.example.department=Finance'], 'extends': {'service': 'redisbase', 'file': '../redis.yml'}, 'net': 'bridge', 'ports': ['6380:6379', '127.0.0.1:6371:6370']}, 'projbmail': {'domainname': 'application.host', 'expose': [25, 143], 'hostname': 'mail', 'build': 'proj_b/images/mail', 'volumes': ['./proj_b/images/mail/spamassassin:/tmp/spamassassin/', './proj_b/images/mail/postfix:/tmp/postfix/', './proj_b/images/mail/mail:/tmp/mail/'], 'ports': ['25:25', '143:143']}, 'projamail': {'domainname': 'application.host', 'expose': [25, 143], 'hostname': 'mail', 'build': '../proj_a/images/mail', 'volumes': ['./../proj_a/images/mail/spamassassin:/tmp/spamassassin/', './../proj_a/images/mail/postfix:/tmp/postfix/', './../proj_a/images/mail/mail:/tmp/mail/'], 'ports': ['26:25', '144:143']}, 'pgsql': {'image': 'pgsql:latest', 'expose': [5432], 'ports': '5432:5432'}, 'projbapplication': {'hostname': 'application.host', 'links': ['projaredis:redis', 'projarabbitmq:rabbitmq', 'projamail:mail'], 'cgroup_parent': 'm-executor-abcd', 'labels': {'com.example.description': 'Accounting webapp', 'com.example.department': 'Finance'}, 'extra_hosts': ['somehost:162.242.195.82'], 'environment': [{'VARIABLE': 'value'}], 'working_dir': '/var/application.host', 'command': '/start.sh', 'build': 'proj_b/images/java', 'dns': ['8.8.8.8', '9.9.9.9'], 'volumes_from': ['projbsources'], 'env_file': ['proj_b/env_files/application.env', 'proj_b/env_files/rabbit.env'], 'dockerfile': 'application.yml', 'ports': ['80:80', '1098:1098'], 'external_links': ['redis_1', 'project_db_1:mysql']}, 'projaapplication': {'hostname': 'application.host', 'links': ['projaredis:redis', 'projarabbitmq:rabbitmq', 'projamail:mail', 'pgsql:db'], 'cgroup_parent': 'm-executor-abcd', 'labels': {'com.example.description': 'Accounting webapp', 'com.example.department': 'Finance'}, 'extra_hosts': ['somehost:162.242.195.82'], 'environment': {'DB_DRIVER': 'pgsql', 'DB_PORT': 5432}, 'working_dir': '/var/application.host', 'command': '/start.sh', 'build': '../proj_a/images/java', 'dns': ['8.8.8.8', '9.9.9.9'], 'volumes_from': ['projasources'], 'env_file': ['../proj_a/env_files/application.env', '../proj_a/env_files/rabbit.env'], 'dockerfile': 'application.yml', 'ports': ['81:80', '1099:1098'], 'external_links': ['redis_1', 'project_db_1:mysql']}, 'projamysql': {'build': '../proj_a/images/mysql', 'ports': []}, 'projarabbitmq': {'volumes_from': ['projasources'], 'env_file': ['../proj_a/env_files/rabbit.env'], 'command': '/start.sh', 'build': '../proj_a/images/rabbitmq', 'ports': ['15672:15672']}, 'projbredis': {'log_opt': {'syslog-address': 'tcp://192.168.0.42:123'}, 'log_driver': 'syslog', 'expose': None, 'labels': ['com.example.description=Accounting webapp', 'com.example.department=Finance'], 'extends': {'service': 'redisbase', 'file': 'redis.yml'}, 'net': 'bridge', 'ports': ['6379:6379', '127.0.0.1:6370:6373']}, 'projbsources': {'build': 'proj_b/images/sources', 'volumes': ['./proj_b:/var/application.host']}}
DEBUG:root:Save result scope in the file "docker-compose.yml"


Результуючий docker-compose.yml
pgsql: 
expose: 
- 5432
image: "pgsql:latest"
ports: "5432:5432"
projaapplication: 
build: ../proj_a/images/java
cgroup_parent: m-executor-abcd
command: /start.sh
dns: 
- "8.8.8.8"
- "9.9.9.9"
dockerfile: application.yml
env_file: 
- ../proj_a/env_files/application.env
- ../proj_a/env_files/rabbit.env
environment: 
DB_DRIVER: pgsql
DB_PORT: 5432
external_links: 
- redis_1
- "project_db_1:mysql"
extra_hosts: 
- "somehost:162.242.195.82"
hostname: application.host
labels: 
com.example.department: Finance
com.example.description: "Accounting webapp"
links: 
- "projaredis:redis"
- "projarabbitmq:rabbitmq"
- "projamail:mail"
- "pgsql:db"
ports: 
- "81:80"
- "1099:1098"
volumes_from: 
- projasources
working_dir: /var/application.host
projamail: 
build: ../proj_a/images/mail
domainname: application.host
expose: 
- 25
- 143
hostname: mail
ports: 
- "26:25"
- "144:143"
volumes: 
- "./../proj_a/images/mail/spamassassin:/tmp/spamassassin/"
- "./../proj_a/images/mail/postfix:/tmp/postfix/"
- "./../proj_a/images/mail/mail:/tmp/mail/"
projamysql: 
build: ../proj_a/images/mysql
ports: []
projarabbitmq: 
build: ../proj_a/images/rabbitmq
command: /start.sh
env_file: 
- ../proj_a/env_files/rabbit.env
ports: 
- "15672:15672"
volumes_from: 
- projasources
projaredis: 
expose: ~
extends: 
file: ../redis.yml
service: redisbase
labels: 
- "com.example.description=Accounting webapp"
- com.example.department=Finance
log_driver: syslog
log_opt: 
syslog-address: "tcp://192.168.0.42:123"
net: bridge
ports: 
- "6380:6379"
- "127.0.0.1:6371:6370"
projasources: 
build: ../proj_a/images/sources
volumes: 
- "./../proj_a:/var/application.host"
projbapplication: 
build: proj_b/images/java
cgroup_parent: m-executor-abcd
command: /start.sh
dns: 
- "8.8.8.8"
- "9.9.9.9"
dockerfile: application.yml
env_file: 
- proj_b/env_files/application.env
- proj_b/env_files/rabbit.env
environment: 
- 
VARIABLE: value
external_links: 
- redis_1
- "project_db_1:mysql"
extra_hosts: 
- "somehost:162.242.195.82"
hostname: application.host
labels: 
com.example.department: Finance
com.example.description: "Accounting webapp"
links: 
- "projaredis:redis"
- "projarabbitmq:rabbitmq"
- "projamail:mail"
ports: 
- "80:80"
- "1098:1098"
volumes_from: 
- projbsources
working_dir: /var/application.host
projbmail: 
build: proj_b/images/mail
domainname: application.host
expose: 
- 25
- 143
hostname: mail
ports: 
- "25:25"
- "143:143"
volumes: 
- "./proj_b/images/mail/spamassassin:/tmp/spamassassin/"
- "./proj_b/images/mail/postfix:/tmp/postfix/"
- "./proj_b/images/mail/mail:/tmp/mail/"
projbredis: 
expose: ~
extends: 
file: redis.yml
service: redisbase
labels: 
- "com.example.description=Accounting webapp"
- com.example.department=Finance
log_driver: syslog
log_opt: 
syslog-address: "tcp://192.168.0.42:123"
net: bridge
ports: 
- "6379:6379"
- "127.0.0.1:6370:6373"
projbsources: 
build: proj_b/images/sources
volumes: 
- "./proj_b:/var/application.host"


Хочеться звернути увагу на розділ виведення дебагера `
DEBUG:root:Redefined ports
`, який видає інформацію про автоматично змінених портах.

Исходники прикладу можна знайти на тут

Ресурси


Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.