Книга: Site Reliability Engineering. Надежность и безотказность как в Google
Назад: 7. Эволюция автоматизации в Google
Дальше: 9. Простота

8. Технологии выпуска ПО

Автор — Дина Макнатт.

Под редакцией Бетси Бейер и Тима Харви

Управление выпуском новых версий, или релизами (release engineering), — это относительно новое и быстро растущее направление в индустрии ПО, которое может быть кратко определено как разработка и доставка программного обеспечения [McNutt, 2014a]. Релиз-инженеры — высококвалифицированные специалисты: они разбираются в принципах управления исходным кодом, в работе компиляторов, инструментов автоматизации сборок, менеджеров пакетов и установщиков, а также знают языки конфигурирования. Они владеют информацией из многих областей, таких как разработка, управление конфигурацией, интеграция тестов, системное администрирование и поддержка пользователей.

Запуск надежных сервисов требует наличия надежных процессов выпуска ПО. SR-инженеры должны знать, что бинарные и конфигурационные файлы, которые они используют, созданы воспроизводимым, автоматизированным способом. Это важно для того, чтобы релизы были повторяемыми и не становились «уникальными снежинками». Изменения на любом этапе этого процесса должны быть намеренными, а не случайными. SR-инженеры заботятся об этом, начиная с момента написания исходного кода и заканчивая развертыванием продукта.

Управление релизами в Google — это особая задача. Релиз-инженеры работают с программными инженерами (software engineers, SWE), когда продукт создается, и с SR-инженерами для того, чтобы определить все шаги, необходимые для выпуска ПО, начиная от способа хранения исходных текстов в репозитории до написания скриптов (правил) компиляции и сборки, методов выполнения тестирования, формирования пакетов и развертывания.

Роль релиз-инженера

Google — компания, ориентированная на данные, и управление релизами также придерживается этой линии. У нас есть инструменты, которые сообщают о показателях вроде количества времени, требуемого на то, чтобы изменение в коде было развернуто в промышленной среде (другими словами, быстрота выпуска), и статистики, показывающей, какие функции использовались для создания конфигурационных файлов сборки [Adams et al., 2015]. Большая часть этих инструментов спроектирована и разработана релиз-инженерами.

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

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

Основные положения

В управлении релизами следует придерживаться четырех основных принципов. Рассмотрим их.

Модель самообслуживания

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

Высокая скорость

Пользовательское ПО (например, многие компоненты системы Google Search) часто модернизируется, поскольку мы ставим перед собой цель развертывать предназначенную для клиентов функциональность настолько быстро, насколько это возможно. В рамках нашего подхода частые релизы приводят к меньшему количеству изменений между версиями. Это упрощает тестирование и поиск ошибок. Некоторые команды выполняют сборку каждый час, а затем выбирают одну из готовых версий для развертывания в промышленной среде. Выбор делается на основе результатов тестирования и функциональности, содержащейся в каждой сборке. Другие команды пользуются моделью релизов Push on Green и развертывают каждую сборку, выполняя для нее все тесты [Klein, 2014].

«Герметичные» сборки

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

Повторная сборка более старых релизов, когда нам нужно исправить ошибку в ПО, уже работающем как промышленное, может оказаться трудной задачей. Мы решаем ее путем сборки той же самой «ревизии», что была в оригинальной версии, включив туда появившиеся позже необходимые изменения. Мы называем этот процесс избирательной модификации «извлечением изюминок» (в оригинале — вишенок, cherry picking.Примеч. пер.). Нашим инструментам сборки также присваиваются номера версий, согласованные с «ревизиями» репозитория исходного кода проекта, для которого выполняется сборка. Таким образом, проект, который собирали в предыдущем месяце, и для «изюминок» не будет использовать версию компилятора текущего месяца, ведь эта версия может содержать несовместимую или нежелательную функциональность.

Обязательные политики и процедуры

Несколько уровней безопасности и контроля доступа определяют, кто может выполнять специфические операции при релизе проекта. К контролируемым операциям относятся:

подтверждение изменений исходного кода — эта операция управляется с помощью конфигурационных файлов, распределенных по всей базе кода;

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

• создание нового релиза;

• подтверждение исходного «запроса на интеграцию» (то есть на выполнение сборки с заданным номером «ревизии» в репозитории исходного кода) и последующие «извлечения изюминок»;

• развертывание нового релиза;

• внесение изменений в конфигурацию сборки проекта.

Практически все изменения базы кода требуют просмотра кода и подтверждения сделанных изменений (code review) — эта типовая стадия включена в обычный трудовой процесс разработчика. Наша автоматизированная система релизов формирует отчет обо всех изменениях в релизе, который поставляется вместе с прочими файлами, созданными в результате сборки. Этот отчет позволяет SR-инженерам понять, какие изменения включены в новую версию проекта, и может ускорить поиск проблем, если таковые появляются во время релиза.

