ZIM-НИЙ SAAALEЗимние скидки: до −50% на старт и −20% на продление
до 31.01.2026 Подробнее
Выберите продукт

I/O под контролем: iostat, iotop, fio и выбор планировщика для веб‑сервисов

Узкие места хранилища бьют по отклику сайта и API сильнее, чем кажется. Разбираем, как быстро понять, что упёрлись в диск, чем измерять (iostat, iotop), как воспроизводимо тестировать (fio), и какой I/O scheduler выбрать для HDD, SATA SSD, NVMe и виртуальных дисков веб‑сервисов.
I/O под контролем: iostat, iotop, fio и выбор планировщика для веб‑сервисов

Дисковая подсистема остаётся одной из главных причин «мистических» просадок RPS и роста TTFB у веб‑сервисов. Даже при достаточном количестве CPU и RAM неправильные профили ввода‑вывода, неудачный планировщик или случайная фонова задача могут привести к лавинообразной деградации. В этой статье соберу рабочие приёмы диагностики и тестирования I/O на Linux с упором на веб‑нагрузку: покажу, как читать метрики iostat, как разоблачить «пожирателей диска» с помощью iotop, как воспроизводимо моделировать нагрузку через fio, и как осознанно выбрать I/O‑планировщик (scheduler) для разных типов носителей — HDD, SATA SSD, NVMe и виртуальных дисков.

Что именно мерить в I/O: латентность, IOPS, пропускная способность и глубина очереди

Для веб‑сервисов критичнее всего латентность операций записи/чтения и стабильность задержек под нагрузкой. Ширина канала (MB/s) важна для бэкапов и больших скачиваний, но в типичном API/динамическом сайте основная боль — это случайный доступ маленькими блоками (метаданные, журналы БД, сессии, кэш), где решают IOPS и хвостовые задержки (p95/p99). Дополняют картину глубина очереди (queue depth), процент загрузки диска (%util) и средний размер запроса. В связке они отвечают на базовый вопрос: «Мы упираемся в диск или причина не здесь?»

iostat: быстрый срез состояния диска и понимание контекста

iostat из пакета sysstat — удобный инструмент для периодического сэмплинга дисковых метрик. Веб‑админам полезен расширенный вывод с показателями по устройствам и разделам.

iostat -dxm 1

На что смотреть в первую очередь:

  • %util — приблизительная загруженность устройства. Длительное нахождение около 100% означает насыщение очереди.
  • r/s, w/s и rkB/s, wkB/s — интенсивность и характер нагрузки (чтение/запись, случайная/последовательная).
  • await (или раздельно r_await/w_await в новых версиях) — средняя задержка операции с учётом очереди. Для NVMe под смешанной нагрузкой десятки миллисекунд — уже тревожный сигнал.
  • avgqu-sz или aqu-sz — средний размер очереди. Стабильно высокий на фоне выросших задержек намекает на перегруз диска.
  • rareq-sz/wareq-sz — средний размер запросов. Полезно для понимания, насколько «мелкими» блоками работает приложение.

Полезные варианты запуска:

# Показать логические тома (LVM) и их вклад
iostat -N -dx 1

# Аггрегировать по всем устройствам, если важно общее состояние узла
iostat -dxy 1

Интерпретация: если %util прыгает под 100%, await растёт, а CPU простаивает — вероятно, «узкое горлышко» именно диск. Если же %util низкий, но сайт тормозит — ищите блокировки в БД, сетевые проблемы или «застрявшие» воркеры PHP‑FPM (смотрите наше руководство по slowlog PHP‑FPM).

Правило большого пальца: для NVMe в веб‑нагрузке держите p95 задержек операций на уровне единиц миллисекунд. Всё, что заметно выше десятков миллисекунд под стедитейт‑нагрузкой — повод копать.

Пример диагностики диска с iostat и iotop на сервере

iotop: кто прямо сейчас жрёт диск

iotop показывает, какие процессы создают I/O и каков их вклад. На продакшене чаще всего полезен режим вывода только активных операций, с накоплением статистики по процессам/CGroups.

# Только активные процессы с суммированием и PID/Command
iotop -oPa

# Неболтливый batch-режим для логирования (5 выборок с интервалом 2с)
iotop -b -qqq -n 5 -d 2

