Скованные одним цефом: как тестируем Ceph в MWS Cloud Platform

от автора

Смело предположу, что каждый инженер, на регулярной основе работающий с SDS Сeph, не единожды находился в состоянии фрустрации от сложности и неоднозначности этой технологии. Я хотел бы попробовать помочь и поделиться своим опытом решения проблем с производительностью. В этой статье я кратко расскажу про некоторые инструментальные подходы к решению возникающих задач.

Всем привет! Меня зовут Александр Пивкин, я ведущий SRE-инженер в MWS Cloud Platform. Сейчас Ceph — основная технология хранения данных в MWS Cloud Platform, и поэтому она должна работать хорошо.

Сегодня сфокусируемся на инструментах диагностики и устранения проблем производительности в Ceph-кластерах.

Введение

Наверняка вы знакомы с Ceph, поэтому не буду подробно разбирать, что это такое, а лишь напомню основные положения:

Ceph — это распределённая программно-определяемая система хранения данных с открытым исходным кодом.

Почему Ceph так привлекателен для крупных компаний:

Экономическая эффективность по сравнению с проприетарными решениями. А значит, помимо очевидной вендор-лок ловушки, Ceph, при наличии квалифицированных инженеров, сравнительно недорогой в обслуживании. А концепция open source позволяет модифицировать готовый продукт под свои нужды, а не вести разработку с нуля.

Гибкость и масштабируемость для растущих инфраструктур. Популярность облачного хранения данных в последние 10 лет демонстрирует устойчивый рост, и предпосылок для перелома тренда не просматривается в ближайшем будущем. Это значит, что объём данных будет неизменно расти. Поэтому возможность гибкой и быстрой адаптации к текущим требованиям становится важнейшим качеством как бизнес, так и технической составляющей любого облачного провайдера.

Поддержка различных сценариев использования в одной системе. Ceph, как всем известно, является, пожалуй, лучшим комбайном для всех популярных распределённых технологий хранения — ФС, объектного хранилища S3 и сетевых блочных устройств.

Развитие проекта и активное сообщество. Проект активно развивается и постоянно совершенствуется. Без поддержки со стороны разработчиков пользователи не останутся.

Выбор Ceph крупными бизнесами — лучшая похвала для технологии. Если ряд технологических компаний-гигантов доверяют Ceph свои данные — это говорит о надёжности системы больше, чем любые утверждения маркетологов. Размеры их кластеров поистине гигантские. Лично на меня в своё время большое впечатление произвёл своими размерами кластер CERN (> 110PB). И он работает!

Кстати, ознакомиться с публичной информацией о различных кластерах мира можно по ссылке.

К сожалению, как и любой сложный программный продукт, Ceph иногда не работает так, как хочется. Помимо отдельных и чётко очерченных проблем, типа проблем с мониторами, менеджерами или самими ОСД, описанными в официальной документации, существует огромный раздел проблем под общим названием «Что-то ceph медленно работает». И вот это «медленно работает» — это маленькая верхушка айсберга, в то время как под водой скрывается огромное тело, состоящее из возможных причин. Эти причины, в свою очередь, также можно условно разделить на 2 основные категории:

  1. Неверно подобранное оборудование. Несмотря на то что Сeph позиционирует себя как SDS (software defined storage), способная работать на оборудовании, которое можно купить в ближайшем компьютерном магазине, маркетологи скромно умалчивают, насколько хорошо он будет работать. И в случае неверного выбора придётся либо покупать новое оборудование, которое позволит кластеру работать на проектных скоростях, либо радоваться приобретённому опыту через собственные ошибки.

  2. Неправильно рассчитанные потребности и настройка оборудования. Попытки сэкономить там, где этого делать нельзя. Например, экономя на ЦПУ или оперативной памяти. Ceph не для бедных. Результат этой ошибки будет в целом похож на первый пункт — кластер просто не будет выдавать требуемой производительности.

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

Игра, которую играет Ceph

Игра, которую играет Ceph

Если владелец кластера Сeph избежал первых двух ловушек и всё сделал правильно, то оборудование ещё нужно верно настроить. И здесь появляются всевозможные кеши, режимы энергосбережения, режимы работы ЦПУ, буфера на различных уровнях, настройки ядра ОС и т. д. Количество настроек настолько велико, что легко потеряться без системного подхода. 

