Постоянное улучшение и автоматизация ПО

Более 10 лет назад мы начали проект, целью которого было понять, насколько быстро наши команды реализуют идеи в виде качественных промышленных систем. В его рамках мы измеряли, как быстро мы работаем над созданием ПО, чтобы повысить скорость исполнения. Оказалось, что с момента сдачи кода до его вывода в рабочую среду требовалось в среднем 16 дней. Сотрудники Amazon начинали с идеи, для реализации которой требовалось примерно полтора дня. На формирование сборки и развертывание кода уходило меньше часа. Оставшееся время, почти 14 дней, тратилось на то, что сотрудники ждали, пока можно будет начать работу над сборкой, развертыванием и тестированием. По результатам проекта мы сформулировали рекомендацию автоматизировать процессы, следующие за сдачей кода, чтобы ускорить исполнение. Нужно было устранить задержки, сохранив или даже повысив качество.

Рекомендация основывалась на программе постоянного улучшения, которая имела своей целью ускорить исполнение. Решение ускорить исполнение базировалось на руководящем принципе «Достижение высочайшего уровня». Суть этого принципа заключается в том, что стандарты должны быть неизменно высокими, планка – постоянно повышаться, а продукты, сервисы и процессы – быть высокого качества. Наши руководящие принципы описывают то, как Amazon ведет бизнес, как осуществляется руководство и как мы принимаем решения, в центре которых находится клиент.

У Amazon уже были средства разработки, которые повышали скорость работы программистов. Мы создали собственную централизованную главную систему управления сборками, Brazil, которая исполняет на сервере серию команд с целью создания артефакта, который может быть развернут. На тот момент Brazil не учитывала изменений в исходном коде. Сотруднику нужно было инициировать сборку. У нас также была собственная система развертывания, Apollo, куда нужно было загрузить сборку артефакта для того, чтобы инициировать развертывание. Интерес к непрерывной доставке, существовавший в отрасли, привел к созданию нашей собственной системы Pipelines, которая автоматизировала процесс доставки ПО между Brazil и Apollo.

Piplelines: наша система непрерывного развертывания

Для автоматизации процесса доставки ПО мы начали пилотную программу для некоторых команд. К моменту его завершения основной пилотной команде требовалось на 90 процентов меньше времени с момента сдачи кода до его ввода в промышленную эксплуатацию.
 
Проект проверял концепцию «конвейера» как способа определить все этапы, необходимые нашим командам для того, чтобы предоставлять ПО клиентам. Первый этап конвейера – создание артефакта. Далее сборка артефакта проходит еще несколько этапов конвейера, по окончании чего артефакт передается всем клиентам. Мы пользуемся конвейерами для снижения риска того, что новое изменение кода негативно отразится на клиентах. Каждый этап конвейера должен привносить уверенность в том, что сборка артефакта не содержит дефектов. А если дефект все же достигнет этапа промышленной эксплуатации, мы хотим, чтобы рабочую среду можно было возвратить в нормальное рабочее состояние максимально быстро.
 
На момент запуска Pipelines система могла моделировать только один процесс выпуска на приложение. Это ограничение способствовало единообразию, стандартизации и упрощению процессов выпуска ПО в команде. Это помогало уменьшить количество дефектов. До начала использования конвейеров у команд, как правило, были различные процедуры выпуска для версий с исправлением ошибок и для основных версий с новой функциональностью. Когда другие команды увидели успех пилотного проекта по автоматической доставке, они тоже начали переносить процессы выпуска ПО на конвейеры для повышения единообразия в своем ПО. Команды, у которых было несколько процессов выпуска версий, отказались от них в пользу одного стандартного процесса, которым пользовались все. Кроме того, при переносе процессов выпуска в новый инструмент, сотрудники часто начинали подходить к работе по-новому и находили способы делать процессы проще.
 
