Производительность Ceph — различия между версиями

Материал из YourcmcWiki
Перейти к: навигация, поиск
Строка 85: Строка 85:
 
То есть, под Ceph следует закупать '''только''' SSD с конденсаторами. Даже если рассматривать NVMe — NVMe без конденсаторов хуже, чем SATA с оными.
 
То есть, под Ceph следует закупать '''только''' SSD с конденсаторами. Даже если рассматривать NVMe — NVMe без конденсаторов хуже, чем SATA с оными.
  
И ещё один вариант — Intel Optane. Это тоже SSD, но они основаны не на Flash памяти (не NAND и не NOR), а вообще на другой технологии, называющейся 3D XPoint. Хз, как она работает, но заявляются 550000 iops при полном отсутствии необходимости в стирании блоков, кэше и конденсаторах. Но а) в применении к Ceph — нужно проверять — не факт, что Ceph вообще сможет выжать из них их iops-ы б) вариант дорогой, раза в 3 дороже типичной SSD (1500$ за 960 гб, 500$ за 240 гб).
+
И ещё один вариант — Intel Optane. Это тоже SSD, но они основаны не на Flash памяти (не NAND и не NOR), а вообще на другой технологии, называющейся 3D XPoint. Хз, как она работает, но заявляются 550000 iops при полном отсутствии необходимости в стирании блоков, кэше и конденсаторах. Но если даже задержка диска и равна 0.01мс, то задержка Ceph всё равно как минимум в 50 раз больше, соответственно, с Ceph оптаны использовать чуть менее, чем бессмысленно — за большие деньги (1500$ за 960 гб, 500$ за 240 гб) вы получите не сильно лучший результат.
  
 
== BlueStore vs FileStore ==
 
== BlueStore vs FileStore ==

Версия 20:44, 11 марта 2019

Бенчмаркинг

Основные направления тестирования:

  • Линейное чтение/запись (большими блоками)
  • Пиковая производительность высоко-параллельного случайного чтения/записи мелкими блоками
  • Задержка однопоточного случайного чтения мелкими блоками (4-8 Кб)
  • Задержка однопоточной транзакционной записи мелкими блоками (4-8 Кб) — обычно последовательной, как в журнал СУБД, но в один поток это обычно слабо отличается от случайной

Задержки обычно важнее простой пиковой производительности случайного чтения/записи, так как далеко не каждое приложение может загрузить диск при большом параллелизме / глубокой очереди (32-128 запросов).

Ceph — это SDS, его задержки всегда выше, чем у устройств при прямом доступе, и от этого никуда не денешься. В интернете есть доклад Nick Fisk «Low-Latency Ceph», в его исполнении Low latency это 0.7ms, то есть на лучший результат рассчитывать особенно не приходится. 0.7ms — это всего лишь примерно ~1500 iops в 1 поток (хорошая новость — другие SDS и просто SAN-ы тоже тормозят :)).

Тестирование дисков

Сначала прогоните fio на голом диске:

Warning icon.svg ВНИМАНИЕ! Для тех, кто в танке — fio-тест записи на диск ДЕСТРУКТИВНЫЙ. Не вздумайте запускать его на дисках/разделах, на которых есть нужные данные… например, журналы OSD (был прецедент).
  • Перед тестированием отключите кэш записи диска: hdparm -W 0 /dev/sdX (SATA-диски через SATA или HBA), sdparm --set WCE=0 /dev/sdX (SAS-диски). Не совсем ясно, почему, но эта операция на серверных SSD может увеличить IOPS-ы на 2 порядка. Также см.ниже #Картина маслом «Тормозящий кэш».
  • Линейное чтение: fio -ioengine=libaio -direct=1 -invalidate=1 -name=test -bs=4M -iodepth=32 -rw=read -runtime=60 -filename=/dev/sdX
  • Линейная запись: fio -ioengine=libaio -direct=1 -invalidate=1 -name=test -bs=4M -iodepth=32 -rw=write -runtime=60 -filename=/dev/sdX
  • Пиковые IOPS случайного чтения: fio -ioengine=libaio -direct=1 -invalidate=1 -name=test -bs=4k -iodepth=128 -rw=randread -runtime=60 -filename=/dev/sdX
  • Задержка случайного чтения: fio -ioengine=libaio -sync=1 -direct=1 -invalidate=1 -name=test -bs=4k -iodepth=1 -rw=randread -runtime=60 -filename=/dev/sdX
  • Пиковые IOPS случайной записи: fio -ioengine=libaio -direct=1 -invalidate=1 -name=test -bs=4k -iodepth=128 -rw=randwrite -runtime=60 -filename=/dev/sdX
  • Задержка записи в журнал: fio -ioengine=libaio -sync=1 -direct=1 -invalidate=1 -name=test -bs=4k -iodepth=1 -rw=write -runtime=60 -filename=/dev/sdX
  • Задержка случайной записи: fio -ioengine=libaio -sync=1 -direct=1 -invalidate=1 -name=test -bs=4k -iodepth=1 -rw=randwrite -runtime=60 -filename=/dev/sdX

«А почему так мало…» — см.ниже.

