Архитектура Vitastor

Переходим на тёмную сторону и пишем (П)СХД вместе

Виталий Филиппов

Напомню, что за Vitastor

  • vitastor.io
  • Меня задолбал Ceph и я его переписал 🤣
  • Распределённая блочная SDS (программная СХД)
  • 🚀 Быстрая, от ~0.14 мс (Ceph, облака ~1 мс)
  • Простая и всегда такой останется
  • Симметричная, без SPOF
  • QEMU, NBD, K8s, Proxmox, OpenStack, псевдо-NFS
  • Для SSD или SSD+HDD

Почти все SDS

  • Либо теряют данные
  • Либо медленные
  • Либо платные
  • Либо мёртвые
  • Либо всё вместе

Переписываем Ceph

Чтобы не сойти с ума 😆

  • Ограничимся блочной частью, без ФС и S3
    • Сбросим архитектурный груз
  • Позаимствуем общие черты: PG, OSD, мониторы
    • OSD — демон, работающий с 1 диском
    • PG — «шард» кластера
    • Монитор — управляющий демон
    • Всё I/O через первичный OSD для каждой PG

Порядок реализации

  • Прототип:
    • Блочный слой, под EC/XOR
    • База OSD (1 PG, только XOR)
    • Монитор, алгоритм распределения PG
    • Драйвер QEMU
  • Репликация, EC N+K
  • Что-то для монтирования ядром (NBD)
  • Снапшоты
  • Zero-copy сеть
  • Плагины Proxmox, OpenStack и т.п.
  • CLI
  • Что-то для VMWare (NFS)

Вопросы для решения

  • Как сделать быстро
  • Алгоритм записи
  • Алгоритм синхронизации
  • Монитор, алгоритм распределения PG
  • Снапшоты
  • Zero-copy сеть
  • Что-то для VMWare

Производительность (без границ) Производительность (без границ) Производительность (без границ) Производительность (без границ) Производительность (без границ)

Сравнение (T1Q1)

iops – операций в секунду, больше – лучше

Источники тормозов

  • Сеть (но это не точно)
  • Диск (нет)
  • Логика (CPU)         ⟶
    Было трудно, но они смогли
    • Управляемые языки
    • Идиоматичный C++
    • Копирование памяти, сериализация
    • Выделение памяти
    • ФС, работа без O_DIRECT
    • Переключения контекста, блокировки
    • Отсутствие параллелизма
    • Сторонние библиотеки

Как не повторить «успех» 🐌

  • CPU — узкое место. Не диск, не сеть!
  • Простые алгоритмы
  • «Си с классами»
  • Минимум зависимостей (STL, json11, cpp-btree)
  • Никаких boost, folly, grpc, msgpack
  • Потоки Асинхронщина
  • io_uring

io_uring

  • Новый интерфейс асинхронного IO в Linux 5.1+
  • Универсальный и быстрый
  • 🚀 Уже аж 10 млн iops (тут)
  • DPDK нинужен!
  • Умеет всё, чего не умели epoll и Linux AIO:
    • Linux AIO не A без o_direct
    • epoll не умеет файлы и диски
  • kernel.dk/io_uring.pdf

io_uring

Нюансы

  • Лучше ядро >= 5.8, до — видел пару багов
  • Миллионы иопсов — только со стероидами
    (потоком опроса и регистрацией fd и буферов)
  • Не замена epoll до 5.13 (MULTI_SHOT)
  • Очереди ограничены по размеру
    • Очередь запросов может переполниться
    • Очередь ответов может переполниться
    • Либо следить, либо IORING_FEAT_NODROP (5.5+)
    • Приход ответа раньше освобождения запроса

Придумываем алгоритм записи

После включения

Мои данные на месте!

А, чёрт, не совсем

Нужна синхронизация Нужна синхронизация Нужна синхронизация

Как?

  • Целиком → mdadm
  • По контрольным суммам → LizardFS 🦎
  • Намерение записи → Linstor
  • Журнал операций → Ceph, vSAN, YDB
  • А ещё ребаланс
  • А ещё распределение места
  • А ещё Write Hole

RAID 5 Write Hole

Повреждение парного блока

Метаданные

  • Что, если блок запишется, а чексумма нет?
  • LizardFS: ха-ха, Lost chunk 🤣
  • Нужна атомарность
  • Либо WAL, но тогда двойная запись
  • Либо CoW/RoW, но и он не бесплатен
    • Двойная запись--, метаданные++++, CPU++
    • Bluestore → 100 байт ... 16 кб на запись

