Делай как вGoogle
Скачать 5.77 Mb.
|
Одноразовый код Выше большая часть обсуждения была сосредоточена на надежных заданиях, об- служивающих пользовательский трафик или конвейеры, производящие данные в продакшене. Однако инженерам-программистам нередко приходится выполнять однократный анализ, проверять прототипы и нестандартные конвейеры обработки данных и многое другое. Для этого нужны вычислительные ресурсы. Часто рабочая станция инженера обладает достаточными вычислительными ресур- сами для этого. Если инженер решит, например, проанализировать 1 Гбайт журналов, созданных службой за день, чтобы проверить, всегда ли подозрительная строка A появляется перед строкой ошибки B, он может просто загрузить журналы, написать короткий сценарий на Python и дать ему поработать минуту или две. Но если он решит проанализировать 1 Тбайт журналов, созданных службой за год (с той же целью), ему придется ждать результатов в течение примерно одного дня, что, вероятно, неприемлемо. Вычислительная услуга, которая позволяет инженеру выполнить анализ в распределенной среде за несколько минут (с использованием нескольких сотен ядер), находится в пределах между «сейчас» и «завтра». Для по- вторяющихся задач (например, уточнения запроса после просмотра результатов) вычислительная услуга попадет в пределы между «к концу дня» и «никогда». Иногда разрешение инженерам выполнять разовые задания в распределенной среде может привести к напрасному расходованию ресурсов. Маловероятно, что обработка, выполняемая инженером, будет стоить дороже, чем время, затраченное инженером на написание кода обработки. Выбор того или иного компромисса зависит от вы- числительной среды организации и сколько она платит своим инженерам, но едва ли тысячу процессорных часов можно сравнить со стоимостью одного дня работы инженера. Вычислительные ресурсы в этом отношении похожи на маркеры, о кото- 522 Глава 25. Вычисления как услуга рых мы говорили в начале книги; компания может получить некоторую экономию, внедрив процесс для получения дополнительных вычислительных ресурсов, но стоимость упущенных технических возможностей и времени наверняка превысит экономию. И все же вычислительные ресурсы отличаются от маркеров — их легко по случайно- сти взять слишком много. Маловероятно, что кто-то унесет домой тысячу маркеров, но вполне возможно, что кто-то случайно напишет программу, которая задействует тысячу машин, и не заметит этого 1 . Естественным решением этой проблемы является введение ограничений на ресурсы, доступные отдельным инженерам. Альтерна- тивный вариант, используемый в Google: благодаря эффективному выполнению низкоприоритетных пакетных рабочих нагрузок (см. раздел о многоарендности далее в этой главе) мы можем дать нашим инженерам практически неограниченное количество вычислительных ресурсов для низкоприоритетных пакетных заданий, что вполне подходит для большинства разовых инженерных задач. CaaS во времени и масштабе Выше мы говорили о том, как развивались CaaS в Google, и о том, как простая по- требность «дай мне ресурсы для выполнения моих заданий» трансформируется в ре- альную архитектуру, подобную Borg. Некоторые аспекты влияния архитектуры CaaS на жизнь ПО во времени и масштабе заслуживают более пристального внимания. Контейнеры как абстракция Контейнеры, как уже отмечалось выше, в первую очередь играют роль механизма изоляции и способа поддержки многоарендности при минимальном взаимовлиянии различных задач друг на друга, совместно выполняющихся на одной машине. Это было первоначальной мотивацией их создания, по крайней мере в Google. Но, как оказалось, контейнеры также играют очень важную роль в абстрагировании вы- числительной среды. Контейнер обеспечивает границу абстракции между развернутым ПО и реальной машиной, на которой оно выполняется. То есть по мере изменения машины со временем необходимо адаптировать только ПО поддержки контейнеров (предпо- ложительно управляемое одной командой), тогда как прикладное ПО (управляемое отдельными командами на разных этапах развития организации) может оставаться без изменений. Давайте обсудим, как контейнерная абстракция позволяет организации управлять изменениями. 1 В Google такое случалось несколько раз, например из-за того, что кто-то оставил инфра- структуру нагрузочного тестирования, занимающую тысячу виртуальных машин Google Compute Engine, уйдя в отпуск, или из-за того, что новый сотрудник отлаживал главный двоичный файл на своей рабочей станции, не заметив, что тот породил 8000 реплик. CaaS во времени и масштабе 523 Абстракция файловой системы дает возможность внедрения ПО, написанного за пре- делами компании, без необходимости управлять нестандартными конфигурациями. Это может быть ПО с открытым исходным кодом, которое организация использует в своем центре обработки данных, или приобретения, которые она хочет встроить в CaaS. Без абстракции файловой системы запуск двоичного файла, который ожидает другой конфигурации файловой системы (например, наличие вспомогательного двоичного файла в /bin/foo/bar ), потребует изменения базовой конфигурации всех машин в парке, либо фрагментации парка, либо изменения ПО (что может быть за- труднительно или даже невозможно из-за лицензионных требований). Все эти решения могут быть вполне осуществимыми, если импорт внешнего ПО происходит раз в жизни, но ни одно из них не годится, если импорт ПО является обычной (или даже редкой) практикой. Абстракция файловой системы также помогает в управлении зависимостями, потому что позволяет их предварительно объявлять и упаковывать (например, определенные версии библиотек, необходимые для запуска ПО). Если появится протекающая абстракция, вынуждающая всех использовать одну и ту же версию скомпилированных библиотек, обновление любого компонента станет трудным или вообще невозможным. Контейнеры также дают простую возможность управления именованными ресурсами на машине. Канонический пример — сетевые порты. В числе других именованных ресурсов можно назвать графические процессоры и другие ускорители. Изначально в Google сетевые порты не были включены в абстракцию контейнеров, поэтому двоичным файлам приходилось искать свободные порты самостоятельно. В результате в кодовой базе на C++ в Google функция PickUnusedPortOrDie ис- пользовалась более чем в 20 000 местах. Механизм контейнеров Docker, созданный после добавления пространств имен в ядро Linux, использует эти пространства для поддержки виртуальных и приватных сетевых адаптеров (NIC), благодаря чему при- ложения могут прослушивать любые порты по своему выбору. Сетевой стек Docker отображает порт на несущей машине с портом в контейнере. Фреймворк Kubernetes, изначально основанный на Docker, пошел еще дальше и требует от сетевой реа- лизации интерпретировать контейнеры («поды» в терминологии Kubernetes) как «реальные» IP-адреса, доступные в сети хоста. Теперь каждое приложение может прослушивать любой порт, не опасаясь конфликтов. Эти улучшения особенно важны для ПО, не предназначенного для выполнения в конкретном вычислительном стеке. Многие популярные программы с открытым исходным кодом имеют параметры настройки портов, но эти способы настройки не согласованы. Контейнеры и неявные зависимости К абстракции контейнера, как и к любой абстракции, применяется закон Хайрама о неявных зависимостях. К контейнерам этот закон применим даже в большей сте- 524 Глава 25. Вычисления как услуга пени из-за огромного количества пользователей (в Google весь продакшен и многое другое выполняется под управлением Borg), а также из-за того, что пользователи не чувствуют, что обращаются к API, используя, например, файловую систему (и еще менее вероятно, что они задумываются над тем, является ли этот API стабильным, версионированным и т. д.). Для иллюстрации вернемся к примеру исчерпания пространства идентификаторов процессов, с которым система Borg столкнулась в 2011 году. Возможно, вам инте- ресно, как может исчерпаться пространство идентификаторов процессов. Разве это не просто целые числа из 32- или 64-битного пространства? Не совсем так. В Linux идентификаторы назначаются из диапазона [0, ..., PID_MAX – 1], где константа PID_MAX по умолчанию равна 32 000. Однако PID_MAX можно увеличить про- стым изменением конфигурации (и значительно). Задача решена? Увы, нет. Согласно закону Хайрама, тот факт, что идентификаторы процессов, выпол- няющихся в Borg, ограничены диапазоном 0...32 000, стал неявной гарантией API, от которой люди начали зависеть. Например, процессы хранения журналов зависели от того факта, что PID умещается в пять символов, и их работа нарушалась с появлением шестизначных PID, потому что имена записей превышали максимально допустимую длину. Решение проблемы вылилось в длительный двухэтапный проект. Во-первых, была увеличена верхняя граница идентификаторов PID, которые может использовать один контейнер (чтобы одно задание, испытывающее утечки потоков выполнения, не смогло сделать всю машину непригодной для использования). Во-вторых, про- странство PID было разделено на два подпространства — для потоков выполнения и процессов. (Потому что, как оказалось, очень немногие пользователи полагались на ограничение 32 000 в отношении идентификаторов PID, присваиваемых пото- кам. Благодаря этому мы смогли увеличить верхнюю границу идентификаторов для потоков и оставить ее на уровне 32 000 для процессов.) На третьем этапе предпо- лагалось внедрить в Borg пространство имен PID, чтобы дать каждому контейнеру свое собственное полное пространство PID. Но, как и следовало ожидать (опять же согласно закону Хайрама), многочисленные системы стали исходить из предполо- жения, что тройка {имя_хоста, время, pid} однозначно идентифицирует процесс, которое станет недействительным после введения пространства имен PID. Попытки выявить все эти места и исправить их (а также исправить любые соответствующие данные) все еще продолжаются восемь лет спустя. Дело здесь не в том, что контейнеры должны запускаться в пространствах имен PID. Это хорошая идея, но не самая интересная. Когда создавались контейнеры Borg, пространств имен PID не существовало. И даже если бы это было не так, едва ли инженеры, разрабатывавшие Borg в 2003 году, могли осознавать ценность их вне- дрения. Даже сейчас на машинах есть ресурсы, которые недостаточно изолированы, что, вероятно, когда-нибудь вызовет проблемы. Это особенно наглядно подчерки- вает сложность проектирования системы контейнеров, которая со временем будет оставаться простой в обслуживании и ценной для более широкого сообщества, где эти типы проблем уже возникали и были учтены. CaaS во времени и масштабе 525 Одна услуга для управления всем Как обсуждалось выше, раннее решение WorkQueue было нацелено только на некото- рые пакетные задания, которые совместно использовали набор машин, управляемых этим решением, а для обслуживающих заданий использовалась другая архитектура, в которой каждое конкретное обслуживающее задание выполнялось отдельно, в вы- деленном наборе машин. Эквивалентное решение с открытым исходным кодом можно представить как запуск отдельного кластера Kubernetes для каждого типа рабочей нагрузки (+ один пул для всех пакетных заданий). В 2003 году был запущен проект Borg с целью (успешно достигнутой) создать вы- числительную услугу, которая объединяет эти разрозненные наборы машин в один большой набор. Набор Borg охватывал и обслуживающие, и пакетные задания и стал единственным набором в любом центре обработки данных (представьте один большой кластер Kubernetes для всех рабочих нагрузок в каждом географическом местоположении). Здесь стоит отметить два значительных достижения с точки зрения эффективности. Во-первых, машины для обслуживающих заданий превратились в скот (как от- мечено в проектном документе Borg: «Машины анонимны: программам не важно, на какой машине они выполняются, если она имеет требуемые характеристики»). Если бы каждой команде, сопровождающей обслуживающее задание, приходилось управлять своим собственным набором машин (своим кластером), то каждая несла бы одни и те же организационные издержки по обслуживанию и администриро- ванию этого набора. Со временем методы управления этими наборами неизбежно стали бы расходиться, делая изменения в масштабах компании (например, переход на новую серверную архитектуру или переключение центров обработки данных) все более сложными. Унифицированная инфраструктура управления — то есть об- щая вычислительная услуга для всех рабочих нагрузок в организации — позволяет Google избежать линейного роста издержек. Нет различий в методах управления физическими машинами в парке, есть только Borg 1 Второе достижение, более тонкое, вероятно, применимое не ко всем организациям, но очень важное для Google. Различные потребности пакетной и обслуживающей обработки оказываются взаимодополняющими. Обслуживающие задания обычно требуют выделения избыточных ресурсов, потому что должны обладать достаточной емкостью для обслуживания пользовательского трафика без значительного умень- шения задержки даже в часы пик или в случае частичного сбоя инфраструктуры. Это означает, что машина, на которой выполняются только обслуживающие зада- ния, будет недостаточно загружена. Заманчиво попытаться воспользоваться этими избыточными ресурсами, увеличив нагрузку на машину, но это сведет на нет цель 1 Как и в любой сложной системе, здесь есть свои исключения. Не все машины в Google управ- ляются системой Borg, и не каждый центр обработки данных охватывается одной ячейкой Borg. Но большинство инженеров работают в среде, в которой все машины управляются системой Borg. 526 Глава 25. Вычисления как услуга избыточности, потому что, если произойдет всплеск или отключение, необходимые ресурсы окажутся недоступными. Однако это рассуждение относится только к обслуживающим заданиям! Если на машине действует несколько обслуживающих заданий, которые в сумме потребляют полный объем ОЗУ и вычислительной мощности, то на эту машину больше нельзя помещать никаких обслуживающих заданий, даже если реальное потребление ре- сурсов остается на уровне 30 % от общей емкости. Но мы можем (и в Borg это будет реализовано) распределить неиспользуемые 70 % между пакетными заданиями с условием, что если какое-то из обслуживающих заданий потребует выделить до- полнительный объем ОЗУ или процессорное время, мы освободим ресурсы, занятые пакетными заданиями (приостановив их, если потребуется процессорное время, или прервав, если потребуется ОЗУ). Для пакетных заданий важна пропускная способ- ность (измеряемая по совокупности сотен рабочих реплик, а не отдельных задач), а отдельные реплики — это скот, и они охотно используют свободную мощность для обслуживающих заданий. В зависимости от профиля рабочих нагрузок в заданном наборе машин либо вся пакетная рабочая нагрузка эффективно работает на свободных ресурсах (потому что мы все равно платим за них из-за нехватки обслуживающих заданий), либо вся обслуживающая рабочая нагрузка платит только за то, что ею используется, но не за резервную мощность, необходимую для обеспечения отказоустойчивости (потому что в этом резерве выполняются пакетные задания). Как показывает опыт Google, в большинстве случаев мы получаем пакетную обработку бесплатно. Многоарендность для обслуживающих заданий Выше мы обсудили ряд требований, которым должна удовлетворять вычислитель- ная услуга, чтобы быть пригодной для выполнения обслуживающих заданий. Как уже отмечалось, управление обслуживающими заданиями с помощью общего вы- числительного решения дает множество преимуществ, но также сопряжено со слож- ностями, например с требованием наличия механизма обнаружения служб (раздел «Подключение к услуге»). Существуют также другие требования, которые начинают действовать при расширении управляемых вычислений до обслуживающих заданий, например: y Перепланирование заданий должно регулироваться: теоретически возможно уничтожить и вновь запустить 50 % реплик пакетного задания (потому что это вызовет только временный сбой в обработке, тогда как нас больше волнует про- пускная способность), но вряд ли это будет приемлемо в отношении обслужива- ющих заданий (потому что оставшихся заданий, вероятно, будет слишком мало, чтобы обслуживать пользовательский трафик, пока вновь запускаемые задания не вернутся к работе). y Пакетное задание обычно можно прервать без предупреждения. При этом мы потеряем часть выполненной работы, которую придется повторить. Но прерывая без предупреждения обслуживающее задание, мы рискуем вернуть ошибку в от- CaaS во времени и масштабе 527 вет на запрос пользователя или (в лучшем случае) выполнить его с задержкой. Желательно заранее предупредить задание и дать ему несколько секунд, чтобы оно могло завершить обработку уже полученных запросов и не принимать новые. Borg охватывает пакетные и обслуживающие задания, но большинство предложений по организации вычислений поддерживают два понятия — общий набор машин для пакетных заданий и выделенные наборы машин для обслуживающих заданий. И не- зависимо от того, используется ли для обоих типов заданий одна и та же архитектура, обе группы выигрывают от обращения с ними как со скотом. Отправляемая конфигурация Планировщик Borg получает конфигурацию для службы или пакетного задания, запускаемого в ячейке, как результат RPC. Оператор службы может управлять ею с помощью интерфейса командной строки, выполняя RPC вручную. Параметры для этих вызовов обычно хранятся в общей документации или в их голове. Зависимость от документации и личных знаний в противоположность коду, храня- щемуся в репозитории, редко бывает хорошей идеей еще и потому, что и докумен- тация, и личные знания имеют свойство ухудшаться со временем (глава 3). Однако и следующий естественный шаг в эволюции — оформление необходимых команд в виде сценария — тоже уступает использованию специального языка описания конфигураций для определения настроек службы. Со временем присутствие службы обычно перестает ограничиваться рамками одного набора реплицированных контейнеров в одном центре обработки данных и разрас- тается сразу по нескольким направлениям: y Служба будет распространяться по нескольким центрам обработки данных (как для приближения к пользователям, так и для повышения устойчивости к сбоям). y Служба будет развертываться не только в продакшене, но и в тестовой среде, и в среде разработки. y Служба будет накапливать дополнительные реплицированные контейнеры раз- ных типов в виде прикрепленных вспомогательных служб, таких как memcached. Управление службой значительно упростится, если появится возможность выразить сложную настройку на стандартном языке описания конфигураций, позволяющем свободно выражать стандартные операции (например, «обновить службу до новой версии, затрачивая на это не более 5 % ресурсов в любой конкретный момент»). Стандартный язык описания конфигураций позволит другим командам включить стандартную конфигурацию в определения своих служб. Как обычно, во времени и в масштабе стандартная конфигурация приобретает особую ценность. Если каж- дая команда напишет свой код для поддержки службы memcached, то будет очень сложно в масштабах всей организации выполнять такие задачи, как переход на новую реализацию memcache (например, по причинам производительности или лицензирования) или установка обновлений безопасности во всем развертывании 528 Глава 25. Вычисления как услуга memcache. Также обратите внимание, что наличие стандартного языка описания конфигураций является требованием для автоматизации развертывания (глава 24). Выбор вычислительной услуги Маловероятно, что какая-то организация пойдет по пути, по которому пошла Google, создавая свою вычислительную архитектуру с нуля. В наши дни доступно множество современных предложений как с открытым исходным кодом (например, Kubernetes и Mesos, или, на другом уровне абстракции, OpenWhisk и Knative), так и в виде общедоступных управляемых облачных решений (разной степени сложности, от Managed Instance Groups в Google Cloud Platform или автоматически масштабируе- мого Amazon Web Services Elastic Compute Cloud (Amazon EC2) до управляемых контейнеров, подобных Borg, таких как Microsoft Azure Kubernetes Service (AKS) или Google Kubernetes Engine (GKE), и бессерверных предложений, таких как AWS Lambda или Google Cloud Functions). Однако большинство организаций предпочтут организовать вычислительную услугу, как и Google, внутри компании. Обратите внимание, что вычислительная инфраструктура имеет сильное привязывающее действие, поскольку код будет пи- саться так, чтобы использовать все свойства системы (закон Хайрама). Например, в случае выбора решения на основе виртуальных машин команды будут настраивать свои образы виртуальных машин, а в случае выбора решения на основе контейнеров команды будут обращаться к API диспетчера кластера. Если архитектура позволит коду обращаться с виртуальными машинами (или контейнерами) как с домашними любимцами, команды будут делать это, и тогда будет сложно перейти к решению, в котором с этими машинами и контейнерами обращаются как со скотом (или даже с обоими видами домашних животных). Чтобы показать, как даже мельчайшие детали вычислительного решения могут оказать связывающее действие, рассмотрим, как Borg выполняет пользовательские команды, указанные в конфигурации. В большинстве случаев команды запускают двоичный файл (возможно, с дополнительными аргументами). Однако для удобства авторы Borg включили возможность передачи сценария командной оболочки, на- пример while true; do ./my_binary; done 1 . Однако, в отличие от двоичного файла, который можно запустить вызовом пары функций fork и exec (что и делает Borg), сценарий должен запускаться командной оболочкой, такой как Bash. То есть фак- тически Borg выполняет команду /usr/bin/bash -c $USER_COMMAND , которая также позволяет запускать простые двоичные файлы. В какой-то момент разработчики Borg осознали, что в масштабе Google оболочка Bash потребляет значительные ресурсы (в основном память), и решили перейти 1 Эта конкретная команда представляет особую опасность для Borg, потому что препятствует работе механизмов обработки ошибок в Borg. Однако для отладки проблем все еще исполь- зуются сложные обертки, например записывающие настройки среды в журнал. Выбор вычислительной услуги 529 к использованию более легкой оболочки ash. Они внесли изменения в код запуска процессов, чтобы выполнялась команда /usr/bin/ash -c $USER_COMMAND Можно подумать, что это изменение не несет никаких рисков: мы контролируем среду, знаем, что оба файла существуют и это изменение должно сработать. Но оно не сработало, потому что инженеры Borg были не первыми, кто заметил повышенный расход памяти при запуске Bash. Некоторые команды проявили изобретательность в своем желании ограничить использование памяти и заменили (в своих образах файловых систем) команду Bash специально написанным кодом «запустить второй аргумент». Они хорошо знали об использовании памяти, и поэтому, когда команда Borg изменила способ запуска процессов, задействовав для этого оболочку ash (кото- рая не подменялась нестандартным кодом), использование памяти увеличилось (по- тому что вместо экономного нестандартного кода выполнялся интерпретатор ash). Это вызвало предупреждения, откат изменения и недовольство. Еще одна причина, по которой выбор вычислительной услуги трудно изменить со временем, заключается в том, что любой выбор в итоге будет окружен большой эко- системой вспомогательных служб — инструментов журналирования, мониторинга, отладки, оповещения, визуализации, оперативного анализа, языков описания кон- фигураций и метаязыков, пользовательских интерфейсов и многого другого. Эти инструменты придется переписать при переходе на другую вычислительную услугу, и даже простое перечисление этих инструментов, вероятно, станет проблемой для средней или большой организации. Таким образом, выбор вычислительной архитектуры играет важную роль. Как и большинство решений в программной инженерии, это решение предполагает компромиссы. Обсудим некоторые из них. Централизация против индивидуализации С точки зрения накладных расходов на управление вычислительным стеком (а также эффективности использования ресурсов) лучшее, что может сделать организация, — выбрать единое решение CaaS для управления всем парком машин и использовать только доступные инструменты. В этом случае по мере роста организации затраты на управление парком будут оставаться в разумных пределах. Это практически тот же путь, который прошла компания Google с Borg. Необходимость индивидуализации Однако по мере роста в организации будут появляться все более разнообразные по- требности. Например, в 2012 году Google запустила Google Compute Engine (общедо- ступное облачное предложение «виртуальная машина как услуга»). В Google каждая виртуальная машина работала в отдельном контейнере под управлением Borg. Однако «скотоводческий» подход к управлению задачами был малопригоден для рабочих нагрузок Cloud, потому что каждый конкретный контейнер на самом деле был вир- 530 Глава 25. Вычисления как услуга туальной машиной, которую арендовал конкретный пользователь, а пользователи Cloud редко относились к виртуальным машинам как к скоту 1 Устранение этих различий потребовало значительных усилий с обеих сторон. Организация Cloud позаботилась о поддержке бесшовной миграции между вирту- альными машинами: для виртуальной машины, выполняющейся на одном сервере, создается копия на другом сервере, которая доводится до идеального состояния, и затем в нее перенаправляется весь трафик без заметного перерыва в обслужива- нии 2 . В Borg, с другой стороны, потребовалось внести изменения, чтобы избежать случайного уничтожения контейнеров, содержащих виртуальные машины (и дать время для копирования данных из старой виртуальной машины в новую), а также, с учетом ресурсоемкости процесса миграции, алгоритмы планирования Borg были оптимизированы для уменьшения вероятности перепланирования 3 . Конечно, эти изменения были развернуты только для машин, на которых выполняются облачные рабочие нагрузки, что привело к раздвоению (небольшому, но все же заметному) предложения Google для внутренних вычислений. Другой пример — который тоже привел к раздвоению — Google Search. Примерно в 2011 году один из контейнеров, обслуживающих веб-трафик Google Search, имел гигантский индекс на локальных дисках, в котором хранилась редко используемая часть веб-индекса Google (более распространенные запросы обслуживались кеша- ми в памяти в других контейнерах). Для создания этого индекса на конкретном компьютере требовалось несколько жестких дисков и несколько часов времени. Однако в то время Borg предполагала, что если какой-то из дисков, хранящий дан- ные в конкретном контейнере, вышел из строя, то контейнер не может продолжать работу и его необходимо перенести на другую машину. Эта комбинация (наряду с относительно высокой частотой отказов вращающихся дисков по сравнению с другим оборудованием) вызвала серьезные проблемы с доступностью: контей- неры постоянно сталкивались с проблемами, и требовалась целая вечность, чтобы снова запустить их. Для исправления этого недостатка в Borg пришлось добавить возможность, позволяющую контейнерам самостоятельно обрабатывать отказы дис- 1 Задание визуализации графики не является взаимозаменяемым с моим почтовым сервером, даже если обе задачи выполняются в одной и той же форме виртуальной машины. 2 Это не единственный повод для внедрения бесшовной миграции пользовательских вир- туальных машин. Она также предлагает значительные преимущества для пользователя, позволяя обновлять ОС или аппаратное обеспечение хоста без нарушения работы вирту- альной машины. Альтернативой (которая используется другими крупными поставщиками облачных услуг) является доставка «уведомлений о событиях обслуживания», например о том, что виртуальная машина может быть перезагружена или остановлена, а затем вновь запущена поставщиком облачных услуг. 3 Это особенно актуально, учитывая, что не все виртуальные машины клиентов включены в бесшовную миграцию. Для некоторых рабочих нагрузок даже кратковременное сниже- ние производительности во время миграции недопустимо. Эти клиенты будут получать уведомления о техническом обслуживании, а Borg со своей стороны постарается избегать вытеснения контейнеров с этими виртуальными машинами, разве что в случае крайней необходимости. Выбор вычислительной услуги 531 ков, в обход стандартной процедуры в Borg, то есть команде разработчиков Search пришлось адаптировать процесс, чтобы иметь возможность продолжать работу с частичной потерей данных. Множество других раздвоений, охватывающих такие области, как форма файловой системы, доступ к ней, управление памятью, локальность ЦП и памяти, специальное оборудование, особые ограничения планирования и т. д., привели к тому, что Borg API стал большим и громоздким, а результаты пересечения разных вариантов по- ведения стало труднее предсказывать и еще труднее проверять. Никто не знал, что произойдет, если контейнер одновременно запросит у Cloud замену и специальную обработку отказа диска в Search. После 2012 года команда Borg посвятила значительное время чистке Borg API, в ходе которой выяснилось, что некоторые функции Borg вообще вышли из упо- требления 1 . Еще одну важную группу образовали функции, которые используются несколькими контейнерами, но было неясно, действительно ли их применение было осознанным — процесс копирования файлов конфигурации между проектами привел к распространению функций, которые изначально предназначались только для опытных пользователей. Чтобы ограничить распространение таких функций и четко обозначить их предназначение только для опытных пользователей, был введен белый список. Однако чистка все еще продолжается, и некоторые измене- ния (например использование меток для идентификации групп контейнеров) еще не выполнены 2 Но, как это обычно бывает, даже если есть возможность приложить усилия для индивидуализации и получить некоторые преимущества, не добавляя недостатков (таких, как вышеупомянутый белый список для функциональных возможностей), рано или поздно придется сделать трудный выбор: нужно ли расширить явно (или, что еще хуже, неявно) поверхность API, чтобы приспособить ее для конкретного пользователя нашей инфраструктуры, или лучше доставить ему значительные не- удобства, но сохранить единообразие? Уровень абстракции: бессерверные вычисления Описание, как Google приручает вычислительную среду, можно воспринимать как рассказ о совершенствовании абстракции — более продвинутые версии Borg взяли на себя больше управленческих функций и еще больше изолировали контейнеры от базовой среды. Может показаться, что здесь все просто: больше абстракции — хорошо, а меньше абстракции — плохо. 1 Хорошее напоминание о том, что мониторинг и контроль за использованием функций со временем приобретут особую ценность. 2 Фреймворк Kubernetes, извлекший выгоду из опыта чистки Borg, но не испытывавший ограничений из-за обширной базы пользователей, с самого начала был более современ- ным (например, в отношении меток). Однако, получив более широкое распространение, Kubernetes начал испытывать похожие проблемы. 532 Глава 25. Вычисления как услуга Конечно, не все так просто. Пространство со множеством предложений имеет очень сложный ландшафт. Выше, в разделе «Приручение вычислительной среды», мы обсудили переход от домашних любимцев, выполняющихся на физических маши- нах (принадлежащих организации или арендованных в вычислительном центре), к управлению контейнерами как скотом. Между ними находится целый спектр предложений на основе виртуальных машин, которые могут играть самые разные роли, от гибкого средства замены «голого железа» (в предложениях «инфраструктура как услуга», таких как Google Compute Engine (GCE) или Amazon EC2) до тяжело- весных заменителей контейнеров (с автоматическим масштабированием и другими инструментами управления). По опыту Google, решение проблемы масштабного управления заключается в ис- пользовании подхода к управлению машинами как скотом. Повторю: если каждой из ваших команд потребуется всего одна машина — домашний любимец — в каждом из ваших центров обработки данных, то затраты на управление ими будут увеличиваться сверхлинейно с ростом организации (потому что количество команд и количество центров обработки данных тоже будут увеличиваться). А после перехода к управле- нию машинами как скотом контейнеры станут естественным выбором, поскольку они легче (в том смысле, что требуют меньше ресурсов и быстрее запускаются) и могут настраиваться в широких пределах, так что если вам потребуется организовать особый доступ к оборудованию для определенного типа рабочей нагрузки, вы с легкостью сможете (если захотите) сделать это. Преимущество подхода к виртуальным машинам как к скоту заключается, прежде всего, в способности использовать свою ОС, что имеет значение, если для рабочих нагрузок требуется разнообразный набор операционных систем. Кроме того, неко- торые организации уже имеют опыт управления виртуальными машинами, а также знают про конфигурации и рабочие нагрузки на их основе, поэтому могут отдать предпочтение виртуальным машинам вместо контейнеров, чтобы снизить затраты на миграцию. Что такое бессерверные вычисления? Еще более высокий уровень абстракции — бессерверные вычисления 1 . Допустим, что организация обслуживает веб-контент и использует (или хочет использовать) универсальную серверную инфраструктуру для обработки HTTP-запросов и от- правки ответов. Ключевой чертой инфраструктуры является поддержка инверсии управления, поэтому пользователь будет отвечать только за реализацию какого-либо «действия» или «обработчика» — функции на выбранном языке, которая принимает параметры запроса и возвращает ответ. В мире Borg для запуска этого кода создается контейнер, каждая реплика которого содержит сервер, состоящий из кода инфраструктуры и функций. Чтобы справиться 1 К бессерверным вычислениям относятся, например, технологии FaaS (function as a service — функция как услуга) и PaaS (platform as a service — платформа как услуга). Границы между этими терминами размыты. Выбор вычислительной услуги 533 с увеличением трафика, можно добавлять новые реплики или расширять центры обработки данных. Если трафик уменьшится, можно уменьшить количество рабо- тающих реплик до минимума (мы в Google обычно поддерживаем не меньше трех реплик в каждом центре обработки данных, где работает сервер). Однако если одну и ту же инфраструктуру использует несколько команд, возможен другой подход: многоарендность можно присвоить не машинам, а серверной инфра- структуре. Такой подход предполагает запуск большего количества экземпляров инфраструктуры, динамическую загрузку и выгрузку кода, определяющего действия и динамическую отправку запросов тем экземплярам, где загружен соответствующий код действия. В такой ситуации командам не нужно запускать свои серверы, а значит, они становятся «бессерверными». Бессерверные инфраструктуры часто сравнивают с моделью «виртуальные ма- шины как домашние любимцы». Идея бессерверных вычислений — революци- онная, потому что дает все преимущества управления скотом: автоматическое масштабирование, сокращение накладных расходов, отсутствие необходимости явно настраивать серверы. Однако, как отмечалось выше, переход к совместной многоарендной модели управления машинами как скотом уже должен быть целью организации, планирующей дальнейший рост, поэтому лучше сравнивать бессер- верные архитектуры с архитектурой «постоянных контейнеров», такой как Borg, Kubernetes или Mesosphere. Достоинства и недостатки Во-первых, обратите внимание, что бессерверная архитектура требует, чтобы код не имел состояния; едва ли можно запускать виртуальные машины пользователей или реализовать Spanner в бессерверной архитектуре. Никакие способы управ- ления локальным состоянием (за исключением его неиспользования), о которых мы говорили выше, не применимы в бессерверной среде. В мире контейнеров можно потратить несколько секунд или минут во время запуска, чтобы настроить подключения к другим службам, заполнить кеши из холодного хранилища и т. д. и ожидать, что в типичном случае у вас будет время, чтобы предпринять какие-то действия перед завершением. В бессерверной модели нет локального состояния между запросами; все необходимые данные должны подготавливаться в области видимости запроса. Как показывает практика, большинство потребностей организаций невозможно удовлетворить с использованием рабочих нагрузок без состояния. Это может привести к зависимости от конкретных решений (собственных или сторонних) конкретных задач (например, решений управляемых баз данных, которые часто сопровождают бессерверные облачные предложения) или к внедрению двух решений: контейнерного и бессерверного. Стоит упомянуть, что многие или большинство фреймворков бессерверных вычислений основаны на других вычис- лительных решениях: AppEngine — на Borg, Knative — на Kubernetes, Lambda — на Amazon EC2. 534 Глава 25. Вычисления как услуга Управляемая бессерверная модель привлекательна своей способностью адаптивного масштабирования потребления ресурсов, особенно с низким трафиком. Например, контейнер в Kubernetes не может масштабироваться до нуля контейнеров (предпо- лагается, что развертывание контейнера и узла — слишком медленная процедура, чтобы выполнять ее в ходе обслуживания запроса). Это означает, что существуют минимальные затраты, выражающиеся в обязательном наличии приложения в по- стоянной кластерной модели. Бессерверное приложение, напротив, легко масшта- бируется до нуля, поэтому стоимость владения масштабируется в соответствии с трафиком. При очень высоком трафике вступают в силу ограничения базовой инфраструктуры независимо от вычислительного решения. Если вашему приложению необходимо 100 000 ядер для обслуживания трафика, то на любом физическом оборудовании, поддерживающем вашу инфраструктуру, должно быть доступно 100 000 физических ядер. На более низком уровне, когда приложение получает достаточный объем тра- фика, чтобы поддерживать занятость нескольких серверов, но недостаточный, чтобы создать проблемы для поставщика услуги, оба решения — постоянное контейнерное и бессерверное — могут масштабироваться для его обработки, причем бессерверное решение будет масштабироваться активнее и точнее. Наконец, выбор бессерверного решения подразумевает определенную потерю контроля над средой. В некотором смысле это хорошо: наличие контроля означает необходимость его осуществлять и нести дополнительные накладные расходы. И ко- нечно, если вам понадобятся какие-то дополнительные функции, отсутствующие в используемой инфраструктуре, то это станет для вас проблемой. Возьмем один конкретный пример: команда Google Code Jam (проводившая сорев- нование по программированию с тысячами участников и реализовавшая для этого веб-интерфейс на Google AppEngine) запустила специальный сценарий за несколько минут до начала состязания, генерирующий искусственный трафик к веб-интерфейсу конкурса, чтобы разогреть достаточное количество экземпляров приложения для обслуживания фактического трафика после начала конкурса. Это сработало, но от такой своеобразной ручной подстройки можно было бы избавиться, выбрав бес- серверное решение. Компромисс Компания Google не стала вкладывать большие средства в бессерверные решения. Ее решение на основе постоянных контейнеров, Borg, достаточно продвинутое и может предложить большинство преимуществ бессерверных решений (например, автоматическое масштабирование, различные инфраструктуры для различных типов приложений, инструменты развертывания, унифицированные инструменты журналирования и мониторинга и т. д.). Единственное, чего не хватает Borg, — бо- лее агрессивного масштабирования (в частности, возможности масштабирования до нуля), но подавляющее большинство ресурсов в Google приходится на службы Выбор вычислительной услуги 535 с высоким трафиком, поэтому избыточность в предоставлении услуг обходится сравнительно дешево. В то же время многие приложения в Google не способны действовать в мире «без состояния», от GCE до собственных систем баз данных, таких как BigQuery ( https://cloud.google.com/bigquery ) или Spanner, и серверов, которым требуется много времени для заполнения кеша, как вышеупомянутые задания по обслуживанию поиска с длинным хвостом. То есть преимущества единой унифицированной архитектуры для всех этих приложений перевешива- ют потенциальную выгоду от наличия отдельного бессерверного стека для части рабочих нагрузок. Мы не утверждаем, что выбор Google является единственно правильным для всех организаций: многие другие организации успешно используют смешанные (контей- нерные и бессерверные) или чисто бессерверные архитектуры и сторонние решения для хранения данных. Основные выгоды бессерверные системы несут не крупным, а небольшим организа- циям или командам. Бессерверная модель, хотя и является более ограничительной, позволяет поставщику услуги взять на себя большую часть общих издержек по управлению и уменьшить эти издержки для пользователей. Запустить код, принад- лежащий одной команде, в общедоступной бессерверной архитектуре, такой как AWS Lambda или Google Cloud Run, значительно проще (и дешевле), чем настраивать кластер для запуска кода в службе контейнеров, такой как GKE или AKS, если толь- ко кластер не используется совместно многими командами. Если у вашей команды появится желание воспользоваться преимуществами управляемых вычислений, но ваша организация не захочет или не сможет перейти на решение с постоянными контейнерами, то бессерверные предложения поставщиков облачных услуг, вероятно, покажутся вам привлекательными, потому что стоимость (ресурсов и управления) общего кластера хорошо компенсируется, только если кластер действительно ис- пользуется несколькими командами в организации. Однако обратите внимание, что с ростом организации и распространения технологий управляемых вычислений вы, вероятно, перерастете ограничения чисто бессервер- ных решений. Это сделает более привлекательными решения, в которых существует прорывной путь (например, от KNative к Kubernetes), потому что они обеспечивают естественный переход к унифицированной вычислительной архитектуре, такой как в Google. Используйте их, если ваша организация решит пойти по нашему пути. Общедоступный или приватный Когда компания Google только начинала свою деятельность, предложения CaaS были в основном доморощенными: если вам нужно было такое решение, то вы создавали его. Единственный выбор между общедоступным и приватным пространством заклю- чался в выборе между приобретением своих машин и их арендой, но все управление вашим парком полностью возлагалось на вас. 536 Глава 25. Вычисления как услуга В эпоху общедоступных облачных услуг появились более дешевые варианты. Но есть и другие аспекты, которые организациям придется учитывать, делая свой выбор. Организация, использующая общедоступное облако, фактически перекладыва- ет (часть) издержек по управлению на поставщика облачных услуг. Для многих организаций это привлекательное предложение — они могут сосредоточиться на создании ценностей в своей области знаний и не накапливать опыт в области орга- низации инфраструктуры. Конечно, поставщики облачных услуг берут больше, чем стоит содержание чистого железа, чтобы возместить управленческие расходы, но у них уже есть накопленный опыт, и они делятся им со своими клиентами. Кроме того, общедоступное облако упрощает масштабирование инфраструктуры. По мере увеличения уровня абстракции — от покупки времени виртуальных машин до управляемых контейнеров и бессерверных предложений — растет простота мас- штабирования — от необходимости подписывать договор аренды до запуска простой команды, чтобы получить еще несколько виртуальных машин, и предоставления инструментов автоматического масштабирования ресурсов с изменением трафика. Прогнозирование потребления ресурсов — сложная задача, особенно для молодых организаций или продуктов, поэтому отсутствие необходимости заранее выделять ресурсы дает значительные преимущества. Одна из существенных проблем при выборе поставщика облачных услуг — боязнь оказаться в затруднительном положении: поставщик может внезапно повысить цены или просто уйти с рынка, поставив организацию в очень трудное положение. Один из первых поставщиков бессерверных вычислений, Zimki, предлагавший «платформу как услугу» для запуска JavaScript, закрылся в 2007 году, уведомив об этом клиентов за три месяца. Эту проблему можно частично смягчить, выбрав общедоступное облачное решение на архитектуре с открытым исходным кодом (например, Kubernetes). Такой подход оставляет открытыми пути для миграции на тот случай, если услуги конкретного поставщика по какой-то причине окажутся неприемлемыми. Эта стратегия поможет значительно снизить риски, но ее нельзя назвать идеальной. Из-за закона Хайрама трудно гарантировать, что возможности, поддерживаемые только этим конкретным поставщиком, не будут использоваться иначе. Эта стратегия имеет два возможных расширения. Одно из них — использовать обще- доступное облачное решение нижнего уровня (например, Amazon EC2) и запустить на его основе более высокоуровневое решение с открытым исходным кодом (напри- мер, Open-Whisk или KNative). Это поможет гарантировать возможность повторного использования любых настроек для высокоуровневого решения, инструментов, созданных на его основе, и сопутствующих неявных зависимостей, если вдруг вы решите мигрировать. Другое расширение — использовать мультиоблако, то есть управляемые службы, основанные на одних и тех же решениях с открытым исход- ным кодом, двух или более поставщиков облачных услуг (скажем, GKE и AKS для Kubernetes). Это еще больше упростит миграцию между ними, а также поможет Заключение 537 избежать попадания в зависимость от конкретных деталей реализации, доступных у одного из поставщиков. Еще одна стратегия — не столько ослабляющая зависимость, сколько упрощающая миграцию — использовать гибридное облако; то есть часть рабочей нагрузки разме- стить в приватной инфраструктуре, а часть — в общедоступном облаке. При таком подходе общедоступное облако можно использовать, например, для борьбы с пере- полнением. Типичная рабочая нагрузка организации может обрабатываться в при- ватном облаке, а в случае нехватки ресурсов некоторые рабочие нагрузки можно масштабировать в общедоступное облако. И снова, чтобы это решение было эффек- тивным, в обеих областях следует использовать одно и то же решение с открытым исходным кодом для организации вычислительной инфраструктуры. Обе политики — с использованием нескольких облаков или гибридного облака — требуют организации надежной связи между несколькими средами посредством прямых сетевых соединений между машинами и общих API в обеих средах. Заключение В ходе создания, совершенствования и эксплуатации своей вычислительной инфра- структуры мы в Google изучили детали проектирования таких структур. Наличие единой инфраструктуры для всей организации (например, одного или нескольких общих кластеров Kubernetes в каждом регионе) дает значительный выигрыш в эф- фективности управления и снижении затрат и позволяет разрабатывать общие инструменты поверх этой инфраструктуры. Ключевым инструментом в создании такой архитектуры являются контейнеры. Они позволяют разным задачам совмест- но использовать физическую (или виртуальную) машину, что способствует более эффективному расходованию ресурсов, а также обеспечивают уровень абстракции между приложением и ОС, дающий дополнительную устойчивость. Для правильного использования контейнерной архитектуры приложение должно состоять из узлов, которые можно автоматически заменять (как скот), чтобы позво- лить ему масштабироваться до тысяч экземпляров. Разработка ПО с применением этой модели требует определенного отношения к ресурсам, например все локальные хранилища (включая диски) нужно воспринимать как эфемерные и ни в коем случае не использовать жестко определяемые имена хостов. Мы в Google в целом удовлетворены выбором архитектуры, но многим организациям еще предстоит выбирать из широкого спектра вычислительных услуг — от модели «домашних любимцев» виртуальных или физических машин до модели «рабочего скота» реплицируемых контейнеров и абстрактной «бессерверной» модели, до- ступных в вариантах с управляемым и открытым исходным кодом. Ваш выбор будет основан на компромиссе, в котором будут учтены разные факторы. 538 Глава 25. Вычисления как услуга Итоги y Для масштабирования рабочих нагрузок необходима общая инфраструктура в продакшене. y Вычислительное решение может предоставить стандартную и стабильную аб- стракцию и среду для ПО. y ПО необходимо адаптировать для выполнения в распределенной управляемой вычислительной среде. y Организация должна тщательно выбирать вычислительное решение, чтобы обес- печить необходимые уровни абстракции. |