Колонки DISK READ/DISK WRITE показывают текущий и суммарный объём, IO — долю времени процесса в ожидании I/O. Если видите неожиданные акторы (архиватор, индексатор поиска, тяжёлый бэкап, find по всему диску) — изолируйте их в расписании и/или ограничьте полосу/вес I/O через cgroups.

fio: воспроизводимые тесты диска под профиль веб‑нагрузки

Диагностика «вживую» важна, но для инженерных выводов нужно уметь воспроизводимо тестировать. fio — стандарт де‑факто. Идея проста: моделируем профили, похожие на ваше приложение (размер блока, доля чтения/записи, глубина очереди, число потоков), и снимаем метрики латентности/IOPS/MB/s.

Быстрые пресеты

# Последовательное чтение большими блоками (бэкапы, медиаконтент)
fio --name=seqread --filename=/data/fio.test --size=8G --bs=1M --rw=read --iodepth=32 --direct=1 --ioengine=io_uring --runtime=60 --time_based=1 --group_reporting=1

# Случайное чтение мелкими блоками (каталоги, индексы БД)
fio --name=randread --filename=/data/fio.test --size=8G --bs=4k --rw=randread --iodepth=64 --numjobs=4 --direct=1 --ioengine=io_uring --runtime=60 --time_based=1 --group_reporting=1

# Смешанная R/W как у типичной БД или кэша
fio --name=mixed --filename=/data/fio.test --size=8G --bs=4k --rw=randrw --rwmixread=70 --iodepth=64 --numjobs=4 --direct=1 --ioengine=io_uring --runtime=60 --time_based=1 --group_reporting=1

Пояснения: --direct=1 выключает файловый кэш ОС для чистого профиля устройства. --ioengine=io_uring обычно даёт лучшие задержки на новых ядрах. --numjobs и --iodepth подбирайте ближе к реальности вашего сервиса (число воркеров, параллелизм в БД, пул потоков).

Job‑файл для повторения тестов

[global]
ioengine=io_uring
runtime=90
time_based=1
group_reporting=1
direct=1
filename=/data/fio.test
size=16G

[web-rand-reads]
rw=randread
bs=4k
numjobs=4
iodepth=64

[web-mixed]
rw=randrw
rwmixread=70
bs=4k
numjobs=4
iodepth=64

[seq-read]
rw=read
bs=1M
numjobs=1
iodepth=32

Как читать результат: смотрите на lat (avg, p95, p99), IOPS и стабильность в течение теста. Для веб‑нагрузки ключевое — низкая и предсказуемая латентность при разумной глубине очереди. Если рост iodepth почти не увеличивает IOPS, а только портит задержку — упираетесь в контроллер/планировщик/устройство.

Графическое представление результатов fio: латентность и IOPS

Планировщики I/O: none, mq-deadline, bfq, kyber — что выбрать

Современные ядра Linux используют многоквартальную подсистему запросов blk‑mq. Для неё актуальны планировщики: none, mq-deadline, bfq, kyber. Коротко о поведении:

  • none — минимум вмешательства. Хорош для быстрых NVMe, где собственный контроллер диска отлично планирует запросы.
  • mq-deadline — балансирует пропускную и задержку, предотвращает «голодание». Универсальный выбор для SATA SSD и виртуальных дисков.
  • bfq — ориентирован на fairness и интерактивность. Уместен на HDD или десктоп‑сценариях, где важна отзывчивость фоновых задач, но может проигрывать по пиковым IOPS на SSD.
  • kyber — низкие задержки для датацентровых профилей, но встречается реже и не всегда доступен в сборках дистрибутивов.

Практические рекомендации для веб‑сервисов:

  • NVMe: чаще всего none. Если наблюдаете хвосты задержек под смешанной нагрузкой — попробуйте mq-deadline.
  • SATA SSD: стартуйте с mq-deadline. Он сглаживает пики и чаще даёт предсказуемую латентность.
  • HDD: для случайного доступа в проде — спорный выбор. Если важна отзывчивость панели/логов — можно тестово bfq, но для бэкапов и последовательных задач — подойдёт mq-deadline.
  • Виртуальные диски (virtio‑blk/virtio‑scsi, облака): часто всё уже зашедулено на гипервизоре. Практичный выбор — mq-deadline; иногда доступен только none. Проверяйте, что реально поддерживается.

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