Тестирование кластера Ceph

Как тестировать Ceph после сборки:

  • rados bench лучше не использовать — он создаёт для тестирования очень мало объектов (в 1 поток всего 2, в 128 — несколько сотен). Из-за этого, например, на заполненном HDD результаты будут сильно оптимистичнее, так как снимается необходимость постоянного поиска метаданных в RocksDB.
  • fio в RBD: fio -ioengine=rbd -direct=1 -invalidate=1 -name=test -bs=4k -iodepth=128 -rw=randwrite -pool=rpool_hdd -runtime=60 -rbdname=testimg
    • лучше запускать с другого узла — результат будет в 1.5 раза лучше, видимо, из-за отсутствия переключения контекстов между ceph OSD и fio. можно запускать два теста параллельно, bluestore любит параллелизм — суммарный результат вполне может оказаться ещё в 2 раза лучше.
    • для мазохистов — параметр iodepth=128 поменять на iodepth=1. цифра будет раз в 20 хуже и она будет отражать то, сколько примерно TPS сможет выполнить ваша OLTP СУБД изнутри Ceph.
  • Встроенной утилитой rbd bench --io-size 4096 --io-threads 64 --io-total 10G --io-pattern rand --io-type write rpool_hdd/testimg
  • Можно тестировать и fio изнутри виртуалки, rbd драйвер нормально создаёт параллельную нагрузку — проверено.
  • Производительность может отличаться на заполненном и незаполненном RBD-образе. Но отличия небольшие, думать, что там будет разница в несколько раз — не нужно.
  • При тестировании случайной записи в ceph в один поток (fsync/fdatasync/sync/iodepth=1/rados bench -t 1) вы фактически всё время тестируете ОДИН диск. То есть, всё время тестируются разные диски, но в каждый отдельный момент времени запрос идёт только к одной placement group (тройке-четвёрке-пятёрке дисков).
  • Соответственно, вы не увидите 100 % утилизации дисков на хостах при тестировании в один поток, однопоточная нагрузка не может полностью загрузить кластер.

Как тестировать производительность отдельных OSD:

  • Создать pool без репликации ceph osd pool create bench 128 replicated; ceph osd pool set bench size 1; ceph osd pool set bench min_size 1 и с числом PG, достаточным, чтобы при случайном выборе туда попали все OSD
  • Воспользоваться бенчилкой https://github.com/rumanzo/ceph-gobench
    Это прокачанный аналог бенчилки Марка https://github.com/socketpair/ceph-bench (её раньше запускали python main.py --keyring /etc/ceph/ceph.client.admin.keyring bench osd - но она даёт некорректные результаты в HDD+SSD сетапах с Bluestore, т.к. всё время перезаписывает один и тот же блок)
  • Полученный результат, в частности, может помочь выявить отдельную тупящую OSD

О транзакционности записи

Warning icon.svg Плохая новость!

Важная особенность Ceph — вся запись, даже та, для которой никто этого явно не просит, ведётся транзакционно. То есть, никакая операция записи не завершается, пока она не записана в журналы всех OSD и не сделан fsync() диска. Так сделано, чтобы предотвращать RAID WRITE HOLE-подобные ситуации рассинхронизации данных между репликами при отключении питания, потере сети и т.п…

Если конкретизировать сильнее, это означает, что Ceph не использует никакие буферы записи дисков (наоборот, он делает всё, чтобы эти буферы всё время очищать). Это не значит, что буферизации записи нет вообще — она есть на уровне клиентов (page cache в linux, кэш RBD устройства на уровне драйвера librbd qemu…). Но именно внутренние дисковые буферы не используются.

Это приводит к тому, что типичная настольная SSD под журналом в Ceph выдаёт неприлично низкие IOPS-ы — обычно от 500 до 2000. И это при том, что при обычном тестировании почти любая SSD выдаёт > 20000 iops. Даже самый паршивый китайский noname выдаёт не менее 10000 iops. NVMe легко выжимает 150000 и больше. Но стоит начать использовать fsync… и та же NVMe выдаёт 600 iops (на 2.5 порядка меньше).

Без буфера записи, кстати, и 7200 rpm HDD — это не 120 иопс, а всего 40-50. Сорри, наврал.

В общем, чтобы понять, сколько у вас теоретически может быть IOPS-ов на запись в Ceph, диски под него нужно тестировать с опциями fio sync=1 iodepth=1. Это даст «журнальные» иопсы (производительность последовательного коммита операций по одной).

Другие почти идентичные варианты: fdatasync=1 (в файле поверх ФС) либо fsync=1 (на голом девайсе). Разница между опциями:

  • fsync=1 синхронизирует данные и метаданные тестируемого файла отдельным запросом после каждой операции записи. Так работает BlueStore.
  • fdatasync=1 синхронизирует только данные (но не метаданные) тестируемого файла после каждой операции записи. Соответственно, от fsync=1 это отличается, только если тестируется файл в ФС, а не блочное устройство.
    Note.svg fdatasync=1 надо использовать, когда на диске уже есть ФС, а прогнать тест хочется. Результаты будут достаточно корректными.
  • sync=1 использует O_SYNC и синхронный ввод/вывод, то есть, каждая операция начинается только после завершения предыдущей. Так работает FileStore.
    Но ещё нужна опция iodepth=1, иначе в очередь диска до синхронизации «пролезает» несколько операций и IOPS-ы растут, тест перестаёт быть тестом журнала.

