Книга: Site Reliability Engineering. Надежность и безотказность как в Google
Назад: 6. Мониторинг распределенных систем
Дальше: 8. Технологии выпуска ПО

7. Эволюция автоматизации в Google

Авторы — Нейл Мёрфи, Джон Луни и Майкл Кейсирек

Под редакцией Бетси Бейер

Кроме черной магии, есть автоматизация и механизация.

Федерико Гарсия Лорка (1898–1936), испанский поэт и драматург

Для отдела SRE автоматизация является лишь своего рода рычагом, «усилителем», но не панацеей. Конечно, просто увеличив силу, мы никак не повлияем на точность ее приложения: бездумная автоматизация может создать больше проблем, чем решить. И хотя в большинстве случаев считается, что автоматизация (использование программ) предпочтительнее ручного труда, наиболее выгодным вариантом является создание высокоуровневой автономной системы, которая не нуждается ни в автоматизации, ни в ручной работе. Другими словами, польза автоматизации зависит от того, что мы делаем и как это используем. В этой главе мы рассмотрим ценность автоматизации и изменение нашего к ней отношения с течением времени.

Польза автоматизации

Чем же хороша автоматизация?

Постоянство

Хотя масштабирование само по себе является достаточной мотивацией для автоматизации, существует и множество других причин ее использования. Для примера возьмем вычислительные системы университетов, где начинали свою карьеру многие системные инженеры. Работавшие там системные администраторы должны были управлять группой компьютеров или поддерживать разнообразный софт, а также вручную выполнять различные действия. Один из наиболее типичных примеров — создание пользовательских учетных записей. К другим работам относятся чисто эксплуатационные задачи вроде проверки создания резервных копий, устранение сбоев при отказе сервера и выполнение манипуляций с данными, например изменение содержимого файла resolv.conf вышестоящих DNS-серверов, информации о DNS-зонах и т.д.

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

Платформа

Автоматизация дает нам не только постоянство и непротиворечивость. Качественно спроектированные и реализованные автоматические системы также предоставляют нам платформу, которую можно расширить, применить к другим системам или, возможно, извлечь из нее какую-либо дополнительную выгоду. (Противоположный вариант — отсутствие автоматизации — не является ни эффективным с точки зрения затрат, ни расширяемым: вместо этого он увеличивает нагрузку на систему.)

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

Быстрое восстановление

Системы, где автоматизация применяется для устранения наиболее частых ошибок и сбоев (это типично для автоматизации, разработанной командами SRE), получают еще одно дополнительное преимущество. Если автоматизированная часть системы запускается часто и достаточно успешно, результатом будет снижение среднего времени восстановления (mean time to repair, MTTR) после подобных ошибок. Вы сможете потратить свое время на решение других задач, достигая тем самым большей скорости разработки, поскольку вам не придется предотвращать проблему либо (что более распространено) наводить порядок после нее.

В среде разработчиков хорошо известно, что чем позже обнаруживается проблема, тем дороже ее исправить (см. главу 17). Как правило, исправление ошибок, возникающих в уже работающих системах, обходится дороже всего с точки зрения затрат как времени, так и финансов. Это означает, что автоматизированная система, которая оперативно обнаруживает проблемы по мере их появления, с высокой вероятностью может снизить общую стоимость системы, особенно если система достаточно велика.

Быстродействие

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

Экономия времени

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

189323.png

Джозеф Биронас, SR-инженер, который в свое время отвечал за дата-центры компании Google, имеет такое мнение: «Если мы разрабатываем процессы и решения, которые нельзя автоматизировать, нам придется продолжить нанимать людей для обслуживания системы. Если нам придется продолжать нанимать людей для выполнения этой работы, выйдет так, что мы будем кормить машины человеческими кровью, потом и слезами. Представьте себе “Матрицу” без спецэффектов, но с множеством измученных и обозленных системных администраторов».

Польза автоматизации для Google SRE

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

