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

  • Google AppEngine — полноценная платформа Web-разработки;
  • различные сервисы Amazon, например Elastic MapReduce, система выполнения сложных вычислений над данными;
  • Yahoo Query Language — платформа для объединения Web-источников данных;
  • облачные хранилища данных, например Aster Data Cloud Edition;
  • всевозможные инструменты с открытым кодом для облаков, например предлагаемые компанией Cloudera.

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

После работы в компании Yahoo в составе команды, которая спроектировала и построила Sherpa — крупномасштабную СУБД для частного облака Yahoo [1], я, пользуясь облачными сервисами, всегда вспоминаю о собственном опыте построения крупной облачной системы (в некоторых отношениях чрезвычайно трудном). Возможно, уроки, полученные мною при создании облачной базы данных, могут оказаться полезными инженерам, которые строят системы и других типов.

Начала

Жизненный цикл Web-приложения часто начинается с небольшого количества пользователей и ограниченного круга возможностей, но с развитием сервиса обычно растет его пользовательская база и функционал, поэтому в случае с Sherpa нашей задачей было построение гибкой платформы, рассчитанной на быстрое изменение приложений и на эластичное масштабирование до рабочих нагрузок, имеющих огромную интенсивность. Добиться такого сочетания гибкости и масштабируемости, пользуясь традиционными СУБД, сложно, поэтому мы решили построить совершенно новую систему. Более того, мы решили так организовать облачный сервис, чтобы инженеры Yahoo могли быстро разрабатывать приложения для него, не занимаясь при этом настройкой и администрированием базы данных.

На высоком уровне Sherpa имеет четыре компонента [1]. Большинство серверов системы представляют собой модули хранения, которые содержат блоки данных и выполняют операции записи и чтения над ними. Для выполнения запроса маршрутизаторы проверяют права клиента и затем переадресуют запрос модулю хранения, содержащему нужный блок данных. Надежный брокер сообщений, действующий по схеме публикации/подписки, обеспечивает асинхронное тиражирование между центрами обработки данных. Наконец, контроллер табличных фрагментов управляет конфигурацией кластера Sherpa в ЦОД.

Искусство разработки API для облаков

Создатели многих облачных систем отвергают SQL как интерфейс, не пригодный для таких задач, и идут по одному из двух путей. В одних случаях проектировщики берут на вооружение уже существующую парадигму, зачастую копируя интерфейс Google Bigtable для СУБД или модель MapReduce для аналитических систем. В других случаях разработчики создают крайне минималистичный интерфейс, реализующий только операции CRUD (create, read, update, delete — создание, чтение, обновление, удаление) или другой столь же ограниченный набор. В Sherpa мы воспользовались именно таким подходом и спроектировали простой интерфейс наподобие REST (Representational State Transfer, протокол для Web-сервисов, основанный на HTTP), реализующий всего несколько операций.

Однако простого выбора синтаксиса при этом недостаточно — в качественном облачном API также должны быть учтены следующие особенности.

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

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

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

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

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

Что стоит, а что не стоит разрабатывать

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

  • уровень постоянного хранения (на основе MySQL);
  • высокопроизводительный серверный компонент (на основе Apache);
  • высокопроизводительный маршрутизатор запросов (на основе Yahoo Traffic Server);
  • надежную систему обмена сообщениями между центрами обработки данных для тиражирования (специально написанную нами);
  • механизмы управления кластерными системами и их мониторинга (большей частью состоящие из специально разработанного кода);
  • системы безопасности для управления сертификатами и аутентификации (специально разработанный код);
  • собственную логику Sherpa для выполнения операций чтения и записи (специально разработанный код).