Конденсаторы

Нас спасёт такое чудо инженерной мысли, как SSD с конденсаторами (точнее, обычно суперконденсаторами — ионисторами). Которые на M.2 SSD, кстати, прекрасно видны невооружённым глазом:

Micron 5100 sata m2.jpg

Конденсаторы работают фактически как встроенный в SSD ИБП и позволяют SSD успеть сбросить кэш во флеш-память при потере питания. Таким образом кэш становится «энергонезависимым» — и таким образом SSD может просто игнорировать запросы fsync, так как точно знает, что данные из кэша в любом случае доедут до постоянной памяти.

При этом IOPS-ы транзакционной записи становятся равны IOPS-ам нетранзакционной.

Конденсаторы в официальных описаниях SSD-шек обычно называются «enhanced/advanced power loss protection». Этой характеристикой обладают, как правило, только «серверные» SSD, да и то не все. Например, в Intel DC S3100 конденсаторов нет, а в Intel DC S4600 есть.

Note.svg Это и является главным отличием серверных SSD от настольных. Обычному пользователю транзакции нужны редко — а вот на серверах живут СУБД, которым транзакции как раз нужны позарез.

То есть, под Ceph следует закупать только SSD с конденсаторами. Даже если рассматривать NVMe — NVMe без конденсаторов хуже, чем SATA с оными.

И ещё один вариант — Intel Optane. Это тоже SSD, но они основаны не на Flash памяти (не NAND и не NOR), а вообще на другой технологии, называющейся 3D XPoint. Хз, как она работает, но заявляются 550000 iops при полном отсутствии необходимости в стирании блоков, кэше и конденсаторах. Но если даже задержка диска и равна 0.01мс, то задержка Ceph всё равно как минимум в 50 раз больше, соответственно, с Ceph оптаны использовать чуть менее, чем бессмысленно — за большие деньги (1500$ за 960 гб, 500$ за 240 гб) вы получите не сильно лучший результат.

BlueStore vs FileStore

Хоть BlueStore и считается «новым» бэкендом хранилища, есть очень популярное мнение, что он тормозит и поэтому не нужен.

После долгого ковыряния в проблеме разъясняю: нет, BlueStore не тормозит «в целом». В частности:

  • BlueStore в 2 раза быстрее FileStore при линейной записи (можно считать, что всегда), так как в нём крупные блоки пишутся 1 раз — сразу на устройство, а не 2 (в журнал и потом на устройство).
  • BlueStore примерно равен FileStore по производительности случайной записи и latency в All-Flash кластерах. Пиковая производительность при этом обычно немного выше, однопоточная — немного ниже. Жор CPU тоже немного меньше. Всё это варьируется, но по крайней мере блюстор здесь не хуже.

ОДНАКО! BlueStore без дополнительных ухищрений действительно ХУЖЕ FileStore в популярной конфигурации HDD + журнал на SSD:

  • Условно, собрав небольшой кластер на BlueStore, в 1 поток на HDD+SSD вы получите ~100 иопс на запись, а в FileStore ~500 (без SSD вообще будет 30-50 иопс).
  • Почему? Потому, что BlueStore очень часто делает сброс данных из очереди отложенной записи на HDD (каждые 32 операции, deferred_batch_ops) и при сбросе тормозит все последующие операции.
  • В итоге случайная запись ждёт окончания записи не только на SSD, а также и на HDD.
  • Ждёт оно так не каждую операцию, поэтому iops-ы лучше, чем просто на HDD. Но всё-таки ждёт, поэтому iops-ы хуже, чем в FileStore.

Как это полечить?

  • Либо вместо журнала (или рядом с журналом) сделать на SSD bcache для HDD.
  • Либо использовать HDD с SSD Cache, Media Cache или аналогом (перманентным кэшем случайной записи на пластинах). Например, в старых дисках HGST это включается при отключении волатильного кэша командой `hdparm -W 0 /dev/sdXX`. В новых, похоже, включено всё время.

Также BlueStore делает огромное количество fsync-ов (что очень смешно — даже больше, чем запросов записи), из-за чего не терпит десктопных SSD под журналом. Но FileStore работает похоже, и кардинальных различий производительности это не вносит.

Ну и ещё FileStore отстаёт от BlueStore по функциональности:

  • Снапшоты там работают со скоростью LVM, то есть, при записи 4 кб после снятия снапшота копируется весь 4 Мб объект. То есть, после снятия снапшота RBD ВМ тормозят.
  • Плюс отсутствуют некоторые другие фичи BlueStore: нет частичной перезаписи в EC-пулах (соответственно, EC нельзя использовать под CephFS и RBD), нет сжатия (хотя оно и не особо нужно), нет контрольных сумм (а вот они полезны, в частности, из-за их отсутствия нельзя использовать size=2).

