Команда, в которой я работаю, использует микросервисную организацию в проектах.
У каждого микросервиса свой репозиторий. Каждый микросервис это docker контейнер.
Для среды разработки, чтобы запустить все вместе, мы используем docker-compose.


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


Мы столкнулись с двумя проблемами:


  1. При первоначальном разворачивании среды разработки, приходится обьяснять программисту, либо писать скрипт инициализации, который склонирует и создаст необходимую иерархию папок из нескольких репозиториев.
  2. docker-compose не может собрать приложение, а потом упаковать в идижд. он умеет только запускать docker build.

Для решения этих проблем мы сделали управляющий скрипт docker-project, который оказался очень удобным в работе.
Чем мы и хотим поделиться с open-source сообществом.


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


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


Что бы не плодить дополнительных конфигов, docker-project расширяет функцинал docker-compose.yml файла, добавляя в него две новые функции:


  1. Скачивание/обновление исходного кода сервисов из репозиториев
  2. Запуск указанных команд относительно папок с исходным кодом

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


Пример такого docker-compose.yml:


version: "2"
services:
    users:
        image: vendor/users
        expose:
            - 80
        labels:
            project.git: "https://github.com/vendor/users.git"    # ссылка на репозиторий
            project.git.branch: cool-feature        # нужный бранч, по умолчанию - мастер
            project.build: make                          # добавляем команду для сборки имиджа

    web-ui:
        image: vendor/web-ui
        ports:
            - 80:80
        labels:
            project.git: "https://github.com/vendor/web-ui.git"
            project.build: make

# Готовые имиджи используются для сервисов, код которых не приходится менять
    mysql:
        image: mysql
        expose:
            - 3306

Работа с проектом в этом случае выглядит так:


git clone ... myproject
cd myproject
docker-project update # клонирует указанные репозитории в структуру apps/{vendor}/{repo}
docker-project build # собирает имиджи тем где указана команда build, вполне возможно вам это не нужно
docker-compose up # далее обычная работа обычная работа.
...

docker-project update # повторный вызов делает git pull

#Коммит кода в конкретном репозитории сервиса происходит обычным образом.
cd apps/vendor/user
git add .
git commit -m "...."
git push

Внутри docker-project — это просто враппер над git и bash, упрощающий развертывание проекта из нескольких репозиториев, а так же его сборку и других команд, которые надо выполнить над несколькими репозиториями одновременно. Вызов команд идет в интерактивном режиме.


Не забудьте добавить /apps/ в .gitignore


Если внутри сервиса сменить бранч на другой, он таким и останется. Повторный update делает только git pull.


Можно добавлять свои команды.
Например, если определить для сервисов команду test, то можно прогнать юнит тесты у нескольких сервисов одной командой docker-project test.


Вызов docker-project команда. вызовут запуск только у сервисов где эта команда определена.


Посмотреть достпные достпные для данного проекта команды можно вызвав docker-project status


Кроме того docker-project позволяет:


  • Ограничить выполнение выбранными сервисами --app
  • Запустить произвольную шелл команду в папках с проектом shell
  • Добавить в конец выполняемой команды произвольную строку --extra

Вызов docker-project без параметров показывает шпаргалку по командам:


$ docker-project
docker project management tool 0.0.1-alfa

Usage:
  docker-project <command> <arguments>

Commands:
  update - clones or pulls application source
  shell - uses extra parameter to run shell command for each app
  status - prints current services with repos and their commands
  help - prints help
  your_command - defined as label for the service (example: labels: project.test: make test)

Arguments:
  Full name    | Short | Default            | Note
-------------------------------------------------------
  --file         -f      docker-compose.yml   Alternative config file
  --apps         -a      apps                 Applications sources folder
  --extra        -x                           Extra parameters passed to command

Устанавливается в систему довольно тривиально:


curl -O -L https://github.com/webreactor/docker-project/releases/download/0.0.1-alfa/docker-project
chmod a+x docker-project
sudo cp docker-project /usr/local/bin/

Либо можно собрать из исходников:


git clone https://github.com/webreactor/docker-project.git
cd docker-project
make
sudo make install

Для работы требуются установленые рнр-cli и git.

Поделиться с друзьями
-->