Как проверить и переключить scheduler

# Список доступных и активный планировщик для устройства
cat /sys/block/sda/queue/scheduler

# Переключить планировщик на mq-deadline (через tee, чтобы не требовался ">")
echo mq-deadline | sudo tee /sys/block/sda/queue/scheduler

# Для NVMe-устройств
echo none | sudo tee /sys/block/nvme0n1/queue/scheduler

Чтобы сохранить настройку после перезагрузки — используйте udev‑правило или systemd‑юнит, применяющий значение на старте. Не меняйте планировщик на бою без тестов под реалистичной нагрузкой — измерьте до/после через fio и метрики приложения.

Очереди, readahead и прочие регуляторы

Помимо scheduler, влияют параметры очередей и предварительного чтения.

  • Глубина очереди на уровне приложения: iodepth в fio, число воркеров БД/кэша, параллелизм запросов. Слишком маленькая — недоиспользование устройства; слишком большая — растут хвосты задержек.
  • nr_requests и прочие лимиты очереди в /sys/block/<dev>/queue/: тонкая настройка, без нужды лучше не крутить.
  • Readahead: важно для последовательного чтения (медиа, бэкапы). Для случайных чтений большого эффекта нет.
# Посмотреть и изменить readahead (страницы по 512 байт)
sudo blockdev --getra /dev/sda
sudo blockdev --setra 4096 /dev/sda

Файловые системы и монтирование: ext4 и XFS, опции под веб‑нагрузку

И ext4, и XFS отлично подходят для веб‑нагрузки. Больше решают параметры монтирования и дисциплина обслуживания SSD, чем сам выбор FS. Базовые рекомендации:

  • relatime/noatime: отключает лишние записи времени доступа. relatime давно дефолт и обычно достаточно. Для высоких RPS можно использовать noatime.
  • discard: для SSD/TRIM лучше периодический fstrim по таймеру, чем постоянный discard на каждом удалении.
  • Журналирование: оставляйте по умолчанию. Экзотические режимы ради «скорости» часто бьют по надёжности.
# Пример /etc/fstab с ext4
device-uuid /data ext4 defaults,noatime,commit=120 0 2

# Включить еженедельный TRIM для всех поддерживаемых устройств
systemctl enable fstrim.timer
systemctl start fstrim.timer

# Разово
fstrim -av
Виртуальный хостинг FastFox
Виртуальный хостинг для сайтов
Универсальное решение для создания и размещения сайтов любой сложности в Интернете от 95₽ / мес

Ограничение и изоляция I/O: cgroups и systemd

Чтобы фоновая задача не убивала прод, задайте лимиты I/O. В мире systemd это делается свойствами юнитов.

# Снизить вес I/O для сервисов бэкапа
sudo systemctl set-property backup.service IOWeight=50

# Ограничить полосу чтения/записи на конкретном устройстве
sudo systemctl set-property backup.service IOReadBandwidthMax=/dev/sda 50M
sudo systemctl set-property backup.service IOWriteBandwidthMax=/dev/sda 20M

# Для контейнеров systemd-nspawn/podman под управлением systemd — аналогично

На старых системах с cgroup v1 используются blkio.* параметры. Суть одна: критичные веб‑процессы получают высокий приоритет, фоновым — полосу и вес поменьше, чтобы не раздувать латентности.

Связь с приложением: где рождается I/O в веб‑стеке

Даже идеальный диск не спасёт, если само приложение генерирует избыточный I/O. Посмотрите на путь запроса:

  • Логи веб‑сервера: чрезмерная детализация и синхронная запись на busy‑сайте создают лавины мелких записей. Используйте буферизацию логов и ротацию с компрессией вне «прайм‑тайма».
  • PHP‑FPM/Интерпретаторы: кешируйте автозагрузчики, отключайте лишний stat файлов. OpCache для PHP резко снижает I/O по коду.
  • База данных: следите за fsync частотой, журналированием и размером буферов. Неправильные checkpoints или недонастроенный кэш приводят к всплескам записи.
  • Кэши и сессии: переводите на in‑memory (Redis/Memcached) где это уместно, чтобы убрать «мелочь» с диска.

Полезная практика — параллельно запускать iostat и профайлер приложения/БД, чтобы видеть, какие операции в коде бьют по диску.

Типичные сценарии и что делать

