Изменения

Перейти к: навигация, поиск

Производительность Ceph

10 124 байта добавлено, 15:34, 21 мая 2023
Нет описания правки
Ceph — это SDS (по-русски — программная СХД), которая по некоторым параметрам является уникальной в своём роде, и в целом, умеет очень многое — S3, диски виртуалок, кластерную FS + огромный багаж дополнительных фич.
И всё было бы хорошо — бери, ставь, запускай своё облако и руби бабло — если бы не один маленький нюанс: ПРОИЗВОДИТЕЛЬНОСТЬ. Терять 95 95 % производительности в Production-е разумным людям обычно жалко. «Облакам» типа AWS, GCP, Яндекса, по-видимому, не жалко — у них тоже собственные крафтовые SDS и они тоже тормозят примерно так же :-) но этот вопрос оставим — кто мы такие, чтобы их судить.
В данной статье описано, каких показателей производительности можно добиться от цефа и как. Но сразу предупрежу: локальный SSD вы не догонитеЕсли вкратце, то примерным ориентиром служит доклад Nick Fisk «Low-Latency Ceph», в его исполнении Low Latency это 0. Локальные SSD сейчас ОЧЕНЬ быстрые 7 мс (особенно NVMeна запись), порядок их задержки — 0.05ms. Догнать эту цифру SDSЛучший результат с Ceph-ке крайне трудно ом получить практически невозможно (одна только сеть сожрёт те же худший — легко). При этом 0.05ms), перегнать — наверное7 мс — это всего лишь примерно ~1500 iops в 1 поток. На чтение в идеальной ситуации можно получить где-то раза в 2 больше, вообще невозможното есть где-то до 3000 iops в 1 поток.
Для сравнения: любой самый дешёвый серверный SSD-диск раз в 10 быстрее, средний порядок задержки SSD на запись — 0.01-0.04 мс, на чтение — 0.1 мс. '''UPDATE: Догнать (почти догнать) SDS-кой локальной диск можно. Я , я это сделал в своём собственном проекте — Vitastor: https://vitastor.io :-) это блочная SDS с архитектурой, похожей на Ceph, но при этом БЫСТРАЯ — в тесте на SATA SSD кластере задержка и чтения, и записи составила 0.14 мс. На том же кластере задержка записи у Ceph была 1 мс, а чтения — 0.57 мс. Детали есть в [https://yourcmc.ru/git/vitalif/vitastor/src/branch/master/README.md README] — смотрите по ссылке.'''
== Бенчмаркинг ==
Задержки обычно важнее простой пиковой производительности случайного чтения/записи, так как далеко не каждое приложение может загрузить диск при большом параллелизме / глубокой очереди (32-128 запросов).
 
