Анонсы сетевого блочного хранилища

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

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

  1. Сначала поговорим про ускорение сетевых SSD: они заметно ускорились за последний год.
  2. Дальше расскажем про улучшения в области работы с образами и снимками дисков.
  3. В третьей части поговорим про нереплицируемые диски: там произошел ряд улучшений.
  4. В четвертой части поговорим про новую технологию, которая у нас появилась — сетевое файловое хранилище.
  5. В конце пару слов про то, какие мы видим сейчас проблемы и фичи, которыми в ближайшее время займемся.

Часть 1. Ускорение сетевых дисков

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

Так как блочное устройство позволяет писать и читать конкретные блоки по номерам, нам нужен маппинг — таблица соответствия между номерами блоков, id блобов и смещениями внутри блобов, где лежат данные блоков. Эту таблицу мы называем индексом. Индекс хранится в виде LSM-базы поверх того же самого распределенного хранилища блобов.

Индекс состоит из трех основных частей. Первая часть — это лог небольших свежих записей. Если вы делаете небольшие (до 128 КБ) записи для сетевых SSD, то система старается накопить какое-то их количество, чтобы потом перезаписать в виде больших блобов.

Вторая часть индекса — это маппинг: номера блоков, blobid и offset для так называемых разреженных (sparse) блобов, содержащих блоки, которые логически расположены далеко друг от друга.

И третья часть — это индекс dense-блобов, которые хранят в идеале непрерывные диапазоны блоков. То есть блоки 1–1024.

Индекс sparse-блобов занимает довольно много места: приходится для каждого блока из такого блоба хранить отдельную запись в индексе. Ключом является номер блока, значением — id блоба и смещение внутри блоба. Если таких разреженных блобов становится много, индекс разрастается, перестает помещаться в памяти, система начинает работать медленнее.

Dense-блобы, так как они содержат соседние блоки и диапазоны блоков, можно кодировать очень эффективно: держать для каждого такого блоба одну запись в индексе, где в ключе записан диапазон блоков, которые хранятся в данном блобе, а в значении — id блоба и какие-то метаданные.

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

Оптимизация

Первое улучшение, которое мы сделали, это инкрементальный compaction. Поскольку compaction работает на сетке диапазонов по 4 МБ, то для блоков размером 4 КБ это будет диапазон в 1024 блока. Раньше процесс compaction читал все блобы из соответствующего диапазона и перезаписывал их в виде одного плотного блоба. Этот процесс создавал значительную фоновую нагрузку на хранилище помимо нагрузки пользователя. Смысл в том, чтобы уменьшить количество блобов. Поэтому в большинстве запусков compaction мы прекратили обрабатывать самые большие блобы из диапазона и стали перезаписывать основную массу маленьких блобов.

На рисунке ниже изображен этот процесс. По горизонтали — номера блоков, по вертикали — время. Видно, что в диапазоне из 1024 блоков лежал один большой dense-блоб — Blob 1. Сверху было записано несколько небольших блобов. Раньше мы читали все существующие блобы и формировали из них новый блоб. Blob 7 содержит какие-то части данных из Blob 1, которые не были перезаписаны, и данные из этих маленьких блобов 2–6.

Сейчас процесс выглядит так: Blob 1 в большинстве случаев сохраняется неизменным, мы читаем и перезаписываем только маленькие блобы и формируем Blob 7, который содержит данные из блобов 2–6.

Вторая оптимизация — это распараллеливание работы с индексом диска. Раньше запись и чтение в распределенное хранилище проходили через пул потоков, а работа с индексом была однопоточной. Сейчас мы ее распараллелили: для больших дисков представляем индекс в виде stripe из нескольких индексов, каждый из которых обрабатывается своим потоком. Это напрямую влияет на пропускную способность и IOPS для больших дисков. Получилось масштабирование по ядрам.

На картинке изображен stripe из двух частей: Tablet 1 и Tablet 2. Блоки 0–16 МБ хранятся в Tablet 1, 16–32 МБ — в Tablet 2, 32–48 МБ — снова в Tablet 1 и так далее.

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

