Ретроспектива автоматизации и изменений в процессах разработки Timeweb

    1 ноября 2017 года я стал руководителем рабочей группы разработки в отделе программных разработок Timeweb. А 12 ноября 2018 руководитель отдела спросил, когда же будет готова статья для Хабрахабр, потому что отдел маркетинга спрашивает, добровольцы кончились, а контент-план требует чего-то ещё)

    Поэтому хочу дать ретроспективу, как менялись процессы разработки, тестирования и поставки наших продуктов в течение последнего года. Про унаследованные процессы и инструменты, docker, gitlab и то, как идёт у нас разработка.

    Хостер Timeweb существует с 2006 года. Всё это время компания вкладывает много сил, чтобы предоставлять клиентам уникальный и удобный сервис, который выделял бы её среди конкурентов. У Timeweb есть свои мобильные приложения, почтовый web-интерфейс, панели управления виртуальным хостингом, VDS, партнёрской программой, свои инструменты поддержки и многое другое.

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

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

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

    О разных изменениях в инструментах и процессах за минувший год я и расскажу.

    Vagrant → docker-compose


    Проблема


    В первый рабочий день я попробовал поднять локально панели управления. На тот момент это было пять веб-приложений в одном репозитории:

    — ПУ виртуального хостинга 3.0,
    — ПУ VDS 2.0,
    — ПУ вебмастеров,
    — STAFF (инструмент поддержек),
    — Guidelines (демо стандартизированных фронтовых компонентов).

    Для запуска локально использовался Vagrant. В Vagrant запускался ansible. Для запуска и настройки потребовалась помощь коллег и около дня чистого времени. Пришлось ставить особенную версию Virtual Box (на текущей стабильной были проблемы), работа из консоли внутри виртуальной машины здорово нервировала: тривиальные команды вроде npm/composer install ощутимо подтормаживали.

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

    Решение


    Окружение для локальной разработки было переписано для запуска в контейнерах docker. Контейнеризация на базе docker — наиболее распространённое решение для изолирования окружения приложений на всех этапах его жизненного цикла. Поэтому особых альтернатив тут нет.

    Выводы


    Из плюсов:

    — локально приложение стало отзывчивее, контейнеры требуют меньше, чем ВМ,
    — запуск нового экземпляра, как показала практика, занимает считанные минуты и требует только docker(-compose) не ниже определённых версий. После клонирования достаточно выполнить:

    make install-dev
    make run-dev

    Не обошлось и без компромиссов:

    — пришлось написать shell-обвязки для докеризированных команд (composer, npm и т.п.). Они, как и docker-compose.yml, не совсем кроссплатформенны, в сравнении с Vagrant. Например, запуск под Mac требует дополнительных усилий, а под Windows, вероятно, будет проще запустить в виртуалке linux дистрибутив с docker. Но это приемлемый компромисс, т.к. команда использует только debian-based дистрибутивы, это допустимое ограничение для коммерческой разработки,
    — для поддержки виртуальных хостов локально запускается контейнер, основанный на github.com/jwilder/nginx-proxy. Не то чтобы костыль, но дополнительное ПО, о котором иногда надо помнить, хотя проблем оно не вызывает.

    Да, каждому в команде пришлось хоть немного осознать, что такое docker. Хотя благодаря упомянутым shell-скриптам и Makefile разработчики выполняют 95% своих задач, не задумываясь о контейнерах, зато в гарантированно одинаковом окружении.

    newcp-dev → cp-stands


    Эти странные словосочетания — имена машин с тестовыми стендами панелей управления, новой и старой соответственно.

    Проблема


    Рецепты ansible использовались исключительно внутри Vagrant, так что главного преимущества достигнуто не было: версии пакетов в проде и на стендах отличались от того, в чём работали разработчики.

    Несоответствие версий пакетов серверного ПО на старых стендах с тем, что было у разработчиков, приводило к проблемам. Синхронизация осложнялась тем, что у системных администраторов используется другая система управления конфигурацией, и интегрировать её с репозиторием разработчиков не представляется возможным.

    Решение


    После контейнеризации не составило большого труда расширить конфигурацию docker-compose для использования на тестовых стендах. Была создана новая машина, для развёртывания стендов по DOCKER_HOST.

    Выводы


    Разработчики теперь уверены в актуальности локального и тестовых окружений.

    TeamCity → gitlab-ci


    Проблемы


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

    Решение


    Поскольку gitlab уже использовался как хранилище для репозиториев, начать использовать его CI было не только логично, но и легко и приятно. Теперь вся конфигурация CI/CD лежит прямо в репозитории.

    Результат


    За год почти все проекты, собиравшиеся TeamCity, благополучно переехали в gitlab-ci. Мы получили возможность быстро реализовывать самые разные фичи по автоматизации CI/CD процессов.
    Нагляднее всего будут скриншоты pipelines:

    Илл. 1. feature-branch: включены все имеющиеся автоматические проверки и тесты. По завершении отправляет комментарий со ссылкой на pipeline в задачу redmine. Ручные задачи для сборки и запуска стенда с этой веткой.
    Илл. 1. feature-branch: включены все имеющиеся автоматические проверки и тесты. По завершении отправляет комментарий со ссылкой на pipeline в задачу redmine. Ручные задачи для сборки и запуска стенда с этой веткой.

    Илл. 2. develop scheduled build with code freeze (checkout:rc): сборка develop по расписанию с code freeze. Сборка образов для стендов отдельных панелей управления происходит параллельно.
    Илл. 2. develop scheduled build with code freeze (checkout:rc): сборка develop по расписанию с code freeze. Сборка образов для стендов отдельных панелей управления происходит параллельно.

    Илл. 3. tag pipeline: релиз одной из панелей управления. Ручная задача для отката релиза.
    Илл. 3. tag pipeline: релиз одной из панелей управления. Ручная задача для отката релиза.

    Кроме этого, из gitlab-ci происходит смена статусов и назначение ответственного в redmine на этапах In Progress → Review → QA, уведомление в Slack о релизах и обновлениях staging и откатах.

    Это удобно, но мы не учли один методологический момент. Внедрив подобную автоматизацию в одном проекте, люди быстро привыкают к ней. И в случае переключения на другой проект, где такого ещё нет, либо процесс отличается, можно забыть подвинуть и переназначить задачку в redmine или оставить комментарий со ссылкой на Merge Request (что тоже делает gitlab-ci), заставив, таким образом, ревьюера искать нужный MR самостоятельно. При этом просто копировать куски .gitlab-ci.yml и сопутствующего shell-кода между проектами не хочется, ведь придётся поддерживать копипасту.

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

    Pipeline duration: 80 min → 8 min


    Постепенно наш CI стал занимать неприлично много времени. Тестировщики от этого сильно страдали: каждое исправление в master к релизу приходилось ждать по часу. Это выглядело вот так:

    Илл. 4. pipeline 80 lvl min duration.
    Илл. 4. pipeline 80 lvl min duration.

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

    Наиболее долгими местами в процессе оказалась установка npm-пакетов. Без особых проблем заменили его на yarn и сэкономили в нескольких местах до 7 минут.

    Отказались от автоматического обновления staging, предпочли ручной контроль состояния этого стенда.

    Ещё добавили несколько раннеров и разделили в параллельные задания сборку образов приложений и все проверки. После этих оптимизаций pipeline основной ветки с обновлением всех стендов стал занимать в большинстве случаев 7-8 минут.

    Capistrano → deployer


    Для деплоя в production и на qa-стенд использовался (и продолжает использоваться на момент написания статьи) Capistrano. Основной сценарий у этого инструмента: клонирование репозитория на целевой сервер и выполнение там же всех задач.

    Раньше деплой запускался руками QA-инженера, располагающего нужными ssh-ключами, из Vagrant. Затем, по мере отказа от Vagrant, Capistrano переехал в отдельный контейнер. Сейчас деплой производится из контейнера с Capistrano с gitlab-runners, отмеченных специальными тегами и имеющих нужные ключи, автоматически при появлении нужных тегов.

    Проблема здесь в том, что весь процесс сборки:

    а) заметно потребляет ресурсы боевого сервера (особенно node/gulp),
    б) нет возможности держать в актуальном состоянии версии composer, npm. node и т.п.

    Логичнее производить сборку на билд-сервере (в нашем случае это gitlab-runner), а на целевой сервер выкладывать готовые артефакты. Это избавит боевой сервер от сборочных утилит и чуждой ему ответственности.

    Сейчас мы рассматриваем deployer как замену capistrano (т.к. рубистов у нас нет, как нет и желания работать с его DSL) и планируем перенос сборки на сторону gitlab. В некоторых некритичных проектах мы его уже успели попробовать и пока довольны: выглядит проще, с ограничениями не столкнулись.

    Gitflow: rc-branches → tags


    Разработка осуществляется недельными циклами. В течение пяти дней идет разработка новой версии: в develop принимаются улучшения и исправления, запланированные к выпуску на следующей неделе. В пятницу вечером автоматически происходит code freeze. В понедельник начинается тестирование новой версии, вносятся доработки, и к середине-концу рабочей недели происходит релиз.

    Раньше мы использовали ветки с именами вида rc18-47, что означает релиз кандидат 47-й недели 2018 года. Code freeze заключался в checkout rc-ветки от develop. Но в октябре этого года перешли к тегам. Тэги ставились и раньше, но постфактум, после релиза и слияния rc с master. Теперь появление тега приводит к автоматическому деплою, а заморозка это слияние develop в master.

    Так мы избавились от лишних сущностей в git и переменных в процессе.
    Сейчас мы “подтягиваем” отстающие по процессу проекты к аналогичному workflow.

    Заключение


    Автоматизация процессов, их оптимизация, как и разработка, — дело постоянное: пока активно развивается продукт и работает команда, будут и соответствующие задачи. Появляются новые идеи, как избавиться от рутинных действий: реализуются фичи в gitlab-ci.

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

    image
    Timeweb
    VDS, виртуальный хостинг, домены, серверы

    Комментарии 6

      +1
      Вывод: автоматизация хорошо, но когда она одинаковая на уровне всех команд и проектов — ещё лучше. Буду признателен уважаемой публике за идеи, как красиво организовать переиспользование подобной конфигурации.

      Легко! В последнем гитлаба появилась директива include.
      Она позволяет набрать коллекцию шаблонов пайплайнов в отдельных yaml файлах, а потом подключать те, которые подходят под сборку конкретного проекта.
      Больше не нужно бегать по десятках .gitlab-ci.yaml и вносить одни и те же коррективы
        0
        Спасибо большое за наводку на inlcude, ещё не видел этой фичи. В некоторых случаях это действительно может помочь.
        0

        Ещё не понял два момента


        • рецепты ansible. Окей. Админы не используют ansible, а вероятнее всего централизованную систему типа puppet/salt. Так они оба умеют в standalone конфигурацию. Т.е. чисто гипотетически можно натянуть сову на глобус и писать унифицированные "состояния" или "плейбуки", но это несомненно ещё один уровень сложности. Дополнительно докер, ну, никак не решает сам по себе проблему настройки окружения. Доставка кода — да. Окружение же нужно как-то сформировать. И здесь опять очень разумно взять готовый scm и написать нужные роли/состояния/манифесты.
        • капистрано. Было бы круто давать ссылки на все используемые инструменты. Это раз. Два — почему capistrano/deployer, а не octopus deploy? Ну, это как пример. И три — эта вся история с деплоером только лишь потому что докер используется только для разработки/тестов? Было бы круто контейнеры тащить и в production. Никто не говорит, что это легко и просто, но это очень сильно унифицирует среду и позволяет по сути сделать единый относительно быстрый пайплайнов, не загаживая целевую машину ненужными зависимостями. А далее можно мигрировать и на кубернетес когда-нибудь )

        Ещё очень интересно какая культура взаимодействия между отделами. Devops или не-Devops? Как распределяется ответственность между командами эксплуатации и разработки. Нет ли затыков при релизах в прод (это кто делает? Админ или всё-таки qa?)? И т.п.

          0
          1. Docker как раз обеспечивает всё необходимое для запуска приложения окружение. Или мы не поняли друг друга.
          2. Про ссылки на инструменты согласен, хорошая идея. Capistrano — наследие, «живых» свидетелей принятия решения не осталось, искать ответ «почему» смысла не имеет.
          Deployer — большая часть разработчиков владеет php, и значительная часть приложений содержит php. Это значит можно почти любому поручить поддержку/разработку CI/CD и достигнуть большего единообразия в используемых технологиях. Так проще и дешевле.

          Если коротко: администрирование и разработка — отдельные отделы. Группа тестирования (она же QA) входит в отдел разработки. За эксплуатацию отвечают администраторы, за релиз в прод QA. Тема обширная, тут тянет на отдельную «ретроспективу изменений в процессах тестирования и эксплуатации»). Культура взаимодействия, это то что сейчас хочется улучшать, и публикация на подобную тему может стать хорошей совместной работой. Большое спасибо за идею!

          PS: За последний год совсем кардинальных изменений не было, так что в целом ты ещё должен помнить что и как =)
            0
            1. несомненно обеспечивает. Но речь про другое. Условно — если разработка и тестирование в докере, а продакшн — не в докере, то докер решает только задачу ускорения разработки. В этом случае среда в production отличается от тестовой, т.к. те же пакеты, которые тянутся в Dockerfile и php-модули, нужно устанавливать на целевой сервер. И тут как раз начинается ад для ops'а.
              Понятно, что blue/green выкатку можно сделать И БЕЗ Докера. Путем создания "нулевой" ВМ, накатки на нее всех конфигов и переключения трафика на новый инстанс. Но это куча ручной работы (которая, конечно же, автоматизируется в конечном счете).
            2. Я сам capistrano потыкал. И мне этот утиль не очень понравился. Тем более, что требуется знание ruby. Парни говорят, что можно еще для тех же целей fabric использовать (python штука), но по мне — проще от всего этого отказаться и по максимуму писать в гитлабовском yaml'е всю логику. Опять же — в случае докера процесс раскатки может быть очень простой: docker pull обновленные образы, потом docker-compose up -d и полетели. Ну, надо еще про миграции не забыть и т.п.Я бы мог еще посоветовать https://octopus.com/pricing/overview, но он с какого-то момента стал, IMHO, сильно платный, без особых бенефитов.

            Короче. Deployer — понял, унификация среды, чтобы стоимость поддержки разрабами (которые знают php) была не очень большой.


            «ретроспективу изменений в процессах тестирования и эксплуатаци

            ну, будем ждать ))))

          0
          .

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

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