Привет, Хабр. Меня зовут Ори Брук, я ведущий инженер в департаменте разработки объектных хранилищ YADRO. Раньше мы не писали о TATLIN.OBJECT, нашей децентрализованной системе хранения данных (СХД). Исправляемся, ведь у нас есть повод — релиз новой функциональности S3-зеркалирования. Она позволяет бесшовно переносить данные из S3-совместимого хранилища MinIO в нашу СХД. Впрочем, функция может работать с любым S3-совместимым хранилищем в качестве источника данных в рамках поддерживаемых вызовов.
Разберемся, как устроено децентрализованное хранилище, как работает S3 прокси-мигратор, и посмотрим на результаты замеров его производительности на примере MinIO.
Что такое объектное хранилище TATLIN.OBJECT
TATLIN.OBJECT — это программно-аппаратный комплекс с децентрализованным движком хранения данных, который был разработан в YADRO. Децентрализация позволяет масштабировать СХД, добавляя новые узлы хранения в любых локациях. Также она обеспечивает доступ и сохранность данных во многих неприятных сценариях: от отказа одного узла до заметных сбоев интернета. Решение поддерживает протоколы S3, HTTP(S) и gRPC, стандартные Amazon SDK для S3, а также собственные SDK для Go, С#, Python и Java для работы по нативному протоколу gRPC.
Аппаратная платформа
TATLIN.OBJECT работает на базе серверных решений YADRO, таких как VEGMAN. СХД заказчик устанавливает во внутреннем контуре компании (on-premises) и поэтому сохраняет полный контроль над хранящимися данными.

Для запуска TATLIN.OBJECT в компании достаточно всего четырех узлов хранения. Затем систему можно наращивать с кратностью в один узел и вплоть до сотни узлов.
Пример организации кластера из четырех узлов хранения
Состав узла хранения:
2 × CPU x86
256 ГБ RAM
Либо 6/12 × NL SAS HDD 16 TБ + 4 × SATA SSD 1,92 TБ (гибридные узлы)
Либо 6/12 x SAS SSD 7,68 ТБ (узлы all-SSD)
2 × 10/25 Гбит с Ethernet для внутренней сети
2 × 10/25 Гбит с Ethernet для доступа к данным
1 × 1 Гбит с Ethernet для сети управления
1 × 1 Гбит с Ethernet для локального сервисного доступа

Разберем основные особенность TATLIN.OBJECT с точки зрения ПО — отечественного движка хранения данных, разработанного с нуля.
Децентрализация
Движок — это децентрализованная распределенная сеть хранения данных, интегрированная с блокчейном: в ней нет единой точки принятия решений, поэтому нет и единой точки отказа. Узлы сети работают по P2P-принципу, и каждый обеспечивает целостность и доступность данных согласно заданной политике.
Система не разделена на множество мелких отдельных хранилищ, а работает как единая глобальная сеть на базе блокчейна. Он служит своего рода децентрализованными «часами» с монотонно возрастающим временем в виде счетчика блока. Время в движке хранения данных дискретно и не зависит от астрономического.
Блокчейн в системе играет роль распределенной базы данных. Фактически, это хранилище типа «ключ-значение», где смарт-контракты работают как таблицы или хранимые процедуры. Чтобы система работала быстро, обработка и хранение самих объектов (полезной нагрузки) вынесены за пределы блокчейна. В нем мы проводим операции только с конфигурациями компонентов, смарт-контрактами и метаданными о состоянии сети хранения. Если бы мы манипулировали объектами напрямую через блокчейн, то процедура консенсуса занимала бы слишком много времени.
Особенности хранения данных
Как и в других объектных СХД, единицей хранения в TATLIN.OBJECT выступает объект — сами данные и метаданные (системные и пользовательские). Уникальным идентификатором служит хеш объекта — производная от данных и метаданных.
Как TATLIN.OBJECT сохраняет файлы
В зависимости от размера файла сохранение происходит по одному из трех сценариев:
объекты обычного размера записываются без изменений;
большие объекты разделяются на несколько объектов меньшего размера;
мелкие объекты объединяются в более крупную структуру.

