Собираем Docker-образы для CI/CD быстро и удобно вместе с dapp (обзор и видео)

    Это вторая публикация, созданная по мотивам моих выступлений на конференциях. Первая была общей и посвящена обзору практик Continuous Delivery с Docker. Новая основана на более прикладном докладе «Собираем Docker-образы быстро и удобно», который прозвучал 8 ноября на конференции HighLoad++ 2016 в секции «DevOps и эксплуатация».



    Как и в прошлый раз, если у вас есть возможность потратить ~час на видео, рекомендуем посмотреть его полностью (см. в конце статьи). В ином случае — представляем основную суть в текстовом виде.

    Что мы хотим от Docker-образов?


    Наши требования в контексте процессов CI/CD (Continuous Integration, Continuous Delivery и Continuous Deployment) таковы:

    1. Компактный объём. Причина — в рамках CD нужно собирать очень часто и много (каждый коммит), что уже в скором времени может привести к потребности в огромных хранилищах. Мы приняли у себя норму образа в <200 Мб (базовый образ с системой Ubuntu занимает 130 Мб).
    2. При коммите объёмом в 10 Кб мы хотим видеть аналогичное прибавление в размере образа, а не добавленную полную величину образа.
    3. Быстрая сборка образов — за 10 секунд.
    4. Возможность использования сгенерированных образов как конечного продукта для разных площадок (от тестовых до production).


    dapp вместо Dockerfile


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

    Поэтому мы написали свою утилиту — dapp (на Ruby, распространяется под MIT License). На данном этапе она умеет заниматься только сборкой образов, а в планах её развития — поддержка полного цикла CI/CD. При проектировании и реализации dapp отдано предпочтение простоте использования и быстроте/эффективности.

    Конфигурация для образов, собираемых с dapp, описывается в Dappfile по принципу Один репозиторий → Один проект → Один Dappfile. Форматом этого файла на данный момент является Ruby DSL, но мы планируем перейти на более простой и привычный YAML.

    Какие же возможности приносит dapp?

    1. Стадии и кэш


    В dapp реализован паттерн с четырьмя стадиями сборки Docker-образа:

    1. before_install: настройки ОС и т.п., на что (по итогам нашего анализа десятков разных проектов) приходится <1% коммитов;
    2. install: прикладные зависимости — около 5% коммитов;
    3. before_setup;
    4. setup: конфиги — около 2%.

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


    2. Внешний контекст


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

    Пример использования — каталог /var/lib/apt: его содержимое после выполнения apt-get update необходимо и для следующих сборок, но не нужно внутри самого образа (дополнительный объём данных).

    3. Git


    Поддержка изменений из Git тоже сделана в соответствии с идеями оптимизации и гибкости. При первой сборке образа в него добавляются все исходники приложения (git archive), а при последующих — только дельты, т.е. Git-патчи (git patch apply). Содержимое патчей кэшируется для лучшей производительности.



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


    4. Артефакты


    Порой для сборки проекта («компиляции» каких-то его компонентов) требуются большие сторонние инструменты, которые конечным приложением потом не используются (т.е. хранить в образе не нужно). Это относится не только к сборке исходников на языках типа Си, но и, например, генерации ассетов с помощью Node.js. Проблема реализована с помощью так называемых «артефактов». При выполнении команды dapp build создаётся дополнительный Docker-образ с артефактом (т.е. сторонним инструментом, требуемым для сборки образа). Файлы этого артефакта добавляются в реальный (конечный) образ из дополнительного с помощью внешнего контекста. Для артефактов тоже поддерживается кэш.

    5. Поддержка Chef


    Модульность при сборке образов приносит значительную пользу, но её реализация в рамках shell (Bash) — неблагодарное занятие. Зато она прекрасно сделана в системах управления конфигурациями: Chef → Berkshelf, Puppet → Librarian… Мы используем Chef, поэтому добавили его поддержку в dapp. Эта поддержка означает возможность выполнять рецепты внутри создаваемого Docker-образа.

    Технически всё организовано так, что в специальный каталог внутри Git-репозитория (.dapp_chef) помещается Chef cookbook. При выполнении команды dapp build всё необходимое собирается в директорию, монтируемую внутрь Docker-контейнера. Дополнительно в контейнер монтируется и полностью установленный Chef (chefdk). Далее запускается Chef, который настраивает контейнер по cookbook. Полученный в итоге образ настроен по рецептам, но не содержит ни chefdk, ни cookbook.


    6. Несколько образов


    Принятый в Dappfile формат позволяет описывать сразу множество образов в одном файле.

    dapp как Open Source


    Мы используем и развиваем dapp уже почти 2 года и действительно хотим сделать из него полезное Open Source-решение, которое будет помогать вам настраивать свои процессы CI/CD и решать сопутствующие задачи. Напоминаем, что исходный код доступен на GitHub, там же с радостью принимаются issues и pull requests. Документация по dapp на русском языке доступна здесь.

    Кстати, у нас есть работа!


    Для dapp мы ищем уникального энтузиаста, который мечтает стать технологическим евангелистом. Если у вас есть реальный интерес в подобных инструментах, опыт в DevOps, управлении проектами и написании грамотных технических текстов — не откладывайте в долгий ящик и обязательно напишите нам на info@flant.ru.

    Видео и слайды


    Видео с выступления (около часа) опубликовано в YouTube (по ссылке воспроизведение начинается с 13-й минуты, где прекратились технические проблемы с микрофоном и завершена вводная часть, повторяющая общий доклад по CI).

    Презентация доклада:

    Флант 237,26
    Специалисты по DevOps и высоким нагрузкам в вебе
    Поделиться публикацией
    Комментарии 24
    • 0

      Никак не пойму зачем образ операционки в каждом имадже?

      • 0

        Этим достигается атомарность контейнера.

        • 0

          Он и так атомарен, если просто положить в него необходимые сошники.

          • 0
            Было бы интересно почитать подробнее. Расскажешь?
            • 0

              Ну рокетсайнс здесь нету. Бинарный исполняемый файл может быть двух видов: статически слинкованым или динамически. В случае статики никакие дополнительные библиотеки ему не нужны. И тогда докер файл может быть таким https://github.com/tianon/dockerfiles/tree/master/true В случае динамики — необходимо покопаться.


              Вот так выглядят зависимости bash:


              $ ldd /bin/bash
                  linux-vdso.so.1 =>  (0x00007ffc731ea000)
                  libtinfo.so.5 => /lib/x86_64-linux-gnu/libtinfo.so.5 (0x00007f3b391fa000)
                  libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f3b38ff6000)
                  libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f3b38c2c000)
                  /lib64/ld-linux-x86-64.so.2 (0x0000559c9ee28000)

              Если вам нужен баш в докере, то в общем-то достаточно найти и положить эти сошники в образ, проверив, что у них самих нет зависимостей. Можно пойти более корректным, но сложным путем, собирая через configure&&make&&make install нужные зависимости. Это длиннее, но если есть CI решаемо. И главное повторяемо, поскольку инструкции у вас зашиты в CI. Тогда можно взять Alpine для базы.


              Разумеется это сложно делать для большого, стороннего проекта. Например Nginx или Python. Но это отлично работает для всего, что написано на golang, поскольку у него в зависимостях часто вообще ничего нет. А собрав однажды базовый образ для php/python/ruby/java вы можете без проблем запаковывать ваше приложение, написанное на этих язык. Обновление этого образа будет уже не таким сложным.

      • НЛО прилетело и опубликовало эту надпись здесь
        • 0

          Хотя бы чтобы уменьшить количество случаев "а локально у меня работает", если у большинства разработчиков стоит Ubuntu.

          • НЛО прилетело и опубликовало эту надпись здесь
            • 0

              Даже в опросах на Хабре он ещё не победил, а только начинает проникновение со скрипом.


              Я уже сам столкнулся с тем, что для php есть ppa под ubuntu для расширений, а для центоси альтернатива — сборка из исходников. Что-то мне кажется, для alpine тоже не будет готового пакета, хорошо если сам php из исходников собирать не потребуется.


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

            • 0
              Локально как бы должен быть примерно тот же докер образ. Примерно, потому что отличие может быть например в том, что для интерпретируемых языков локально исходники проекта лучше подмонтировать как volume, а не копировать в образ.
          • 0
            Изучал месяц где-то Chef, теперь бегу от него волосы назад каждый раз когда слышу это слово.

            Я на боевых серверах сейчас использую Alpine образы, к примеру архив PHP-FPM образа примерно 80Мb. Он же и выкатывается при релизе, никаких репозиториев.

            Бинарные зависимости собираются из временного контейнера и потом экспортируются. Нужно это опять таки чтобы уменьшить размер образа.

            Все верно, то что происходит в 1% случаев можно сделать руками, без Chef огородов.
            • +1
              Вот тогда-то и не нужно брать Chef.
              А то получается, что делаем «hello world», берём для этого инструмент энтерпрайз уровня и потом волосы <вставить_что_они_делают_при_упоминании_шефа>

              Но ближе к существу, мне действительно интересно, что не так с шефом, когда с ним начинаешь знакомиться. Что лично вам не понравилось?
              • 0
                Слишком большой порог вхождения. Плюс зачастую под него нет адекватных задач, так чтобы не получилось забивание гвоздей микроскопом, в отличие от того же Docker.
                • 0

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

            • +1
              Отличный инструмент, буду пробовать, как раз то, что было нужно)
              P.S. по поводу Chef — мне тоже не нравится, я вот например Ansible люблю. Душу как то он сразу согрел, так и не могу от него отказаться. Хотя пробовал Chef/Puppet/SaltStack/Ansible.
              • +1

                У нас в планах поддержка Ansible, будем пробовать успеть до июля.

                • 0
                  Тогда вообще будет супер. Будем ждать выступлений по поводу Ansible от нового вашего тех.евангелиста)
                  • 0
                    от нового вашего тех.евангелиста)

                    М? О чем речь?

                    • 0
                      Для dapp мы ищем уникального энтузиаста, который мечтает стать технологическим евангелистом. Если у вас есть реальный интерес в подобных инструментах, опыт в DevOps, управлении проектами и написании грамотных технических текстов — не откладывайте в долгий ящик и обязательно напишите нам на info@flant.ru.
                      Я про это. Слишком тонко я просто написал
                      • 0
                        Не слишком — я, например, распознал :-) Но откликов пока не было, так что — всё ещё очень ищем евангелиста!
                        • 0
                          Для откликов наверное все стесняются, «типа куда мне», ну как я например)

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

              Самое читаемое