Изменения

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

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

9237 байтов добавлено, 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 — OSD — либо onode cache, либо rocksdb cache, либо, если включено bluefs buffered io — io — то ещё и в системный page cache. Если кэш-промахов достаточно много, или если OSD упирается в compaction RocksDB, могут даже появляться slow ops-ы.
Так в чём же дело и как это победить? А дело в том, что с выбором раздела для очередного файла БД (RocksDB организована в виде набора файлов) «есть небольшой нюанс», точнее, даже два.
'''Нюанс № 1№ 1: ''' RocksDB кладёт файл на быстрый диск только когда считает, что на быстром диске хватит места под все файлы этого же уровня (для тех, кто ещё не в курсе — курсе — RocksDB это [https://github.com/facebook/rocksdb/wiki/Leveled-Compaction LSM база]).
Дефолтные настройки цефа:
* соответственно, 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 ГБ. Все промежуточные значения бессмысленны — место сверх предыдущего граничного значения использоваться не будет. Например, если БД занимает 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, имеют смысл только размеры раздела blockminio cannot be expanded or shrinkable in this manner.db 4 ГБ, 30 ГБ, 286 ГБ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, если БД занимает 10 ГБ, а раздел SSD — 20 ГБ, то фактически на SSD ляжет только WAL (1 ГБ), L1 и L2 (256 МБ + 2it behaves like blackbox.56 ГБ)It also adds significant complexity to Minio. L3Minio 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, уедет на HDD и будет тормозить работу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>
При этом 4 ГБ — слишком малоКороче, 286 ГБ — слишком многовсё работает как надо, в минио нет возможности расширения, если у вас будут ломаться диски — не меняйте, просто дождитесь, пока из строя выйдет половина дисков и пересоздайте кластер. Так чтоНа самом деле всё не так печально, по сутиможно заменить диск и запустить heal, правильно делать blockно, конечно, без той же прозрачности, что в Ceph — оно будет просто сканировать все объекты и проверять отсутствующие.db размером хотя бы 30 ГБ для всех OSDЕсли дисков много, это очень накладно.
Нюанс № 2: В момент compaction-Ещё Minio хранит объекты в виде обычных файлов, даже не шардируя каталоги (соответствующие бакетам) по подкаталогам, плюс на каждый объект ещё создаёт директорию с парой файлов метаданных. Ну а RocksDB часто требуется целиком переписать уровень целиком. Если при этом на SSD нет запаса места директории в размере этого уровняФС по миллиону файлов — это, то онестественно, опятьудовольствие ниже среднего. Хотя просто для раздачи оно, благодаря всяким dir_index-такиам, утечёт на HDD и так там и останется, ибо перемещать после compaction-а его обратно она не умеет. При этом одновременно могут компактиться как минимум первый и последний уровниработает.
Из этого следуетДля представления о производительности проведём простой тест Ceph (bluestore) vs Minio (ext4) на 1 HDD. Да, чтоя знаю, по идее, на разделе БД нужен ещё что это тупо и запас в размере первого + последнего уровня БДнужно ещё хотя бы посравнивать их на SSD. То есть примерно 30 ГБ превращаются в примерно 60Но всё-таки результаты довольно показательны. Да и объектное хранилище чаще холодное/прохладное и строится на HDD, а не на SSD.
Но что делать, если сами базы у вас размером, скажем, 30 ГБ? Выделять на 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 сек
ИтакДа, помня про необходимость запаса под первый и последний уровни выводим, что общий размер раздела БД должен быть равен k*Xзаливка в Minio быстрее. Но, где X во- размер первого уровняпервых, а k меньшая скорость заливки — это цена, во- коэффициенты из ряда: 2 (1 уровень)первых, 22 консистентности (2 уровняfsync), 212 (под 3 уровня)а во-вторых, bucket index-а и bucket index log-а, которые позволяют RGW, например, 2112 делать геосинхронизацию (под 4 уровняmultisite) и т.п, чего в Minio нет.
СоответственноКроме того, берём целевой максимальный размер нашей БД: 30 ГБ. Это будет размер последнего уровня. Делим его индексы в RGW можно положить на 10 до тех поротдельные SSD (как обычно все и делают), пока значение а если же вам совсем не приблизится где-то к гигабайту и принимаем это за размер первого уровня. Компактится база по 256 МБ за разнужны листинги, так что меньше 256 МБ размер первого уровня ставить точно смысла нет. После этого число делений+1 принимаем за число уровней, домножаем размер первого уровня на соответствующий коэффициент, накидываем +1 ГБ для WAL, округляем чуть вверх синхронизация и получаем необходимый размер раздела SSD. В случае с 30 ГБ - размер первого уровня будет 300 МБпрочее, число уровней 3 и требуемый размер SSD раздела - около 65 ГБ. Прописываем это в конфигурацию Ceph бакеты можно сделать безиндексными (bluestore_rocksdb_options = ...,max_bytes_for_level_base=300MBindexless), деплоим OSD и радуемся жизни. Ну, почти радуемсятогда оверхед bucket index-а вообще исчезает, так как 50% этого раздела у нас используется только в момент компакшена, а остальное время простаивает..и возможные проблемы с его шардированием.
== Снапшоты ==

Навигация