Sherpa не одинока в столь высоком уровне сложности — проекты с открытым кодом, такие как Apache Cassandra и HBase, состоят из множества готовых и специально написанных компонентов. К выбору этих компонентов следует подходить очень тщательно, например, благодаря тому, что мы выбрали MySQL в качестве основы для уровня хранения, а не написали соответствующий код самостоятельно, нам удалось ускорить разработку и добиться хорошей производительности. Однако, остановившись на MySQL, мы во многих отношениях ограничили себя — ввиду особенностей профиля производительности MySQL наша система обеспечивает хорошее быстродействие при считывании, но не настолько эффективна при записи (особенно высокоскоростной), как некоторые другие системы [2]. MySQL необходимо тщательно настраивать, чтобы получить хорошую производительность, а поскольку в этой СУБД используются B-деревья (в которых для произвольной вставки ключей предусмотрены обширные пробелы), пространство хранения используется нашей системой не совсем рационально. К тому же MySQL является отдельным компонентом со своей инфраструктурой администрирования и мониторинга. Кроме того, на каждом сервере хранения нам приходится тщательно распределять ресурсы между серверным процессом (Apache) и MySQL, чтобы добиться оптимальной производительности. Несмотря на все это, мы считаем, что выбор в пользу MySQL был сделан верно — с каким-то другим из готовых компонентов разработка могла бы существенно замедлиться.

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

Первоначально мы предполагали, что Sherpa должна будет поддерживать и хеш-таблицы, и упорядоченные таблицы — как продемонстрировали системы вроде Chord [3], балансировку нагрузки проще выполнять с хеш-таблицами, однако для некоторых приложений более естественно использовать модель упорядоченных данных, как показывают примеры наподобие Bigtable [4]. Следуя принципу «начинать с простого», мы сначала реализовали только хеш-таблицу, но при этом позаботились и о возможности обеспечения поддержки в дальнейшем упорядоченных таблиц. Мы разработали схему секционирования данных, инфраструктуру маршрутизации запросов, механизм тиражирования и т. д. с расчетом на упорядоченные данные, хотя в первой версии поддерживалось только хеширование. Благодаря этому мы со временем смогли реализовать и вариант с упорядоченной таблицей. Тем не менее, несмотря на все принятые меры, добавление структур для упорядоченных таблиц оказалось крайне сложной задачей, а кроме того, нам пришлось перерабатывать балансировку нагрузки, API и модель дискового хранения.

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

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

Тестирование под реальной нагрузкой

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

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

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

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

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

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

***

Построение надежной облачной платформы сопряжено с другими трудностями и препятствиями, не упомянутыми в данной статье, но здесь я постарался обратить внимание на уроки, которые изначально не были очевидными при инициации работ над Sherpa. Исследователи разрабатывают немало облачных баз данных, в том числе системы с открытым кодом, такие как Cassandra и HBase, и проприетарные системы (Sherpa). Облачные СУБД представляют собой гибкие, масштабируемые платформы, способные упростить создание разнообразных Web-приложений, работающих с большими объемами данными — от социальных сетей и позиционно-ориентированных сервисов до сайтов доставки новостей.

Литература

1. B. Cooper et al., PNUTS: Yahoo!’s Hosted Data Serving Platform. Proc. VLDB Endowment, vol. 1, no. 2, 2008.

2. B. Cooper et al., Benchmarking Cloud Serving Systems with YCSB. Proc. 1st ACM Symp. Cloud Computing (SoCC 10), ACM Press, 2010.

3. I. Stoica et al., Chord: A Scalable Peer-to-Peer Lookup Service for Internet Applications. Proc. 2001 Conf. Applica­tions, Technologies, Architectures, and Protocols for Computer Communications (SIGCOMM 01), ACM Press, 2001.

4. F. Chang et al., Bigtable: A Distributed Storage System for Structured Data. ACM Trans. Computer Systems, vol. 26, N2, 2008.

Брайан Купер (cooperb@google.com) — инженер по программному обеспечению компании Google.

Brian Cooper. The Prickly Side of Building Clouds. IEEE Internet Computing, November/December 2010, IEEE Computer Society. All rights reserved. Reprinted with permission.
Облака в развивающихся странах
Для развивающихся стран облака – это шанс получить доступ к передовой ИТ-инфраструктуре, центрам обработки данных и современным приложениям, обеспечить защиту важной информации, однако для этого надо минимизировать связанные с ними риски.

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