Еще один аргумент в пользу автоматизации применительно конкретно к Google — наше сложное, но на удивление унифицированное окружение промышленной эксплуатации систем. Хотя в некоторых организациях значительная часть эксплуа­тируемого оборудования может не иметь общедоступного API, либо для него не предоставляют нужный исходный код, либо возникают иные препоны, осложняющие контроль над промышленной средой, в Google мы стараемся избегать подобного. Если поставщик не предоставлял API системы, мы писали API сами. Несмотря на то что в краткосрочной перспективе бывает проще купить ПО для решения какой-либо конкретной задачи, мы предпочитаем писать программы самостоятельно, поскольку в долгосрочной перспективе польза от такого API собственной разработки будет намного больше. Мы долго пытались устранить проблемы, возникавшие при автоматическом управлении системами, а потом взяли и построили такую систему управления сами. Учитывая то, как в Google организовано управление исходным кодом [Potvin, 2016], нам намного проще обеспечить доступность этого кода практически во всех системах, с которыми SR-инженер имеет дело. Кроме того, упрощается задача «владения промышленно эксплуатируемым продуктом», поскольку мы сами контролируем весь стек.

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

Применение автоматизации

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

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

создание пользовательских учетных записей;

• включение и отключение кластеров сервисов;

• подготовка к установке или к выводу из эксплуатации ПО или оборудования;

• развертывание и установка новых версий ПО;

• изменение конфигурации ПО во время его работы;

• особый случай изменения конфигурации во время работы: изменения согласно зависимостям.

Этот список можно продолжать до бесконечности.

Применение автоматизации в Google SRE

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

В этом смысле служба SRE в плане автоматизации делает примерно то же, что и другие люди и организации, но мы пользуемся другими средствами и ставим другие цели (это будет рассмотрено далее).

Такие средства, как Puppet, Chef, cfengine и даже Perl, доступны широкому кругу пользователей. Они предоставляют возможность автоматизировать выполнение ряда задач, различаясь в основном уровнями абстракции компонентов, которые применяются при автоматизации. В языках программирования вроде Perl можно работать на уровне вызовов POSIX, что в теории дает практически неограниченные возможности для автоматизации с использованием всех интерфейсов, доступных системе. Chef и Puppet предлагают готовые абстракции «из коробки», с помощью которых можно манипулировать сервисами или другими высокоуровневыми объектами. Перед нами классический компромисс: абстракции более высокого уровня проще для управления и понимания, но, если вам попадется «дырявая» («протекающая») абстракция, ошибки будут появляться систематически.

Например, мы часто подразумеваем, что отправка нового файла программы в кластер является атомарной операцией: в итоге в кластере либо останется старая, либо появится новая версия. Однако в действительности поведение оказывается несколько сложнее. Сбой может произойти в сети кластера, в компьютере либо на уровне управления кластером (это приведет к тому, что система останется в нестабильном состоянии). В зависимости от ситуации новые исполня­емые файлы могут быть проиндексированы, но не отправлены; или отправлены, но кластер не перезапустится; или кластер перезапустится, но его нельзя будет проверить. Лишь некоторые абстракции успешно справляются с подобными ситуациями — они, как правило, прекращают работу и требуют вмешательства инженера. Совсем плохо построенные системы автоматизации не делают даже этого.

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

Уровни автоматизации

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

Рассмотрим другой пример. Большинство решений по автоматизации процесса запуска систем в Google сталкиваются с проблемами, поскольку они сопровождаются отдельно от основной системы и зачастую становятся неактуальны, когда, например, основная система изменяется, а они — нет. Несмотря на все усилия, попытки более тесно связать решения по автоматизации и основную систему зачастую проваливаются из-за несбалансированной расстановки приоритетов. Разработчики продукта отказываются выполнять контрольное развертывание после каждого изменения. Кроме того, критически важная часть автоматизации, которая выполняется лишь изредка и которую трудно протестировать, зачастую оказывается особенно уязвимой из-за более продолжительного цикла обратной связи. Восстановление после отказа кластера — один из классических примеров редко выполняемой автоматизации. Кластер может восстанавливаться после отказа раз в несколько месяцев — в общем, недостаточно часто для того, чтобы стало заметно несоответствие экземпляров сервиса. Автоматизация развивается по следующему пути.

1. Нет автоматизации. Для главной базы данных восстановление после отказа выполняется вручную на всех площадках.

2. Управляемая извне автоматизация, характерная для данного типа систем. SR-инженер создал сценарий для восстановления в своей «домашней» директории.

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

