Linux kernel 4.2 и eBPF: трассировка дисковых задержек без модулей ядра
Ядро 4.2 вышло с расширенным eBPF. Попробовали bcc-tools для диагностики задержек IO на нескольких серверах - нашли неочевидное узкое место в storage-драйвере.
Выпущено ядро Linux 4.2 с расширенной подсистемой eBPF и улучшениями сетевого стека
Linux kernel 4.2 вышел в конце августа, и первые несколько недель мы смотрели на него с осторожным интересом. Не потому что чего-то ждали от сетевых улучшений или пересмотра планировщика - это всё штатная эволюция. Интерес был в другом: расширенная поддержка eBPF и первые нормальные инструменты поверх неё.
Для тех кто не следил: eBPF (extended Berkeley Packet Filter) - это механизм безопасного выполнения пользовательских программ прямо в ядре, без написания модулей. Изначально BPF был для фильтрации пакетов (tcpdump работает на нём), но расширенная версия позволяет трассировать практически любую точку в ядре - syscall'ы, функции блочного уровня, сетевой стек - в реальном времени и с минимальным оверхедом.
Почему это важно практически: раньше для такой трассировки нужно было либо писать модуль ядра (страшно, долго, ломается при обновлении), либо использовать SystemTap с его собственными сложностями, либо просто не трассировать и догадываться по косвенным признакам. eBPF предлагает третий путь: пишешь маленькую программу на ограниченном C, верификатор ядра проверяет что она безопасна, и она запускается без перезагрузки и без риска уронить систему.
Почему мы вообще полезли в это
У нас несколько серверов под хранилище - не SAN, обычные сервера с наборами дисков, где крутятся задачи с интенсивным IO. Периодически замечали странную картину: утилизация дисков по iostat выглядит нормально, очередь не растёт, а приложения всё равно жалуются на задержки. Классическая ситуация «метрики зелёные, а больно».
Стандартный набор - iostat, iotop, strace на процессе - особо не помогал. Задержки были, но прерывистые, воспроизвести по требованию не получалось.
Тут и вспомнили про bcc - набор инструментов поверх eBPF, который как раз тогда начал приобретать человеческий вид. В частности, biolatency - инструмент который строит гистограмму задержек блочных IO-операций прямо в ядре.
Что нашли
Поставили ядро 4.2 на один из серверов, подтянули bcc. Запустили biolatency и оставили собирать статистику минут на двадцать в рабочий период.
Гистограмма вышла интересная: основная масса операций укладывалась в нормальные для HDD диапазоны, но был отчётливый хвост с задержками на порядок выше нормы. Не много таких операций, но они были регулярны.
Следующим шагом запустили biosnoop - он пишет каждую блочную операцию с задержкой, именем процесса и номером блока. Вот тут началось интересное: аномальные задержки группировались вокруг конкретных диапазонов блоков и появлялись через более-менее регулярные интервалы. Это не случайный шум - это что-то периодическое.
Поковырялись в логах, посмотрели что происходит в этот момент на уровне драйвера. Оказалось - firmware у storage-контроллера выполнял фоновую проверку целостности (patrol read, в терминологии конкретного контроллера), и делал это в способ, который не очень корректно взаимодействовал с очередью IO при определённой нагрузке. Конфигурация была дефолтной с момента установки, никто специально не трогал.
Само по себе это не катастрофа - patrol read нужен, это профилактика. Но расписание и агрессивность по умолчанию оказались неоптимальны для нашей нагрузки.
Что сделали
Перенастроили расписание patrol read на ночные часы с минимальной нагрузкой, выставили ограничение на потребление IO. Эффект был виден сразу - аномальный хвост на гистограмме biolatency сократился. Приложения перестали жаловаться на задержки в рабочее время.
Три вещи из этого кейса:
- Без eBPF-инструментов мы бы продолжали смотреть на iostat и разводить руками. Задержки на уровне ядра, которые не агрегируются в утилизацию диска, iostat просто не показывает в нужном разрезе.
- Проблема была в конфигурации по умолчанию storage-контроллера, а не в железе и не в приложении. Стандартный диагностический путь (логи приложения, strace, iotop) не привёл бы туда.
- bcc ещё сырой - установка на 4.2 потребовала руками собрать несколько зависимостей, документация местами отсутствует, часть инструментов упала с ошибкой на нашем конкретном ядре. Это не готовый production-инструмент, это пока «умелые руки требуются».
Где это применимо
Честно говоря, для большинства типовых задач bcc - избыточно. Если сервер тормозит и iostat показывает 100% утилизацию диска, eBPF не нужен - и так понятно. Инструмент полезен именно для неочевидных ситуаций: когда метрики в норме, а что-то всё равно не так.
В нашем случае хорошо сработало для диагностики storage. Коллеги из других команд пробовали biolatency для анализа задержек в PostgreSQL - там тоже нашлись интересные паттерны. Для сетевой трассировки потенциал есть, но мы пока не пробовали всерьёз.
Ядро 4.2 в production на рабочих серверах мы пока не катим широко - немного подождём пока наберёт стабилизацию. Но для инструментальных целей и диагностики использовать вполне можно. Главное - иметь возможность быстро поднять такое окружение рядом с проблемным сервером, что в связке с контейнерами и namespaces стало заметно проще.
- Docker Swarm 1.0: нативная кластеризация без сторонних костылей · 4 сентября 2015