Годовые цели команды создателей Pipelines предусматривали повышение масштабов ее использования благодаря ее популярности. Иными словами, продукт должен быть настолько хорош, чтобы пользователи требовали его. Мы измерили, сколько команд используют конвейеры для вывода своего ПО в промышленную эксплуатацию и классифицировали их по уровню их автоматизации. Мы увидели, что команды ставят перед собой цели использовать конвейеры для выпуска ПО и переходить к полностью автоматизированному выпуску ПО. В то же время, мы заметили, что в некоторых организациях наш поход к оценке качества может привести к тому, что команды автоматизируют процесс выпуска без учета тестирования.

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

Еще одна проблема, с которой мы столкнулись, заключалась в том, что команды не перенимали передовой опыт выпуска ПО друг у друга. Автономная работа небольших групп поощряется, и это значит, что инженеры решали проблемы при развертывании самостоятельно. Находя решение, которое удовлетворяло их потребности, связанные с выпуском ПО, они рассказывали о нем другим инженерам в рассылках, на производственных совещаниях и по другим каналам взаимодействия. Но такой обмен информацией был связан с двумя проблемами. Во-первых, эти каналы использовались «по мере возможности», следовательно, о новых методах узнавали не все. Во-вторых, руководители, которые рекомендовали своим командам использовать новые методы, не могли понять, действительно ли сотрудники проделали работу, нужную для внедрения этих методов. Мы поняли, что нужно, чтобы у всех инженеров был доступ к новым методам, которые нам известны, а у руководителей должна быть возможность определить, каким конвейерам следует уделить внимание.
 
В качестве решения мы формализовали обучение, внедрив проверку использования передовых методов в средства разработки и выпуска ПО. Понимая, что метод, который является передовым для одной организации, может не быть таковым для другой, мы сделали механизмы проверки гибкими в зависимости от организации. Проверка на соответствие передовым методам на организационном уровне дала руководителям возможность адаптировать процессы выпуска ПО в соответствии с потребностями бизнеса. Руководители, которые хотели рекомендовать к использованию или принудительно внедрить новый передовой метод, могли начать с предупреждений в программах, которыми инженеры пользовались ежедневно. Эти информационные сообщения практически гарантировали, что сотрудники изучат новые методы и будут знать о том, когда следование им станет обязательным. Мы выяснили, что когда у команд появлялось время познакомиться с новым методом и обсудить его, у организации с каждым разом улучшались возможности контроля использования этих методов. В конечном счете, это повышало качество использования нового метода и его активное принятие инженерами.
 
Мы систематически определяли, какие новые методы необходимо использовать. Группа наиболее опытных инженеров составила каталог наиболее распространенных причин, по которым новый выпуск ПО мог не работать. Они также определили шаги, которые заставляли его работать. Затем мы использовали этот список для формирования механизмов соответствия передовым методам. В ходе этого процесса мы поняли, что хотя мы хотели бы, чтобы новые версии ПО появлялись бы у клиентов немедленно, без каких-либо усилий и без ухудшения доступности, на первое место мы ставим именно доступность, затем скорость, а затем простоту для наших инженеров.

Снижение риска проявления дефекта у клиентов

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

Гигиена развертывания: самая базовая форма тестирования при развертывании позволяет убедиться, что вновь развернутый артефакт запускается и реагирует на запросы. Операции, следующие за развертыванием, включают в себя оперативные проверки, которые гарантируют, что развернутый артефакт запустился и обслуживает трафик. Например, в файле AWS Codedeploy AppSpec мы используем процедуры жизненного цикла для выполнения простых скриптов для запуска, остановки и проверки развертывания. Мы также проверяем, достаточны ли ресурсы для обслуживания клиентского трафика. Чтобы гарантировать, что у нас всегда есть ресурсы обслуживать клиентов, мы разработали такие принципы, как принцип минимального количества рабочих узлов в CodeDeploy. И, наконец, если средство развертывания может найти ошибку, оно должно уметь осуществлять откат изменений, чтобы время, в течение которого клиенты будут замечать ошибку, было бы минимальным.