Что касается аппаратных настроек, лично я рекомендую помнить, что Ceph весьма требователен к ЦПУ, не дружит с аппаратными RAID-контроллерами и прожорлив по части пропускной способности сети. То есть следует давать максимум мощи процессорам, ставить ХБА и внимательно подходить к выбору сетевых карт. Ну и, конечно, есть различные варианты конфигураций в зависимости спецификации серверов. Например, включать или не включать IOMMU на процах, включать или не включать  write back кеш на дисках, какие настройки следует ставить на сетевых картах. Чтобы видеть, как в качественных и количественных показателях работают отдельные части, рекомендую опираться на книгу Брендана Грегга «System performance».

Ещё хочу предостеречь админов цефа от тех граблей, на которые наступал сам. А именно от использования двух плохих методов поиска узких мест в конфигурации: метода уличных фонарей и метода stack-overflow.

Метод уличных фонарей: взял название от бородатого анекдота. Пьяный что-то ищет под фонарем. Тут к нему подходит милиционер и спрашивает: «Что вы тут делаете?» Мужик отвечает: «Ключи от квартиры ищу». — «А где потерял?» — «В парке». — «А зачем здесь ищешь?» — «А здесь светлее».

В разрезе поиска узких мест в производительности это выглядело бы так, что администратор смотрит утилиту htop не потому, что это имеет какой-то смысл, а потому, что не знает, как использовать другие. Поэтому следует расширять администраторский кругозор в плане экипировки полезными инструментами.

Второй метод: известен почти всем. Гуглим симптомы и применяем найденные настройки, не особо понимая их суть, и смотрим, ушла ли проблема. Этот подход очень трудозатратный, потому что обычно нагугленные настройки не решают проблемы и, второе и самое главное, эти случайные настройки вызывают ряд других проблем по цепочке. После пары-тройки итераций бывает очень сложно найти, какие настройки и зачем менялись на первом круге.

Правильный метод — это не гадание, а измерение. Нужно понять, где возникает задержка, а потом искать решение.

Остановлюсь на типичном сценарии, когда пользователь приходит к администратору и говорит: «Знаешь, братишка, что-то Сeph тормозить стал. Вчера вот было нормально, а сегодня уже не нормально». Главная задача здесь — выяснить, где именно тормозит. Кратко напомню, какие стадии проходят данные на пути от клиента к ОСД. 

  1. Messenger: сетевой этап, получение данных.  

  2. OSD: уровень PG, здесь происходит проверка кредов, прав и праймари. ОСД посылает команды вторичным ОСД.

  3. Bluestore: здесь осуществляется подготовка и запись данных.

Все эти стадии в подробностях можно отслеживать по дампам ops, historic_ops либо ops_in_flight.

В кластерах, как правило, каждую секунду проходят тысячи запросов. Вручную анализировать выводы отдельных команд крайне утомительно и операционно затратно. И здесь на помощь приходит инструментарий.

Инструментарий

CBTRook: тестирование общей производительности

Существует давний и проверенный инструмент для различных видов тестирования цефа — CBT — ceph benchmark tool. Это фреймворк, написанный на питоне, который обеспечивает интеграционное, юнит, нагрузочное и регрессивное тестирование. 

Однако он полагается в основном на bare metal установку и, в частности, на параллельное распределённое SSH в виде PDSH. У нас в МВС же все цефы работают под управлением оркестратора rook в кубернетесе, и, соответственно, возможности использования этого инструмента весьма ограниченны. К счастью, CBTRook способен выполнять бОльшую часть своих функций даже с учётом контейнеризации.

Меня в первую очередь интересовало именно нагрузочное тестирование, которое бы выполнялось декларативно и по мере возможностей удобно. Поэтому я решил несколько модернизировать этот инструмент, приспособив его к использованию в среде кубернетеса. Находится он на Github по ссылке.

В предыдущих статьях от моих коллег: почему мы делаем своё объектное хранилище и как мы его делаем — рассказали, что реализацию S3 мы пишем свою, отказавшись от RGW в силу определённых обстоятельств. Отсюда следует второй момент, который я учитывал при изменении CBT — нагрузка только на rados-слой хранения. Это значит, что, например, нагрузочные тесты RBD меня не беспокоили. Тем более что для этих целей есть прекрасный инструмент FIO.