Непрерывная сборка и развертывание

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

Сборка

Для выполнения сборки в компании Google был выбран инструмент Blaze. Он поддерживает получение исполняемых файлов программ на множестве языков, включая стандартные языки вроде С++, Java, Python, Go и JavaScript. Инженеры используют Blaze для определения так называемых целей сборки (например, генерируемых JAR-файлов) и указания зависимостей для каждой цели. При выполнении сборки Blaze автоматически выполняет сборку и для зависимостей.

Цели сборки для исполняемых файлов и юнит-тестов определены в файлах конфигурации проекта. Специфические для проектов флаги (например, уникальный идентификатор сборки) Rapid передает Blaze. Все бинарные файлы поддерживают флаг, который отображает дату сборки, номер «ревизии» и идентификатор сборки, что позволяет нам легко связать бинарный файл и информацию о том, как он был собран.

Ветвление

Весь код регистрируется в главной ветви дерева исходного кода (mainline). Однако многие крупные проекты не выходят на релиз непосредственно из главной ветви. Вместо этого мы создаем новую ветвь для конкретной «ревизии» и никогда не объединяем код этой ветви с кодом главной. Изменения для исправления ошибок вносятся сначала в основную ветвь и затем как отдельные «изюминки» выбираются в заданную ветвь, чтобы попасть в релиз. Это позволяет избежать непреднамеренного объединения не связанных с текущим релизом изменений, отправленных в основную ветвь уже после того, как была выполнена первая сборка данной «ревизии». Такой подход позволяет нам точно знать содержимое каждого релиза.

Тестирование

Система непрерывного тестирования запускает модульные тесты для кода основной ветви каждый раз при внесении очередных изменений, что позволяет нам быстро обнаруживать дефекты. С точки зрения управления релизами рекомендуется использовать объекты и цели тестирования, согласованные с релизом проекта. Мы также рекомендуем создавать релизы под номером «ревизии» (версии) последней «непрерывной» тестовой сборки, успешно прошедшей все тесты. Эти меры снижают риск того, что последующие изменения, внесенные в основную ветвь, вызовут сбои во время выполнения сборки релиза.

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

Чтобы дополнить систему непрерывного тестирования, мы используем для него независимую среду, которая запускает тесты на уровне системы для пакетов, созданных во время сборки. Эти тесты могут быть запущены вручную или с помощью Rapid.

Пакеты

Программное обеспечение распространяется между компьютерами, составля­ющими нашу «промышленную среду», с помощью менеджера пакетов Midas (Midas Package Manager, MPM) [McNutt, 2014c]. Он собирает пакеты, основываясь на правилах Blaze, где перечислены созданные во время сборки файлы, которые следует включить в пакет, а также их владельцы и разрешения. Пакеты имеют имена (например, search/shakespeare/frontend), для них указывается версия, уникальный хеш-код и подпись для гарантии аутентичности. MPM поддерживает метки для отдельных версий пакета. Rapid прикрепляет метку с идентификатором сборки — это гарантирует, что для пакета будет создана уникальная ссылка, которая будет представлять собой имя пакета и эту метку.

Метки можно прикрепить к пакету MPM, чтобы указать его местоположение в процессе релиза (например, dev, canary или production). Если вы примените существующую метку к новому пакету, она будет автоматически перемещена от старого пакета к новому. Например, если пакет имеет метку canary, то человек, который впоследствии будет устанавливать canary-версию (версию, предназначенную для канареечного тестирования) этого пакета, автоматически получит самую свежую версию пакета с меткой canary.

Rapid

На рис. 8.1 показаны основные компоненты системы Rapid. Она сконфигурирована с помощью файлов, которые называются макетами (blueprints). Макеты написаны на внутреннем конфигурационном языке и используются для определения целей сборки и тестирования, правил развертывания и административной информации (вроде данных о владельце проекта). Списки управления, основанные на ролях, определяют, кто может выполнять конкретные действия для проектов в Rapid.

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

Обычный процесс релиза выполняется так.

1. Rapid использует запрошенный интеграционный номер «ревизии» (зачастую получаемый автоматически от системы непрерывного тестирования) для создания «релизной» ветки.

209180.png 

Рис. 8.1. Упрощенный вид архитектуры Rapid, демонстрирующий основные компоненты системы

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

3. Файлы, созданные во время сборки, становятся доступными для тестирования и пробного развертывания — canary-релиза. Обычное развертывание canary-версии включает в себя запуск нескольких задач в режиме промышленной эксплуатации после прохождения всех системных тестов.

4. Результаты каждого этапа процесса заносятся в журнал. Создается отчет обо всех изменениях, произведенных с момента создания последнего релиза.