Третья оптимизация лога свежих записей. Раньше мы хранили лог в виде таблицы в самой LMS-базе индекса. Сейчас мы его пишем отдельно от индекса, кешируем в память, читаем из памяти и при перезаписи читаем его в виде блобов из памяти. Это напрямую влияет на latency маленьких записей.

Тесты производительности

Сценарий, который мы используем для тестирования, состоит из двух стадий. На первой стадии мы заполняем диск большими запросами, на второй стадии мы подаем нагрузку и снимаем показатели производительности. Все тесты проводились на сетевом SSD объемом 1 ТБ. Ниже описаны варьируемые параметры:

  • параллельность;
  • iodepth;
  • режим: последовательное или параллельное чтение-запись;
  • размер запроса: 4 КБ или 4 МБ.

С запросами 4 МБ изначально все было неплохо и мы упирались не в производительность индекса, а в другие ограничения. При этом размере запроса даже с параллельностью 2 можно легко упереться в дисковый лимит, который сейчас составляет 450 МБ/с. Этот лимит мы позже будем пересматривать.

Основные улучшения коснулись сценариев с маленькими запросами по 4 КБ и сценариев случайного чтения-записи.

Первый тест: параллельность 32, размер запроса 4 КБ, продолжительность 2 минуты, случайное чтение-запись. Сетевое блочное устройство годичной давности выдало в таком тесте 7200 IOPS, сейчас это 11 200 IOPS — то есть более чем в полтора раза лучше.

Далее аналогичный тест, но параллельность увеличена до 256. Этот тест служит для того, чтобы показать, какой максимум может выдать устройство. Раньше это было 11 500, сейчас 15 000 IOPS — это дисковый лимит.

Дальше интересный тест продолжительностью три часа. Первый час мы просто подаем нагрузку, следующие два часа подаем ту же нагрузку и параллельно снимаем показания. Если начать замеры производительности с пустого диска, сначала производительность будет выше, а потом деградирует и выйдет на средний уровень. Этот тест нужен, чтобы показать sustained IOPS — количество операций, которые проходят при длительной нагрузке. Год назад при таком сценарии было 5000, сейчас 7700 IOPS — тоже в полтора раза больше.

Далее тест с последовательной записью с запросами по 4 КБ. И здесь тоже видно улучшение, не такое большое: раньше было 26 000, сейчас 32 500 IOPS — улучшение примерно на четверть.

Далее для сравнения к показанным тестам добавлен столбец с производительностью нереплицируемых дисков. Тест показывает повышение производительности более чем в 3 раза. Это тест с параллельностью 32.

В недалеком будущем у нас планируются серьезные оптимизации нереплицируемых дисков.

Часть 2. Улучшения в области образов и снимков дисков

Мы дали возможность без копирования быстро создавать большое количество дисков из образов. Сейчас эта фича в статусе Preview.

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

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

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

Также мы поработали над сервисом снимков дисков — snapshot service. Улучшения еще не дошли до продакшена, но в ближайший месяц дойдут. Мы полностью переписали этот сервис, убрали некоторые лишние зависимости между данными снимков, которые созданы с разных дисков. Это также позволило значительно продвинуться в реализации инкрементальных снимков. В ближайший месяц мы планируем выкатить эти оптимизации.

Оптимизации будут выражаться в следующих цифрах. Если в версии, которая была у нас в продакшене последний год, создание снимка занимало примерно 160 секунд, то сейчас это будет в два раза быстрее, около 80 секунд. Кроме того, цифры станут более предсказуемыми и стабильными: раньше в среднем было 160 секунд, но иногда бывали «выбросы» и snapshot создавался существенно дольше. Для терабайтного сетевого SSD было 80 минут, а станет 40.

Часть 3. Нереплицируемые диски

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

Заявлено на Preview GA
Maintenance железа Предупреждаем. Автоматически мигрируем данные.
Поломка железа Через некоторое время после поломки автоматически заменяем физ.диск на работающий пустой. Диск не трогаем, биллить при этом перестаем, появится возможность включения автопочинки через поддержку.*
Уведомления Через поддержку. Через API, email, UI.
VHost Нельзя попробовать в production. Можно попробовать в рамках Preview.