Все объекты хранятся в контейнерах, которые создают пользователи. У контейнера есть собственные метаданные (атрибуты) и ряд заданных параметров:
политика доступа, определяющая правила доступа к объектам контейнера;
политика хранения, определяющая количество копий и местоположение объектов в системе.
Если взять хеш-структуру контейнера, то мы получим его уникальный идентификатор.
Уникальный адрес объекта формируется из связки идентификатора контейнера и самого объекта. То есть в TATLIN.OBJECT используется концепция неизменяемости данных с глобальной CAS-адресацией (Content-Addressable Storage). Вместо пути к файлу или URL адресом служит хеш-сумма комбинации идентификатора объекта и контейнера, в котором он хранится.
Важно, что архитектура TATLIN.OBJECT децентрализована: все сервисы доступа активны на каждом узле, а данные распределены по кластеру с использованием политик избыточности. Это гарантирует сохранность данных и доступ к ним даже при одновременном отказе нескольких узлов или дисков, а конкретные параметры можно настроить в политике защиты.
Протоколы TATLIN.OBJECT для общения с внешним миром
Глобальная CAS-адресация хорошо подходит для работы распределенной сети, но внешняя IT-инфраструктура заточена под стандартные сетевые интерфейсы. Поэтому в нашей СХД есть нативный SDK для Go, С#, Python и Java, а также протокольные шлюзы:
gRPC — протокол на базе открытой спецификации, предназначенный для создания кастомных интеграций.
HTTP(S) — стандартный веб-протокол с поддержкой REST API для автоматизации управления и возможностью прямой потоковой загрузки данных.
S3 (AWS S3 API) — для интеграции с S3-совместимыми приложениями.
Как работает S3 прокси-мигратор

При миграции данных из S3-совместимого хранилища в TATLIN.OBJECT ключевую роль играет S3-шлюз, который работает в режиме зеркалирования. В этом сценарии весь трафик S3-запросов перенаправляется через TATLIN.OBJECT. Логика работы шлюза настроена так, чтобы обеспечивать бесшовный доступ к данным. Например, при запросе содержимого бакета система возвращает информацию из целевого хранилища. Если объекта там нет, то запрос автоматически пересылается в зеркалируемое хранилище (хранилище, которое мы копируем). Одновременно с этим удаление объектов происходит синхронно как в целевой среде TATLIN.OBJECT, так и в зеркалируемом хранилище, что предотвращает потерю данных в процессе миграции. Миграция проходит в фоновом режиме.
S3 прокси-мигратор поддерживает пять основных операций:
GetObject,
PutObject,
ListObjects,
ListObjectsV2,
DeleteObject.
Каждый конкретный случай миграции с MinIO уникален, и специалисты YADRO проводят его оценку, чтобы процесс прошел без проблем. Информация ниже — верхнеуровневое описание работы S3 прокси-мигратора, но не руководство к действию.
GetObject и PutObject
В TATLIN.OBJECT есть S3-шлюз, который конфигурируется и отвечает за доступность по S3-протоколу. После перехода СХД в режим миграции все вызовы по S3-протоколу теперь направляются в S3-шлюз TATLIN.OBJECT, а прямой доступ к зеркалируемому S3-хранилищу блокируется.
Если в TATLIN.OBJECT нет нужного объекта (ошибка 404), то запрос перенаправляется в хранилище MinIO. Найденный объект передается в TATLIN.OBJECT, а уже из него — пользователю.

Операция PutObject нужна для загрузки нового объекта или перезаписи уже существующего. Если включено версионирование, то будет создана новая версия объекта, которая станет актуальной.
Под капотом форк open source-утилиты RClone в фоновом режиме запрашивает список объектов в бакетах для копирования из зеркалируемого хранилища. Параметры подключения к каждому хранилищу — S3-credentials (access key и secret key) и адрес endpoint — задаются в конфигурационном файле RClone (rclone.conf). Названия бакетов для синхронизации указываются непосредственно при запуске команды rclone.
Для каждого хранилища в конфигурационном файле прописаны свои S3-credentials:
> cat rclone.conf [minio] type = s3 provider = Other access_key_id = w***********T secret_access_key = Ib**************************************Qs endpoint = http://localhost:9000 bucket_acl = public-read [frostfs] type = s3 provider = Other access_key_id = 8Y*******************************************************************************************************BC secret_access_key = 7cb****************************************************************************fc6 endpoint = http://localhost:8089 bucket_acl = public-read
Так в фоновом режиме происходит копирование данных из зеркалируемого S3-хранилища в целевое — TATLIN.OBJECT. В форк RClone мы также планируем в ближайшее время добавить режим клонирования объектов для версионированных бакетов.
ListObjects, ListObjectsV2 и DeleteObject
Удаление объекта происходит по ключу из обоих хранилищ: зеркалируемого и целевого. Впрочем, удалять исходные объекты необязательно.