Rapid позволяет нам управлять «релизными» ветвями и избирательной модификацией кода («изюминками»). Отдельные запросы на модификацию кода могут быть одобрены или отклонены при добавлении в релиз.

Развертывание

Для проведения простых развертываний зачастую применяется непосредственно система Rapid. Она обновляет задания Borg для того, чтобы они использовали только что собранные пакеты MPM, основываясь на настройках развертывания, заданных в файлах макетов и специализированных управляющих программах.

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

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

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

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

Управление конфигурацией

Управление конфигурацией — это область, в которой релиз-инженеры и SR-инженеры взаимодействуют наиболее тесно. Несмотря на то что управление конфигурацией на первый взгляд может показаться обманчиво простой задачей, изменения конфигурации — это потенциальный источник нестабильности. В результате наш подход к созданию релизов и управлению системой, а также ее конфигурацией со временем значительно эволюционировал. Сегодня мы можем применять несколько моделей для распространения файлов конфигурации, как это описано далее. Все схемы включают в себя хранение конфигурации в основном репозитории исходного кода и требуют строгого предпросмотра кода.

Использование для конфигураций главной ветви репозитория. Этот метод был первым из тех, что мы использовали для конфигурирования сервисов в Borg (и систем, которые ей предшествовали). Придерживаясь такой схемы, разработчики и SR-инженеры просто модифицировали конфигурационные файлы в верхней «ревизии» основной ветви. Изменения анализировались и применялись к запущенной системе. В результате релизы исполняемых файлов и изменения в конфигурациях не были связаны друг с другом. Несмотря на простоту идеи и ее реализации, такой прием зачастую приводил к рассогласованию между проверенной (зарегистрированной) и запущенной версиями конфигурационных файлов, поскольку задачи должны обновляться перед тем, как к ним будут применены какие-то изменения.

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

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

Например, изменение, в котором реализуется новая функциональность, может быть выпущено с конфигурирующим ее флагом. Создавая два пакета MPM, один для бинарного файла, а второй — для конфигурационного, мы сохраняем возможность изменять каждый пакет независимо от другого. Иными словами, если функциональность была выпущена с установленным флагом first_folio, но мы обнаружили, что это должен быть флаг bad_quarto, мы можем включить это изменение в «релизную» ветвь, повторно собрать конфигурационный пакет и развернуть его. Преимущество такого подхода заключается в том, что он не требует повторной сборки исполняемого файла.

Мы можем воспользоваться метками для MPM-пакетов, чтобы указать, какие версии этих пакетов следует устанавливать вместе. Метка much_ado может быть применена к MPM-пакетам, описанным в предыдущем абзаце, что позволит нам выбрать оба пакета. При сборке новой версии проекта метка much_ado будет применена к новым пакетам. Поскольку эти теги уникальны в рамках пространства имен пакета MPM, будет использован только самый последний пакет с этим тегом.

• Считывание конфигурационных файлов из внешнего хранилища. Отдельные проекты имеют конфигурационные файлы, которые нужно изменять часто или динамически (например, при работающем исполняемом файле). Эти файлы могут храниться в Chubby, Bigtable или нашей файловой системе [Kemper, 2011].

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

Итоги главы

Несмотря на то что в этой главе рассматривается подход компании Google к управлению релизами, а также способы работы релиз-инженеров и их взаимодействия с SR-инженерами, эти приемы могут быть использованы и в других областях.

Это работает не только для Google

При наличии правильных инструментов, соответствующей автоматизации и качественно настроенных политиках разработчики и SR-инженеры не должны волноваться о создании релизов. Релиз должен создаваться простым нажатием кнопки.

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

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

Управляйте релизами с самого начала

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

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

Критически важно, чтобы разработчики, SR-инженеры и релиз-инженеры работали вместе. Релиз-инженеры должны понять, как код должен быть собран и развернут. Разработчики не должны ограничиваться лишь сборкой проекта, оставляя результат на откуп релиз-инженерам.

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

Дополнительная информация

Более подробную информацию о технологиях выпуска ПО вы можете получить в следующих презентациях, для каждой из которых доступны видеозаписи:

How Embracing Continuous Release Reduced Change Complexity (), USENIX Release Engi­neering Summit West 2014, [Dickson, 2014];

Maintaining Consistency in a Massively Parallel Environment (), USENIX Configuration Mana­gement Summit 2013, [McNutt, 2013];

The 10 Commandments of Release Engineering (), 2nd International Workshop on Release Engineering 2014, [McNutt, 2014b];

Distributing Sovware in a Massively Parallel Environment (), LISA 2014, [McNutt, 2014c].

Компания Google использует единый унифицированный репозиторий для всего исходного кода; см. [Potvin, Levenberg, 2016].

Исходный код Blaze был размещен под названием Bazel. Взгляните на Bazel FAQ по адресу .

Назад: 7. Эволюция автоматизации в Google
Дальше: 9. Простота