4. Управляемая изнутри автоматизация, специфичная для данного типа систем. База данных поставляется с собственным сценарием восстановления.

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

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

Существует еще одна разновидность автоматизации, когда изменения применяются не только в той части конфигурации, которая связана с конкретной системой, но и во всей конфигурации в целом. В высокоцентрализованных проприетарных производственных средах, в том числе в компании Google, многие изменения затрагивают не только один конкретный сервис. Сюда относятся, например, изменения в цепочке серверов Chubby, изменение флагов в клиентской библиотеке Bigtable для повышения надежности доступа и т.д. Тем не менее ими нужно безопасно управлять и откатывать их по мере необходимости. Начиная с определенного объема изменений, вносить их вручную в масштабах всей производственной среды становится невозможно, и даже до этого момента ручное выполнение большого количества изменений, которые либо тривиальны, либо успешно реализуются с помощью стратегии «перезапустить и проверить», оказывается пустой тратой времени.

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

Исключаем себя из процесса: автоматизируем все!

Долгое время данные продуктов Ads хранились в БД MySQL. Поскольку они, очевидно, требовали высокой надежности, за инфраструктуру этой системы отвечала команда SR-инженеров. С 2005 по 2008 год база данных для приложения Ads находилась в стабильном и управляемом состоянии. Например, мы автоматизировали худшую часть рутинной работы (но не всю) по реплицированию. Мы были уверены, что база данных хорошо управляется и что сделано все возможное с точки зрения оптимизации и масштабирования. Однако, как только стало удобно выполнять повседневные операции, члены команды задумались о следующем уровне разработки системы: перейти с MySQL на созданную в Google систему-планировщик для кластеров под названием Borg.

Мы рассчитывали, что такой переход предоставит нам два основных преимущества:

полностью исключит необходимость обслуживания машин с развернутыми на них репликами — Borg будет автоматически обрабатывать запуск и перезапуск новых и аварийно завершившихся задач;

• даст возможность размещать несколько экземпляров MySQL на одной машине — Borg позволит более эффективно пользоваться ресурсами благодаря контейнерам.

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

В то время процесс восстановления после отказа мастера длился 30–90 минут для одного экземпляра. Поскольку мы запускали процесс в распределенных системах и должны были перезапускать их для обновления ядра, в дополнение к обычному количеству сбоев мы ожидали некоторое количество не связанных друг с другом отказов каждую неделю. Этот фактор вкупе с количеством серверов, на которых размещалась наша система, означал, что:

выполнение вручную процедуры восстановления после отказа потребует значительного количества человеко-часов и в лучшем случае даст нам доступность 99 %, недостаточную для выполнения бизнес-требований к продукту;

• чтобы уложиться в бюджет времени недоступности сервиса, после каждого отказа система должна восстанавливаться не позже чем через 30 секунд. Оптимизировать зависимую от человека процедуру, заставив ее выполняться менее чем за 30 секунд, было невозможно.

В итоге у нас оставался единственный вариант — автоматизировать процедуру восстановления после сбоев, и не только ее.

В 2009 году SR-инженеры, работающие с продуктами Ads, создали программу-демон для восстановления после сбоев. Его назвали Decider. В течение 95 % времени работы он мог выполнять процедуру восстановления для MySQL менее чем за 30 секунд. Благодаря появлению Decider использование MySQL на Borg наконец стало реальностью. Мы больше не пытались оптимизировать нашу инфраструктуру так, чтобы процедура восстановления требовалась как можно реже. Мы пришли к пониманию, что сбои неизбежны, и поэтому нужно оптимизировать сам процесс восстановления после сбоев, автоматизировав его.

Итак, автоматизация позволила нам получить базу данных MySQL с лучшим в мире показателем доступности, но она должна была перезапускаться два раза в неделю, и за это приходилось платить свою цену. Все наши приложения нужно было доработать, внедрив гораздо больше логики для обработки ошибок. При разработке с использованием MySQL подразумевается, что сам экземпляр MySQL будет наиболее стабильной частью стека. Поэтому подобный переход означал бы, что необходимо настроить прочие программы вроде стандарта JDBC так, чтобы он был более терпимым к нашей склонной к сбоям системе. Однако преимущества перехода на Borg с помощью Decider того стоили. После этого перехода время, которое наша команда тратила на выполнение рутинных операционных задач, сократилось на 95 %. Процедура восстановления после сбоев была автоматизирована, поэтому ошибка при выполнении одной задачи базы данных больше не требовала вызова инженера.