*В ближайшее время в продакшене.

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

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

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

Сейчас в большинстве случаев диск не трогаем. Нереплицируемый диск состоит из нескольких неймспейсов NVMe с одного физического диска или с разных. Если какой-то из этих неймспейсов сломался, мы переводим весь диск в состояние read-only и на две недели перестаем его биллить. Если нужно, мы даем возможность перенести какие-то уцелевшие данные либо сразу удалить диск.

Сейчас мы реализуем автопочинку диска и автозамену на пустой работающий диск. В ближайшем будущем появится возможность через поддержку включать автопочинку для отдельных фолдеров или cloud id.

По поводу уведомлений. На стадии Preview мы уведомляли пользователей о поломках через службу поддержки. Сейчас мы выбрали такой механизм уведомлений: в случае поломки диска вам приходит e-mail, факт поломки видно в консоли, а в API и в консоли в списке дисков видно актуальное состояние дисков: Ready или Error.

Про VHost user. Год назад мы говорили про экспериментальную технологию подключения дисков к гостю — VHost user. Сейчас подключение через VHost user можно попробовать на ваших тестовых нагрузках в продакшене (в статусе Preview).

Также год назад мы говорили про транспорт между инициатором и таргетом через SPDK, то есть между гостем и удаленным физическим устройством. В недалеком будущем он появится в Preview.
В тесте ниже сравнивается производительность нереплицируемого диска, который подключен по NBD (способ подключения по умолчанию для дисков) и VHost. Параллельность 32, запрос 4 КБ, случайное чтение-запись. С NBD получается до 40 000, с VHost — до 60 000 IOPS, то есть в полтора раза лучше.

Часть 4. Сетевое файловое хранилище

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

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

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

Пара слов про устройство файлового хранилища. Сетевое файловое хранилище сделано не поверх наших сетевых дисков. Эта система находится на том же уровне, что и сетевые диски, то есть она хранит данные поверх распределенного хранилища блобов. Отличие в разделении блоков по inode"ам, то есть по файлам. Система должна знать, какой блок какому файлу принадлежит. В остальном блочный слой сделан похожим образом. Также есть индекс inode, но он тоже хранится в виде LSM-базы поверх распределенного хранилища блобов. Подключается файловое хранилище к виртуалкам через virtio-fs.

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

На старте есть ряд ограничений. Файловая система сейчас может быть размером до 8 ТБ (в будущем можно увеличить). Количество файлов — до миллиона на 1 ТБ, то есть до 8 миллионов на всю файловую систему. Максимальный размер файла — до размера всей FS.

Также есть другое ограничение для виртуальных машин, к которым будет подключаться файловое хранилище. На стадии Preview для них будет недоступна миграция — это ограничение мы в будущем устраним. И диски, подключенные к такой виртуальной машине, сразу же подключаются через VHost user. Будет возможность создавать файловые системы поверх HDD и SSD с соответствующей производительностью, похожей на производительность сетевого блочного устройства.

Напомним, что находится на стадии Preview. Это быстрые образы, которые поддержаны в API и в консоли. Чтобы ими воспользоваться, нужно через службу поддержки поднять квоту на количество таких образов, оптимизированных под развертывание. Можно попробовать подключение нереплицируемых дисков через VHost с соответствующим ростом производительности. VHost сейчас в тестовом режиме, не для продакшн-нагрузок. И сетевое файловое хранилище — в течение ближайшего месяца. Чтобы во всем этом поучаствовать, можно обратиться к личному менеджеру или в службу поддержки.

Часть 5. Проблемы и фичи

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

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

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

А еще есть такая идея: сейчас все снимки хранятся глобально — в хранилище, распределенном на все зоны доступности. Но если это ограничение снять и хранить снимки в рамках одной зоны, будет возможность поддержать мгновенное создание снимков из дисков и мгновенное создание дисков из снимков.

Анонсы сетевого блочного хранилища
Войдите, чтобы сохранить пост