1) Высокий %util, растущий await, iotop показывает активные бэкапы

Перенесите окна бэкапов, ограничьте им полосу через systemd, увеличьте размер блока и последовательность операций. На SSD отдайте приоритет планировщику mq-deadline для лучшей предсказуемости.

2) NVMe с неплохим MB/s, но латентность скачет под мелким randrw

Проверьте, не завышена ли глубина очереди на уровне пула воркеров; попробуйте mq-deadline вместо none, если хвосты задержек слишком велики. Убедитесь, что файл под fio на том же томе, где лежат данные сервиса.

3) Виртуальный диск в облаке, iostat стабильно 100% util при умеренной нагрузке

Скорее всего, достигли лимитов тарифа или noisy neighbor. Ограничьте фоновый I/O, проверьте, нет ли перегретого логгинга, и измерьте fio базовые профили. Если потолок низкий и подтверждается синтетикой — рассматривайте перенос на более быстрый класс хранилища или горизонтальное разделение. Подбор ресурсов поможет материал как выбрать конфигурацию VDS.

FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

4) HDD и веб‑сайт «подлагивает» при одновременной индексации

Для интерактивности попробуйте bfq и жёстко ограничьте полосу индексатору. Переведите горячие каталоги (сессии, кэши) на SSD или tmpfs при достаточной RAM.

Минимальный план наведения порядка

  1. Зафиксируйте базовую линию: iostat -dxm 1 под реальной нагрузкой, снимите p95/p99 отклика приложения.
  2. Найдите «шумных» соседей по диску через iotop -oPa; ограничьте полосу и приоритеты фоновым задачам.
  3. Проведите fio с профилями, близкими к продовой нагрузке: randread/randrw 4k, seqread 1M. Запомните IOPS и латентность.
  4. Выберите подходящий scheduler (NVMe: none или mq-deadline; SATA SSD: mq-deadline; HDD: по ситуации). Измерьте до/после.
  5. Проверьте опции монтирования (noatime, fstrim.timer), readahead для последовательного доступа.
  6. Оптимизируйте слой приложения: логгирование, кэш, параметры БД. Сравните метрики снова.

Замечания по методике и безопасности

Не запускайте тяжёлый fio по рабочему LUN в прайм‑тайм — синтетика может «выдавить» продовый I/O. Лучше отдельный том или окно низкой нагрузки. Если тестируете на многопользовательском узле, скоррелируйте действия с остальными сервисами. Любые изменения scheduler/монтирования фиксируйте и возвращайте при откате.

Итоги

Поставить I/O «под контроль» — это сочетание быстрых наблюдений (iostat, iotop), воспроизводимых тестов (fio) и аккуратной настройки окружения (scheduler, очереди, опции FS, cgroups). Для веб‑сервисов цель номер один — стабильная низкая латентность под реалистичной параллельной нагрузкой. Поддерживайте дисциплину бэкапов и индексаторов, выбирайте планировщик по типу носителя, держите в порядке логи и кэш — и ваш TTFB скажет «спасибо».

Поделиться статьей

Вам будет интересно

Nginx DNS в Docker/Kubernetes: resolver, valid и ipv6=off без сюрпризов OpenAI Статья написана AI (GPT 5)

Nginx DNS в Docker/Kubernetes: resolver, valid и ipv6=off без сюрпризов

Когда backend в Docker или Kubernetes меняет IP, Nginx может продолжать подключаться к «старому» адресу. Разбираем, как работает D ...
DNSSEC на практике: KSK/ZSK, DS record и безопасный rollover без SERVFAIL OpenAI Статья написана AI (GPT 5)

DNSSEC на практике: KSK/ZSK, DS record и безопасный rollover без SERVFAIL

Разбираем DNSSEC на практике: как устроены KSK/ZSK и DS record, как читать DNSKEY/RRSIG, почему при ошибках появляется SERVFAIL и ...
Linux passthrough (VFIO): включение IOMMU (VT-d/AMD-Vi), проверка и типовые проблемы OpenAI Статья написана AI (GPT 5)

Linux passthrough (VFIO): включение IOMMU (VT-d/AMD-Vi), проверка и типовые проблемы

Практический разбор IOMMU в Linux для PCI passthrough: включаем VT-d/AMD-Vi в BIOS и через grub, проверяем /proc/cmdline и dmesg, ...