Основное преимущество этого нового подхода к автоматизации заключалось в том, что у нас появилось гораздо больше свободного времени. И его можно было потратить на улучшение других частей нашей инфраструктуры. Таким образом, чем больше времени мы экономили, тем больше было возможностей оптимизировать и автоматизировать приложения. В результате мы смогли автоматизировать работу по изменению схем, что привело к снижению общего количества операций для обслуживания базы данных Ads на 95 %. Наше аппаратное обеспечение также было улучшено. Переход на MoB в сочетании с Decider высвободил значительное количество ресурсов — мы смогли выполнять задачи для нескольких экземпляров базы MySQL на одних и тех же серверах, что повысило коэффициент использования нашего оборудования. В общей сложности мы смогли высвободить около 60 % машинного парка. После этого наша команда не испытывала недостатка в аппаратных и инженерных ресурсах.

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

Облегчаем жизнь: автоматизируем процесс запуска кластера

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

Для подготовки кластера к использованию требовалось выполнить следующие действия.

1. Оборудовать здание дата-центра системами питания и охлаждения.

2. Установить основные коммутаторы и настроить соединения с объединяющей магистралью.

3. Установить несколько первых стоек с серверами.

4. Сконфигурировать базовые сервисы вроде DNS и установщиков, затем сконфигурировать сервисы блокировки, хранения и вычисления.

5. Развернуть остальные стойки серверов.

6. Распределить ресурсы сервисов, с которыми работают пользователи, чтобы команды инженеров могли настроить эти сервисы.

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

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

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

Создавая первые решения по автоматизации, мы стремились ускорить включение кластера в работу. При этом использовался интерактивный доступ по протоколу SSH, который позволял распределять пакеты и решать другие проблемы, возникшие при инициализации сервиса. Стратегия поначалу казалась удачной, но подобные «вольные» сценарии приводили к увеличению технического долга.

Выявление несоответствий с помощью Prodtest

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

Креативные (но нестабильные) сценарии оболочки (скрипты shell — общее название для различных версий командных интерпретаторов или оболочек в Unix-системах. — Примеч. пер.), которые мы использовали для конфигурирования кластеров, не масштабировались ни в зависимости от количества людей, желающих внести изменения, ни в зависимости от общего количества требуемых изменений кластера. Эти сценарии оболочки также не могли дать ответ на следующие серьезные вопросы, из-за которых невозможно было объявить, что сервис готов принимать пользовательский трафик.

Доступны ли все компоненты, от которых зависит сервис, и корректно ли они сконфигурированы?

• Согласуются ли все конфигурации и пакеты с другими развернутыми конфигурациями и пакетами?

• Может ли команда подтвердить, что каждое исключение, сделанное при конфигурировании, было осознанным?

Система Prodtest (Production test) стала оригинальным решением для предотвращения этих нежелательных сюрпризов. Мы расширили фреймворк модульного тестирования (юнит-тестирования), написанный на Python, чтобы иметь возможность проводить юнит-тесты для реальных сервисов. Для юнит-тестов характерно наличие зависимостей — можно выполнять цепочки тестов, и сбой в одном тесте прекратит выполнение всей цепочки. Рассмотрим в качестве примера тест, показанный на рис. 7.1.

189380.png 

Рис. 7.1. Prodtest для сервиса DNS, который показывает, как один непройденный тест прекращает выполнение всей цепочки

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

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

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

Идемпотентное разрешение несоответствий

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

Как оказалось, эти проблемы можно решить быстрее, перейдя от парадигмы «юнит-тесты Python используются для поиска несоответствия конфигураций» к парадигме «код Python применяется для исправления несоответствия в конфигурации».

