Инфраструктура и пайплайн (CI/CD)

В этой теме у меня мало опыта, вероятность белиберды увеличена втрое.

Сейчас компетентность в сфере TestOps является таким же базовым требованием к QA-инженерам, как и написание автоматизированных тестов. Причина - в активном развитии CI/CD в проектах и необходимости QA-инженерам работать с пайплайнами

Многие начинающие автоматизаторы бросаются учить язык программирования, пишут первые учебные тесты и даже успешно делают тестовые задания по автоматизации тест-кейсов. Однако не все задумываются о том, что с этими тестами в реальной работе делать дальше. Кто их будет запускать? Когда? Каким образом? Здесь я бы хотел рассказать о пайплайне CI, месте автотестов в нем, а так же как и на чем тесты запускаются.

Прежде всего, откуда этот пайплайн взялся и что за CI/CD.

Непрерывная интеграция (Continuous Integration, CI) и непрерывная поставка (Continuous Delivery, CD) представляют собой культуру, набор принципов и практик, которые позволяют разработчикам чаще и надежнее развертывать изменения программного обеспечения.

CI/CD - это одна из DevOps-практик. Она также относится и к agile-практикам: автоматизация развертывания позволяет разработчикам сосредоточиться на реализации бизнес-требований, на качестве кода и безопасности.

Непрерывная интеграция (Continuous Integration, CI) - это практика разработки программного обеспечения, которая заключается в автоматическом и регулярном объединении (интеграции) кода, написанного разными членами команды разработчиков, в общий репозиторий или хранилище кода. Основная цель непрерывной интеграции - обеспечить раннюю обнаружение и устранение конфликтов, ошибок и проблем интеграции в процессе разработки.

Основные элементы и практики непрерывной интеграции включают:

  1. Автоматическую сборку (Build Automation): Автоматическое создание исполняемых файлов (бинарных файлов) из исходного кода после каждого коммита в репозиторий.

  2. Автоматическое тестирование (Automated Testing): Запуск автоматических тестов, включая модульные, интеграционные и функциональные тесты, для проверки работоспособности приложения после интеграции нового кода.

  3. Интеграцию сборок и тестов (Integration of Builds and Tests): Интеграция сборки и тестирования в автоматический процесс CI.

  4. Обнаружение конфликтов (Conflict Detection): Раннее обнаружение конфликтов между коммитами разных разработчиков, которые могут вызвать ошибки интеграции.

  5. Автоматическое развертывание (Automated Deployment): Автоматическое развертывание собранного и протестированного приложения на тестовых или staging-серверах.

  6. Мониторинг результатов (Monitoring of Results): Отслеживание результатов сборок и тестов с целью быстрого реагирования на ошибки.

  7. Автоматическое уведомление (Automated Notification): Автоматическое уведомление команды разработчиков о результатах сборки и тестирования.

  8. Восстановление после сбоев (Failure Recovery): Восстановление процесса CI после сбоев и автоматическое уведомление о сбоях.

  9. Использование инструментов CI/CD (CI/CD Tools): Использование специализированных инструментов и платформ для реализации процессов непрерывной интеграции и доставки (Continuous Delivery, CD).

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

Непрерывная поставка (Continuous Delivery, CD) - это практика разработки программного обеспечения, которая расширяет принцип непрерывной интеграции (CI) и включает в себя автоматическое развертывание (доставку) приложения в целевую среду (production, staging, testing и т. д.) после успешного завершения процесса непрерывной интеграции и тестирования. Основная цель непрерывной поставки - обеспечить готовность к выпуску новых версий приложения в любой момент времени.

Основные элементы и практики непрерывной поставки включают:

  1. Автоматическое развертывание (Automated Deployment): Автоматическое развертывание приложения на целевых серверах или платформах после успешного прохождения тестов в процессе CI.

  2. Автоматический пайплайн (Automated Pipeline): Создание автоматизированного пайплайна (цепи поставок), который включает в себя этапы сборки, тестирования и развертывания.

  3. Управление конфигурацией (Configuration Management): Управление конфигурациями целевых сред и ресурсов для обеспечения консистентности и воспроизводимости развертывания.

  4. Управление версиями (Version Control): Использование систем управления версиями для контроля изменений в коде, конфигурации и ресурсах приложения.

  5. Автоматическое уведомление (Automated Notification): Автоматическое уведомление команды разработки и операций о статусе процесса поставки.

  6. Мониторинг и обратная связь (Monitoring and Feedback): Установление мониторинга производительности и доступности приложения в реальном времени, а также получение обратной связи для быстрого реагирования на сбои и проблемы.

  7. Резервное копирование и восстановление (Backup and Recovery): Разработка процедур резервного копирования данных и возможности восстановления при необходимости.

  8. Разделение на этапы (Staging): Использование этапа предварительного развертывания (staging) для проверки приложения перед финальным развертыванием в продакшн.

  9. Оперативное развертывание (Rolling Deployment): Внедрение новой версии приложения путем поэтапного переключения пользователей на новую версию без простоев и перерывов в работе.

  10. Микросервисная архитектура (Microservices): Использование архитектуры, основанной на микросервисах, для более гибкой и быстрой поставки.

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

