Производительность Ceph
Содержание
- 1 Бенчмаркинг
- 2 О транзакционности записи
- 3 Конденсаторы
- 4 Bluestore vs Filestore
- 5 Контроллеры
- 6 Процессоры
- 7 Настройка виртуалок и ФС
- 8 Оценка производительности кластера
- 9 Картина маслом «Тормозящий кэш»
- 10 Почему вообще Bluestore такой медленный?
- 11 DPDK и SPDK
- 12 RAID WRITE HOLE
- 13 Краткий экскурс в устройство SSD и флеш-памяти
- 14 Пример теста от Micron
- 15 Модели
- 16 Резюме
- 17 Примечание
- 18 См. также
Бенчмаркинг
Основные направления тестирования:
- Линейное чтение/запись (большими блоками)
- Пиковая производительность высоко-параллельного случайного чтения/записи мелкими блоками
- Задержка однопоточного случайного чтения мелкими блоками (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 на голом диске:
- Перед тестированием попробуйте отключить кэш записи диска: hdparm -W 0 /dev/sdX (SATA-диски через SATA или HBA), sdparm --set WCE=0 /dev/sdX (SAS-диски). Не совсем ясно, почему, но эта операция на серверных SSD может увеличить IOPS-ы на 2 порядка (а может НЕ увеличить, поэтому пробуйте оба варианта — и W0, и W1). Также см.ниже #Картина маслом «Тормозящий кэш».
- Линейное чтение: 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 — также стоит повторить тот же тест с -fsync=1 вместо -sync=1 и принять худший результат, так как иногда бывает, что одним из методов sync игнорируется (зависит от контроллера).
- Задержка случайной записи: fio -ioengine=libaio -sync=1 -direct=1 -invalidate=1 -name=test -bs=4k -iodepth=1 -rw=randwrite -runtime=60 -filename=/dev/sdX
«А почему так мало…» — см.ниже.
Когда разворачиваете Ceph OSD на SSD — очень разумно не отдавать её под Ceph целиком, а оставить небольшой раздел (10-20 гб) пустым для будущего использования под бенчмаркинг. Ибо SSD имеют свойство со временем (или при забивании данными под 80%) начинать тормозить. Очень удобно иметь возможность гонять fio на пустом никем не используемом разделе.
Тестирование кластера 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/vitalif/ceph-bench
- Обе — прокачанные аналоги бенчилки Марка https://github.com/socketpair/ceph-bench (её раньше запускали python main.py --keyring /etc/ceph/ceph.client.admin.keyring bench osd — но она даёт некорректные результаты в HDD+SSD сетапах с Bluestore, так как всё время перезаписывает один и тот же блок)
- Полученный результат, в частности, может помочь выявить отдельную тупящую OSD
О транзакционности записи
Важная особенность 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 это отличается, только если тестируется файл в ФС, а не блочное устройство.
fdatasync=1 надо использовать, когда на диске уже есть ФС, а прогнать тест хочется. Результаты будут достаточно корректными. - sync=1 использует O_SYNC и синхронный ввод/вывод, то есть, каждая операция начинается только после завершения предыдущей. Так работает FileStore.
- Но ещё нужна опция iodepth=1, иначе в очередь диска до синхронизации «пролезает» несколько операций и IOPS-ы растут, тест перестаёт быть тестом журнала.
Конденсаторы
Нас спасёт такое чудо инженерной мысли, как SSD с конденсаторами (точнее, обычно суперконденсаторами — ионисторами). Которые на M.2 SSD, кстати, прекрасно видны невооружённым глазом:
Конденсаторы работают фактически как встроенный в SSD ИБП и позволяют SSD успеть сбросить кэш во флеш-память при потере питания. Таким образом кэш становится «энергонезависимым» — и таким образом SSD может просто игнорировать запросы fsync, так как точно знает, что данные из кэша в любом случае доедут до постоянной памяти.
При этом IOPS-ы транзакционной записи становятся равны IOPS-ам нетранзакционной.
Конденсаторы в официальных описаниях SSD-шек обычно называются «enhanced/advanced power loss protection». Этой характеристикой обладают, как правило, только «серверные» SSD, да и то не все. Например, в Intel DC S3100 конденсаторов нет, а в Intel DC S4600 есть.
Это и является главным отличием серверных SSD от настольных. Обычному пользователю транзакции нужны редко — а вот на серверах живут СУБД, которым транзакции как раз нужны позарез.
То есть, под Ceph следует закупать только SSD с конденсаторами. Даже если рассматривать NVMe — NVMe без конденсаторов хуже, чем SATA с оными.
И ещё один вариант — Intel Optane. Это тоже SSD, но они основаны не на Flash памяти (не NAND и не NOR), а на Phase-Change-Memory «3D XPoint». По спецификации заявляются 550000 iops при полном отсутствии необходимости в стирании блоков, кэше и конденсаторах. Но если даже задержка такого диска и равна 0.01мс, то задержка Ceph всё равно как минимум в 50 раз больше, соответственно, с Ceph оптаны использовать чуть менее, чем бессмысленно — за большие деньги (1500$ за 960 гб, 500$ за 240 гб) вы получите не сильно лучший результат.
Bluestore vs Filestore
Блюстор — «новое» хранилище. От «нового» хранилища честно ожидаешь лучшей или хотя бы не худшей производительности в любых сценариях.
И таки да, при линейной записи Bluestore фактически в 2 раза быстрее Filestore — двойную запись там честно ликвидировали, крупные блоки пишутся только 1 раз — сразу на устройство, а не 2 (в журнал и потом на устройство).
Но вот со случайной записью всё не так однозначно.
- В HDD-only конфигурациях (по крайней мере, если «снять ручник» https://tracker.ceph.com/issues/38559) по random 4k write iops Bluestore тоже в честных 2 раза быстрее, чем Filestore.
- Фактически можно сказать, что для HDD и плохих SSD схема записи Bluestore по-настоящему оптимальна и выжимает максимум, который возможно выжать из диска в транзакционном режиме
- В HDD+SSD кластерах производительность у Bluestore идеально стабильная, но сильно меньшая, чем у Filestore в пике. От блюстора невозможно добиться «сглаживания пиков» случайной записи через SSD-журнал. Он устроен так, что отказывается писать быстрее, чем в среднем может писать HDD. То есть даже с SSD-журналом вы получите лишь 100—200 iops с 1 диска — в то время, как в Filestore, пока в журнале есть место, можно иметь 1000—2000 iops.
- И проблема не только в том, что параметры по умолчанию — deferred_batch_ops и max_deferred_txc — задают частый сброс операций на медленный диск (раз в 64 операции). Проблема ещё в том, что в Bluestore отсутствуют механизмы фоновой очистки «журнала» (очереди отложенной записи). Поэтому, когда очередь забивается, производительность просто падает до HDD-шной до перезапуска OSD. Ну и сама очередь находится в RocksDB, поэтому сильно поднимать её размер, по идее, неполезно.
- В All-Flash кластерах (то есть, на быстрых дисках) Bluestore латентнее Filestore, возможно, на 30-50 %. Однако эти 30-50 % относятся к latency именно самого блюстора, в абсолютном выражении составляют ~0.1 мс и на фоне общей задержки Ceph-а практически не заметны. Кроме того, latency ничего не говорит о параллельной пиковой пропускной способности, а она по крайней мере не хуже, чем в Filestore (обычно чуть лучше, +5..10 %). Жор CPU тоже чуть меньше.
- Жор памяти у Bluestore ощутимо больше, потому что RocksDB и потому что кэш у него собственный вместо использования системного page cache-а, как в Filestore.
Как полечить медленный коммит на SSD?
- Либо вместо журнала (или рядом с журналом) сделать на 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).
Про размер block.db
В общем, как всегда, «есть небольшой нюанс». Нюанс в том, что RocksDB кладёт файл на быстрый диск только когда считает, что на быстром диске хватит места под все файлы этого же уровня.
Дефолтные настройки цефа:
- 1 Гб WAL = 4x256 Мб
- max_bytes_for_level_base и max_bytes_for_level_multiplier не изменены, поэтому равны 256 Мб и 10 соответственно
- соответственно L1 = 256 Мб
- L2 = 2560 Мб
- L3 = 25600 Мб
…Соответственно!
Rocksdb положит L2 на block.db, только если раздел имеет размер хотя бы 2560+256+1000 Мб (округлим вверх до 4 Гб).
А L3 она положит на block.db, только если block.db размером хотя бы 25600+2560+256+1000 Мб = около 30 Гб.
А L4, соответственно, если ещё +256, то есть итого 286 Гб.
Иными словами, имеют смысл только размеры раздела block.db 4 Гб, 30 Гб, 286 Гб. Все промежуточные значения бессмысленны — место сверх предыдущего граничного значения использоваться не будет.
Контроллеры
- 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 — имейте в виду, что некоторые HBA (в частности, Adaptec) могут всё равно не сбросить кэш корректно и устроить вам Cloudmouse при отключении питания. Но по крайней мере точно известно, что LSI ведут себя нормально.
- Хороший пост про RAID-кэши в списке рассылки: http://lists.ceph.com/pipermail/ceph-users-ceph.com/2019-July/036237.html - если вкратце - человек, админивший 6000 OSD, пишет, что никогда больше не свяжется с RAID0-режимами.
- У 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 $(seq 0 $((`nproc`-1))); do cpufreq-set -c $i -g performance; done — отключает снижение частоты через множитель
- После этих двух команд процессор начинает греться как ПЕЧ, но iops-ы увеличиваются сразу раза в 2 (а то и 3)
- Также жор CPU — одна из причин НЕ делать из Ceph «гиперконвергентное облако» (в котором совмещены узлы хранения и запуска виртуальных машин)
- Ещё можно отключить все mitigation-ы аппаратных уязвимостей: noibrs noibpb nopti nospectre_v2 nospectre_v1 l1tf=off nospec_store_bypass_disable no_stf_barrier
Настройка виртуалок и ФС
- С дефолтными опциями qemu подключает RBD, увы, криво.
- Криво — это значит, что а) используется медленная эмуляция lsi-контроллера б) используется режим с кэшированием чтения, но без кэширования записи.
- Кэш в qemu регулируется опцией, собственно, cache. Бывает <не указано>, writethrough, writeback, none, unsafe, directsync. С RBD эта опция регулирует работу rbd cache, т.е. кэша на стороне клиентской библиотеки Ceph (librbd).
- Но cache=unsafe с RBD не работает, операции записи всё равно ждут подтверждения. А writethrough, <не указано> и directsync, по сути, эквивалентны.
- RBD cache сильно помогает на HDD, но на SSD-кластере наоборот вносит заметное торможение. Что-то там сделано с блокировками, что-то там однопоточное, всё это оптимизируют, но пока не оптимизировали.
- Есть следующие способы эмуляции дисков: lsi (самый медленный), virtio-scsi (достаточно быстрый), virtio (самый быстрый, но до QEMU 4.0 не умел TRIM). Вообще virtio-scsi умеет multiqueue и поэтому на быстром хранилище должен быть быстрее virtio — но в случае с Ceph multiqueue, по-видимому, значения не имеет.
- А ещё тормозит файловая система! Конкретно, если у вас не включена опция lazytime, то при каждой мелкой записи ФС обновляет mtime, то есть время модификации inode-а. Так как это метаданные, а ФС журналируемые — это изменение журналируется. Из-за этого при тесте fio -sync=1 -iodepth=1 -direct=1 поверх ФС без lazytime iops-ы уменьшаются в 3-4 раза.
- А если у вас (не дай бог) внутри Oracle, то ему надо обязательно поставить опцию FILESYSTEMIO_OPTIONS=SETALL.
Поэтому:
- Для HDD / SSD+HDD крайне желательно на уровне виртуалок включать опцию qemu cache=writeback. Этот режим безопасный, так как fsync-и от гостевых ВМ заставляют qemu сбрасывать кэш в Ceph. То есть, транзакционно записываемые данные не теряются.
- Для SSD-only лучше выключать кэш вообще (cache=none), максимальные параллельные iops на запись от этой процедуры растут с примерно 11000 до ~25000.
- Лучше всего использовать драйвер virtio. Как всё это настраивается в вашем GUI виртуалок (Proxmox, Opennebula) — отдельная песня :) в опеннебуле сделано довольно-таки через жопу.
- Обязательно используйте опцию монтирования lazytime. Для неё нужны свежие ядра: с ext4 — хотя бы 4.0, с XFS — хотя бы 4.17. Также нужны соответствующие (свежие) util-linux (подсказка: в протухшей 7-й центоси их нет, поставить можно только из исходников).
Оценка производительности кластера
- Оценка производительности кластера просто по спецификациям входящих в него SSD не совсем верна (точнее, скорее совсем не верна), причём в обе стороны:
- Bluestore, видимо, за счёт параллелизма, выдаёт чуть больше iops-ов даже на SSD без конденсаторов, чем просто те же ssd могут выдать с fsync — я лично смог добиться от тестового пула на 3-х Intel SSDSC2KW256G8 8000 iops (случайная запись), хотя сами ssd с fsync выдают примерно 5500
- И обратно, даже если SSD мегабыстрая, цеф — это огромный оверхед, он жрёт проц и никаких 220000 iops из одной SSD не выжмет. См. ниже #Пример теста от Micron — там в суперкрутом сетапе у них вышло всего 8750 iops в пересчёте на 1 NVMe (после небольшого тюна они улучшили результат до ~12000 iops на 1 NVMe).
- Можно считать, что лимит 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 не работает вообще).
Картина маслом «Тормозящий кэш»
O_SYNC vs fsync vs hdparm -W 0
У SATA и SCSI дисков есть два способа сброса кэша: команда FLUSH CACHE и флаг FUA (Force Unit Access) на команде записи. Первый — это явный сброс кэша, второй — это указание записать операцию на диск, минуя кэш. Точнее, у SCSI оно есть, а с SATA ситуация точно не ясна: в спецификации NCQ бит FUA есть, но факту FUA большинством дисков вроде как не поддерживается и, соответственно, эмулируется ядром/контроллером (если контроллер не кривой).
По всей видимости, fsync() отправляет диску команду FLUSH CACHE, а открытие файла с O_SYNC устанавливает бит FUA на все команды записи.
Есть ли разница? Обычно нет. Но на некоторых контроллерах и/или с некоторыми настройками различия по неустановленным причинам встречаются. И тогда fio -sync=1 и fio -fsync=1 начинают давать разные результаты — возможно, даже на порядки разные результаты.
Кроме того, у дисков есть команда отключения кэша. Когда он отключен, запросы сброса (fsync) Linux диску не отправляет. Казалось бы, такой режим тоже должен быть эквивалентен выполнению fsync и/или O_SYNC после каждой команды. Но и это не всегда так! На SSD с конденсаторами (то есть серверных моделях с Advanced Power Loss Protection) при отключении кэша iops-ы случайной записи часто вырастают на порядок (например, с 5000 до 40000). Но не всегда, так как это, опять-таки, зависит от контроллера.
Почему? По-видимому, потому, что команда FLUSH CACHE трактуется диском как «сбрось все кэши» (включая энергонезависимый), а отключение кэша — как «отключи энергозависимый кэш» (а энергонезависимый можешь оставить включенным). Соответственно, запись со сбросом кэша становится медленнее, чем просто отключённый кэш.
А что с NVMe? В NVMe разнообразие чуть меньше — возможность отключить кэш в спецификации не предусмотрена вообще, но точно так же есть команды FLUSH CACHE и бит FUA. При этом по личным наблюдениям FUA часто игнорируется то ли диском, то ли Linux-ом, и fio -sync=1 выдаёт с NVMe такие же результаты, как и без sync вообще. -fsync=1 при этом ведёт себя как надо и приземляет производительность туда, где ей самое место (на десктопных NVMe — до тех же 1000—2000 iops).
P.S: Bluestore использует fsync. Filestore использует O_SYNC.
Серверные SSD
Disabling cache is not a joke!
fio -ioengine=libaio -name=test -filename=/dev/sdb -(sync|fsync)=1 -direct=1 -bs=(4k|4M) -iodepth=(1|32|128) -rw=(write|randwrite)
Micron 5100 Eco 960GB
sync или fsync | bs | iodepth | rw | hdparm -W 1 | hdparm -W 0 |
---|---|---|---|---|---|
sync | 4k | 1 | write | 612 iops | 22200 iops |
sync | 4k | 1 | randwrite | 612 iops | 22200 iops |
sync | 4k | 32 | randwrite | 6430 iops | 59100 iops |
sync | 4k | 128 | randwrite | 6503 iops | 59100 iops |
sync | 4M | 32 | write | 469 MB/s | 485 MB/s |
fsync | 4k | 1 | write | 659 iops | 25100 iops |
fsync | 4k | 1 | randwrite | 671 iops | 25100 iops |
fsync | 4k | 32 | randwrite | 695 iops | 59100 iops |
fsync | 4k | 128 | randwrite | 701 iops | 59100 iops |
fsync | 4M | 32 | write | 384 MB/s | 486 MB/s |
bs | iodepth | rw | результат |
---|---|---|---|
4k | 1 | randread | 6000 iops |
4k | 4 | randread | 15900 iops |
4k | 8 | randread | 18500 iops |
4k | 16 | randread | 24800 iops |
4k | 32 | randread | 37400 iops |
4M | 1 | read | 460 MB/s |
4M | 16 | read | 514 MB/s |
Результаты по чтению не отличаются на hdparm -W 0 и 1.
Seagate Nytro 1351 XA3840LE10063
Диск заполнен почти полностью, на 90-100 %.
sync или fsync | bs | iodepth | rw | hdparm -W 1 | hdparm -W 0 |
---|---|---|---|---|---|
sync | 4k | 1 | randwrite | 18700 iops | 18700 iops |
sync | 4k | 4 | randwrite | 49400 iops | 54700 iops |
sync | 4k | 32 | randwrite | 51800 iops | 65700 iops |
sync | 4M | 32 | write | 516 MB/s | 516 MB/s |
fsync=1 | 4k | 1 | randwrite | 288 iops | 18100 iops |
fsync=1 | 4k | 4 | randwrite | 288 iops | 52800 iops |
fsync=4 | 4k | 4 | randwrite | 1124 iops | 53500 iops |
fsync=1 | 4k | 32 | randwrite | 288 iops | 65700 iops |
fsync=32 | 4k | 32 | randwrite | 7802 iops | 65700 iops |
fsync=1 | 4M | 32 | write | 336 MB/s | 516 MB/s |
bs | iodepth | rw | результат |
---|---|---|---|
4k | 1 | randread | 8600 iops |
4k | 4 | randread | 21900 iops |
4k | 8 | randread | 30500 iops |
4k | 16 | randread | 39200 iops |
4k | 32 | randread | 50000 iops |
4M | 1 | read | 508 MB/s |
4M | 16 | read | 536 MB/s |
Если не хотите 288 иопс — отключайте кэш.
Ceph HDD+SSD
Дано: 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, но результат идентичный).
- Настройки по умолчанию — все кэши дисков включены (везде hdparm -W 1, в /sys/block/*/queue/write_cache везде write back) — Ж О П А, iops=59, avg lat = 16.88ms
- Отключаю кэш записи SSD с журналами: hdparm -W 0 /dev/sdb — остаётся Ж О П А, iops=58, avg lat = 16.99ms
- Всем LVM-девайсам отключаю кэш записи: for i in /sys/block/dm-*; do echo write through > $i/queue/write_cache; done` — А Ф И Г Е Т Ь, iops=584, avg lat = 1.7ms
- Обратно включаю кэш SSDшке с журналами: hdparm -W 1 /dev/sdb — остаётся iops=582, avg lat = 1.7ms
- Откручиваю все отключения кэшей LVM: for i in /sys/block/dm-*; do echo write back > $i/queue/write_cache; done — обратно жопа, 57 iops, avg lat = 17.2ms
- Опять отключаю кэш журнальным LVM-девайсам: for i in `ls /dev/ceph-journals/lvol*`; do j=readlink $i; echo write through > /sys/block/${j##../}/queue/write_cache; done — никакого улучшения, всё та же жопа (но с ними это точно безопасно, так как они с конденсаторами :))
- Отключаю кэш 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
- Ага. Простите. Обнаруживаю, что просто писать куда-то 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 — но это всё равно лучше, чем изначальная жопа.
Виртуалку, в которой тестировал — даже не перезапускал между тестами.
Мораль: отключайте кэш записи всем дискам!
Частичная разгадка:
-
В жёстких дисках HGST есть Media Cache(энергонезависимый кэш случайной записи прямо на пластинах) — во-первых, там не HGST, во-вторых, в HGST медиакэш включён всегда и не зависит от hdparm -W 0. - В жёстких дисках Seagate Enterprise Capacity ST8000NM0055 (коих из общего числа 3) есть встроенный SSD-кэш. И вот он, видимо, действительно включается только при hdparm -W 0.
- Блюстор блокирует запись в журнал записью на HDD. Когда включен спец.кэш, HDD начинает рандомно писать сильно быстрее, и блокировки при сбросе уходят. Действует кэш, естественно, временно — когда он кончится, производительность случайной записи опять упадёт. Однако плюс в том, что в Ceph-е этого, скорее всего, не произойдёт, так как скорость случайной записи ограничивается, собственно, самим Ceph-ом и распределяется по всем дискам кластера :).
- Однако, для обычных дисков без 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 такой медленный?
Третья редакция hatespeech’а.
Речь о 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. Как так?
Отчаиваемся и по советам знатоков прикручиваем туда SSD под wal+db. И думаем: ну, теперь у нас быстрая SSD с конденсаторами под журналом, латенси записи 50 микросекунд, значит должно быть много иопсов — ну, в устоявшемся режиме хотя бы 300 (9 * 100 / 3). Тестируем. В 1 поток получаем ну… 60 иопс. Во много — где-то 200. Опять плохо.
Смысл в том, что собственной реализации журнала у блюстора нет, есть очередь отложенной записи, живущая прямо в RocksDB. RocksDB — это LSM keyvalue база, по сути база-журнал. В принципе, это достаточно разумно, так как всё равно нужно журналировать изменения метаданных, которые там держит блюстор. Когда очередь отложенной записи засунута туда же, изменение можно коммитить одной транзакцией (соответственно, одним fsync-ом).
И в этой схеме есть одно большое отличие от filestore — оно заключается в том, что в filestore журнал работал как буфер для временного повышения нагрузки на запись. Пока в журнале было место, случайная запись была очень быстрой, а журналы обычно делали размеров в несколько гигабайт. В bluestore же «очередь отложенной записи» очень маленькая и сбрасывается через каждые 64 запроса. То есть, bluestore не пишет быстрее, чем в среднем может медленное устройство (HDD).
Плюс к этому на практике (при просмотре strace) оказывается, что fsync-ов на каждую операцию записи делается не 1, а 2. Второй fsync — это лишняя транзакция записи в журнал BlueFS, сводящаяся к обновлению размера лог-файла RocksDB. Это нафиг не нужно, так как в опциях RocksDB в цефе по умолчанию стоит wal_recovery_mode=kTolerateCorruptedTailRecords и recycle_log_number=4, но это так, потому что без этого у них из-за другого бага корраптятся данные при падении OSD. Что на самом деле исправляется легко, я им даже отправил фикс — https://tracker.ceph.com/issues/38559 https://github.com/ceph/ceph/pull/26909 - и они даже вроде как обещают его влить. С фиксом на HDD ускорение случайной записи при глубине очереди 1 — двукратное (с 33 % до 66 % возможностей самого HDD, обычно как раз с 33 до 66 иопс). При глубине очереди 128 — почти нулевое.
ОК, ладно. В конце концов мы решаем — гулять так гулять и собираем кластер на серверных SSD (или вообще NVMe). Думаем — ну теперь-то?!… Бенчим в 1 поток. 300 иопс. Охреневаем окончательно и идём гуглить эту статью :)
Здесь смысл в том, что в голове у всех сидит мысль «а, ну да, оно медленное, потому что слишком много пишет на диск — диск же относительно медленный, а софт быстрый». А вот хрен. :) оказывается, Ceph довольно сложно разогнать до latency < 1 ms, и виной тому не диски, а сам Ceph. То есть да, Ceph при записи мелкими блоками порождает WA (Write Amplification) 3..5 на каждой OSD — это легко посчитать через тот же strace. Но на хороших SSD это практически не занимает времени, одна операция 4кб записи занимает условно 20 микросекунд. Проблема именно в С++ коде Ceph.
Причём даже не до конца понятно, что конкретно там тормозит — такое ощущение, что всё целиком. Выявить какие-то «горячие точки» при профилировании трудно, просто при записи выполняется много всякой C++ной мелочи, которая суммарно отъедает достаточно много времени. Одно горячее место — вычисление цифровых подписей пакетов (включено по умолчанию, можно отключить), другое — сериализация/десериализация (код обрабатывает каждое поле пакета, чуть ли не каждый байт, отдельным вызовом функции). Дальше идут уже malloc-и, которых тоже происходят тонны. Причём всё это происходит в несколько потоков. На это ещё навёрнута какая-то странная смесь буферизованного и прямого I/O.
RocksDB не виновата — её я пробовал бенчить, она быстрая, ~8000 транзакций в секунду на NVMe в 1 поток она даёт и даже масштабируется до ~120000 tps в 256 потоков. А Ceph OSD даже с максимальным параллелизмом на той же NVMe даёт только 10-20 тысяч iops.
Сеть тоже не виновата — её я пробовал бенчить с помощью nbd (network block device). При прямом доступе диск выдаёт 50000 iops, при пробросе диска с одного сервера на другой через nbd — 8000 iops. То есть, добавленная latency сети — примерно 0.1ms. Это не много.
И даже Bluestore не совсем виноват. На NVMe Bluestore с некоторыми тюнами всё-таки осиливает завершить запись примерно за 0.3мс. И в то же время код самого Ceph сжирает ещё 0.4мс. Авторы сейчас пытаются пилить новую реализацию OSD на асинхронном фреймворке Seastar (Crimson OSD). Но проблема в том, что, скорее всего, это им поможет слабо. Проблема не в том, что многопоточный код плохо работает с вводом-выводом — проблема в том, что он сам по себе сильно жрёт CPU. Чтобы всё это работало быстрее, нужно упрощать логику.
По сути, нужно было бы добиться ситуации, в которой Ceph бы отъедал 0.1ms и Bluestore ещё 0.1ms. Сеть съест ещё 0.1ms и тогда на хорошей NVMe получится latency ~ 0.33ms, что в 1 поток соответствует 3000 операциям записи в секунду. До локальной SSD это всё равно не дотянуло бы, но, тем не менее, было бы уже очень-очень неплохо. Тогда, скажем, можно будет бороться за CPU: поставив самолётные CPU по 20000$ каждый, настроив ядро и снизив время Ceph+Bluestore ещё в 2 раза, вы получите уже 0.23ms = ~4350 iops. А допилив поддержку Infiniband, может, получите и все 6000 iops. Пока же код не оптимизирован… всё это — мёртвому припарки.
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
RAID WRITE HOLE
В RAID-е есть один интересный момент: при отказе диска и одновременном отключении питания RAID 5 может кораптить данные.
Суть такая: допустим, есть три диска в рейд5. Есть какая-то пара блоков данных A и B. Соответственно на дисках хранятся: A, B, A xor B.
Теперь представим, что мы пишем в блок B данные B2. Для этого нам надо обновить данные на двух дисках: B -> B2, A+B -> A+B2. Теперь представим, что один из них успел записать, а второй не успел. Тут вырубилось питание и одновременно сдох диск A (или диск сдох от умирания питания, или контроллер повис и ядро в панику упало...). Что мы имеем на дисках?
?, B2, A+B либо ?, B, A+B2.
Теперь при попытке восстановить A мы получим A+B+B2 => опа! Покорраптились данные, которые даже не записывались!
Из-за этого raid всегда делает полный resync после нештатного вырубания питания. И, собственно, такая же потеря данных возможна при отказе диска до завершения resync. mdadm RAID5 в таких ситуациях (когда одновременно потерян диск и массив помечен как грязный) просто отказывается стартовать.
И именно чтоб этого избежать, в цефе сделано полное журналирование всех данных на уровне отдельных дисков (т.е. OSD). Потому что других способов борьбы с этой проблемой НЕТ, а при работе по сети отказы гораздо более вероятны, чем при работе RAID-массива на локальных дисках. Даже write intent bitmap может только сказать вам, потеряли вы какие-то данные или нет, но не может помочь их восстановить, если они потеряны.
Так что Ceph надёжнее RAID-а. :) медленнее (на SSD). Но надёжнее и не требует resync-а.
Краткий экскурс в устройство 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 Platinum 8168), 2x100-гбит сетью (точнее 2x2x100, так как 2 карты по 2 порта) и 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. Есть большая вероятность, что в их случае выигрыш мог быть в несколько раз. Нет, это бессмысленно.
Апдейт
NVMe обновились до Micron 9300 (максимальной ёмкости 12.8 ТБ). iops-ов такие диски дают даже не 260 тыс., а 310 тыс. Всё остальное осталось прежним. Итого 100 клиентами на запись сняли 477029 iops. Однако надо понимать, что каждому клиенту при этом досталось лишь 4770 iops. 10 клиентами сняли 294000 iops — то есть на 1 клиента 29400 иопс, что всё-таки поприличней.
Почему стало лучше? Предположительно, благодаря тюнингу. По сравнению с прошлым тестом они:
- отключили чексуммы мессенджера (ms_crc_data=false) и чексуммы блюстора (bluestore_csum_type=none)
- затюнили rocksdb: bluestore_rocksdb_options = compression=kNoCompression,max_write_buffer_number=64,min_write_buffer_number_to_merge=32,recycle_log_file_num=64,compaction_style=kCompactionStyleLevel,
write_buffer_size=4MB,target_file_size_base=4MB,max_background_compactions=64,level0_file_num_compaction_trigger=64,level0_slowdown_writes_trigger=128,
level0_stop_writes_trigger=256,max_bytes_for_level_base=6GB,compaction_threads=32,flusher_threads=8,compaction_readahead_size=2MB- 64x32x4 MB memtable (number x merge x size) вместо стандартных 4x1x256 MB. Скорее всего, это и сыграло основную роль. Эффект не совсем очевиден, но, вероятно, это снижает нагрузку на CPU, потому что поиск в 64 маленьких memtable быстрее, чем поиск в 1 (или 4) больших.
- сильно изменён max_bytes_for_level_base — с 256 мб он поднят до 6 гб!
- добавлены потоки compaction-а.
- выдали 14 гб RAM каждому OSD
- osd_max_pg_log_entries=osd_min_pg_log_entries=osd_pg_log_dups_tracked=osd_pg_log_trim_min = 10 (хз, по-моему, ничего не даёт)
Также надо отметить, что:
- cephx у них уже был отключён. В этот раз зачем-то добавили и отключение подписей — видимо, читали мою статью. Но это нафиг не надо, при отключенном cephx подписи можно уже не отключать.
- debug objecter = 0/0 и вообще отключенные дебаги у них тоже уже были
- с prefer_deferred_size и min_alloc_size они, видимо, не игрались (а зря)
- обновлённые диски не играют никакой роли гарантированно. 260000 или 310000 iops — для цефа никакой разницы нет.
Модели
- Micron серий 5100/5200
- HGST SN260
- Intel P4500
https://docs.google.com/spreadsheets/d/1E9-eXjzsKboiCCX-0u0r5fAjjufLKayaut_FOPxYZjc
Резюме
Резюмируя вышесказанное, для random write iops:
- Использовать только SSD с конденсаторами. NVMe это тоже касается. Подсказка: 99 % desktop SSD и NVMe конденсаторов не имеют.
- …и надо отключать этим SSD кэш (hdparm -W 0), если они SATA!
- Отключить powersave: governor performance, cpupower idle-set -D 0
- Отключить электронные подписи:
cephx_require_signatures = false
cephx_cluster_require_signatures = false
cephx_sign_messages = false
(и монтировать rbd map / cephfs ядерным драйвером с опциями -o nocephx_require_signatures,nocephx_sign_messages) - Для хороших SSD или NVMe: min_alloc_size=4096, prefer_deferred_size_ssd=0 (ДО создания OSD)
- Актуально для версий до Nautilus включительно — [global] debug objecter = 0/0 (там большой тормоз на клиентской стороне)
- Если нужны только random write iops, то в userspace драйвере (например, QEMU) — отключить rbd cache
- Для HDD-only или Bad-SSD-Only и версий до Nautilus включительно — снять ручник https://github.com/ceph/ceph/pull/26909
- mount -o lazytime
Примечание
Написанное в статье актуально для версий Ceph, доступных на момент последней правки (см. «История» вверху страницы). Конкретно — для 12 luminous, 13 mimic, да и 14 nautilus тоже — всё пока что актуально. Если вдруг в будущем что-то пофиксят и всё вдруг станет чудесно быстрым — сам побегу обновляться первым и поправлю статью :).
См. также
- http://vasilisc.com/bluestore-ceph-2017 - кое-что про bluestore, местами не совсем верно, но тем не менее
- https://amarao-san.livejournal.com/3437997.html - «IOPS не существует» — сказ о latency
- Прим.вред — iops существуют, но с обязательным указанием режима тестирования и параллелизма