Итоговая схема

  • Все объекты по 128 КБ*
  • ID образа + смещение + версия ⇒ адрес
  • 256 МБ на 1 ТБ
  • Перенаправление при полной записи
  • Журналирование при частичной
  • Двухфазный коммит против Write Hole

* можно менять

Нюанс

Со сжатием уже не соскочим — придётся делать и CoW, и дефрагментацию 😊

Синхронизация по версиям объектов

Синхронизация по версиям объектов

  • При каждой записи версия++
  • При подъёме PG сверяем полные списки объектов
    PG = 1/100 OSD ~ несколько МБ меты
  • Нет нужды сериализовать записи, как в Ceph, vSAN
  • Нужно вести историю PG (эпохи)

1. Старые_OSD Новые_OSD = ∅

Нельзя поднимать PG

2. версия++ ⇒ возможны коллизии

Версия = (Эпоха, Версия)

3. Удаление или неполная запись?

Либо просто запретить неполное удаление
Либо по эпохе записи: если была (1,2,3), то удаление

Монитор

Монитор

🐍
  • Хранилище с Raft:
    • Своё на braft ↗️
    • Consul
    • etcd 👍
  • Логика:
    • Встроить в OSD
    • Отдельно на C++
    • Отдельно на node.js 👍

А что там будет?

  • Хранение:
    • Конфигурация (пулы, PG, OSD, образы)
    • Состояние (OSD, PG, история PG)
    • Блокировки (lease)
    • Статистика
  • Логика:
    • (Пере)распределение данных
    • Назначение PG первичных OSD
    • Агрегация статистики

Как распределять данные?

Хеши vs БД

  • Храним объекты в БД («MDS-based»)
  • Используем консистентные хеши
  • Ceph:
    • Хеш ID объекта % число PG → номер PG
    • CRUSH хеш(Номер PG) → набор OSD
    • CRUSH — рандеву-хеш ↗️
    • CRUSH не идеален, равномерность 80-90%
    • Из-за этого теряется место

Как сделать лучше

  • Так же, хеш ID объекта % число PG → номер PG
  • PGOSD просто сохраним в etcd!
  • Оптимизируем через сведение к ЛП:
    • Распределённое место → max
    • Занятое место на OSD ≤ размера OSD
    • Сумма весов PG → max
    • Сумма весов PG по OSDi ≤ размер OSDi
    • PGi ≥ 0

Перераспределение

    • Распределённое место → max
    • Перемещение данных → min
    • Занятое место на OSD ≤ размера OSD
    • Нельзя убрать больше, чем было
    • Нельзя убрать или добавить меньше нуля
    • Сумма((PG - PG) * похожесть_PG) → max
    • похожесть_PG = 1 + набор - отличия_набора
    • Сумма PG по OSDi ≤ размер OSDi
    • PG - PG ≤ PG_было
    • PG ≥ 0, PG ≥ 0

Снапшоты

Снапшоты с CoW

Легко и откатить, и удалить, но оверхед и
в распределённую архитектуру не вписывается

Снапшоты без CoW

Ceph смог усидеть на 2 стульях

  • rbd snap — «назад», «глобальные номера»
    ужасный откат
  • rbd clone — «вперёд», object-map
    немножко WA (1024x 😆 🤣)
  • Не будь как цеф

Снапшоты в Vitastor

  • Всегда «вперёд»
  • Образ данные + родитель ...
  • Снимок == клон
  • Битмапы в объектах (всего 4 байта)
  • Локальность
  • Быстрое чтение, нет WA при записи
  • Слияние не мгновенное, но норм
  • Можно ещё улучшить

Zero-copy и kernel bypass* Zero-copy и kernel bypass*

* хрен ты у меня проскочишь

Сеть без копирования данных

Не все zero-copy одинаково полезны

  • Zero-copy не бесплатен
  • Ядро и так не копирует skb без нужды
  • Копировать 4 КБ дешевле, чем ремапить
  • Особенно в кэше CPU
  • MSG_ZEROCOPY осмыслен от ~16 КБ
     
     

DPDK + SPDK

  • Сеть (и NVMe) в юзерспейсе
  • Модно, молодёжно
  • Проблемы:
    • ❌ Оптимизирован под ПСП
    • ❌ Родного TCP нет
    • ❌ 1 процесс — либо IPC
    • ❌ Настройка зависит от карт
    • ❌ NVMeoF нам бесполезен 🤔
    • Выровненного zero-copy приёма нет