Комментарии (40)


  1. hanovruslan
    26.07.2016 08:25
    +1

    Интересная штука! Как считаете, есть смысл писать подобные приложения на python\golang ?


    1. ecto
      26.07.2016 08:32
      +3

      Думаю да, меньше зависимостей.
      Питон по умолчанию в системе есть. golang собирается в статический бинарник.
      Если бы я хорошо знал эти языки, то скорее использвал бы их. РНР все-таки не у всех стоит.
      У меня есть в планах переписать эту утилиту на питоне.


  1. andyceo
    26.07.2016 11:15
    +4

    Подобные приложения уже написаны, Ansible называется.

    Я, например, написал пару ролей, первая configurator делает то же самое с папками, файлами, конфигами.

    Вторая docker ставит докер и docker-compose, если они не установлены, и управляет контейнерами.

    В одном Ansible-репозитории можно держать все нужные настройки для того или иного хоста разработчика или тестировщика, и/или продакшена, и с помощью Ansible это можно натравить на много хостов сразу, или просто на локалхост.


    1. hanovruslan
      26.07.2016 17:14
      +1

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


      Правда, всё выше сказанное, не про замену сабжа этого поста на ансибл, а просто про ансибл.


      1. andyceo
        27.07.2016 16:08

        Да, вот только у ансиблса задачи сильно фрагментированы обычно, а плейбуки — что-то типа монолита, причем по несколько штук на разные случаю


        Используйте теги и будет счастье! --tags='docker-orchestrate', --skip-tags='ssl' например.

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


        Согласен полностью, в свое время просто исплевался, работало нестабильно. Сейчас вроде более-менее устаканилось все.


        1. hanovruslan
          27.07.2016 20:05

          имхо, от тэгов как правило все только усугубляется. по крайней мере это все сложно использовать если не заворачивать в зонтичные скрипты\бинари\пакетные_операции.


          Сам по себе ансибл — очень крутая штука. просто кривая обучения слегка недружелюбна )))


          1. VolCh
            28.07.2016 08:13

            Да вроде вполне нормально, за пару дней разобрался. По сути достаточно лишь нескольких сервисов типа copy, template и shell плюс register, when и whith_items


          1. andyceo
            28.07.2016 11:51

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


    1. ecto
      26.07.2016 18:28

      Мне кажется подход


      git clone
      make 

      более привычен.


      Удаленная установка на машину разработчика — возможно это удобно в офисе.


      1. andyceo
        27.07.2016 16:09

        Удаленная установка на машину разработчика — возможно это удобно в офисе.


        Да, а еще и на тест-сервер, и на прод-сервер.


        1. symbix
          27.07.2016 22:43

          git clone и сборка на продакшн-сервере?

          Мсье знает толк в извращениях…


          1. andyceo
            28.07.2016 11:47

            Это вы ошиблись, про git clone не я писал. Напротив, у нас даже накатывание проекта для разработчика происходит через докер.


            1. symbix
              28.07.2016 17:18

              Сорри, не так понял ваш ответ :)

              А, кстати, как делаете для разработчиков? Через docker-machine? У меня docker-machine для parallels как-то толком не завелся в свое время, пользуюсь vagrant-ом.


              1. andyceo
                28.07.2016 20:23

                Я создал образ andyceo/phpdevenv

                Наш образ внутри компании немного отличается, но тем не менее суть ясна — там находятся все нужные для работы инструменты. Вот этот образ и разворачиваем разработчику через Ansible, он выгружает туда код из IDE, и запускает сборку (скрипт для сборки проекта находится в самом проекте).

                Ну а продакшен отличается, есть образы-сборщики и Докерфайл для продакшен-сборки. Образ-сборщик поднимается, в нем настроен Docker-in-Docker, и собирает проект, создает продакшен-имадж, который потом разворачивается для тестировщиков и далее, через Ansible.

                Подробнее про andyceo/phpdevenv уже отвечал здесь

                Если есть еще вопросы, отвечу.


                1. symbix
                  28.07.2016 20:46

                  А, я немного про другое. Когда пинг до серверов в США (я работаю на удаленке) ~200ms, а билд надо запускать после любого изменения в коде (та же сборка typescript/babel), синхронизировать каждый раз — несколько некомфортно, хочется всю радость поднять локально. Я сейчас для этого использую vagrant, но поскольку я один с такими проблемами, приходится образы клепать самому.

                  Не, понятно, что можно запустить в parallels линукс, а внутри него уже запускать докеры, но оверхедненько выходит, ноутбук то не резиновый…


          1. ecto
            28.07.2016 21:19

            Аналогично, я тоже имел ввиду про git clone на машине разработчика.
            А если это открытый проект или нет просто доступа к машине разработчика, он удаленный.


            docker-project используется, что бы развернуть приложение из нескольких репозиториев и собрать его.


            Получается привычный, классический флоу:
            git clone
            make
            и работает


            Когда тебе что-то разворачивают на машине. Это ненужная зависимость от людей.


            Установить себе на машину нужную версию docker/compose обычно люди могут без проблем.
            Если не могут, сами тогда Ансибл уже в помошь, но на уровне установки рантайма, а не самого приложения.


            Собирать проект в продакшен докер файле тоже не лучший вариант.
            Лучше разделять процессы сборки и упаковки, что бы не тянуть утилиты разработчика в продакшен.


            Как из микросервис из репозитория в продакшен — это отдельный процесс опять же.


  1. forty_two
    26.07.2016 11:40

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


    1. ecto
      26.07.2016 18:30

      Спасибо за отзыв.


      У меня есть в планах переписать ее на питоне. Лишние зависимости никто не любит.


      Она нужна была срочно для команды в 20 человек.
      Поэтому я взял язык, который знаю лучше. А утилита внезапно всем понравилась.


  1. Sirian
    26.07.2016 11:49

    Почему бы не использовать Symfony Console Component для написания команд?


    1. ecto
      26.07.2016 21:10

      Мне он не очень нравится.


  1. yvm
    26.07.2016 14:04

    >Для работы требуются установленные рнр-cli и git
    почему бы вам не сделать контейнер для вашего docker-project


    1. ecto
      28.07.2016 21:20

      Он будет переписан на питоне и поэтому будет запускаться без дополнительных записимостей.


  1. Yozi
    26.07.2016 16:45

    Как насчёт варианта с вынесением кода контейнеров на хост машину с использованием mount + git submodule в репозитории с .docker-compose.yml?

    Благодаря mount не требуется пересборка контейнера в случае обновления исходников.
    submodule позволяет логически связать версии микросервисов.

    И не нужно писать своих скриптов


    1. VolCh
      26.07.2016 17:21

      Если машин несколько и на каждой свой набор сервисов (пускай даже статический для простоты), то как оркестрировать?


      1. Yozi
        26.07.2016 20:05

        *оркестрировать*, конечно, специализированным ПО, я предпочитаю для этого Ansible, о котором здесь уже много комментариев. Моя позиция лишь в том, что следует избегать усложнения Dockerfile


        1. ecto
          26.07.2016 20:44

          Мы используем Ansible для провижена серверов, которые образуют облако Mesos/Marathon.
          А оркестирует контейнеры уже соотвественно Marathon.


          С содержимым Dockerfile эта часть вообще уже никак не связана (как и docker-project). Контейнер проектируется максимально закрытым и условно не важно, где и кто его запускает.


          Я не занимаюсь администрированием, Ansible в случае выпадения сервера может автоматом перекинуть контейнеры на другой более свободный сервер?


          У нас эта задачу решает Marathon.


          Опять же, это не значит вы делаете неправильно. У нас разные проекты, нагрузка и вводные.
          Вообще, работающих решений обычно несколько, и что из них лучшее зависит не только от задачи но и исполнителя.


    1. ecto
      26.07.2016 18:11

      Для дев среды мы какраз так и делаем — маунтим папки в контейнеры и не пересобираем их каждый раз.
      Но как вы собираете приложение в этом случае?

      Мы например какраз делаем docker-project build — он собирает фронтенд, запускает композер и т.д. не собирая имиджи
      Это прям наш юзкейс вы описали.

      Перед деплоем либо идет работа с индивидуальным приложением — тут все стандартно.
      Либо если надо собрать все имиджи разом это уже может сделать docker-compose.

      git submodule — очень не удобны в работе, по крайне мере нам.


  1. VolCh
    26.07.2016 17:22

    А как происходит установка зависимостей (тем же composer'ом) в контейнер? Особенно, если в зависимостях есть приватные репозитории?


    1. ecto
      26.07.2016 18:26

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

      Если интересно раскажу подробнее.

      У нас есть базовый имидж в котром установлен рнр с нужными настройками.
      От базового имиджа наследуюется билд имидж — в который добавляеются nvm, grunt, composer и т.д.
      при сборке приложения (не путаь со сборкой имиджа)
      1 вызывается просто make (через тот же docker-project build)
      2 который запускает билд контейнер с подмонтированым кодом, где идет его сборка. — запускается composer и т.д.
      3 Билд контейнер сам выключается
      4 имеем папки с собраным приложением
      6 если мы в дев среде — эти папки напрямую подмонтированы в уже работющие контейнеры и их обновлять каждый раз не надо.
      если мы делаем имиджи для продакшена, то начинаем сборку имиджей, докер-композером или индивидуально.

      Базовый имидж обеспечивает соотвествие среды билда и работы, а так же ускоряет развертывание имиджа в продакшене.
      В продакшене используется mesos, marathon, consul, поэтому тут уже нужны собраные имиджи, без монтирования.

      Если что-то не очень хорошо описал, спрашивайте, буду рад помочь.


      1. VolCh
        26.07.2016 19:22

        Спасибо, понятно в принципе.

        Я сам пытался подобный флоу реализовать, но столкнулся проблемой, что если в composer.json/package.json/… указаны в зависимостях приватные git-репозитории, то composer/npm install очень сложно заставить отработать без помещения приватных ключей в билд-контейнер.


        1. Yozi
          26.07.2016 20:27

          Ну как приватных, есть же ключи развёртывания у того же bitbucket, хорошо подходят для таких целей


          1. VolCh
            29.07.2016 02:25

            Даже ключи развёртывания помещать в контейнер (включая промежуточные слои, пускай даже в конечном нет) мне кажется не лучшей идеей.


            1. alexkunin
              29.07.2016 03:39

              А если ключ монтировать как файл внутрь контейнера, задавая параметром докеру? Я так и делаю, но может что-то упускаю, и метод этот на самом деле «не очень».


              1. VolCh
                30.07.2016 14:42

                Во-первых, проблемы с правами доступа — у ключей они сильно ограничены обычно (400), а их использование при сборке/запуске контейнера может быть от произвольного пользователя (многие рекомендуют создавать пользователей в контейнерах на каждый чих, а не гонять всё от рута) с маппингом которого на текущего очень непросто — возможно, но сложно.

                Во-вторых, проблемы с распределенными окружениями — монтирование работает только если и клиент, и демон на одном хосте запускаются. Со swarm-кластерами или просто с удаленными docker-хостами не работает.

                Ещё вариант передавать значения через переменные окружения или билд-параметры, но везде пишут, что не безопасно. Вообще интересная ситуация с секретами в докере — везде в доках пишут в описании разных возможностей «не рекомендуется для передачи секретов», а рекомендуемого способа нет.


                1. alexkunin
                  30.07.2016 15:01

                  Во-первых, проблемы с правами доступа — у ключей они сильно ограничены обычно (400)…
                  … создавать пользователей в контейнерах на каждый чих...
                  Ключ нужен для гит клона, подписи и т.д. Можно запускать эти операции под рутом, а сам билд (compose update, npm install) под чем-то более ограниченным.

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

                  Ещё вариант передавать значения через переменные окружения или билд-параметры...
                  Это устраняет ограничение на распределенный билд. Но лично я категорически против того, чтобы приватные ключи покидали мой хост. Я и в контейнер их монтирую только потому, что он мною отнаследован от официального «папы».


                  1. VolCh
                    30.07.2016 19:33

                    сам билд (compose update, npm install) под чем-то более ограниченным.

                    Так именно для установки зависимостей эти ключи прежде всего и нужны. Клон как раз можно сделать на хосте, клиент передаст серверу или кластеру COPY/ADD с клиентского компа и проблем нет. Но вот установить зависимости так уже проблематично. Одно дело иметь на клиентском хосте git, а совсем другое — развёрнутое билд-окружение.
                    Я говорю о монтировании ключей исключительно в билдер-контейнер. Можно в сворме билдить, конечно, но пока такой юз-кейз не придумал еще.

                    Я тоже о нём. Но даже без сворма, докер-демон может крутится на виртуалке (например через docker-engine) и пробрасывать локальные файлы и через виртуалку, и через контейнер ещё сложнее.
                    Но лично я категорически против того, чтобы приватные ключи покидали мой хост.

                    Само покидание может не особо критично для меня лично, но при условии гарантий, что нигде их в открытом виде не хранится. В переменных же они, не сильно утрируя, всему миру видны, как минимум, всё время работы контейнера, если специальных мер не принимать по ограничению времени доступности. Но это лишь ограничение будет, но не исключение.


                    1. alexkunin
                      30.07.2016 21:09

                      Так именно для установки зависимостей эти ключи прежде всего и нужны. Клон как раз можно сделать на хосте, клиент передаст серверу или кластеру COPY/ADD с клиентского компа и проблем нет. Но вот установить зависимости так уже проблематично. Одно дело иметь на клиентском хосте git, а совсем другое — развёрнутое билд-окружение
                      Ну, в общем да, в моей работе приватных пакетов нет, только гит с кодом и зависимости от опен соурса.

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

                      Но в общем паранойка остается, потому в момент недоверия установщику он имеет доступ к вашим исходникам и уже установленным приватным пакетам.

                      Ну, и все же гит я бы назвал частью билд-окружения, и тогда в реквайрментах будет исключительно докер. И сделал бы я это потому, что часто встречался с проблемой «работаем на том, на чем можем»: убунта 12.04, или OS X, или что-то еще, где гит ставится не простым «apt-get install git», а какой-то серией па и коленцев бубном.
                      Я тоже о нём. Но даже без сворма, докер-демон может крутится на виртуалке (например через docker-engine) и пробрасывать локальные файлы и через виртуалку, и через контейнер ещё сложнее.
                      Вы, наверное, про docker-machine.

                      Я большей частью под OS X сижу, докер крутится в VirtualBox под управлением docker-machine. Лично для меня как пользователя такой связки никаких проблем нет, все прозрачно монтируется. Вы о какой-то конкретной проблеме из вашей практики говорите?
                      Само покидание может не особо критично для меня лично, но при условии гарантий, что нигде их в открытом виде не хранится.
                      О, ваша паранойя еще маленькая, ей расти нужно. :) Собственно, я к такой фигне легко отношусь, а начальник — нет. Поэтому в нашем случае ключ никогда не покидает хост (ну, типа).
                      В переменных же они, не сильно утрируя, всему миру видны
                      Нет, не вариант, конечно. Разве что какой-то общеизвестный ключ который и так у всех есть. Хотя такой и в контейнер запилить не было бы проблемой.


                      1. VolCh
                        31.07.2016 15:15

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

                        Ну вот как-то так проблема и решается. Разные варианты использования ssh-аgent, монтирования SSH_AUTH_SOCK в контейнер, контейнеры/сервисы с хранилищами ключей, как отдающие сами ключи (может быть генерируя одноразовые), так и проксирующие запросы, как с аутентификацией (хотя бы по IP), так и без неё. Но все (из тех, что я нашёд) либо имеют ограничения по применению (типа клиент и демон должны быть на одном хосте), либо содержат прорехи в безопасности мало отличимые от простого копирования ключа в контейнер (так же не рекомендуются в манах докера).

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


                        Да, докер-машина, конечно. Были проблемы с монтированием того же ~/.ssh/id_rsa с правами 400 в контейнер через виртуалку, когда все три пользователя (локальный, докер-демона в виртуалке и процеса в контейнере разные и не руты).


  1. m8rge
    28.07.2016 18:48

    Почему не использовать git submodules?


    1. ecto
      28.07.2016 22:09

      1 Они привязаны к конкретному коммиту и именение любого из модулей вызывает необходимость коммита в проджект репозиторий
      2 Не все разработчики знают, как с ними работать = больше стоимость разработки


      есть еще git subtree с приблизтельно темиже проблемами.


      Я не рассматриваю проект репозиторий, как мета приложение, это скорее среда разработки.
      Она не должна меняться если происходят внутренние изменения в микросервисах.