Тестирование до промышленной эксплуатации: одним из передовых методов Amazon является автоматизация элементного, интеграционного и опытно-промышленного тестирования. Мы настаиваем на самостоятельном проведении нагрузочного тестирования и тестирования безопасности, стремясь добавить эти тесты в конвейеры. Говоря о тестировании элементов, мы имеем в виду все тесты, которые можно выполнить на компьютере, где есть сборка, включая оценку стиля, охват кода, его сложность и многое другое. Интеграционные тесты мы представляем как тесты, включающие все «неосновные» задачи, включая введение отказа, автоматическое тестирование браузера и пр. О тестировании компонентов и интеграционном тестировании есть множество отличных статей, поэтому я не буду останавливаться на них подробнее.

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

В Amazon мы также используем методику, называемую «опытно-промышленным тестированием». Опытно-промышленная среда – последнее место, в которой производится тестирование до развертывания изменений в промышленной среде. Тест в опытно-промышленной среде использует конфигурацию промышленной системы, поэтому она действует в точности как таковая. У этого подхода есть два преимущества. Во-первых, тестирование в опытно-промышленной среде позволяет убедиться, что сервис может правильно подключаться ко всем рабочим ресурсам, включая места хранения рабочих данных. Во-вторых, она позволяет убедиться, что система правильно взаимодействует в API-интерфейсами действующих сервисов, от которых она зависит. Опытно-промышленные среды используются только ответственной за сервис командой, поэтому клиентский трафик на них никогда не направляется. Выполнение опытно-промышленного тестирования повышает уверенность в том, что этот же код и конфигурация будут нормально функционировать в рабочей среде.

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

Затруднение может возникнуть в том случае, если в автоматических тестах не отражен какой-либо пользовательский сценарий. Мы стремимся устранять все ошибки с помощью структурированных и повторяемых тестов, как автоматизированных, так и ручных. Тем не менее, как бы мы ни пытались, иногда дефекты неизбежны. Для проверки собственных тестов мы оставляем новое изменение в рабочей среде на фиксированный период времени, чтобы понять, сможет ли сторонний наблюдатель найти проблему. Мы долго обсуждали, стоит ли оставлять изменения в рабочей среде или сколько ждать после «канареечного» развертывания перед тем, как развертывать новый выпуск для всех. Многие команды решили, что перед дальнейшим развертыванием лучше всего выждать фиксированное время, а также собрать положительные отзывы. То, как долго может ожидать конвейер, во многом зависит от команды. Некоторые ждут несколько часов, другие – несколько минут. Чем больше охват и чем больше потребуется времени на устранение проблемы, тем медленнее идет процесс выпуска.

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

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

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

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

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

Мы также можем останавливать все процессы в конвейере на основании содержимого сборки артефакта. Например, мы можем блокировать сборку артефактов, в которой содержится известный проблемный пакет или определенная Git-ссылка. Мы использовали эту функцию, когда выяснялось, что изменения в пакете отрицательно влияли на производительность. Если бы мы просто удалили этот пакет из репозитория пакетов, то конвейеры, которые уже содержали дефектный пакет, все равно выполнили бы его развертывание у клиентов.

О нашем подходе к скорости исполнения.

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

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

Amazon уже много лет реализует программы постоянного улучшения с упором на скорость выпуска ПО для клиентов и его безопасность. Тесты и контрольные механизмы, описанные в этой статье, существовали не с самого начала. С годами мы изобрели ряд способов выявления и смягчения рисков.

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

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

Вернемся в сегодняшний день. Сегодня команды программистов Amazon стремятся добиться полной автоматизации при написании нового кода. Для нас автоматизация – единственный способ дальнейшего роста бизнеса.
 


Об авторе

Марк Мансур занимает должность старшего менеджера по разработке программного обеспечения в Amazon Web Services. Он присоединился к Amazon в 2014 году и работал над большим количеством внутренних и внешних сервисов, сосредоточившись на развертывании ПО и непрерывной доставке программного обеспечения с возможностью масштабирования. В свободное от работы время Марк увлекается ремонтом часов, настольными играми и гольфом.

Обеспечение безопасности отката во время развертывания