Юнит-тесту известно, какой кластер мы исследуем, а также какой именно тест еще не пройден, поэтому мы объединили тесты с кодом, исправлявшим проблему. Если каждое решение было написано так, чтобы быть идемпотентным и удовлетворять все зависимости, то решить проблему можно было просто и безопасно. Требование к идемпотентности исправлений заключалось в том, что команды могут запускать собственный «скрипт починки» каждые 15 минут, не боясь повредить конфигурацию кластера. Если тест команды DNS заблокирован в конфигурации Machine Database нового кластера, то, как только кластер появится в базе данных, тесты и решения команды DNS начнут работать.

В качестве примера рассмотрим тест, проиллюстрированный на рис. 7.2. Если не проходит тест TestDnsMonitoringConfigExists, как это показано на схеме, мы можем вызвать код шага FixDnsMonitoringCreateConfig, который получает конфигурацию из базы данных, а затем проверяет основной конфигурационный файл в нашей системе контроля версий. Затем со второй попытки тест TestDnsMonitoringConfigExists выполняется, и теперь можно попробовать выполнить тест TestDnsMonitoringConfigPushed. Если этот тест не проходит, то выполняется код шага FixDnsMonitoringPushConfig. Если исправление не достигает успеха после нескольких попыток, то оно считается некорректным, и автоматизированное выполнение тестов прекращается, оповещая пользователя.

189466.png 

Рис. 7.2. Prodtest для сервиса DNS, показывающий, что один невыполненный тест инициирует применение только одного исправления

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

Оглядываясь назад, мы можем сказать, что такой подход имел множество недостатков. Из-за большой задержки между этапами выполнения теста, решения и выполнения второго теста появились «капризные» тесты, которые иногда работали, а иногда — нет. Не все решения были естественным образом идемпотентными, поэтому «капризный» тест, за которым следовало решение, мог сделать систему нестабильной.

Движение к специализации

Процессы автоматизации отличаются друг от друга тремя характеристиками.

Компетентностью, то есть способностью давать корректные, точные результаты.

• Задержкой (временем ожидания) — временем до выполнения всех шагов.

• Релевантностью, или долей реальных эксплуатируемых процессов, охваченных автоматизацией.

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

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

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

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

Освободив работающие с сервисами команды от обязанности обслуживать код средств автоматизации, мы утратили организационные стимулы.

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

• Команда, не использующая автоматизацию, не имеет стимула создавать легко автоматизируемые системы.

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

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

Процесс ввода кластеров в строй вновь перестал быть оперативным, компетентным и безошибочным — это худшее, что с ним могло случиться. Однако требование к безопасности, не связанное с нашей проблемой, позволило нам выбраться из этой ловушки. Большая часть распределенной автоматизации в то время зависела от SSH. Это было неправильно с точки зрения безопасности, поскольку для запуска многих команд требовались права администратора. Понимание возможных проблем безопасности заставило нас уменьшить права SR-инженеров, сделав их минимально необходимыми для выполнения работы. Нам пришлось заменить sshd аутентифицируемым, управляемым ACL (Access Control List — список управления доступом), основанным на RPC-демоне Local Admin Daemon, также известном как Admin Server. Он позволял настроить права доступа для внесения локальных изменений. В результате никто не мог бесследно поменять настройки сервера. Изменения в Local Admin Daemon и репозитории пакетов проходили с разбором кода, что сделало очень маловероятным превышение своих полномочий. Тот, кто получает доступ к установочным пакетам, не сможет просматривать находящиеся на тех же машинах журналы. Admin Server записывает в журнал необходимые для последующих отладки и аудита безопасности сведения: инициатора вызова RPC, все параметры и результаты всех вызовов.

Запуск кластера, ориентированный на сервисы

На следующем этапе демон Admin Server стал частью технологического процесса для команд, поддерживающих экземпляры демона, ориентированные как на сервисы (для установки пакетов и перезагрузки), так и на кластеры (для действий вроде отвода трафика или включения сервиса). SR-инженеры перешли от написания скриптов shell в домашних каталогах к построению взаимнопросматриваемых RPC-серверов с подробными ACL.