Инструменты CI/CD помогают настраивать специфические параметры окружения, которые конфигурируются при развертывании. А также CI/CD-автоматизация выполняет необходимые запросы к веб-серверам, базам данных и другим сервисам, которые могут нуждаться в перезапуске или выполнении каких-то дополнительных действий при развертывании приложения.

Непрерывная интеграция и непрерывная поставка нуждаются в непрерывном тестировании, поскольку конечная цель - разработка качественных приложений. Непрерывное тестирование часто реализуется в виде набора различных автоматизированных тестов (регрессионных, производительности и других), которые выполняются в CI/CD-конвейере (pipeline, build chain и т.д.). Зрелая практика CI/CD позволяет реализовать непрерывное развертывание: при успешном прохождении кода через CI/CD-конвейер, сборки автоматически развертываются в продакшн-окружении. Команды, практикующие непрерывную поставку, могут позволить себе ежедневное или даже ежечасное развертывание.

Типичный CD-конвейер состоит из этапов сборки, тестирования и развертывания. Более сложные конвейеры включают в себя следующие этапы:

  • Получение кода из системы контроля версий и выполнение сборки;

  • Настройка инфраструктуры, автоматизированной через подход “инфраструктура как код”;

  • Копирование кода в целевую среду;

  • Настройка переменных окружения для целевой среды;

  • Развертывание компонентов приложения (веб-серверы, API-сервисы, базы данных);

  • Выполнение дополнительных действий, таких как перезапуск сервисов или вызов сервисов, необходимых для работоспособности новых изменений;

  • Выполнение тестов и откат изменений окружения в случае провала тестов;

  • Логирование и отправка оповещений о состоянии поставки.

Например, в Jenkins конвейер определяется в файле Jenkinsfile, в котором описываются различные этапы, такие как сборка (build), тестирование (test) и развертывание (deploy). Там же описываются переменные окружения, секретные ключи, сертификаты и другие параметры, которые можно использовать в этапах конвейера. В разделе post настраивается обработка ошибок и уведомления. В более сложном CD-конвейере могут быть дополнительные этапы, такие как синхронизация данных, архивирование информационных ресурсов, установка обновлений и патчей. CI/CD-инструменты обычно поддерживают плагины. Например, у Jenkins есть более 1500 плагинов для интеграции со сторонними платформами, для расширения пользовательского интерфейса, администрирования, управления исходным кодом и сборкой.

Многие команды, использующие CI/CD-конвейеры в облаках используют контейнеры, такие как Docker, и системы оркестрации, такие как Kubernetes. Контейнеры позволяют стандартизировать упаковку, поставку и упростить масштабирование и уничтожение окружений с непостоянной нагрузкой. Есть множество вариантов совместного использования контейнеров, инфраструктуры как код (Iaas) и CI/CD-конвейеров. Архитектура бессерверных вычислений представляет собой еще один способ развертывания и масштабирования приложений. В бессерверном окружении инфраструктурой полностью управляет поставщик облачных услуг, а приложение потребляет ресурсы по мере необходимости в соответствии с его настройками. Например, в AWS бессерверные приложения запускаются через функции AWS Lambda, развертывание которых может быть интегрировано в CI/CD-конвейер Jenkins с помощью плагина.

Больше о CI/CD можно почитать тут и тут.

Этапы конвейера

В момент, когда триггерится сборка, например, когда разработчик сделал коммит в свою ветку, запускается процесс, который выполняется специально написанными скриптами и утилитами. Этот процесс состоит из нескольких обязательных шагов. Простой пример для PR:

  • При открытии каждого Pull Request, Git-сервер отправляет уведомление CI-серверу;

  • CI-сервер клонирует репозиторий, проверяет исходную ветку (например bugfix/wrong-sorting), и сливает код с кодом master-ветке;

  • Тогда запускается билд-скрипт (сценарий сборки). Например ./gradlew build;

  • Если эта команда возвращает код ответа “0”, то билд успешно выполнен. (Другой ответ означает ошибку);

  • CI-сервер направляет уведомление об успешном билде на Git-сервер;

  • Если билд был успешен, то Pull Request разрешается слить с существующим кодом. (Если не успешен, то, соответственно, не разрешается).

