Abstract: О новом тренде — software defined strorge и главной родовой травме блочных устройств — обещании бесконечной надёжности.
Лирика
На горизонте новый buzzword: Software defined $thing. Мы уже имеем состоявшийся и сформировавшийся круг всего, относящегося к software defined networks (SDN), пришла очередь и storage (SDS). Видимо, дальше у нас будет software defined computing или ещё что-то подобное, потом резко всполошатся и подтянутся HP/VMWare и предложат (private) «software defined enterprise», который будет означать всё тоже, что было, но ещё моднее и актуальнее.
Впрочем, рассказ не про баззворды. За каждым таким странным названием (grid, elastic, cloud) стоит дальнейшее развитие технологий — построение дальнейших слоёв взаимодействия компонент (эм… взаимодействия участников взаимодействия, иначе не скажешь), основным мотивом которых является уход от гранулированности компьютерной системы, так, чтобы вся терминология, вся предметная область ушла от «межпроцессного взаимодействия» и стала автономной. В более-менее приличном виде мы это (в виде уже свершившегося факта) мы видим в волшебном мире javascript работе www, когда нас никаким образом не волнуют сервера, на которых крутятся задачи — всё общение происходит на уровне между браузером (с учётом его интимных подробностей DOM, JS и т.д.) и абстракцией, под названием URI, которой не важно — один это сервер или сотни разных.
Это взаимодействие выглядит очень соблазнительным, так что его распространяют на все остальные области, по мере возможности.
Перед рассказом про SDS, посмотрим на уже состоявшееся: SDN (software defined network).
В SDN всё сетевое оборудование (реальное железо или виртуальные свичи на хостах виртуализации) используются в качестве тупых исполнителей, а вся интеллектуальная работа по построению реальной сети делегируется приложению, которое тем или иным методом «понимает» что нужно и изготавливает топологию сети по необходимости. Я опускаю все названия конкретных технологий (openflow, big switch, floodlight, Nicra), так как главное в SDN идея о формировании конфигурации сети с помощью софта, а не детали реализации.
Так вот, что же такое тогда Software Defined Storage (SDS)? По аналогии мы можем сказать, что это такая система хранения данных, в которой вся интеллектуальная работа по построению системы хранения данных делегируется программе, а железо и «локальный софт» (уровня хостов) работает в качестве тупых исполнителей.
Наверное, самым успешным и образцово-показательным решением тут выступает Openstack's Swift, который создаёт устойчивое и масштабируемое хранилище блобов с помощью тупых дисков и xfs на них, от которых ничего-ничего не требуется — только capacity и немножко performance. Всё остальное делает софт.
Но swift — это не совсем «storage», это «object storage». То есть хранилка для файликов. Без возможности писать в середину файла, и уж точно не обеспечивающая десятки тысяч IOPS'ов на запись с микросекундными задержками.
А общественность жаждет именно этого. Надёжно, дёшево, с произвольной и гарантированной избыточностью, fault tolerance, high availability, geo replication, auto ballanced, self healing, из коммодити железа (то есть ещё раз дёшево), высокопроизводительно, с неограниченным масштабированием производительности и емкости по мере роста числа нод, muti tentant, accountable (тут клиент не выдержал возбуждения и начал, упав на ковёр, сучить ножками). Всё это, да ещё и ложкой.
В реальности
Аналогия SDN-SDS обладает одним маленьким нюансом, который всё делает сложным. В SDN от сетевого оборудования (того, что тупое и просто слушается командного центра) требовалось одно — перекладывать байтики. В SDS от тупых устройств хранения требуется не только взять байтики и донести до/от клиента, но ещё и хранить их.
В этом месте кроется самая большая, сложная и неприятная проблема. Мы можем взять и выкинуть сдыхающий свитч. Мы можем это сделать даже программно. Никто ничего не заметит.
Но мы не можем просто так взять и выкинуть работающее «тупое» хранилище. До того, как другое хранилище сможет продолжать работу, кто-то должен пойти и скопировать к себе его данные.
Да-да, всё дело в хранении. Если бы у нас были write-only хоронилища для информации, то их реализация была бы тривиальной. Не можем писать сюда? Поднять ещё одну ноду, начать писать туда.
Но ведь нам со сдохшей ноды надо было бы ещё и прочитать, что было записано… А нода умерла. Ой?
Таким образом, модель SDS полностью совпадает с SDN с точки зрения процесса IO. А вот хранение — совсем новая, отдельная проблема. Которая называется CAP-теорема. И решения там не видно.
В чём же проблема? Если задача не может быть решена, значит надо менять условия задачи.
И вот тут вот начинается самое интересное — если верхи не могут, а низы не хотят — это же начало революции, правда? Смена задачи — это же смена модели, по которой идёт работа с блочными устройствами. Вся чехарда вокруг SDS — это же, в конце-концов, про файловую систему на блочном устройстве, на которую можно положить SQL-базу и работать с ней очень-очень быстро, надёжно, дешёво, консистентно (опять клиент зашёлся в счастливой истерике...).
Добрый TCP и злая файловая система
Если вам кто-то предоставит сеть, в которой будет теряться 1 из 10000 пакетов, вы будете считать, что у вас идеальная сеть. Все сетевые приложения без исключения готовы к потере пакетов, и проблемы начинают появляться, когда потери поднимаются к десяткам процентов.
Добрый-добый TCP прощает почти всё — повторы, потери, джиттеринг (резкое изменение latency), изменение пропускной полосы, порчу данных внутри пакета… Если становится совсем плохо, то и TCP начинает работать медленно и вяло. Но работать! Более того, даже если условия работы становятся невыносимыми даже для TCP (например, 70-80% потери пакетов), то большая часть сетевых приложений готова к ситуации обрыва сетевого соединения, и она просто переподключается заново, без далеко идущих последствий.
Сравним это с блочными устройствами. Что будет, если вам продадут дисковое устройство, которое теряет 1 из 1000000 запросов? Злая файловая система этого не простит. Что будет, если вы улучшите качество в 100 раз, и у вас будет ломаться 1 из 100000000 запросов? Файловая система это не простит. И не просто не простит, но отомстит самым ужасным образом. Если файловая система обнаружит, что 1 из триллиона запросов на запись не удался, то она откажется работать с таким позорным блочным устройством. В лучшем случае она перейдёт в режим read only, в худшем — просто перестанет работать.
А что произойдёт с программой, у которой файловая система выкинула такую вещь? Никто не знает. Может быть, она просто завершится. А может быть, начнёт плохо работать. Или зависнет. Если на этом блочном устройстве был файл подкачки, то некоторые операционные системы запаникуют. Особенно, если там были какие-то важные данные (например, кусочек файлового буфера на чтение у программы cat — и весь сервер со всеми своими тысячами клиентов идёт мигать тремя светодиодами на клавиатуре).
Что же сделает, например, база данных, если мы поменяем в результате ошибки один-единственный из миллиарда блоков? (один 4к-сектор на 4Тб диске). Во-первых, она этого не заметит. Во-вторых, если она заметит (ей что-то не понравится в прочитанном) — объявит базу данных неполноценной, подвергнет апартеиду, обрезанию, лишению гражданских прав и объявит basa non granta в системе.
Другими словами, от дискового стека ожидают бесконечной надёжности.
Весь блочный стек беспощаден к ошибкам. Вендоры просят десятки и сотни миллионов рублей за системы, которые почти никогда не делают ошибок. Но даже их системы делают ошибки. Реже, чем коммодити железо. Но кому от этого легче, если вам не прощают даже одной ошибки на квадриллион операций? (1 сбойный блок на 4 Еб записанного/прочитанного, 4к блоки).
Разумеется, решением на это будет повышение надёжности. Рейды, кластерные системы, мейнфреймы… Где-то мы это уже видели. Получается не дорого, а запредельно дорого. Если бы ноутбуки делали по технологиям мейнфреймов, то они бы ломались в тысячу раз реже, а стоили бы в миллион раз дороже.
Кто-то шепчет что-то про RAID'ы. Что ж, давайте разберём, что делают рейды. Рейд берёт несколько блочных устройств и собирает из них новое блочное устройство. С повышенной надёжностью (а быть может, и производительностью). При этом он предъявляет ровно такие же требования к качеству устройств снизу — ошибка — и диск объявляется плохим. Навсегда. Дальше там ребилд той или иной степени культурности.
Наиболее продвинутые проприентарные решения позволяют дискам иногда делать ошибки и бракуют их после превышения некоторого порога.
Но при этом, случись какая-то проблема, любая ошибка рейда (например, таймаут на IO) приведёт к такому же объявлению всего рейда «плохим». С такими же последствиями для приложений, использующих данные на файловой системе на этом рейде. Другими словами, от рейда требуется из нескольких ненадёжных устройств сделать… опять, бесконечную надёжность (нулевую вероятность отказа). Теорвер негодуэ.
… А добрый, всепрощающий TCP, взирает на заблудшие души с состраданием и любовью.
Что же делать?
Во-первых, надо признать, что не бывает идеальных вещей. Если DNA с миллиардолетней эволюцией не сумела защитить себя от ошибок, то наделяться на пару лет (десятилетий) инженерной мысли, мягко говоря, не разумно. Ошибки могут быть. И главное, что нужно научиться делать с этими ошибками — не устраивать истерики из-за мельчайшего несовершенства.
Нам вернули ошибку? Попытаться повторить, не удалось повторить — вернуть выше по стеку. Файловая система молча идёт и кладёт метаданные другое место, если не удалось записать в это (а не устраивает истерику размером во весь сервер). СУБД, получив ошибку записи (чтения) в/из журнал(а) не объявляет базу одержимой, и не проклинает до седьмого колена все приложения, с этой базой работающие, а просто достаёт резервную копию, нет резервной копии, аккуратно помечает данные как повреждённые, возвращает ошибку или пометку о повреждении. Приложение, работающее с базой данных, получив подобное, не делает глупостей, а спокойно работает с тем, что есть, стараясь минимизировать повреждения и честно говоря о размере повреждений тому, кто с этими данными работает. И каждый из уровней полностью проверяет правильность данных с нижележащего уровня, не полагаясь на слова «да, я сумел прочитать число пи из файла, его значение 0x0000000000000»
Да, у нас повредилась одна банковская транзакция по вашей карте. Да, мы не знаем точно, сколько у вас было списано денег. Но у нас есть промежуточные балансы, так что вы можете продолжать пользоваться картой, а повреждённые данные мы либо спишем за старостью лет, либо восстановим на следующей неделе. Это вместо «Неизвестная ошибка. Операция по карте невозможна, обратитесь в службу поддержки карт вашего банка».
Отъедание малого кусочка данных не должно приводить к порче большего куска данных. В древнееврейской мифологии описан один случай, когда из-за надкусанного яблока выбраковали целое человечество, разогнали весь рай, ободрали змее все ноги и, вообще, повели себя так, как ведёт себя современная файловая система при обнаружении надкусанного жёсткого диска. Насколько я знаю, это событие считается трагичной ошибкой. Не надо так больше делать. Надкусили яблоко — выкиньте яблоко, и не более.
Таким образом, главное изменение, которое должны принести за собой SDS — это изменение отношения к ошибкам блочных устройств. Так, чтобы 1% дисковых ошибок считался не очень хорошим, но терпимым показателем. А 0.01% — просто замечательным сервисом.
В этих условиях появится возможность делать сервисы без ожидания бесконечной надёжности — разумные ожидания за разумные деньги.
Блочные устройства будущего
И как тогда выглядит software defined storage будущего? Если мы позволяем себе иногда делать ошибки, то нашей задачей становится не их предотвращение, а уменьшение их числа.
Например, мы можем сильно распараллелить операции. Если за хранение данных отвечает 1000 узлов, то отказ одного или двух из них для нас всего лишь означает 0.1% или 0.2% ошибок чтения или записи. Нам не надо изгаляться с гарантированно синхронными репликациями. Ну да, «вылетела нода, выкинули из сервиса, добавили новую». В принципе, это не очень хорошая ситуация (т.к. если потом вылетит ещё парочка, то мы подползём аж к 0.4% потерь, что снизит качество хранения данных). Но мы можем поднять ноду из бэкапа. Да, там будут устаревшие на сутки данные, и мы для части данных будем нещадно врать (возвращать не то, что записали). Но ведь вышестоящий уровень готов к этому, правда? А за счёт того, что поменялось всего 2-3% от данных с ноды, то мы вместо 0.1% отказов в чтении (и почти 0% отказов в записи — ведь мы пишем на другие ноды) мы получим 0.002% ложных данных на чтении.
0.002% — это ведь 99.998% надёжность. Мечта? Если к такому готовы — да.
И получающаяся конструкция оказывается невероятно простой: свифтоподобная система хранения блоков, размазанных по куче серверов и куче дисков. Без особых требований к обязательной целостности данных — если мы иногда отдаём устаревшие данные, то это всего лишь «брехня при чтении», и если мы это делаем не слишком часто, то всех всё устраивает. Мы можем в любой момент времени «потерять» запрос клиента и быть уверенным, что он его, если надо, несколько раз пришлёт. Мы можем работать не в революционно-героическом режиме "СХД бы делать из этих людей: Надёжнее бы не было в мире СХД", а в комфортном режиме, когда прилежность и исполнительность в большую часть времени, вполне компенсирует редкие ошибки.
А где тут SDS?
Во всём предыдущем не было ни слова про SDS. Где тут 'software defined'?
В описанной выше схеме «ноды-исполнители» будут всего лишь выполнять то, что им командует ПО. ПО же в свою очередь будет формировать описание того, где и что читать и куда писать. В принципе, это всё уже есть. Кластерные файловые системы предыдущего поколения, CEPH, возможно, чуть-чуть переразвившаяся до сетевого уровня BTRFS, может быть, подоспевший elliptics — оно, практически, готово. Остаётся написать нормальную multi-tenancy, конвертацию из логической топологии клиентского представления в команды «тупому железу» (контроллер для SDN) — и всё готово.
Итог
Основной вывод: ключевой проблемой в развитии блочных устройств в настоящий момент являются чрезмерно завышенные (бесконечные) ожидания по надёжности и достоверности работы блочных устройств, а так же существующая дурная традиция раздувать ошибки блочных устройств, увеличивая размер домена повреждений до домена задачи (а иногда и за его пределы). Отказ от 100% надёжности всегда и везде позволит с куда меньшими усилиями (т.е. с меньшей стоимостью) обеспечить условия для создания (или даже применения существующих) решений для SDN.
Автор: amarao