Суть работы cbt-rook заключается в параллельном выполнении команды rados bench и парсинге результатов тестов. Для его работы необходим рабочий кластер, представляющий собой цель для тестов. Деплоим cbt-rook в любой кластер, имеющий сетевую связность с целевым. Можно деплоить и в тот же кластер, где находится целевой цеф, однако желательно на свободную ноду, чтобы не вносить искажения, связанные с генерацией нагрузки, в итоговые результаты. 

Несмотря на то что во многих источниках высказываются сомнения по поводу тестов rados bench, я активно применяю этот инструмент на этапе начального тестирования перед вводом кластера в продуктовую среду. Потому что он даёт опорные значения о максимально возможных показателях производительности. То есть если здесь всё плохо, то очевидно, что не следует ожидать поражающих воображение результатов и на других тестах.

Ниже приведён типичный конфигфайл для автоматизированного теста цефа с помощью форка CBT:

```cluster: # Глобальные настройки для всех тестов  iterations: 1 # Сколько раз прогонится каждый тест  tmp_dir: "/tmp/cbt" # Временная директория для хранения временных файлов  health_wait: 60 # Время ожидания готовности пула (при бОльшем кол-ве ПГ нужно бОльшее время. На 2048 ПГ 60 секунд хватает)  pool_profiles: # Список профилей для создания тестовых пулов (если указано создание после каждого теста)    rep_test: # Имя профиля с последующими характеристиками      pg_size: 32      pgp_size: 32      replication: 3    erasure42:      pg_size: 2048      pgp_size: 2048      replication: 'erasure'      erasure_profile: 'ec42' # Имя ЕС профиля для создания пула      ec_overwrites: True  erasure_profiles:    ec42:      erasure_k: 4      erasure_m: 2      plugin: isabenchmarks: # Список тестов  radosbench: # Явное указание, что тестирование производится только в рамках rados слоя    op_size: # Размер объектов в байтах. Замечу, что Ceph оперирует в MiB, а не в MB. Можно указать списком      - 4096      - 4194304    write_only: true # Явное указание, что тестируется только запись    time: 300 # Время прогона одного теста    concurrent_ops: 128 # Кол-во потоков в одном процессе    concurrent_procs: # Кол-во процессов. Можно указать списком        - 1        - 4    target_pool: test # Целевой пул, куда лить. Если его нет, то Ceph создаст    rebuild_every_test: True # Пересоздавать пул после каждого теста. Если False, то нужно перед тестом создать пул руками    pool_profile: erasure42 # Профиль, который применится при создании пула```

Постарался подробно объяснить, что значит каждая опция, но если возникнут вопросы, задавайте — отвечу в комментариях. 

В процессе использования я опытным путём выяснил, что проще руками создавать пул и натравливать на него утилиту с помощью команды python3 cbt.py -a output example/rados-test4K.yaml

output — это директория для хранения результатов тестирования.  

rados-test4K.yaml — файл настроек конкретного теста (в данном случае запись блоком 4К). 

Отдельно замечу, что директорию output следует очищать перед каждым тестом, чтобы не смешивать результаты. 

За парсинг результатов отвечает питоновский скрипт rados_parser.py, находящийся в этом же репозитории. 

```usage: rados_parser.py [-h] --dir DIR [--details] [--comma]parse results of radosbench testsoptions:  -h, --help  show this help message and exit  --dir DIR   find files to parse  --details   Show results of all processes files  --comma     comma instead of dot in output```

Osdbench: поиск проблемной ОСД

Osdbench — творческое переосмысление известной концепции тестирования задержек отдельный ОСД путём записи мелких блоков в большие объекты по случайному смещению. Изначально была создана Марком Кроненбергом (по крайней мере, я впервые увидел код за его авторством), затем переписана на golang Rumanzo. Osdbench — форк инструмента Rumanzo.

В настоящий момент поддерживает следующие опции:

```  -bs string    size of the block to be inserted into objects (default "4K")  -clean    cleanup objects after test  -config string    path to config file (default "/etc/ceph/ceph.conf")  -debug    debug mode with verbose messages  -keyring string    path to keyring file (default "/etc/ceph/ceph.client.admin.keyring")  -os string    size of the object initially written to a cluster (default "4M")  -pool string    target pool for benching (default "test")  -seq    sequential write?  -target int    single target osd to run test on (default 10000)  -threads int    amount of threads to perform writes (default 1)  -time int    duration of test in seconds (default 10)```

Результатом работы является вывод списка всех ОСД в кластере с характеристиками в виде средней задержки на запись, среднего числа операций ввода/вывода в секунду и общее количество завершённых операций записи. В основном я использую этот инструмент для нахождения той ОСД, которая тормозит и замедляет собой весь кластер. 

Osdtrace: поиск логического слоя ОСД, тормозящего работу ОСД

Osdtrace — это инструмент, созданный разработчиками Сanonical на основе eBPF и презентованный на «Цефалоконе 2025». Утилита служит целям отслеживания задержек на разных этапах прохождения данных внутри самого цефа. Краткий обзор этих стадий был приведён в начале статьи.

Логически все стадии цефа можно условно разделить на три группы: Messenger, OSD и Bluestore. Первая отвечает за приём и передачу данных по сети. Вторая — за расчёты и назначения операций ОСД. Третья — непосредственное выполнение чтения или записи. В свою очередь, каждая из стадий имеет внутреннее разбиение на подстадии:

Messenger:  throttle → recv → dispatch

OSD: queue → osd → peers

Bluestore: Prepare → aio_wait → seq_wait → kv_commit

throttle: контроль наполнения очереди во избежание переполнения

recv: время на получение полного пакета данных из сети

dispatch: передача данных из слоя мессенджера в следующий слой OSD

queue: очередь шардирования OSD

osd: поверка клиентского запроса, проверка авторизации, координация репликации и рассылка запросов вторичным OSD

peers: ожидание ответов от вторичных ОСД перед отправкой подтверждения клиенту

prepare: подготовка транзакции, выделение дискового пространства, построение записи

aio_wait: асинхронные операции ввода/вывода

seq_wait: обработка запроса секвенсером, который логически находится следующим после планировщика mclock

kv_commit: сброс данных на устройство и запись метаданных в rocksDB.

Как правило, kv_commit — бесспорная доминанта в общем ряду задержек по всем стадиям.

В результате работы показывается следующая информация:

```osd 0 pg 4.c subop_w size 4195121 client 134125 tid 37 throttle_lat 1 recv_lat 1027 dispatch_lat 16 queue_lat 15325 osd_lat 101 bluestore_lat 1164015 (prepare 841676 aio_wait 3306 (aio_size 64) seq_wait 71 kv_commit 318960) subop_lat 1180641```

Вывод очень массивный, поэтому вручную анализировать его крайне трудозатратно. В связи с этим существует дополнительный скрипт, написанный на питоне, который автоматически парсит вывод в агреггированную и удобочитаемую форму.

```./analyze_osdtrace_output.py /tmp/osdlog -i -t 1000osd.0:  subop_w:    68.51% from bluestore_lat     4.94% from queue_lat     0.38% from recv_lat     0.01% from osd_lat     0.00% from dispatch_lat     0.00% from throttle_lat  op_w:    34.79% from bluestore_lat     2.94% from queue_lat     0.12% from recv_lat     0.01% from osd_lat     0.00% from dispatch_lat     0.00% from throttle_lat```

В данном примере ясно видно, что подавляющие задержки возникают на стадии блюстора. В полную силу этот инструмент полезен при bare metal установке, потому что все процессы OSD используют 1 исполняемый файл /usr/bin/ceph-osd, и поэтому очень удобно прикреплять к нему пробы — каждая из ОСД заставит BPF-пробу триггернуться. В случае же контейнерной установки ситуация сложнее. 

Вследствие изоляции каждого процесса в своём неймспейсе, у каждого из ОСД свой исполняемый файл в /proc/PID/root/usr/bin/ceph-osd. Обходной вариант — парсинг списка ОСД с их номерами процессов и прикрепление проб в /proc к соответствующим файлам. Но кубернетес — динамическая среда, и ОСД может рестартануться с изменением PID, и это влечёт за собой реализацию механизма отслеживания актуальных номеров процессов и перестройку BPF-проб на ходу. Эта тема очень интересная и обширная, детальное обсуждение выходит далеко за рамки текущей статьи.

