Как Alluxio сохраняет состояние файловой системы с высокой доступностью и отказоустойчивостью

Как Alluxio сохраняет состояние файловой системы с высокой доступностью и отказоустойчивостью

15 апреля 2022 г.

Сценарист: Тайлер Крейн


Вступление


Alluxio реализует виртуальную распределенную файловую систему, которая позволяет получать доступ к независимым большим хранилищам данных с вычислительными механизмами, такими как Hadoop или Spark, через единый интерфейс. В кластере Alluxio мастер Alluxio отвечает за координацию и поддержание согласованности доступа к базовым хранилищам данных или файловым системам (сокращенно UFS). Мастер содержит глобальный снимок метаданных файловой системы, и когда клиент хочет прочитать или изменить файл, он сначала связывается с мастером. Учитывая его центральную роль в системе, мастер должен быть отказоустойчивым, высокодоступным, строго согласованным и быстрым. В этом блоге будет обсуждаться эволюция мастера Alluxio от сложной многокомпонентной системы с использованием Zookeeper до более простой и эффективной с использованием Raft.


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


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


Журнал и высокая доступность (высокая доступность)


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


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


Реализация нашего первоначального дизайна


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


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


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



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


Теперь мы опишем, как с помощью Raft можно сделать более простой и эффективный дизайн.


Улучшенный дизайн на основе Реплицированного конечного автомата



Сначала давайте введем понятие (реплицированного) конечного автомата. Конечный автомат принимает на вход ряд команд, которые изменяют внутреннее состояние системы и могут производить некоторые выходные данные. В нашем случае конечный автомат должен быть детерминированным, а именно при одном и том же наборе входных данных система будет выдавать одинаковые выходные данные. Если мы думаем о нашем журнале как о машине состояний, мы можем думать о нем как о простом журнале, который имеет команду добавления, которая добавляет запись журнала в конец журнала. В таком случае реплицированный конечный автомат является конечным автоматом, обеспечивающим высокую доступность и отказоустойчивость. Это достигается путем репликации конечного автомата на нескольких узлах и запуска алгоритма консенсуса, чтобы гарантировать выполнение одних и тех же команд в одном и том же порядке на каждой реплике. Raft [4] является одним из таких реплицированных протоколов конечных автоматов, пользователю просто нужно предоставить детерминированный конечный автомат в качестве входных данных для протокола. Одним из важных свойств Raft является то, что он обеспечивает условие непротиворечивости линеаризуемости[5]. Учтите, что каждая команда, выполняемая клиентом на реплицированном конечном автомате, имеет вызов и ответ. Линеаризуемость гарантирует, что операция будет наблюдаться всеми клиентами в некоторый момент времени между вызовом и ответом. Этот момент времени называется точкой линеаризации операции. Точки линеаризации всех операций дают нам общий порядок операций, следующих за последовательной спецификацией реализуемого конечного автомата. Таким образом, несмотря на то, что наш конечный автомат реплицирован, клиенту кажется, что он выполняет операции последовательно в режиме реального времени.


Реализация журнала с помощью Raft


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


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



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


Использование Raft значительно упростило внедрение и улучшило производительность и масштабируемость высокодоступных и отказоустойчивых мастеров Alluxio. Работа внешних систем Zookeeper и UFS была заменена реализацией Apache Ratis [8] Raft, работающей непосредственно на главных узлах Alluxio. Концепция низкоуровневого журнала, который будет воспроизводиться после восстановления, больше не нужна. Вместо этого нам нужно только получить доступ к быстрым хранилищам ключей и значений с помощью RocksDB, которые эффективно и последовательно реплицируются Raft для обеспечения высокой доступности и отказоустойчивости. Мы также рекомендуем прочитать, как Confluent упростила и улучшила производительность своей распределенной архитектуры, перейдя с Zookeeper на Raft в Apache Kafka [9].


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


[1] Хант, Патрик и др. «{ZooKeeper}: Координация без ожидания для систем интернет-масштаба». Ежегодная техническая конференция USENIX 2010 (USENIX ATC 10). 2010. https://zookeeper.apache.org.


[2] Жункейра, Флавио П., Бенджамин С. Рид и Марко Серафини. «Заб: высокопроизводительная трансляция для основных резервных систем». 2011 г. 41-я Международная конференция IEEE/IFIP по надежным системам и сетям (DSN). ИИЭР, 2011.


[3] Выборы лидера куратора Apache. https://curator.apache.org/curator-recipes/leader-election.html


[4] Онгаро, Диего и Джон Оустерхаут. «В поисках понятного алгоритма консенсуса». Ежегодная техническая конференция USENIX 2014 (Usenix ATC 14). 2014. https://raft.github.io.


[5] Херлихи, Морис П. и Жаннетт М. Винг. «Линеаризуемость: условие правильности для параллельных объектов». Транзакции ACM по языкам и системам программирования (TOPLAS) 12.3 (1990): 463-492.


[6] RocksDB. http://rocksdb.org.


[7] О’Нил, Патрик и др. «Логово-структурированное дерево слияния (LSM-дерево)». Acta Informatica 33.4 (1996): 351-385.


[8] Апач Ратис. https://ratis.apache.org.[9] Apache Kafka — это просто: первый взгляд на Kafka без ZooKeeper. https://www.confluent.io/blog/kafka-without-zookeeper-a-sneak-peek/.


Также опубликовано [Здесь] (https://www.alluxio.io/blog/from-zookeeper-to-raft-how-alluxio-stores-file-system-state-with-high-availability-and-fault-tolerance/ )



Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE