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 файла по своей сути является небольшим конфигом, который описывает как именно два или более проектов будут стартовать вместе.

Скажем у нас есть 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 paths with 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`, который выдает информацию об автоматически измененных портах.

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

Ресурсы

ссылка на оригинал статьи http://habrahabr.ru/post/274581/


Комментарии

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

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