Новинка Виртуальный VDS сервер в Финляндии от 390р
Выберите продукт

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-сервер
Виртуальные серверы с быстрым запуском и гибкой конфигурацией от 390₽ / мес
Доступные локации
Россия Нидерланды Финляндия

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 скажет «спасибо».

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

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

Как безопасно выполнить bootstrap MariaDB Galera после полного останова

Как безопасно выполнить bootstrap MariaDB Galera после полного останова

После одновременной остановки всех узлов Galera обычного запуска MariaDB недостаточно. Сначала найдите сервер с последней транзакц ...
CoreDNS в Kubernetes: исправляем CrashLoopBackOff, SERVFAIL и DNS-петлю

CoreDNS в Kubernetes: исправляем CrashLoopBackOff, SERVFAIL и DNS-петлю

Разберём, почему CoreDNS перезапускается или отвечает SERVFAIL, как обнаружить замкнутую пересылку DNS-запросов, проверить доступн ...
Apache Kafka KRaft на VDS: отдельные controller и broker

Apache Kafka KRaft на VDS: отдельные controller и broker

Соберём новый Kafka-кластер без ZooKeeper на шести Linux VDS: три узла controller образуют отказоустойчивый metadata quorum, а три ...