После того как мы поняли, что процесс ввода кластера в строй должен проводиться владельцами сервисов, мы решили попробовать рассматривать его как задачу вида Service-Oriented Architecture (SOA). В этом случае владельцы сервиса должны нести ответственность за создание экземпляров демона Admin Server для обработки удаленных вызовов процедур (RPC) запуска и остановки кластера, к которым обратится система, знающая, когда кластеры переходят в состояние готовности. В свою очередь, каждая команда должна предоставить контракт (API), необходимый для автоматизации процесса ввода кластера в строй, имея при этом возможность менять реализацию по своему усмотрению. Как только кластер приходил в состояние «готов к работе», автоматизация отправляла RPC на каждый Admin Server, который имел отношение к процессу.

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

Как упоминалось ранее, эволюция процесса ввода кластера в строй прошла следующий путь.

1. Ручные действия, выполняемые оператором (нет автоматизации).

2. Написанное оператором решение, специфичное для определенной системы.

3. Общая автоматизация, поддерживаемая извне.

4. Автоматизация, специфичная для определенной системы и поддерживаемая изнутри.

5. Автономные системы, которым не требуется вмешательство человека.

Эта эволюция была успешной, но пример Borg демонстрирует еще один подход к решению проблемы автоматизации.

Borg: появление компьютера размером с дом

Рассмотрим историю развития наших систем управления кластерами, на примере которой тоже можно увидеть, как менялось наше отношение к автоматизации и наше понимание того, когда и где ее лучше разворачивать. Как и при использовании MySQL для Borg (когда мы увидели, что можно успешно преобразовать ручные операции в автоматические) и во время ввода кластера в строй (когда мы узнали о проблемах непродуманного планирования автоматизации), разработка принципов управления кластерами преподнесла еще один урок правильной автоматизации. Как и в предыдущих двух примерах, мы создали сложную систему, которая стала результатом длительной эволюции более простых объектов.

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

Началась разработка решения по автоматизации. Изначально оно состояло из простых сценариев Python, предназначенных для выполнения таких несложных операций, как:

управление сервисами: поддержание их доступности (например, перезагрузка после ошибок доступа к памяти);

• выяснение, какие сервисы должны выполняться на конкретных компьютерах;

• анализ сообщений журнала: соединение по SSH с каждым компьютером и поиск по регулярным выражениям.

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

Оглядываясь назад, нужно отметить, что такая автоматизация была удобна, но серьезно ограничена тем, что абстракции системы были жестко привязаны к физическим машинам. Требовалась новая методика, поэтому на свет появилась система Borg [Verma et al., 2015], позволившая перейти от предыдущего подхода, когда мы имели относительно статические назначения «хост/порт/задача», к подходу, когда набор компьютеров рассматривался как управляемое множество ресурсов. Идея рассматривать управление кластером как сущность, для которой использовались вызовы API, как некий центральный координатор была основополагающей для успеха. Это позволило нам высвободить дополнительные ресурсы для повышения эффективности, гибкости и надежности: в отличие от предыдущей модели «владения» машинами Borg позволял запланировать, например, пакетную обработку и выполнение интерактивных процессов пользователей на одном и том же компьютере.

Наличие этой функциональности в итоге привело к тому, что мы смогли выполнять автоматические длительные обновления операционной системы, прикладывая очень небольшие усилия, которые не возрастали при изменении общего количества развернутых «промышленных» систем. Небольшие отклонения в работе компьютера теперь исправляются автоматически; постоянное обеспечение доступности и управление жизненным циклом больше не лежат на плечах SR-инженеров. Тысячи компьютеров появляются, прекращают работу и отправляются в ремонт, и это не требует никаких усилий со стороны отдела SRE. Бен Трейнор Слосс сказал: «Считая это задачей программного обеспечения, мы за счет первоначальной автоматизации выиграли достаточно времени, чтобы сделать механизм управления кластерами автономным, а не автоматическим. Мы достигли этой цели, реализуя идеи, связанные с распределением данных, API, архитектурами hub-and-spoke и разработкой классических распределенных систем, что позволило справиться и с уровнем управления инфраструктурой».