eBPF: исследование под микроскопом

В этот раздел под общим названием я включаю всё, что связано с технологией BPF. Предыдущий инструмент имеет свой раздел только лишь по причине самостоятельности как утилиты. С помощью технологии BPF можно отследить различные аспекты выполнения программного кода как в пользовательском  пространстве, так и в пространстве ядра. С активным внедрением в технологию BPF концепции CO-RE (compile once — run everywhere) работать с этими утилитами стало намного проще. Яркий пример — набор утилит из коллекции IOVisor. Если раньше необходимо было таскать за собой связку из различных питоновских либ, виртуальных окружений и прочих радостей, то теперь всё нужное статически линкуется в один исполняемый файл.

Мне нравятся и Python, и Golang, и я в первую очередь искал инструменты для загрузки, компиляции и дебага BPF-кода именно в разрезе этих языков программирования. Однако минусы сложных программ на питоне были описаны выше, и поэтому компилируемый Golang отлично подходит для этой цели.

Нашлось не так уж и много, но всё же кое-что:

1. gobpf от мэтров и, наверное, родоначальников популяризации технологии BPF — проекта IOVisor. Проект, скорее всего, будет закрыт, потому что поддерживать его некому.

2. ebpf-go от команды Cillium. Имеет почти 8 000 звёзд на «Гитхабе» и выглядит фаворитом.

3. libbpf-go от Aqua Security. Выглядит перспективно и пока, как будто бы, даже не собирается закрываться.

Для успешной разработки программ для BPF необходимо знание языка программирования С. В BPF используется его упрощённая версия, потому что сложные конструкции не пропустит проверка, например циклы или Rust. 

Однако следует отметить, что разработка работающих (и работающих правильно!) программ BPF — занятие хардкорное, а в контексте Ceph ещё и похоже на лабиринт без карты. Как верно заметил Алексей Баранов в предыдущей статье, в Ceph код очень фрагментирован и найти нужную функцию для прикрепления очень не просто.  Есть инструмент гораздо более дружелюбный к пользователю — bpftrace. Отличное описание этого инструмента находится в книге его непосредственного создателя Брендана Грегга «BPF Performance Tools». Тем не менее я выбрал сложный путь, потому что он предоставляет более широкие возможности, хоть и требует солидного времени на изучение. 

Как уже было сказано, подробное обсуждение возможностей использования BPF в разрезе цефа займёт слишком большой объём в рамках одной статьи. Возможно, я вернусь к этому вопросу в следующих выпусках, когда они заработают в Kubernetes:)

Заключение

Если резюмировать, то диагностика проблем производительности в Ceph — это не волшебство, а систематический подход с правильным инструментарием. Ключевая идея в том, что не существует универсального способа найти узкое место. Вместо этого нужно двигаться от общего к частному, отсеивая возможные причины на каждом шаге. Общий алгоритм выглядит примерно так:

  1. Запуск нагрузочного тестирования и проверка узких мест по части оборудования (ЦПУ, сеть, память, диски).

  2. При появлении подозрительной ОСД — точечный тест с помощью osdbench.

  3. С помощью osdtrace выявляются стадии, где происходят максимальные задержки.

  4. С помощью дополнительного инструментария на основе eBPF проводятся дополнительные расследования, если замечено необычное поведение.

Повторюсь, в Ceph есть соблазн попробовать угадать нужные настройки. Я же настаиваю на том, что нужно не гадать, а измерять. И следует помнить, что kv_commit — обычно доминирующая стадия по задержкам. Если видно, что это не так, значит, происходит что-то подозрительное.

Если вы хотите разобраться, как это всё работает на практике, приглашаем вас посмотреть, как мы применяем эти инструменты в нашем облаке MWS Cloud Platform. Крутите ручки, экспериментируйте с разными конфигурациями — только так можно действительно понять поведение Ceph. Пишите о своём опыте в тг-сообщество MWS Cloud Platform, задавайте вопросы, а мы постараемся на них ответить.

ссылка на оригинал статьи https://habr.com/ru/articles/1031132/