Ошибка в любом из шагов приводит к полному падению всей сборки. Ну и, само собой разумеется, шаги расположены в таком порядке, чтобы сужать воронку потенциальных проблем. Если Quality Gate предыдущего этапа не пройдет, то на проверку следующего уже можно не тратить ресурсы.

Пример Quality Gates, которые встроены в pipeline отсюда:

  • Сборка сервиса:

    • Проверка наличия конфигурации корректного формата;

    • Проверка стандартов оформления кода;

    • Проверка на необходимое покрытие Unit-тестами;

    • Генерации и публикации контрактов (контроль обратной совместимости).

  • Запуск Beta-тестов;

  • Обязательный code-review;

  • Сканирование на уязвимости.

Пример сферического пайплайна в вакууме отсюда:

  • Code scanning: код проверяется на соответствие общему гайдлайну (linters), уязвимости (code security) и качество (code quality);

  • Unit tests;

  • Build: этап для сборки artifacts/packages/images и т.д. Здесь уже можно задуматься о том, каким будет стратегия версионирования всего приложения. Во времена контейнеризации, в первую очередь интересуют образы для контейнеров и способы их версионирования;

  • Scan package: пакет/образ собрали. Теперь нужно просканировать его на уязвимости. Современные registry уже содержат инструментарий для этого;

  • Deploy: стадия для развертывания приложения в различных окружениях;

  • Integration testing: приложение задеплоили. Оно где-то живет в отдельном контуре. Наступает этап интеграционного тестирования. Тестирование может быть как ручным, так и автоматизированным;

  • Performance testing (load/stress testing): данный вид тестирования имеет смысл проводить на stage/pre-production окружениях. С тем условием, что ресурсные мощности на нем такие же, как в production;

  • Code Review / Approved: одним из важнейших этапов являются Merge Request. Именно в них могут производиться отдельные действия в pipeline перед слиянием, а также назначаться группы лиц, требующих одобрения перед слиянием.

Больше про build-agent можно почитать тут: TeamCity: настраиваем CI/CD в вашей команде и тут Что такое сборщик продукта, про окружения тут Создаем инфраструктуру для интеграционных тестов

Е2Е автотесты

Теперь пора поговорить непосредственно про то, что чаще всего касается рядового автоматизатора - Е2Е автотесты. Как мы выяснили выше, до прогона Е2Е сборка сначала проходит несколько шагов, а условиями запуска чаще являются ключевые моменты: commit, pull request и merge request. В моей прошлой компании был очень простой конвейер, где на коммит в фича-ветку запускались тесты разработчика, на вливание в develop стартовали критичные Е2Е тесты, а на вливание в main уже всё что есть, включая регрессионные тесты. Теперь, когда известны места автотестов в конвейере, нужно еще понять, на чем эти тесты будут прогоняться и как имея скрипт автотеста его запустить в конвейере.

Все эти моменты конфигурируются в самом CI-агенте, в jenkins это были джобы (job). Пример: How to Add your First Android Job to Jenkins. За запуск кода тестов, когда вы инициируете запуск из конвейера или локально, отвечает Test Runner. Это лишь одна из задач, в зону ответственности раннера входят:

  • подготовка окружения;

  • формирование набора тестов для исполнения;

  • запуск тестов;

  • получение результатов выполнения тестов;

  • подготовка отчетов о прохождении тестов;

  • сбор статистики.

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

Параллельно с вопросом о том, какой раннер выбрать для тестов, перед вами встает другой: а на чем лучше запускать тесты? Есть три опции:

  • Настоящий девайс.

    • Плюсы. Покажет проблемы, специфичные для конкретных устройств и прошивок. Многие производители меняют Android под себя - как UI, так и логику работы ОС. И бывает полезно проверить, что ваше приложение корректно работает в таком окружении.

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

  • Чистый эмулятор. Под «чистым» мы подразумеваем, что вы запускаете эмулятор у себя или где-то на машине, используя установленный на эту машину AVD Manager.

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

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

  • Docker-образ Android-эмулятора. Docker решает недостатки чистых эмуляторов.

    • Плюсы. Docker и соответствующая обвязка в виде подготовки и раскатки образа эмулятора - это полноценное масштабируемое решение, позволяющее быстро и качественно готовить эмуляторы и раскатывать их на все хосты, обеспечивая их достаточную изолированность.

    • Минусы. Более высокий входной порог.

В сети есть разные Docker-образы Android-эмуляторов:

Предстоит сделать сложный выбор между облачным решением, локальным решением с нуля и локальным решением на базе чего-то, если в компании есть своя инфраструктура по запуску тестов других платформ. Вся эта инфраструктура уже связывается с раннером для запуска тестов, после чего уже решаются остальные моменты, такие как вывод отчета по прогону тестов (например, Allure) и внедрение/синхронизация с TMS.

Источники:

Доп. материал:

Last updated