Инфраструктура и пайплайн (CI/CD)
В этой теме у меня мало опыта, вероятность белиберды увеличена втрое.
Сейчас компетентность в сфере TestOps является таким же базовым требованием к QA-инженерам, как и написание автоматизированных тестов. Причина - в активном развитии CI/CD в проектах и необходимости QA-инженерам работать с пайплайнами
Многие начинающие автоматизаторы бросаются учить язык программирования, пишут первые учебные тесты и даже успешно делают тестовые задания по автоматизации тест-кейсов. Однако не все задумываются о том, что с этими тестами в реальной работе делать дальше. Кто их будет запускать? Когда? Каким образом? Здесь я бы хотел рассказать о пайплайне CI, месте автотестов в нем, а так же как и на чем тесты запускаются.
Прежде всего, откуда этот пайплайн взялся и что за CI/CD.
Непрерывная интеграция (Continuous Integration, CI) и непрерывная поставка (Continuous Delivery, CD) представляют собой культуру, набор принципов и практик, которые позволяют разработчикам чаще и надежнее развертывать изменения программного обеспечения.
CI/CD - это одна из DevOps-практик. Она также относится и к agile-практикам: автоматизация развертывания позволяет разработчикам сосредоточиться на реализации бизнес-требований, на качестве кода и безопасности.
Непрерывная интеграция (Continuous Integration, CI) - это практика разработки программного обеспечения, которая заключается в автоматическом и регулярном объединении (интеграции) кода, написанного разными членами команды разработчиков, в общий репозиторий или хранилище кода. Основная цель непрерывной интеграции - обеспечить раннюю обнаружение и устранение конфликтов, ошибок и проблем интеграции в процессе разработки.
Основные элементы и практики непрерывной интеграции включают:
Автоматическую сборку (Build Automation): Автоматическое создание исполняемых файлов (бинарных файлов) из исходного кода после каждого коммита в репозиторий.
Автоматическое тестирование (Automated Testing): Запуск автоматических тестов, включая модульные, интеграционные и функциональные тесты, для проверки работоспособности приложения после интеграции нового кода.
Интеграцию сборок и тестов (Integration of Builds and Tests): Интеграция сборки и тестирования в автоматический процесс CI.
Обнаружение конфликтов (Conflict Detection): Раннее обнаружение конфликтов между коммитами разных разработчиков, которые могут вызвать ошибки интеграции.
Автоматическое развертывание (Automated Deployment): Автоматическое развертывание собранного и протестированного приложения на тестовых или staging-серверах.
Мониторинг результатов (Monitoring of Results): Отслеживание результатов сборок и тестов с целью быстрого реагирования на ошибки.
Автоматическое уведомление (Automated Notification): Автоматическое уведомление команды разработчиков о результатах сборки и тестирования.
Восстановление после сбоев (Failure Recovery): Восстановление процесса CI после сбоев и автоматическое уведомление о сбоях.
Использование инструментов CI/CD (CI/CD Tools): Использование специализированных инструментов и платформ для реализации процессов непрерывной интеграции и доставки (Continuous Delivery, CD).
Целью непрерывной интеграции является ускорение процесса разработки, повышение качества кода и уменьшение рисков, связанных с интеграцией и слиянием кода. CI также способствует автоматизации процессов и улучшению сотрудничества между членами команды разработки.
Непрерывная поставка (Continuous Delivery, CD) - это практика разработки программного обеспечения, которая расширяет принцип непрерывной интеграции (CI) и включает в себя автоматическое развертывание (доставку) приложения в целевую среду (production, staging, testing и т. д.) после успешного завершения процесса непрерывной интеграции и тестирования. Основная цель непрерывной поставки - обеспечить готовность к выпуску новых версий приложения в любой момент времени.
Основные элементы и практики непрерывной поставки включают:
Автоматическое развертывание (Automated Deployment): Автоматическое развертывание приложения на целевых серверах или платформах после успешного прохождения тестов в процессе CI.
Автоматический пайплайн (Automated Pipeline): Создание автоматизированного пайплайна (цепи поставок), который включает в себя этапы сборки, тестирования и развертывания.
Управление конфигурацией (Configuration Management): Управление конфигурациями целевых сред и ресурсов для обеспечения консистентности и воспроизводимости развертывания.
Управление версиями (Version Control): Использование систем управления версиями для контроля изменений в коде, конфигурации и ресурсах приложения.
Автоматическое уведомление (Automated Notification): Автоматическое уведомление команды разработки и операций о статусе процесса поставки.
Мониторинг и обратная связь (Monitoring and Feedback): Установление мониторинга производительности и доступности приложения в реальном времени, а также получение обратной связи для быстрого реагирования на сбои и проблемы.
Резервное копирование и восстановление (Backup and Recovery): Разработка процедур резервного копирования данных и возможности восстановления при необходимости.
Разделение на этапы (Staging): Использование этапа предварительного развертывания (staging) для проверки приложения перед финальным развертыванием в продакшн.
Оперативное развертывание (Rolling Deployment): Внедрение новой версии приложения путем поэтапного переключения пользователей на новую версию без простоев и перерывов в работе.
Микросервисная архитектура (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