Также предусмотрены операции листинга — в S3-протоколе есть операция ListObjects. Она возвращает данные из TATLIN.OBJECT:

Если нужно получить данные из исходного хранилища, то мы используем операцию ListObjectsV2 из S3-протокола:

С ее помощью мы получаем объект из исходного S3-хранилища, затем конвертируем в формат TATLIN.OBJECT и возвращаем пользователю.
Как работает S3-зеркалирование
Конфигурация максимально простая: S3-шлюз, куда идут все запросы, хранит информацию и ключи доступа от зеркалируемого хранилища.
s3_mirroring: enabled: false region: ru access_key: k********h secret_key: a***********k endpoint: https://minio.com namespace: root
У заказчика есть S3-совместимое хранилище, с которого нужно мигрировать на TATLIN.OBJECT. В конфигурационном файле одной командой устанавливается значение S3-зеркалирования: адрес S3-хранилища и ключи доступа. Зеркалируемое хранилище продолжает работать в обычном режиме.
Параллельно нужно запустить утилиту RClone, которая будет переносить данные из S3-хранилища заказчика в TATLIN.OBJECT. Как только все данные перенесены в целевое хранилище — режим совместимости отключается, а заказчик может использовать оборудование зеркалируемого S3-хранилища для других задач.
Как проверить целостность данных при миграции
Проверку можно провести с помощью RClone:
./rclone-static-v1.69.1+frostfs.9 lsf minio:serv > list.txt ./rclone-static-v1.69.1+frostfs.9 copy minio:serv frostfs:serv --files-from list.txt --ignore-existing ./rclone-static-v1.69.1+frostfs.9 copy minio:serv frostfs:serv --files-from list.txt --ignore-existing -V ./rclone-static-v1.69.1+frostfs.9 copy minio:serv frostfs:serv --files-from list.txt --ignore-existing -P ./rclone-static-v1.69.1+frostfs.9 copy minio:serv frostfs:serv --files-from list.txt --ignore-existing -P --no-check-dest
Для миграции в TATLIN.OBJECT нужно заранее создать бакет, куда будут копироваться данные из бакета зеркалируемого хранилища (в примере: serv) — утилита работает только с бакетами. В RClone есть команда LSF для получения списка ключей, по которым из бакета запрашиваются данные объектов:
> cat list.txt sample1 sample2
Полученный список ключей нужно сохранить, чтобы по завершении работы RClone сравнить, все ли ключи бакета из зерклалируемого хранилища совпали с ключами в бакете TATLIN.OBJECT. Если проверить нужно больше, чем один бакет, то пишем скрипт, который идет по списку парных бакетов зеркалируемого и целевого хранилищ и сверяет их идентичность.
В RClone также есть свои механизмы, которые проверяют, что объект скопирован корректно.
Совместная работа S3-хранилищ с TATLIN.OBJECT
S3 прокси-мигратор позволяет дополнить уже существующее совместимое S3-хранилище системой хранения данных TATLIN.OBJECT, даже если миграция пока не планируется или совершается поэтапно. Данные будут запрашиваться через TATALIN.OBJECT из остальных хранилищ.
Благодаря S3-зеркалированию новые данные можно сохранять на TATLIN.OBJECT, а старые — получать из уже работающего S3-хранилища. Операцию DeleteObject, то есть удаление объектов из зеркалируемого хранилища, проводить необязательно.
TATLIN.OBJECT, как распределенная система, позволяет сделать логическое разделение и роутинг объектов по пространству имен — настройка режима S3-совместимости и зеркалирования ограничена именно им. Менеджер конфигураций передает указанные пространства имен и соответствующие настройки всем узлам TATLIN.OBJECT и делает их идентичными.
Если зеркалирование выключено, то объект через S3-шлюз запрашивается только в TATLIN.OBJECT: приходит ответ 200 (найден) или 404 (не найден).

При включенном зеркалировании и запросе в пространство имен, которое указано в его настройках, запрос при получении от хранилища TATLIN ответа 404 (не найден) перенаправляется в S3-хранилище. Где объект также может быть либо найден, либо нет:

Замеры производительности переноса данных из MinIO в TATLIN.OBJECT
Мы протестировали S3 прокси-мигратор в дата-центре YADRO, поэтому результаты ниже не учитывают сценарии с высокой задержкой (например, Москва — Владивосток), где итоговая скорость будет ограничена параметрами сетевой связности.
Запуск производился на аналогичных платформах для MinIO и TATLIN.OBJECT на базе серверов VEGMAN.
Для замеров производительности использовались размеры объектов:
8 кБ,
128 кБ,
1 МБ,
32 МБ,
128 МБ.
Тестовые объекты для миграции были расположены:
в одном (1b),
либо в ста (100b) бакетах.
VUs — количество параллельных потоков нагрузки. Зеркалируемым S3-хранилищем был инстанс MinIO. Мы проводили операции чтения (r) и записи (w) объектов, продолжительность одного теста составляла 5 минут.
Для миграции данных с зеркалируемого хранилища мы:
Создали в MinIO тестовые бакеты и записали в них тестовые объекты.
Настроили утилиту RClone на вспомогательном сервере, с которого тоже подается нагрузка.
В TATLIN.OBJECT создали аналогичные MinIO пустые бакеты (с теми же именами и в том же количестве) с политикой хранения по умолчанию.
Запустили клонирование данных через RClone по списку тестируемых бакетов и паралелльно с этим подали на MinIO нагрузку.
Сравнение результатов в одинаковых тестовых сценариях без клонирования данных (run1) и с ним (run2):
Сценарий | Средняя пропускная способность, run1, МиБ/с | Средняя пропускная способность, run2, МиБ/с | Разница, % |
|---|---|---|---|
w_8k_1b_800VUs | 21,81 | 18,392 | 15,7 |
w_128k_1b_600VUs | 142,578 | 126,302 | 11,4 |
w_1024k_1b_400VUs | 324,544 | 300,456 | 7,4 |
w_32768k_1b_200VUs | 1367,188 | 1269,532 | 7,1 |
w_131072k_1b_100VUs | 1562,5 | 1432,292 | 8,3 |
r_8k_1b_800VUs | 573,893 | 519,206 | 9,5 |
r_128k_1b_600VUs | 613,281 | 580,078 | 5,4 |
r_1024k_1b_400VUs | 626,302 | 566,732 | 9,5 |
r_32768k_1b_200VUs | 526,367 | 387,044 | 26,5 |
r_131072k_1b_100VUs | 543,294 | 318,034 | 41,5 |
w_8k_100b_800VUs | 13,672 | 15,462 | 13,1 |
w_128k_100b_600VUs | 125,651 | 133,626 | 6,3 |
w_1024k_100b_400VUs | 279,948 | 303,874 | 8,5 |
w_32768k_100b_200VUs | 1204,427 | 1285,808 | 6,8 |
w_131072k_100b_100VUs | 1269,531 | 1416,016 | 11,5 |
r_8k_100b_800VUs | 777,67 | 735,189 | 5,5 |
r_128k_100b_600VUs | 733,724 | 728,678 | 0,7 |
r_1024k_100b_400VUs | 515,951 | 520,02 | 0,8 |
r_32768k_100b_200VUc | 512,696 | 525,391 | 2,5 |
r_131072k_100b_100VUs | 273,438 | 433,431 | 58,5 |
Из таблицы видно, что включение клонирования объектов между системами не приводит к систематическим падениям производительности тестового стенда с MinIO. В большинстве сценариев разница в результатах не превышает 10–15%, что укладывается в рамки естественного разброса данных между итерациями тестов.
Тестирование длилось 9 часов 8 минут. За это время в 1294 бакета склонировано 846 000 объектов пяти разных размеров общим объемом 329 ГБ. За то же время на стенде MinIO выполнено 50,46 млн операций на 27,14 ТБ.
В каждом склонированном бакете объекты одного размера:
Размер объектов в бакете | Количество бакетов | Количество объектов во всех бакетах заданного размера | Передано данных, МБ |
|---|---|---|---|
8 кБ | 519 | 627592 | 4903 |
32 кБ | 422 | 173610 | 21701 |
1 МБ | 170 | 38764 | 38764 |
32 МБ | 132 | 5203 | 166496 |
128 МБ | 51 | 824 | 105472 |
Всего: | 1294 | 845993 | 337336 |
TATLIN.OBJECT, помимо поддержки S3-протокола, включает нативный SDK, который ускоряет миграцию минимум на 25%, а больший прирост производительности зависит от особенности реализации системы. Кроме того, SDK предоставляет низкоуровневый доступ к записи в хранилище из пользовательского кода.