Здесь можно провести интересную аналогию: мы можем установить прямую связь от случая единственного компьютера к разработке абстракции для управления кластерами. С этой точки зрения перенос выполнения программы на другой компьютер очень похож на перенос с одного процессора на другой: конечно, эти вычислительные ресурсы могут находиться на другом конце сетевого подключения, но насколько это важно? Здесь перераспределение задач выглядит как неотъемлемая функция системы, а не что-то, что можно «автоматизировать», — люди все равно не могут реагировать достаточно быстро. Аналогично в рамках этой метафоры ввод в строй кластера — всего лишь дополнительная планируемая производительность, похожая на добавление диска или оперативной памяти для одного компьютера. Однако мы не ожидаем, что одиночный компьютер будет продолжать работать после отказа нескольких его компонентов. В этом его отличие от «глобального компьютера», который, начиная с определенного «размера», может и должен иметь способность самовосстанавливаться, поскольку для него будет статистически гарантировано большое количество сбоев ежесекундно. Это подразумевает, что по мере перехода систем вверх по иерархии — от систем, инициируемых вручную, через системы, инициируемые автоматически, до автономных систем — для обеспечения работоспособности системы необходим некоторый объем самодиагностики.

Основное качество — надежность

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

Мы также сталкивались с ситуациями, когда автоматизация наносила ущерб (см. врезку «Автоматизация: появление сбоев при масштабировании» ниже). Но, по опыту компании Google, сейчас появилось гораздо больше систем, для которых автоматизация или автономное поведение уже не являются чем-то дополнительным. Это также применимо и для ваших систем по мере их масштабирования, однако существует множество весомых аргументов для того, чтобы создавать автономные системы независимо от их размера. Доступность — ключевое качество, а автономное, устойчивое поведение — один из способов ее достижения.

Автоматизация: появление сбоев при масштабировании

Компания Google запустила более дюжины собственных крупных дата-центров, но мы зависим и от оборудования многих сторонних площадок, или «колокаций» (colos). Наши машины на этих площадках используются как оконечные для большинства входящих соединений или в качестве кэша нашей собственной Content Delivery Network, позволяя снизить задержку отклика для конечных пользователей. Какое-то количество этих стоек постоянно вводят в работу или списывают. Оба этих процесса практически автоматизированы. Один из этапов списания стойки состоит в перезаписывании3 содержимого жестких дисков всех машин в ней, после чего независимая система проверяет успешность удаления данных. Мы называем этот процесс Diskerase (Очистка диска).

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

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

Всего за несколько минут высокоэффективный процесс Diskerase очистил диски всех машин нашего CDN, и машины больше не могли поддерживать соединения пользователей (или делать что-то еще полезное). Мы все еще могли обслуживать пользователей с помощью наших собственных дата-центров, и спустя несколько минут единственным заметным эффектом было лишь небольшое увеличение задержки отклика. Насколько мы могли судить, лишь малое количество пользователей заметили, что есть проблема, и все благодаря хорошему планированию производительности (хоть это мы сделали правильно!). Тем временем мы потратили почти два дня на переустановку машин в стойках, затронутых сбоем. Затем мы две недели проводили аудит и добавляли в код больше проверок работоспособности нашего процесса автоматизации, включая ограничение скорости, и сделали рабочий процесс по списанию оборудования идемпотентным.

Рекомендации

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

Автономные операции трудно качественно внедрить в достаточно крупных системах, но стандартные приемы разработки ПО — наличие несвязанных систем, введение API, минимизация побочных эффектов и т.д. — существенно помогут вам в этом.

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

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

Обратите внимание на следующий комикс XCKD: /.

Для примера прочтите следующую статью: .

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

Демоны — разновидность фоновых программ в Unix-системах. Аналогичное место в Windows занимают системные сервисы (службы). — Примеч. пер.

Идемпотентная операция — действие, многократное повторение которого эквивалентно однократному.

Для упрощения мы приводим ее в сокращенном виде.

И при этом неизменные.

См., например, .

См., например, [Bainbridge, 1983] и [Sarter et al.,1997].

Еще одна хорошая причина проводить регулярные тренировки; см. подраздел «Катастрофа: ролевая игра» раздела «Пять приемов для вдохновления дежурных работников» главы 28.

Имеется в виду запись неинформативных данных поверх прежнего содержимого диска для затруднения его восстановления. — Примеч. пер.

Назад: 6. Мониторинг распределенных систем
Дальше: 8. Технологии выпуска ПО