DPDK TCP

  • mTCP: студенческая поделка (пинг-понг 16000 мкс)
  • ANS: китайские блобы
  • Seastar TCP: самопал на 2к строк ↗️
  • F-Stack: тыренный BSD стек
    Работает, пинг-понг 26 мкс (40 мкс ядром) ↗️
    Не хватает настроек, например, MTU
  • VPP: машинерия с демоном и IPC
    Много умеет, настройки есть (vppctl)
    Медленно: пинг-понг 39 мкс (40 мкс ядром) ↗️
  • TLDK: не проверял 😊

Резюме

DPDK хорош, если вы пишете фаерволл или DPI

Но не хранилку

Zero-copy TCP в ядре Linux

  • На отправку — MSG_ZEROCOPY:
    • setsockopt SO_ZEROCOPY в 1
    • Не освобождаем память до подтверждения
      отправки через recvmsg MSG_ERRQUEUE
    • ± Юзабельно
    • Пока маринуется в ветке
    • Ибо ухудшает задержки и иногда жрёт CPU
  • На приём — ?

Zero-copy TCP в ядре Linux

  • На отправку — MSG_ZEROCOPY
  • На приём — доклад с Netdev 2020:
    • MTU = 4096 + заголовки (Ethernet, IP, TCP) = 4096 + 86
    • Заголовок отдельно, данные отдельно
    • Для этого патчим драйвер сетевой карты 🤣
    • Потом mmap на сокете
    • И потом через getsockopt материализуем там данные
    • Работает только в гугле, где и внедрили 🤣🤣🤣

OpenOnload

  • LD_PRELOAD-библиотека для ускорения сокетов
  • SolarFlare кем-то куплен, OpenOnload полумёртв
  • Карточки есть на ebay 🙃
  • Я проверял (SFN5122) 🙃
  • Профит: 40 мкс → 32 мкс
  • С io_uring не дружит
  • Работает нестабильно
  • Mellanox libvma похожа, но не нужна — RDMA лучше

RDMA

  • Remote Direct Memory Access
  • Карта пишет в вашу память
  • Mellanox, есть пара карт от Intel
  • Infiniband 🤨
  • iWARP, RoCEv1, RoCEv2 🤟
  • PFC+ECN на свиче

Как RoCE работает в Linux?

Из коробки, даже MLNX_OFED не нужен, если ядро 5.9+

Автоматически создаются roce*, LACP, ECMP LAG RDMA устройства

$ apt install ibverbs-utils
...
$ ibv_devinfo
hca_id: rocep5s0f0
    transport:          InfiniBand (0)
    fw_ver:             14.25.1020
    node_guid:          248a:0703:002c:47c6
    sys_image_guid:     248a:0703:002c:47c6
    vendor_id:          0x02c9
    vendor_part_id:     4117
    hw_ver:             0x0
    board_id:           DEL2420110034
    phys_port_cnt:          1
        port:   1
            state:          PORT_DOWN (1)
            max_mtu:        4096 (5)
            active_mtu:     4096 (5)
            sm_lid:         0
            port_lid:       0
            port_lmc:       0x00
            link_layer:     Ethernet

Как использовать?

  • libibverbs
  • rdmacm — обёртка над ibverbs
  • Проверка:
    ibv_rc_pingpong -O -g 3 -r 1 -s 4096 -m 4096 -n 100000
    ibv_rc_pingpong -O -g 3 -r 1 -s 4096 -m 4096 -n 100000 192.168.7.2
    
  • rping — от rdmacm
  • Документация: man -k ibv_, rdmamojo.com

Нюансы программизма

  • Пары очередей (QP), типы: RC, UC, UD
  • Есть операции передачи, есть — записи в память
  • Буфер для приёма должен быть готов заранее,
    иначе "RNR retry" и адские тормоза
  • Без ODP (On-Demand Paging) неудобно, до ConnectX-4 его нет
  • Zero-copy приём всё равно не получается 🤣🤣 [картинка]
  • Но профит есть, в Vitastor с 2021

Профит RDMA

TCP RDMA
Q=1 randread 5300 iops (188us) 6800 iops (147us)
Q=1 randwrite 5300 iops (188us) 7200 iops (138us)
Q=128 randread 151000 iops 263000 iops
Q=128 randwrite 53000 iops 63000 iops

2 десктопа по 1 NVMe, 10G ConnectX-4

NFS-прокси

NFS-прокси

  • Не полноценная ФС
  • Эмулятор ФС для закрытых👎 гипервизоров
  • VMWare, HyperV
  • Альтернативы — iSCSI, NVMeoF
  • Но тогда VMFS и всё в одном образе
  • NFS 3.0 — stateless, можно масштабировать
  • Свой парсер протокола

Резюме

Вышло интересно, то ли ещё будет 🙃

Пробуйте → vitastor.ioapt install vitastor 🤟

Контакты