Контроллеры

  • SATA — это нормально, SAS не обязателен от слова «совсем». SATA за счёт того, что «не умничает», достаточно быстрая и точно лучше, чем старые RAID контроллеры.
  • Разница в IOPS между RAID и HBA/SATA может быть колоссальна. В производительность не самого нового RAID контроллера легко упереться. Плохо даже не то, что на 1 диск вы получите 48000 iops вместо 60000, хуже то, что при подключении 8 дисков вы получите 6000 iops на каждый диск вместо 60000, так как 48000 поделятся на всех. Также в RAID режиме увеличивается задержка в 1 поток.
  • Так что свой RAID контроллер либо переключите в режим passthrough (если он умеет), либо перепрошейте, чтобы умел, либо выкиньте в помойку и купите HBA («RAID без RAID-функционала», например, LSI 9300-8i). Это актуально для всех видов программных хранилок — Ceph, ZFS и т. п.
  • Если не выкинули RAID — отключайте все кэши контроллера, чтобы уменьшить влияние прослойки и не страдать при разряде батарейки / перемещении диска в другой сервер. Наверное, в теории можно выжить и с включенным кэшем, но это стрельба себе в ногу.
  • У HBA тоже есть предел IOPS. К примеру, у LSI 9211-8i это ~280000 iops на весь контроллер.
  • При подключении через SATA или HBA контроллер не забывайте для SATA дисков сделать hdparm -W 0 /dev/sdX, для SAS — sdparm --set WCE=0 /dev/sdX.
  • Для SAS и NVMe включайте blk-mq (ну или юзайте свежие ядра, в районе 4.18 оно включается по умолчанию). Но для SATA blk-mq обычно бесполезен или почти бесполезен.
  • Фактическая глубина очереди, используемая Ceph OSD при случайной записи, редко больше 10 (посмотреть можно при работе утилитой iostat -xmt 1).

Процессоры

  • На SSD Ceph ОЧЕНЬ СИЛЬНО упирается в процессор. Можно сказать, что процессор — основной bottleneck.
  • Как сказано в презентации Ника Фиска — Ceph is a Software-Defined Storage and every piece of Ceph «Software» will run faster with every GHz of CPU frequency.
  • Кроме частоты, на серверных процессорах часто наличествует NUMA (Non-Uniform Memory Access). То есть, часть памяти и оборудования доступна процессору напрямую, а часть — только через другой процессор.
  • Для максимизации производительности конфигураций с NUMA лучше избегать, а процессорам с бОльшим числом ядер и меньшей частотой лучше предпочитать бОльшую частоту и меньшее число ядер…
  • …но в пределах разумного, так как даже один OSD на серверном SSD под нагрузкой может спокойно выжрать на 100 % ядер 6.
  • Под частотой подразумевается номинальная частота, а не Turbo Boost, так как оный актуален только для однопоточных нагрузок.
  • Рекомендации по привязке OSD к отдельным CPU (taskset), можно сказать, неактуальны, так как Ceph OSD сильно многопоточные — при записи постоянно активно как минимум 4 потока, и ограничение их несколькими ядрами сильно урезает производительность.
  • Есть два параметра, которые регулируют число рабочих потоков OSD — osd_op_num_shards и osd_op_num_threads_per_shard…
  • …Но менять их бесполезно, поднять производительность таким образом не получается абсолютно, дефолтные значения (1x5 на HDD и 2x8 на SSD) оптимальны. kv_sync_thread-то всё равно только один.
  • Есть одна мера, которая помогает поднять производительность сразу раза в 2-3: отключение экономии энергии процессором:
    • cpupower idle-set -D 1 — отключает C-States (либо опции ядра processor.max_cstate=1 intel_idle.max_cstate=0)
    • for i in {0..$((`nproc`-1))}; do cpufreq-set -c $i -g performance; done (вместо 63 подставьте своё число ядер минус 1) — отключает снижение частоты через множитель
  • После этих двух команд процессор начинает греться как ПЕЧ, но iops-ы увеличиваются сразу раза в 2 (а то и 3)
  • Также жор CPU — одна из причин НЕ делать из Ceph «гиперконвергентное облако» (в котором совмещены узлы хранения и запуска виртуальных машин)

Оценка производительности кластера

  • Оценка производительности кластера просто по спецификациям входящих в него SSD не совсем верна (точнее, скорее совсем не верна), причём в обе стороны:
    • Bluestore, видимо, за счёт параллелизма, выдаёт чуть больше iops-ов даже на SSD без конденсаторов, чем просто те же ssd могут выдать с fsync — я лично смог добиться от тестового пула на 3-х Intel SSDSC2KW256G8 8000 iops (случайная запись), хотя сами ssd с fsync выдают примерно 5500
    • И обратно, даже если SSD мегабыстрая, цеф — это огромный оверхед, он жрёт проц и никаких 220000 iops из одной SSD не выжмет. См. ниже #Пример теста от Micron — там в суперкрутом сетапе у них вышло всего 8750 iops в пересчёте на 1 NVMe (но это у них без SPDK/DPDK).
  • Можно считать, что лимит iops-ов в пересчёте на одну SSD/NVMe находится на уровне ~10000-15000.
  • Большой разницы между хорошей SATA SSD и даже NVMe в цефе — нет.
  • Если SATA SSD плохая — разница есть. :) плохим можно считать всё, что даёт меньше 20000 iops в один поток с sync.
  • Лайфхак для ускорения однопоточной нагрузки: mdadm RAID 0 из RBD-образов внутри самой виртуалки — проверено, не работает.
  • Лайфхак для очень быстрых дисков: несколько OSD на одном диске — работает, но ценой сильного увеличения жора CPU.
  • Гипотетический монстр производительности в вакууме: мощные процы, Intel NVMe, сеть 25+ Гбит/с или Infiniband, SPDK/DPDK (SPDK работает, но не даёт прироста, DPDK не работает вообще).