Примерным ориентиром наилучшей возможной задержки Ceph служит доклад Nick Fisk «Low-Latency Ceph», в его исполнении Low latency это 0.7ms. Это и есть ориентир, на лучший результат рассчитывать особенно не приходится. 0.7ms — это всего лишь примерно ~1500 iops в 1 поток.
=== Тестирование дисков ===
* Задержка случайной записи: {{Cmd|1=fio -ioengine=libaio -sync=1 -direct=1 -invalidate=1 -name=test -bs=4k -iodepth=1 -rw=randwrite -runtime=60 -filename=/dev/sdX}}
«А почему так мало…» — см.нижесагу про конденсаторы.
[[Файл:Warning icon.svg|32px|link=]] Когда разворачиваете Ceph OSD на SSD — очень разумно не отдавать её под Ceph целиком, а оставить небольшой раздел (10-20 гб) пустым для будущего использования под бенчмаркинг. Ибо  Ибо SSD имеют свойство со временем (или при забивании данными под 80%) начинать тормозить. Очень удобно иметь возможность гонять fio на пустом никем не используемом разделе. ==== Лирическое отступление ==== Почему нужно тестировать именно так? Ведь в целом производительность диска зависит от многих параметров:* Размер блока* Режим — чтение, запись или смешанный режим чтение+запись в разных пропорциях* Параллелизм — размер очереди и число потоков, то есть, в целом число одновременно запрашиваемых у диска операций* Длительность теста* Исходное состояние — пуст, заполнен линейной записью, заполнен случайной записью, заполнен случайной записью на протяжении какого-то времени и т. п.* Распределение данных — например, 10% горячих данных и 90% холодных — или, например, определённое расположение горячих данных (в начале диска)* Другие смешанные режимы тестов, например, тестирование одновременно с разными размерами блоков Также и результаты можно интерпретировать с разной степенью детализации — вместо простого среднего числа операций или мегабайт в секунду можно также приводить графики, гистограммы, перцентили и так далее — это, естественно, даст больше информации о поведении тестируемого образца. Есть и философская сторона тестов — например, производители серверных SSD иногда заявляют о необходимости подготовки диска к тестам путём 2-х кратной полной случайной перезаписи, чтобы нагрузить слой трансляции адресов диска, а я считаю, что это на самом деле ставит SSD в неправдоподобно плохие по сравнению с реальной нагрузкой условия; есть сторонники рисования графиков формата «задержка в зависимости от числа операций в секунду», что я считаю немного странным, но тоже возможным подходом — в нём, по сути, строится график F1(q) в зависимости от F2(q) и график обычно получается достаточно замысловатый — но для каких-то применений, может быть, и тоже разумный. В общем, бенчмаркингом заниматься можно бесконечно, и уж несколько дней, чтобы предоставить полную информацию, точно уйдёт. Этим обычно и занимаются ресурсы типа 3dnews в своих обзорах SSD. А мы не хотим сидеть несколько дней. Мы хотим обозначить набор тестов, которые можно провести быстро и сразу составить примерное представление о производительности. Посему общая идея — выделить несколько наиболее «крайних» режимов, протестировать диск в них и представить, что остальная часть «амплитудно-скоростной характеристики» диска является некоторой гладкой функцией в пределах изменения параметров между крайними точками. Тем более, что каждому из крайних режимов соответствует и реальное применение в своей категории приложений:# Использующих в основном линейный или крупноблочный доступ. Для таких приложений наиболее важная характеристика — производительность линейного доступа в мегабайтах в секунду. Отсюда режим тестирования линейным доступом 4 МБ блоком со средней очередью — 16-32 операции. Результаты — только в МБ/с.# Использующих случайный доступ мелким блоком и при этом способных к распараллеливанию. Отсюда — режимы тестирования случайным доступом 4 КБ блоком (стандартный блок для большинства ФС и, плюс-минус, СУБД) с большой очередью — 128 операций или, если диск не удаётся нагрузить одним потоком CPU с глубиной очереди 128 — тогда в несколько (2-4-8 или больше) потоков по 128 операций. Результаты — только в iops. Задержку (latency) указывать не нужно, так как в данном тесте её можно произвольно увеличить, просто подняв размер очереди — задержка жёстко связана с iops формулой latency=queue/iops.# Использующих случайный доступ мелким блоком и при этом НЕспособных к распараллеливанию. Таких приложений больше, чем вы могли подумать — например, в части записи сюда относятся все транзакционные СУБД. Отсюда вытекают режимы тестирования случайным доступом 4 КБ блоком с очередью 1 и, для записи, с fsync после каждой операции, чтобы диск/СХД не могли нас обмануть и положить запись во внутренний кэш. Результаты — iops или latency, по желанию — но выберите что-то одно, так как числа, опять же, жёстко связанные.
=== Тестирование кластера Ceph ===
Как тестировать Ceph после сборки:.
* fio -ioengine=rbd. Нужно сделать следующее:*# fio -ioengine=rbd -direct=1 -name=test -bs=4M -iodepth=16 -rw=write -pool=rpool_hdd -runtime=60 -rbdname=testimg*# fio -ioengine=rbd -direct=1 -name=test -bs=4k -iodepth=1 -rw=randwrite -pool=rpool_hdd -runtime=60 -rbdname=testimg*# fio -ioengine=rbd -direct=1 -name=test -bs=4k -iodepth=128 -rw=randwrite -pool=rpool_hdd -runtime=60 -rbdname=testimg*: И потом то же самое для read/randread.*: Смысл в том, чтобы протестировать а) задержку в идеальных условиях б) линейную пропускную способность в) случайные iops-ы.*: Перед тестами чтения образ сначала нужно заполнить линейной записью, так как чтение из пустого образа очень быстрое :)*: Запускать оттуда, где будут реальные пользователи RBD. В целом, с другого узла результаты обычно лучше.* Всё то же самое изнутри виртуалки или через krbd:*# fio -ioengine=libaio -direct=1 -name=test -bs=4M -iodepth=16 -rw=write -runtime=60 -filename=/dev/rbdX*# fio -ioengine=libaio -direct=1 -sync=1 -name=test -bs=4k -iodepth=1 -rw=randwrite -runtime=60 -filename=/dev/rbdX*# fio -ioengine=libaio -direct=1 -name=test -bs=4k -iodepth=128 -rw=randwrite -runtime=60 -filename=/dev/rbdX*: Заметьте, что при тестировании задержки добавилась опция -sync=1. Это не случайно, а соответствует режиму работы СУБД (транзакционная запись в 1 поток). В ioengine=rbd понятие sync отсутствует, там всё всегда «sync».* ceph-gobench*: Или https://github.com/vitalif/ceph-bench, что примерно то же самое. Родоначальник идеи — @socketpair Марк Коренберг ([https://github.com/socketpair/ceph-bench оригинал]). Бенчилка тестирует ''отдельные OSD'', что очень помогает понять, кто же из них тупит-то.*: Перед запуском надо создать пул без репликации {{Cmd|ceph osd pool create bench 128 replicated; ceph osd pool set bench size 1; ceph osd pool set bench min_size 1}} и с числом PG, достаточным, чтобы при случайном выборе туда попали все OSD (ну или прибить их вручную к каждому OSD upmap-ами)* CephFS** Нормальных инструментов для тестирования ФС, сцуко, нет!!!** «Нормальный» инструмент — это такой инструмент, который вёл бы себя, как файловый сервер: случайно открывал, создавал/писал/читал и закрывал маленькие файлы среди большого общего количества, разбитые по набору каталогов** Всё, что есть, какое-то кривожопое: bonnie++, например, зачем-то тестирует запись по 1 байту. iometer, fs_mark не обновлялись лет по 10, но и паттерн файл сервера не умеют. Лучшее, что умеют — это тест создания файлов.** Пришлось написать свой ioengine для fio: https://github.com/vitalif/libfio_fileserver :)* S3 (rgw):** [https://github.com/intel-cloud/cosbench cosbench] — очень толстый, Java с Web-интерфейсом, XML-настройки** [https://github.com/vitalif/hsbench hsbench] — ссылка дана на исправленную версию (!). Максимально простой, консольное Golang приложение. Оригинальная версия пока что имеет 2 неприятных бага: во-первых, вместо чтения объектов целиком читает только первые 64 КБ, во-вторых, производит последовательное, а не случайное, чтение. Что, например, с minio приводит к слишком оптимистичным результатам тестов.** [https://github.com/minio/warp minio warp] — тестов чуть больше, чем в hsbench, но зато тестирует только 1 бакет и при каждом тесте загружает данные заново
Примечанияfio -ioengine=rbd. Нужно сделать следующее:# fio -ioengine=rbd -direct=1 -name=test -bs=4M -iodepth=16 -rw=write -pool=rpool_hdd -runtime=60 -rbdname=testimg# fio -ioengine=rbd -direct=1 -name=test -bs=4k -iodepth=1 -rw=randwrite -pool=rpool_hdd -runtime=60 -rbdname=testimg# fio -ioengine=rbd -direct=1 -name=test -bs=4k -iodepth=128 -rw=randwrite -pool=rpool_hdd -runtime=60 -rbdname=testimg# ...и потом то же самое для read/randread. Смысл в том, чтобы протестировать а) задержку в идеальных условиях б) линейную пропускную способность в) случайные iops-ы. Перед тестами чтения образ сначала нужно заполнить линейной записью, так как чтение из пустого образа очень быстрое :) Запускать нужно оттуда, где будут реальные пользователи RBD. В целом, с другого узла результаты обычно лучше. Также всё то же самое можно повторить изнутри виртуалки или через krbd:# fio -ioengine=libaio -direct=1 -name=test -bs=4M -iodepth=16 -rw=write -runtime=60 -filename=/dev/rbdX# fio -ioengine=libaio -direct=1 -sync=1 -name=test -bs=4k -iodepth=1 -rw=randwrite -runtime=60 -filename=/dev/rbdX# fio -ioengine=libaio -direct=1 -name=test -bs=4k -iodepth=128 -rw=randwrite -runtime=60 -filename=/dev/rbdX Заметьте, что при тестировании задержки через libaio добавилась опция -sync=1. Это не случайно, а соответствует режиму работы СУБД (транзакционная запись в 1 поток). В ioengine=rbd понятие sync отсутствует, там всё всегда «sync». ==== Отдельные OSD ==== ceph-gobench: https://github.com/rumanzo/ceph-gobench/ Либо https://github.com/vitalif/ceph-bench, что примерно то же самое. Родоначальник идеи — @socketpair Марк Коренберг ([https://github.com/socketpair/ceph-bench оригинал]). Бенчилка тестирует ''отдельные OSD'', что очень помогает понять, кто же из них тупит-то. Перед запуском надо создать пул без репликации {{Cmd|ceph osd pool create bench 128 replicated; ceph osd pool set bench size 1; ceph osd pool set bench min_size 1}} и с числом PG, достаточным, чтобы при случайном выборе туда попали все OSD (ну или прибить их вручную к каждому OSD upmap-ами). ==== CephFS ==== «Нормальных» инструментов для тестирования ФС, сцуко, нет!!! «Нормальный» инструмент — это такой инструмент, который вёл бы себя, как файловый сервер: случайно открывал, создавал/писал/читал и закрывал маленькие файлы среди большого общего количества, разбитые по набору каталогов Всё, что есть, какое-то кривожопое: bonnie++, например, зачем-то тестирует запись по 1 байту. iometer, fs_mark не обновлялись лет по 10, но и паттерн файл сервера не умеют. Лучшее, что умеют — это тест создания файлов. Пришлось написать свой ioengine для fio: https://github.com/vitalif/libfio_fileserver :) ==== S3 (rgw) ==== Предпочтительный вариант: [https://github.com/vitalif/hsbench hsbench] — ссылка дана на исправленную версию (!). Максимально простой, консольное Golang приложение. Оригинальная версия пока что имеет 2 неприятных бага: во-первых, вместо чтения объектов целиком читает только первые 64 КБ, во-вторых, производит последовательное, а не случайное, чтение. Что, например, с minio приводит к слишком оптимистичным результатам тестов. В моей данные баги исправлены. [https://github.com/intel-cloud/cosbench cosbench] — очень толстый, Java с Web-интерфейсом, XML-настройки. [https://github.com/minio/warp minio warp] — тестов чуть больше, чем в hsbench, но зато тестирует только 1 бакет и при каждом тесте загружает данные заново. ==== Что использовать не надо ====
* dd и hdparm для бенчмаркинга не использовать вообще никогда!!!
* rados bench использовать тоже не надо, так как он создаёт для тестирования очень мало объектов (в 1 поток всего 2, в 128 — несколько сотен). «Случайная» запись в такое число объектов не очень-то и случайная.
* rbd bench лучше тоже не использовать можно. В принципе, он адекватен, но fio всё равно лучше.* Не надо удивляться, что если Ceph не может загрузить диски на 100 % при случайной записи. Он тормоз :) ==== Про RBD и параллелизм ==== [[File:Warning icon.svg|32px|link=]] Тестировать запись несколькими параллельными процессами (fio numjobs > 1) в один RBD-образ бесполезно. Из-за особенностей реализации RBD, в частности, из-за object-map, при параллельной записи из нескольких источников производительность СИЛЬНО проседает (в 2-10 раз). Можно отключить object-map, но это будет некорректный тест, т.к. в реальной эксплуатации в 99% случаев он нужен, так что с отключенным object-map вы лишь получите неправильный (слишком хороший) результат. Если вы не можете загрузить кластер одним процессом fio, то нужно создать несколько отдельных RBD-образов и запустить несколько процессов fio параллельно, каждый на своём RBD-образе.
=== Тестирование сети ===
sockperf. На одной ноде запускаем сервер: <tt>sockperf sr -i IP --tcp</tt>. На другой клиент в режиме ping-pong: <tt>sockperf pp -i SERVER_IP --tcp -m 4096</tt>. Нормальная средняя цифра ВНИМАНИЕ: В выводе фигурирует '''половина''' задержки (задержка в районе 0одну сторону).05мс (50usТаким образом, плюсдля получения RTT её стоит умножить на 2. Нормальный средний RTT -минус 20usв районе 30-50 микросекунд (0.05ms).
<s>Также qperf. На одной ноде просто <tt>qperf</tt>. На второй <tt>qperf -vvs -m 4096 SERVER_IP tcp_lat</tt>.</s>
== Конденсаторы ==
Нас спасёт такое чудо инженерной мысли, как '''SSD с конденсаторами''' (точнее, обычно или с суперконденсаторами — ионисторами). Которые на M.2 SSD, кстати, прекрасно видны невооружённым глазом(только тут это не ионисторы :)):
[[File:Micron 5100 sata m2.jpg]]
=== Про размер block.db ===
Кто задолбался '''Внимание:''' актуально до Ceph 14. Начиная с Ceph 15, благодаря добавленным «allocation hints» RocksDB, Bluestore стал нормально утилизировать раздел block.db. Для истории — это коммит 5f72c376deb64562e5e88be2f22339135ac7372b, там добавили опцию bluestore_volume_selection_policy. Дальше стоит читать, только если у вас всё ещё проблемы со спилловерами? Все задолбались со спилловерами! :)spillover-ами.
Спилловер — это когда вы собрали Bluestore на SSD+HDD, выделив SSD под базу (block.db), но при этом эта самая база постоянно частично утекает на HDD. При этом она, вроде бы, даже влезает в SSD с запасом — но всё равно утекает. Начиная, кажется, с Ceph 14 Nautilus, о спилловерах предупреждает <tt>ceph -s</tt>, а с Ceph 15 Octopus авторы попытались победить spillover-ы через дополнительные «allocation hint»-ы RocksDB (надо потестировать: коммит 5f72c376deb64562e5e88be2f22339135ac7372b, добавили опцию bluestore_volume_selection_policy).
Когда случается спилловер в SSD+HDD конфигурациях, работа кластера замедляется — в большей или меньшей степени, в зависимости от размеров RocksDB и паттерна нагрузки, так как когда метаданных не очень много, они влезают в кэш OSD — либо onode cache, либо rocksdb cache, либо, если включено bluefs buffered io — то ещё и в системный page cache. Если кэш-промахов достаточно много, или если OSD упирается в compaction RocksDB, могут даже появляться slow ops-ы.
Так в чём же дело и как это победить? А дело в том, что с выбором раздела для очередного файла БД (RocksDB организована в виде набора файлов) «есть нюанс», точнее, даже два.
'''Нюанс № 1№ 1:''' RocksDB кладёт файл на быстрый диск только когда считает, что на быстром диске хватит места под все файлы этого же уровня (для тех, кто ещё не в курсе — RocksDB это [https://github.com/facebook/rocksdb/wiki/Leveled-Compaction LSM база]).
Дефолтные настройки цефа:
Иными словами, имеют смысл только размеры раздела block.db 4 ГБ, 30 ГБ, 286 ГБ. Все промежуточные значения бессмысленны — место сверх предыдущего граничного значения использоваться не будет. Например, если БД занимает 10 ГБ, а раздел SSD — 20 ГБ, то фактически на SSD ляжет только WAL (1 ГБ), L1 и L2 (256 МБ + 2.56 ГБ). L3, составляющий бОльшую часть базы, уедет на HDD и будет тормозить работу.
При этом 4 ГБ — слишком мало, 286 ГБ — слишком много. Так что, по сути, правильно делать block.db размером 30 ГБ для OSD любого размера. Ещё раз повторюсь: это актуально до Ceph 14, с Ceph 15 уже не актуально.
Кстати, из этого же следует то, что официальная рекомендация — выделять под block.db то ли 2 %, то ли 4 % от размера устройства данных — полный отстой.
Но что делать, если у вас разделы другого размера? Например, 80 ГБ, и вы по каким-то причинам не хотите делать bcache, но хотите использовать эти 80 ГБ по максимуму. В этом случае можно поменять базовый размер уровня RocksDB (max_bytes_for_level_base). multiplier менять не будем, оставим по умолчанию 10 — его значение влияет на итоговое количество уровней RocksDB, а это уже более тонкая материя. Теоретически, меньшее число уровней снижает read и space amplification, но замедляет compaction и из-за этого может сильно повысить итоговый write amplification. Также есть тема с уменьшением размера отдельных memtable и кратным увеличением общего их числа, то есть, например, установки 32*32 МБ вместо дефолтных 4*256 МБ и min_write_buffer_to_merge=8, но эффект от этого тоже не совсем понятен (возможно, немного экономится CPU при compaction-е), так что это тоже пока лучше не трогать.
Так как каждый уровень отличается от предыдущего в 10 раз, общий размер раздела БД должен быть равен k*X, где k — коэффициенты из ряда: 1, 11, 111, 1111 и т. п. (по числу уровней RocksDB). Значит, мы можем взять размер нашего block.db, вычесть из него 1 ГБ WAL (лучше даже вычесть с запасом 2 ГБ) и делить его последовательно на каждую из цифр до тех пор, пока не получим значение, близкое к 256 МБ … 1 ГБ. Это значение округлить вниз, принять за базовый размер уровня RocksDB и прописать в конфиг как max_bytes_for_level_base. База компактится по 256 МБ за раз, так что меньше 256 МБ размер первого уровня ставить точно смысла нет. Например, для 80 ГБ раздела это будет 719 МБ, только не забываем считать всё в двоичных мегабайтах — MiB. Остаётся прописать это значение в конфигурацию (bluestore_rocksdb_options = …,max_bytes_for_level_base=719MB), перезапустить OSD и сделать ручной compaction (можно дважды).
'''Нюанс № 2№ 2:''' При ручном compaction-е RocksDB переписывает уровни целиком. Если при этом на SSD нет запаса места в размере этого уровня, то уровень, опять-таки, утечёт на HDD и так там и останется, ибо перемещать после compaction-а его обратно она не умеет. Теоретически, если после этого сделать compaction ещё раз, то уровень должен вернуться на SSD (поэтому выше дана рекомендация делать ручной compaction дважды). Однако по сведениям из чата якобы бывает так, что один-два файла *.sst на SSD не возвращается. Чтобы это побороть на 100 100 %, можно предусмотреть на SSD-разделе ещё и запас в размере первого + последнего уровня БД. В этом случае коэффициенты вместо 1-11-111-1111 превращаются в 2-22-212-2112 и т. п.
== RGW vs Minio ==
* Отключить оффлоады: ethtool -K enp3s0f0 gro off gso off tso off lro off sg off
* Отключить объединение прерываний: ethtool -C enp3s0f0 rx-usecs 0
* Самый дешёвый 10G свитч с Ebay: Quanta LB6M / Brocade TurboIron 24X24X — но говно старое унылое и жрёт под 200 ватт, и греется и жужжит соответственно* UPD Самый дешёвый свитч с Aliexpress: [https://aliexpress.ru/item/1005004429524441.html TP-LINK TL-ST5008F]. На чипе [https://www.realtek.com/en/products/communications-network-ics/item/rtl9303-cg Realtek RTL9303-CG]
Если совсем задолбала латенси, как отключить ВСЕ оффлоады?
* virtio — самый быстрый, но до QEMU 4.0 не умел TRIM. Юзать надо его.
* virtio-scsi — достаточно быстрый. На самом деле, умеет multiqueue и поэтому на быстром хранилище (например, при прямом доступе к локальной NVMe) должен быть быстрее virtio — но в случае с Ceph это не так, так как цеф не настолько быстрый, чтобы multiqueue играл роль
* nvme — тоже достаточно быстрый, но немного медленнее virtio. Принцип работы похожий — и там, и там кольцевые буферы. Можно использовать для тех образцов ПО, которые не умеют virtio, потому что не уметь nvme сейчас совершенно точно не может никто. Например, если хочется потестировать VMWare ESXi с вложенной виртуализацией внутри QEMU, то nvme — для вас!
=== cache=writeback ===

Навигация