Делай как вGoogle
Скачать 5.77 Mb.
|
474 Глава 23. Непрерывная интеграция Идеи непрерывной интеграции Начнем с обзора некоторых основных идей непрерывной интеграции. Короткий цикл обратной связи Стоимость ошибки растет почти экспоненциально в зависимости от времени ее обнаружения (глава 11). На рис. 23.1 показаны все места в жизненном цикле кода, где может быть обнаружено проблемное изменение. Правка/ компиляция/ отладка Проверка перед фиксацией Проверка после фиксации Предварительная версия (RC) Продвижение предварительной версии (в промежуточных средах и т. д.) Выпуск предварительной версии в эксплуатацию Рис. 23.1. Жизненный цикл изменения в коде В общем, чем позднее обнаруживаются проблемы, тем дороже они обходятся, по- тому что: y их должен решить инженер, который, скорее всего, не знаком с проблемным из- менением; y автору изменения требуется дополнительное время, чтобы вспомнить и изучить это изменение; y они негативно влияют как на инженеров, так и на конечных пользователей. Чтобы уменьшить стоимость ошибок, непрерывная интеграция рекомендует ис- пользовать как можно более короткие циклы обратной связи 1 . Каждый раз, когда мы интегрируем изменение в сценарий тестирования и наблюдаем за результатами, мы создаем новый цикл обратной связи. Обратная связь может иметь разные фор- мы (некоторые из них приведены ниже в порядке от самой быстрой к самой долгой): y результаты, получаемые на этапе правки-компиляции-отладки в локальной среде разработки; y результаты автоматического тестирования на этапе предварительной проверки; y результаты интеграционного тестирования изменений в двух проектах после тестирования и фиксации изменений в этих проектах по отдельности; y несовместимость проекта с вышестоящим микросервисом, обнаруженная тести- ровщиком из подразделения контроля качества в промежуточной среде после развертывания последних изменений в вышестоящей службе; 1 Иногда это называют «сдвигом влево при тестировании». Идеи непрерывной интеграции 475 y отчет об ошибке, созданный внутренним пользователем, опробовавшим изменение раньше сторонних пользователей; y отчет об ошибке, созданный сторонним пользователем или опубликованный в прессе. Канареечное развертывание, при котором измененная версия получает ограниченное использование, может помочь минимизировать последствия проблем в продакше- не, если таковые появятся, за счет включения в цикл обратной связи только части продакшена. Однако канареечное развертывание тоже может вызвать проблемы, особенно в отношении совместимости между развертываниями, происходящими одновременно. Иногда это состояние распределенной системы, в которой содержится несколько несовместимых версий кода, данных и конфигурации, называют переко- сом версий. Как и многие другие проблемы, которые рассматриваются в этой книге, перекос версий является еще одним примером сложной проблемы, которая может возникнуть при разработке ПО и управлении им с течением времени. Эксперименты и флаги переключения функций — это чрезвычайно мощные циклы обратной связи. Они снижают риски, обусловленные изоляцией изменений в мо- дульных компонентах, которые можно динамически переключать в продакшене. Применение флагов переключения функций — это распространенная парадигма непрерывной поставки (глава 24). Доступная и полезная обратная связь Также очень важно, чтобы обратная связь от непрерывной интеграции была широко доступна. В дополнение к нашей культуре открытости кода мы также культивиру- ем широкую доступность результатов тестирования. У нас есть унифицированная система отчетов о тестировании, в которой любой может легко найти сборку или результаты тестирования, включая все журналы (кроме личной информации поль- зователя), будь то локальный прогон, выполненный отдельным инженером, или результаты автоматической или промежуточной сборки. Наряду с журналами наша система отчетов о тестировании предоставляет подробную историю, когда цели сборки или тестирования начали терпеть неудачу, включая сведе- ния о том, где сборка прерывалась в каждой попытке, где она запускалась и кем. Также у нас есть система нестабильных тестов, которая использует статистику для классифи- кации нестабильных тестов на уровне всей компании Google, поэтому инженерам не нужно выяснять, привело ли их изменение к неудачному завершению теста в другом проекте (если тест нестабильный, то, возможно, неудача не связана с изменением). Доступность истории тестирования позволяет инженерам обмениваться полученны- ми результатами, что очень важно для разрозненных групп, пытающихся выяснить причины сбоев интеграции между своими системами и извлечь уроки. Точно так же сообщения об ошибках доступны всем в Google с полной историей комментари- ев (кроме личных данных клиента). Наконец, любая обратная связь, касающаяся тестов, выполняемых системой непре- рывной интеграции, должна быть не только доступна, но и полезна — быть простой 476 Глава 23. Непрерывная интеграция в использовании и способной находить и устранять проблемы. Далее в этой главе мы рассмотрим пример улучшения недружественной обратной связи. Повышая удо- бочитаемость результатов тестирования, вы автоматически упрощаете понимание обратной связи. Автоматизация Хорошо известно, что автоматизация задач, связанных с разработкой, экономит инженерные ресурсы ( https://oreil.ly/UafCh ). Интуитивно понятно, что автоматизация проверки измененного кода перед отправкой снижает вероятность ошибки. Конечно, в автоматизированных процессах, как и в любом другом ПО, возможны свои ошибки; но, реализованные достаточно эффективно, автоматические проверки все равно будут быстрее, проще и надежнее, чем проверки, выполняемые вручную. Непрерывная интеграция, в частности, автоматизирует процессы сборки и выпуска, осуществляя непрерывные сборку и поставку. Непрерывное тестирование мы рас- смотрим в следующем разделе. Непрерывная сборка Непрерывная сборка (сontinuous build) интегрирует последние изменения в главную ветвь 1 и выполняет автоматическую сборку и тестирование. Поскольку непрерывная сборка запускает тесты, а также код сборки, к нарушениям сборки или сбоям в сборке относятся ошибки тестирования, а также ошибки компиляции. После отправки изменения непрерывная сборка должна запустить все необходимые тесты. Если изменение успешно пройдет все тесты, оно отмечается зеленым цветом, как это часто имеет место в пользовательских интерфейсах. Этот процесс эффективно вводит в репозиторий две разные версии главной ветви: истинную главную ветвь, содержащую последнее зафиксированное изменение, и зеленую главную ветвь, содер- жащую последнее изменение, проверенное системой непрерывной сборки. Инженеры могут синхронизировать свою локальную среду разработки с любой из этих версий. Обычно для работы со стабильной версией, проверенной системой непрерывной сборки, и создания новых изменений синхронизация выполняется с зеленой главной ветвью, но некоторые процессы требуют синхронизации с истинной главной ветвью. Непрерывная поставка Первый шаг в непрерывной поставке (continuous delivery, глава 24) — автомати- зация выпуска, которая непрерывно собирает последний код и конфигурацию из главной ветви и создает предварительную версию, рассматриваемую как кандидат на выпуск. В Google большинство команд используют для этого зеленую, а не ис- тинную главную ветвь. 1 Главная ветвь — это последняя версия кода в нашем монолитном репозитории. В других рабочих процессах ее также называют основной (master) ветвью или магистралью (trunk). Соответственно интеграция с главной ветвью также известна как разработка в главной ветви. Идеи непрерывной интеграции 477 Предварительная версия (RC, release candidate) — подготовленный к разверты- ванию модуль, созданный с помощью автоматизированного процесса 1 из кода, конфигурации и других зависимостей, прошедших этап непрерывной сборки. Обратите внимание, что в предварительную версию включается также конфигура- ция — это чрезвычайно важно, несмотря на то что она может меняться в зависимости от среды по мере продвижения предварительной версии. Мы не призываем вас ком- пилировать конфигурацию в свои двоичные файлы — на самом деле рекомендуем использовать динамическую конфигурацию, позволяющую проводить эксперименты и переключать флаги управления функциями 2 Мы лишь говорим, что любая имеющаяся статическая конфигурация должна про- двигаться в составе предварительной версии, чтобы ее можно было протестировать вместе с кодом. Как показывает практика, значительная доля ошибок, появляющихся в продакшене, вызвана «глупыми» ошибками в конфигурации, поэтому тестировать конфигурацию так же важно, как тестировать код (и нужно тестировать ее вместе с кодом, который будет ее использовать). Часто в процессе продвижения пред- варительной версии обнаруживается перекос версий. Это, конечно, предполагает, что статическая конфигурация должна храниться в VCS — в Google статическая конфигурация хранится в VCS вместе с кодом и, соответственно, проходит тот же процесс обзора. Теперь можно дать определение непрерывной поставки: Непрерывная поставка — непрерывная сборка предварительных версий с по- следующим их продвижением и тестированием в нескольких средах, иногда включающих продакшен. Процесс продвижения и развертывания во многом зависит от команды (см. пример далее в этой главе). Для команд в Google, которым нужна постоянная обратная связь от новых изменений в продакшене (создаваемых, например, непрерывным развертыванием), обычно не- возможно постоянно отправлять целые двоичные файлы, как правило, очень большие, по зеленой ветви. По этой причине часто используется выборочное непрерывное развертывание с помощью экспериментов или флагов переключения функций. По мере продвижения предварительной версии через среду ее артефакты (например, двоичные файлы, контейнеры) не должны повторно компилироваться или собираться. Использование контейнеров, таких как Docker, помогает обеспечить согласованность 1 В Google автоматизация выпуска реализуется отдельной от TAP системой. Мы не будем акцентировать внимание на том, как автоматизация выпуска собирает предварительную версию, но если вам это интересно, то обращайтесь к книге «Site Reliability Engineering. Надежность и безотказность как в Google» ( https://landing.google.com/sre/books ), в которой подробно рассматривается наша технология автоматизации выпуска (система под названием Rapid). 2 Подробнее о непрерывной поставке с экспериментами и флагами переключения функций в главе 24. 478 Глава 23. Непрерывная интеграция предварительной версии между средами, начиная с локальной среды разработки. Точно так же использование инструментов оркестрации, таких как Kubernetes (у нас чаще используется Borg ( https://oreil.ly/89yPv )), помогает обеспечить согласованность между развертываниями. Обеспечивая согласованность выпуска и развертывания между средами, мы достигаем более высокой достоверности результатов тестирования на ранних этапах и меньшего количества сюрпризов в продакшене. Непрерывное тестирование Давайте посмотрим, как непрерывные сборка и поставка сочетаются с непрерывным тестированием (сontinuous testing) изменений кода на протяжении всего его жиз- ненного цикла (рис. 23.2). Правка / компиляция / отладка Проверка после фиксации / главная ветвь Проверка перед фиксацией Предварительная версия (RC) Продвижение предварительной версии (в промежуточных средах и т. д.) Выпуск предварительной версии в эксплуатацию Непрерывная сборка Непрерывная поставка Рис. 23.2. Жизненный цикл изменения кода с непрерывными сборкой и поставкой Стрелка вправо показывает направление движения одного изменения кода от ло- кальной среды разработки до продакшена. Как вы помните, одна из ключевых задач непрерывной интеграции — определить, что и когда тестировать. Далее в этой главе мы познакомимся с разными этапами тестирования и рассмотрим некоторые способы определения, что тестировать до и после фиксации, а также на этапе создания предва- рительной версии и следующих за ним этапах. Мы покажем, что при движении вправо изменение подвергается все более масштабному автоматизированному тестированию. Почему тестирования перед фиксацией недостаточно Стараясь как можно быстрее выявлять проблемные изменения и выполнять авто- матическое тестирование перед фиксацией, вы можете задаться вопросом: почему нельзя просто запускать все тесты перед фиксацией? Во-первых, это слишком дорого. Продуктивность инженера имеет высокую цену, и долгое ожидание выполнения всех тестов во время фиксации может серьезно сни- зить ее. Кроме того, устраняя ограничение на полноту предварительных проверок, можно добиться значительного увеличения эффективности, если тесты будут завер- шаться успехом гораздо чаще, чем неудачей. Например, тесты могут ограничиваться определенными областями или выбираться на основе модели, прогнозирующей вероятность обнаружения сбоя. Идеи непрерывной интеграции 479 Точно так же слишком дорого будут обходиться простои инженеров из-за сбоев не- стабильных тестов, не имеющих ничего общего с фиксируемым изменением. Во-вторых, пока мы выполняем тесты перед фиксацией, чтобы убедиться в безопас- ности изменения, база кода в репозитории может измениться и стать несовместимой с тестируемыми изменениями. То есть два изменения, затрагивающие совершенно разные файлы, могут вызвать сбой теста. Мы называем эту ситуацию столкновением в воздухе, и в масштабе нашей компании оно очень редко случается. Системы непре- рывной интеграции для небольших репозиториев или проектов могут выстраивать фиксации в очередь, чтобы не было разницы между тем, что предполагалось добавить, и тем, что в итоге добавлено. Тестирование до и после фиксации Итак, какие тесты следует выполнять перед фиксацией? Мы следуем общему пра- вилу: только быстрые и надежные. На этапе тестирования перед фиксацией можно пожертвовать некоторой полнотой охвата, но это означает, что вы должны выявить любые оставшиеся проблемы на следующем этапе тестирования после фиксации и быть готовыми выполнить некоторое количество откатов. После фиксации можно позволить себе потратить больше времени на борьбу с нестабильностью, если для этого есть надлежащие механизмы. В разделе «Непрерывная интеграция в Google» мы увидим, как TAP осуществляет управление сбоями. Мы не хотим жертвовать продуктивностью инженеров, поэтому на этапе предва- рительной проверки выполняем только тесты для проекта, в котором происходят изменения. Мы также выполняем тесты параллельно, поэтому учитываем затраты вычислительных ресурсов. Наконец, мы не хотим выполнять ненадежные тесты на этапе предварительной проверки, потому что слишком высока стоимость при- влечения инженеров к устранению проблемы, которая не связана с изменениями. На этапе предварительной проверки большинство команд в Google выполняют свои маленькие тесты (например, юнит-тесты) 1 , которые, как правило, самые быстрые и надежные. Но есть ли смысл выполнять тесты с широким охватом перед фиксаци- ей? Ответ на этот вопрос зависит от команды. Команды, считающие нужным их вы- полнять, используют проверенный подход герметичного тестирования, помогающий уменьшить нестабильность, свойственную таким тестам. Другой возможный подход: выполнять ненадежные тесты с широким охватом при предварительной проверке, но отключать их, когда они начинают терпеть неудачу. 1 Каждая команда в Google настраивает подмножество тестов для своего проекта, которые должны выполняться до (а не после) фиксации. Наша система непрерывной сборки опти- мизирует некоторые такие тесты и переносит их выполнение в этап тестирования после фиксации. Мы поговорим об этом далее в этой главе. 480 Глава 23. Непрерывная интеграция Тестирование предварительной версии После того как изменение прошло непрерывную сборку (на это может потребоваться несколько циклов, если имели место сбои при тестировании), его вскоре обнаружит система непрерывной поставки и включит в предварительную версию. Во время сборки предварительной версии система непрерывной поставки будет вы- полнять более крупные тесты. Предварительная версия неоднократно тестируется по мере продвижения через серию тестовых сред и при каждом развертывании. Эта серия может включать комбинацию временных и изолированных или общих тесто- вых сред, таких как среда разработки и промежуточные испытательные среды. Также в общих тестовых средах обычно выполняется тестирование вручную для проверки качества предварительной версии. Есть несколько причин, почему важно выполнять комплексный набор тестов для предварительной версии, даже если он в точности совпадает с набором, который был выполнен системой непрерывной сборки после фиксации (при условии, что непрерывная поставка завершилась успехом): Для проверки работоспособности Мы дважды проверяем, что ничего необычного не произошло, пока код тестиро- вался и собирался в предварительную версию. Для контроля Если инженер захочет изучить результаты тестирования предварительной версии, он с легкостью сможет получить их и ему не придется для этого искать инфор- мацию в журналах системы непрерывной сборки. Чтобы позволить выборочное применение исправлений Если к предварительной версии применялось некоторое исправление, то ее ис- ходный код будет отличаться от последней версии, протестированной системой непрерывной сборки. Для экстренных случаев Система непрерывной поставки может извлечь код из истинной главной ветви и запустить минимальный набор тестов, не дожидаясь окончания полного цикла непрерывной сборки. Тестирование в продакшене Наш автоматизированный процесс непрерывного тестирования распространяется и на конечную точку развертывания — продакшен. Мы должны выполнить в про- дакшене тот же набор тестов (которые иногда называют зондами), что и для пред- варительной версии, чтобы проверить: 1) работоспособность продакшена и 2) акту- альность наших тестов для продакшена. Непрерывное тестирование на каждом этапе развития приложения служит напо- минанием о ценности подхода «глубокоэшелонированной защиты» с точки зрения обнаружения ошибок — это не просто часть технологии или политики, которые обеспе- чивают качество и стабильность, это сочетание множества подходов к тестированию. |