Картина маслом «Тормозящий кэш»

Дано: 3 компа с 4x 7200rpm SATA HDD, с 1 SSD (десктопным) под систему и ceph-mon и с 1 SSD (старым, но серверным, 25000 iops) под журналы. Не самая быстрая 10-гигабитная сеть — флуд пингом средний RTT (задержка) 0.098ms. Развёрнут Ceph + OpenNebula с KVM. Диски под Ceph отформатированы в Bluestore утилитой ceph-volume (то есть используется LVM). Диски виртуалок лежат в обычном реплицированном ceph pool с size=3.

Создаём Debian-виртуалку (настройки диска kvm по умолчанию — bus=virtio, cache=none), ставим fio, запускаем в ней тест на задержку транзакционной случайной записи: fio -ioengine=libaio -size=10G -sync=1 -direct=1 -name=test -bs=4k -iodepth=1 -rw=randwrite -runtime=60 -filename=./testfile (или можно не случайной, тогда rw=write, но результат идентичный).

  1. Настройки по умолчанию — все кэши дисков включены (везде hdparm -W 1, в /sys/block/*/queue/write_cache везде write back) — Ж О П А, iops=59, avg lat = 16.88ms
  2. Отключаю кэш записи SSD с журналами: hdparm -W 0 /dev/sdb — остаётся Ж О П А, iops=58, avg lat = 16.99ms
  3. Всем LVM-девайсам отключаю кэш записи: for i in /sys/block/dm-*; do echo write through > $i/queue/write_cache; done` — А Ф И Г Е Т Ь, iops=584, avg lat = 1.7ms
  4. Обратно включаю кэш SSDшке с журналами: hdparm -W 1 /dev/sdb — остаётся iops=582, avg lat = 1.7ms
  5. Откручиваю все отключения кэшей LVM: for i in /sys/block/dm-*; do echo write back > $i/queue/write_cache; done — обратно жопа, 57 iops, avg lat = 17.2ms
  6. Опять отключаю кэш журнальным LVM-девайсам: for i in `ls /dev/ceph-journals/lvol*`; do j=readlink $i; echo write through > /sys/block/${j##../}/queue/write_cache; done — никакого улучшения, всё та же жопа (но с ними это точно безопасно, так как они с конденсаторами :))
  7. Отключаю кэш HDD LVM-разделам (for i in `ls /dev/ceph-*/osd-block*`; do j=readlink $i; echo write through > /sys/block/${j##../}/queue/write_cache; done) — бинго, iops=603, avg lat = 1.65ms
  8. Ага. Простите. Обнаруживаю, что просто писать куда-то write through небезопасно без hdparm -W 0 /dev/sd*, так как https://www.kernel.org/doc/Documentation/block/queue-sysfs.txt - Writing to this file can change the kernels view of the device, but it doesn’t alter the device state. ок, добавляю for i in /dev/sd?; do hdparm -W 0 $i; done (отключаю все кэши) — результат похуже, iops=405, avg lat = 2.47ms — но это всё равно лучше, чем изначальная жопа.

Виртуалку, в которой тестировал — даже не перезапускал между тестами.

Note.svg Мораль: отключайте кэш записи всем дискам!

Частичная разгадка:

  1. В жёстких дисках HGST есть Media Cache (энергонезависимый кэш случайной записи прямо на пластинах) — во-первых, там не HGST, во-вторых, в HGST медиакэш включён всегда и не зависит от hdparm -W 0.
  2. В жёстких дисках Seagate Enterprise Capacity ST8000NM0055 (коих из общего числа 3) есть встроенный SSD-кэш. И вот он, видимо, действительно включается только при hdparm -W 0.
  3. Блюстор блокирует запись в журнал записью на HDD. Когда включен спец.кэш, HDD начинает рандомно писать сильно быстрее, и блокировки при сбросе уходят. Действует кэш, естественно, временно — когда он кончится, производительность случайной записи опять упадёт. Однако плюс в том, что в Ceph-е этого, скорее всего, не произойдёт, так как скорость случайной записи ограничивается, собственно, самим Ceph-ом и распределяется по всем дискам кластера :).
  4. Однако, для обычных дисков без SSD-кэша отключение кэша тоже даёт выигрыш… есть гипотеза, что из-за того же тормоза с bluefs (проверю).

Данные тестов

Более свежие тесты бенчилкой ceph-gobench на том же самом стенде. Версия Ceph Mimic 13.2.2, Bluestore, журналы на SSD.

osd.1 и osd.3 без выноса журналов на SSD оба показывали 75-80 iops вне зависимости от отключения кэша.

В таблице показаны однопоточные IOPS с глубиной очереди Q=1 без репликации.

Номер OSD Патч + кэш Кэш выкл Кэш вкл Модель диска Номер модели
osd.0 365 308 226 Seagate Constellation ES.2 ST32000645NS
osd.1 322 325 234 Hitachi Ultrastar 7K3000 HUA723020ALA640
osd.2 1278 1392 230 Seagate Enterprise Capacity ST8000NM0055
osd.4 198 244 140 Hitachi Ultrastar A7K2000 HUA722020ALA330
osd.5 251 185 154 Hitachi Ultrastar A7K2000 HUA722020ALA330
osd.7 1351 1129 253 Seagate Enterprise Capacity ST8000NM0055
osd.9 399 315 184 Hitachi Ultrastar 7K3000 HUA723020ALA640
osd.13 226 212 142 Hitachi Ultrastar A7K2000 HUA722020ALA330
osd.3 393 386 241 Hitachi Ultrastar 7K3000 HUA723020ALA640
osd.8 340 323 156 Hitachi Ultrastar A7K2000 HUA722020ALA330
osd.10 1461 1319 273 Seagate Enterprise Capacity ST8000NM0055
osd.14 302 229 135 Hitachi Ultrastar A7K2000 HUA722020ALA330

Почему вообще Bluestore такой медленный?

Note.svg По итогам дополнительных изысканий хейтспич несколько подправлен.

Речь о random write iops. Ведь вроде старались-старались, уходили от двойной записи и «журналирования журнала» в filestore…

Все мы держим в уме, что 1x 7200rpm HDD может выдать примерно 100—120 iops. Дальше нам говорят — ну, там типа журналирование. Ну ок, как мы рассуждаем — ну, типа, есть журнал, есть диск. Значит типа вроде как синхронно записало в журнал, потом асинхронно постепенно перенесло на диск. Значит, берём 100, умножаем на число дисков в кластере, делим на фактор репликации (3), делим на 1.5-2 (данные+журнал), мы же держим в уме, что наверняка там всё асинхронно и оптимизировано… Получаем, скажем, 100 * 9 дисков / 1.5-2 / 3 = 150—200 iops. Запускаем fio iodepth=128 на собранном кластере — ОЙ, 30 iops. Как так?

Окей, дальше нам говорят — эээ, не. 120 random iops с 1 HDD — это без sync, случайная запись с sync — примерно 50-60 iops. Окей, говорим мы, но во-первых у нас же глубина очереди 128, значит, параллелизм, значит, операции должны пролезать между sync-ами и быть быстрее — а во-вторых, 50*9/1.5/3 — это всё равно 100. Ну хоть 100 иопс-то должно у нас быть на запись? А у нас 30.

Дальше мы отчаиваемся и по советам знатоков прикручиваем туда SSD под wal+db. И думаем: ну, теперь-то у нас запись идёт на SSD, SSD хорошая, с конденсаторами. Теперь суммарно у нас должно быть не меньше 300 иопс (N * скорость шпинделя / фактор репликации), да и в 1 поток должно быть столько же. Тестируем. В 1 поток получаем ну… 60 иопс. Во много — где-то 200. Опять медленно.

Вариант решения № 1: у наших HDD обнаруживается media cache, мы его включаем, получаем нормальную производительность и успокаиваемся (хотя осадочек-то остался). Вариант № 2: медиакэша у наших дисков нет и мы продолжаем ломать голову «почему ж так медленно-то».

Так вот. Журнала у блюстора собственного нет, вместо него RocksDB. Это как бы keyvalue база, но она же LSM, она же так и работает — типа до лимита пишет в память+журнал, потом когда упирается в лимит — делает compaction по уровням. По сути как бы БД-журнал. Вот они её как журнал и юзают. В той же RocksDB метаданные, и их дохрена, ибо виртуальные клоны и всякое такое. Плюс чексуммы. Плюс два варианта записи — прямой и отложенный.

Теоретически в этом ничего плохого нет. Наоборот, хорошо то, что и метаданные, и данные записываются 1 транзакцией.

Однако на практике (при просмотре strace) оказывается, что при записи 4к-блоками OSD демонстрируют фактор Write Amplification от 3 до 5 плюс делают огромное количество commit-ов. Запись делается двумя системными вызовами — pwritev и io_submit. Commit — тоже двумя, sync_file_range и fdatasync.

Например, по итогам простого теста с SSD-настройками получается, что эта тварь на 6193 запроса записи делает 18585 записей (суммарно записывая 21674 4к-блока) и 24776 коммитов (больше, чем запросов записи)! Итого Write Amplification = 3.5.

Тот же тест на HDD: на 1183 операции 3020 запросов записи суммарным объёмом 4926 * 4 кб и 3664 sync-а. Итого WA = 4.16 (4926/1183), а sync-ов опять больше, чем запросов записи.

Причины найдено две:

  • На HDD — блюстор всё время «едет на ручнике» из-за совершенно идиотской проблемы, которую я не далее чем вчера зарепортил сюда https://tracker.ceph.com/issues/38559 - при каждой записи в журнал RocksDB происходит дополнительная «ненужная» транзакция записи в журнал BlueFS, сводящаяся к обновлению размера лог-файла RocksDB. Теоретически это не нужно, так как RocksDB настроена с wal_recovery_mode=kTolerateCorruptedTailRecords и recycle_log_number=4. Но практически — в коде баг, из-за которого журнал при этом не sync-ается. На HDD ускорение случайной записи при глубине очереди 1 — двукратное. При глубине очереди 128 — почти нулевое.
  • На SSD — блюстор жёстко упирается в CPU и блокировки.

DPDK и SPDK

  • DPDK = Data Plane Developer Kit, быстрая библиотека от Intel для работы с (в основном) сетевыми устройствами в userspace, без задействования драйверов ядра
  • SPDK = Storage Performance Developer Kit, основанная на DPDK библиотека для работы с NVMe SSD, тоже в пространстве пользователя. Ещё есть форк libnvme — SPDK, отвязанный от DPDK
  • DPDK включается через ms_type=async+dpdk
  • SPDK включается для NVMe-шек передачей в качестве пути девайса spdk:<серийный номер pcie устройства> и ручным созданием OSD по инструкции Manual Deployment
  • Это в теории — на практике (проверялся Mimic 13.2.x) НИ ХРЕНА не работает, ни DPDK, ни SPDK
    • С DPDK Ceph «из коробки» даже не собирается — это в общем-то довольно легко исправить, но даже когда добиваешься сборки и запуска — OSD падают после обработки ~50 пакетов
    • С SPDK Ceph собирается и даже собран по умолчанию — но оно опять-таки не работает — вскоре после запуска OSD просто виснет в пространстве
    • Code is there, так что, вероятно, всё это можно исправить, если подебажить подольше
    • Есть сообщения, что SPDK всё-таки работает из коробки, просто не даёт никакого выигрыша производительности. Но мне пока завести его не удалось
  • Однако, похоже, в силу неоптимальной реализации самого сетевого кода Ceph ни от DPDK, ни от RDMA ожидать ускорения не приходится — потому что один чувак недавно отрезал код AsyncMessenger-а от всего остального цефа и попробовал побенчить его отдельно: https://www.spinics.net/lists/ceph-devel/msg43555.html - и получил всего лишь ~80000 iops.
  • В перспективе SPDK будет на хрен не нужен, так как в ядро приняли штуку под названием io_uring: https://lore.kernel.org/linux-block/20190116175003.17880-1-axboe@kernel.dk/ - с ней обычный код прокачивает через Optane-ы практически столько же iops, сколько и SPDK, при заметно меньшем объёме геморроя на поддержку работы с SPDK/DPDK

Краткий экскурс в устройство SSD и флеш-памяти

Особенность флеш-памяти (NAND/NOR) заключается в том, что пишется она мелкими блоками (обычно 512 байт), а стирается большими (2-4 мегабайта) — и при этом писать можно только в предварительно стёртую область. Чтение блока при этом быстрое, запись тоже быстрая; стирание же медленное, да ещё и число стираний каждого erase unit-а ограничено — после нескольких тысяч (типичное значение для MLC) блок физически выходит из строя. В более дешёвых и плотных (MLC, TLC, QLC — 2-4 бита на ячейку) чипах лимит стираний меньше, в более дорогих и менее плотных (SLC, один бит на ячейку) — больше. Соответственно, при «тупом» подходе — если при записи каждого блока его просто стирать и перезаписывать — случайная запись во флеш-память, во-первых, будет очень медленной, а во-вторых, она будет быстро выводить её из строя.

Но почему тогда SSD быстрые? А потому, что внутри SSD на самом деле есть очень мощный и умный контроллер (1-2 гигагерца, типично 4 ядра или больше, примерно как процессоры мобильников), и на нём выполняется нечто, называемое Flash Translation Layer — прошивка, которая переназначает каждый мелкий логический сектор в произвольное место диска. FTL всё время поддерживает некоторое количество свободных стёртых блоков и направляет каждую мелкую случайную запись в новое место диска, в заранее стёртую область. Поэтому запись быстрая. Одновременно FTL делает дефрагментацию свободного места и Wear Leveling (распределение износа), направляя запись и перемещая данные так, чтобы все блоки диска стирались примерно одинаковое количество раз. Кроме того, во всех SSD некоторый % реального места зарезервирован под Wear Leveling («overprovision»), а в хороших серверных SSD этот процент весьма большой — например, в Micron 5100 Max это +60 % ёмкости.

Именно из наличия FTL вытекает и проблема с энергонезависимостью и «power loss protection»-ом. Карты отображения секторов — это метаданные, которые при сбросе кэша тоже нужно сбрасывать в постоянную память, и именно этот сброс и вносит торможение в работу настольных SSD с fsync.

Дополнение: когда я попытался кого-то в списке рассылки полечить на тему, что «все SSD делают fsync», мне в ответ кинули статью: https://www.usenix.org/system/files/conference/fast13/fast13-final80.pdf. В общем, суть статьи в том, что в 2013 году нормой было то, что SSD вообще не сбрасывали метаданные на диск при fsync, и при отключении питания это приводило к разным весёлым вещам вплоть до (!!!) полного отказа SSD.

Есть экземпляры старых SSD без конденсаторов (OCZ Vector/Vertex), которые при этом выдают большие iops на запись с fsync. Как это возможно? Неизвестно, но есть предположение, что суть как раз в небезопасности записи. Принцип работы флеш-памяти за последние годы вроде как не менялся — в SSD как раньше был FTL, так и сейчас FTL. Как достигнуть быстрой записи, если постоянно сбрасывать на диск карты трансляции — хз… наверное, если только сделать некое подобие лог-структурированной ФС внутри — писать всё время вперемешку метаданные и данные. Но при этом, по идее, при старте всё это «добро» придётся сканировать и старт/монтирование станет долгим. А в SSD долгого монтирования вроде как нет.

Ну и, собственно, «power loss protection», видимо, бывает простой, а бывает advanced. Простой означает просто «мы корректно делаем fsync и не сдохнем при отключении питания», а advanced означает наличие конденсаторов и быструю безопасную запись с fsync. Сейчас, в 2018—2019 годах, «обычный» PLP, похоже, всё-таки стал нормой и при отключении питания большая часть SSD терять данные и умирать уже не должна.

Бонус: USB-флешки

А почему тогда USB-флешки такие медленные? Случайная запись на флешку 512-байтными (или 4 Кб) блоками обычно идёт со скоростью 2-3 iops. А флеш-память там ровно та же, что в SSD — ну, может, более дешёвые вариации, но разница же не на порядки. Ответ кроется в том, что на флешках тоже есть FTL (и даже Wear Leveling), но по сравнению с SSD-шным он маленький и тупой. У него слабый процессор и мало памяти. Из-за малого объёма RAM контроллеру флешки, в отличие от контроллера SSD, негде хранить полную таблицу сопоставления виртуальных и реальных секторов — поэтому отображаются не сектора, а крупные блоки где-то по мегабайту или больше, а при записи есть лимит на количество «открытых» блоков. Как это происходит:

  • Допустим, вы пишете в сектор X.
  • Контроллер отображает блок, которому принадлежит этот сектор, на реальный блок, и «открывает» его — выделяет пустой блок, запоминает, что он «дочерний» для открытого и записывает туда один изменённый вами сектор.
  • Таким макаром можно открыть максимум N разных блоков; число N обычно очень маленькое — от 3 до 6.
  • Дальше если вы пишете следующий сектор из уже открытого блока — он просто записывается в его дочерний блок (что быстро).
  • Если же следующий записываемый сектор принадлежит другому блоку — какой-то из открытых блоков приходится закрывать и «сливать» содержимое дочернего блока с оригинальным.

Для копирования больших файлов на флешку, отформатированную в любую из стандартных файловых систем, двух блоков достаточно: в один открытый блок пишутся данные, во второй — метаданные записываемого файла. Запись последовательная, всё быстро. А вот при случайной записи вы перестаёте попадать в уже «открытые» блоки и каждая операция записи превращается в полное стирание. Тут-то и начинаются тормоза…

Пример теста от Micron

Пример самолётного сетапа от Micron с процами по полляма (2x Xeon Gold), 100-гбит сетью и 10x топовыми NVMe (с конденсаторами, ага) в каждом узле, 4 узла, репликация 2x: https://www.micron.com/resource-details/30c00464-e089-479c-8469-5ecb02cfe06f

Всего 350000 iops на запись в пике на весь кластер, при 100 % загрузке CPU. Казалось бы, довольно много, но если поделить 350000/40 osd — получится 8750 иопс на 1 osd. С учётом репликации на диски нагрузка двойная, выходит, 17500 иопс. Ок, журналы тоже удваивают нагрузку, итого — 35000 iops на запись смог выжать ceph из одной NVMe… которая сама по спеке может 260000 иопс в одиночку. Вот такой вот overhead.

Данных по задержкам в 1 поток нет (а было бы интересно узнать).

UPD: Поправка: в чём микрон неправ — они не использовали SPDK и DPDK. Есть большая вероятность, что в их случае выигрыш мог быть в несколько раз. Нет, это бессмысленно.

Модели

  • Micron серий 5100/5200
  • HGST SN260
  • Intel P4500

https://docs.google.com/spreadsheets/d/1E9-eXjzsKboiCCX-0u0r5fAjjufLKayaut_FOPxYZjc

Примечание

Написанное в статье актуально для версий Ceph, доступных на момент последней правки (см. «История» вверху страницы). Конкретно — для 12 luminous, 13 mimic, да и 14 nautilus тоже — всё пока что актуально. Если вдруг в будущем что-то пофиксят и всё вдруг станет чудесно быстрым — сам побегу обновляться первым и поправлю статью :).