Изменения

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

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

9075 байтов добавлено, 10:17, 23 сентября 2020
м
Нет описания правки
В данной статье описано, каких показателей производительности можно добиться от цефа и как. Но сразу предупрежу: локальный SSD вы не догоните. Локальные SSD сейчас ОЧЕНЬ быстрые (особенно NVMe), порядок их задержки — 0.05ms. Догнать эту цифру SDS-ке крайне трудно (одна только сеть сожрёт те же 0.05ms), перегнать — наверное, вообще невозможно.
 
'''UPDATE: Догнать можно. Я это сделал в своём собственном проекте — 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] — смотрите по ссылке.'''
== Бенчмаркинг ==
*: Или 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/markhpcvitalif/hsbench hsbench] — ссылка дана на исправленную версию (!). Максимально простой, консольное Golang приложение. Оригинальная версия пока что имеет 2 неприятных бага: во-первых, вместо чтения объектов целиком читает только первые 64 КБ, во-вторых, производит последовательное, а не случайное, чтение. Что, например, с minio приводит к слишком оптимистичным результатам тестов.** [https://github.com/minio/warp minio warp] — тестов чуть больше, чем в hsbench, но зато тестирует только 1 бакет и при каждом тесте загружает данные заново
Примечания:
* dd и hdparm для бенчмаркинга не использовать вообще никогда.!!!* rados bench использовать тоже не надо, т.к. так как он создаёт для тестирования очень мало объектов (в 1 поток всего 2, в 128 — 128 — несколько сотен). "Случайная" «Случайная» запись в такое число объектов не очень-то и случайная.
* rbd bench использовать можно, но fio лучше.
* Не надо удивляться, что Ceph не может загрузить диски на 100 100 % при случайной записи. Он тормоз :)
=== Тестирование сети ===
Кто задолбался со спилловерами? Все задолбались со спилловерами! :)
Спилловер — это когда вы собрали 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:''' 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любого размера. Кстати, из этого же следует то, что официальная рекомендация — выделять под 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:''' При ручном compaction-е RocksDB переписывает уровни целиком. Если при этом на SSD нет запаса места в размере этого уровня, то уровень, опять-таки, утечёт на HDD и так там и останется, ибо перемещать после compaction-а его обратно она не умеет. Теоретически, если после этого сделать compaction ещё раз, то уровень должен вернуться на SSD (поэтому выше дана рекомендация делать ручной compaction дважды). Однако по сведениям из чата якобы бывает так, что один-два файла *.sst на SSD не возвращается. Чтобы это побороть на 100 %, можно предусмотреть на SSD-разделе ещё и запас в размере первого + последнего уровня БД. В этом случае коэффициенты вместо 1-11-111-1111 превращаются в 2-22-212-2112 и т. п. == RGW vs Minio == Вопрос частый, так как Ceph и Minio — две наиболее распространённые реализации S3. Сравнение, как всегда, не совсем честное, так как в Minio «бесконечного масштабирования» и произвольных схем избыточности нет. Есть только erasure коды, которые оперируют группами дисков, кратными по количеству 4 или 16 дискам. Расширения кластера в Minio раньше не было вообще, потом в каком-то смысле появилось через создание дополнительных зон. Таких же гарантий целостности, как в Ceph, в Minio тоже нет. Minio работает поверх обычных ФС, даже не делая fsync данных. На практике ext4, правда, делает sync автоматически раз в 5 секунд, да и Minio пишет с O_DIRECT, так что не совсем всё плохо — но тем не менее, потенциально небольшие потери при отключении питания возможны. Особенно классный перл был в баге https://github.com/minio/minio/issues/3478: <blockquote>Minio in this case is working as intended, minio cannot be expanded or shrinkable in this manner. Minio is different by design. It is designed to solve all the needs of a single tenant. Spinning minio per tenant is the job of external orchestration layer. Any addition and removal means one has to rebalance the nodes. When Minio does it internally, it behaves like blackbox. It also adds significant complexity to Minio. Minio is designed to be deployed once and forgotten. We dont even want users to be replacing failed drives and nodes. Erasure code has enough redundancy built it. By the time half the nodes or drives are gone, it is time to refresh all the hardware. If the user still requires rebalancing, one can always start a new minio server on the same system on a different port and simply migrate the data over. It is essentially what minio would do internally. Doing it externally means more control and visibility. Minio is meant to be deployed in static units per tenant.</blockquote> Короче, всё работает как надо, в минио нет возможности расширения, если у вас будут ломаться диски — не меняйте, просто дождитесь, пока из строя выйдет половина дисков и пересоздайте кластер. На самом деле всё не так печально, можно заменить диск и запустить heal, но, конечно, без той же прозрачности, что в Ceph — оно будет просто сканировать все объекты и проверять отсутствующие. Если дисков много, это очень накладно.
'''Нюанс № 2:''' БываетЕщё Minio хранит объекты в виде обычных файлов, что в момент compaction-а RocksDB требуется переписать уровень целиком даже не шардируя каталоги (не всегдасоответствующие бакетам) по подкаталогам, но бывает)плюс на каждый объект ещё создаёт директорию с парой файлов метаданных. Если при этом на SSD нет запаса места Ну а директории в размере этого уровняФС по миллиону файлов — это, то онестественно, опятьудовольствие ниже среднего. Хотя просто для раздачи оно, благодаря всяким dir_index-такиам, утечёт на HDD и так там и останется, ибо перемещать после compaction-а его обратно она не умеет. При этом одновременно могут компактиться как минимум первый и последний уровниработает.
Из этого следуетДля представления о производительности проведём простой тест Ceph (bluestore) vs Minio (ext4) на 1 HDD. Да, я знаю, что в идеале на разделе БД нужен ещё это тупо и запас в размере первого + последнего уровня БДнужно ещё хотя бы посравнивать их на SSD. То есть примерно 30 ГБ превращаются в примерно 60Но всё-таки результаты довольно показательны. Да и объектное хранилище чаще холодное/прохладное и строится на HDD, а не на SSD.
Но что делать, если вам надо выделить под базы, например, по 50 ГБ? Делать SSD разделы по 550 ГБ как-то уж очень жирно, но и спилловеров иметь не хочетсяТест делался через [https://github. В этом случае можно поменять базовый размер уровня RocksDB (max_bytes_for_level_base)com/vitalif/hsbench hsbench]. multiplier менять не будем, оставим по умолчанию 10 — его значение влияет на итоговое количество уровней RocksDB, а это уже более тонкая материяЗаключался в заливке примерно 1. Теоретически1 миллиона объектов в 1 бакет, меньшее число уровней снижает read потом сброса кэшей и space amplificationперезапуска Ceph/Minio, но замедляет compaction и из-за этого может сильно повысить итоговый write amplification. Также есть тема с уменьшением размера отдельных memtable и кратным увеличением общего потом — их числараздачи в случайном порядке, то естьа также проверки скорости выполнения операций листингов. Результаты:* Заливка в 32 потока: Minio — 305 объектов в секунду, напримерRGW — 135 объектов в секунду. RGW indexless — 288 объектов в секунду.* Раздача в 32 потока: Minio — 45 объектов в секунду, установки 32RGW — 78 объектов в секунду*Листинги в 32 МБ вместо дефолтных 4*256 МБ и min_write_buffer_to_merge=8потока: Minio — после сброса кэша 35 сек, но эффект после прогрева — 2.9 сек с разбросом от этого тоже не совсем понятен (возможно, немного экономится CPU при compaction-е), так что это тоже пока лучше не трогать0.5 до 16 сек. RGW — стабильно — 0.4 сек
ИтакДа, помня про необходимость запаса под первый и последний уровни выводимзаливка в Minio быстрее. Но, что общий размер раздела БД должен быть равен k*Xво-первых, где X — размер первого уровняменьшая скорость заливки — это цена, а k — коэффициенты из ряда: 2 (1 уровень)во-первых, 22 консистентности (2 уровняfsync), 212 (под 3 уровня)а во-вторых, bucket index-а и bucket index log-а, которые позволяют RGW, например, 2112 делать геосинхронизацию (под 4 уровняmultisite) и т. п, чего в Minio нет.
СоответственноКроме того, берём целевой максимальный размер нашей БД: 50 ГБ. Это будет размер последнего уровня. Делим его индексы в RGW можно положить на 10 до тех поротдельные SSD (как обычно все и делают), пока значение а если же вам совсем не приблизится где-то к гигабайту и принимаем это за размер первого уровня. Компактится база по 256 МБ за разнужны листинги, так что меньше 256 МБ размер первого уровня ставить точно смысла нет. После этого число делений+1 принимаем за число уровней, домножаем размер первого уровня на соответствующий коэффициент, накидываем +1 ГБ для WAL, округляем чуть вверх синхронизация и получаем необходимый размер раздела SSD. В случае с 50 ГБ — размер первого уровня будет 500 МБпрочее, число уровней 3 и требуемый размер SSD раздела — около 106 ГБ. Прописываем это в конфигурацию Ceph бакеты можно сделать безиндексными (bluestore_rocksdb_options = …,max_bytes_for_level_base=500MBindexless), деплоим OSD и радуемся жизни. Нутогда оверхед bucket index-а вообще исчезает, почти радуемся, так как 50 % этого раздела у нас используется только в момент компакшена, а остальное время простаивает…и возможные проблемы с его шардированием.
== Снапшоты ==

Навигация