Разбираемся с тормозной файловой системой, часть 3: базовые инструменты.

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

Когда я только начал замечать проблемы и полез в гугл за ответами, со всех сторон начали сыпаться советы по тестированию скорости диска с помощью утилиты dd.


dd


Отгадайте пословицу: dd if=/dev/zero of=/dev/null

Как написано в man’е, производит конвертирование и копирование файла. Описание очень скудное и не дает представления о том на что способна эта замечательная программа.
Это не сильно соотносится сейчас с нашей темой, но принцип устройства VFS предполагает что буквально все в системе должно быть представлено как “файл”, в том числе и устройства. С этим связано одно из самый популярных применений dd – возможность побайтово склонировать целый диск, раздел, или какую-то их часть.

dd if=/dev/sda of=/dev/sdb

Команда выше полностью скопирует информацию с одного диска на другой со всеми “потрохами”: идентификаторами разделов, файловыми системами, метаданными и полезной нагрузкой.
Добавив status=progress можно будет наблюдать за течением сего процесса.

Также, например, если не указывать куда перенаправлять вывод (без директивы of=), то вывод просто получим на экран (stdout).

dd if=/etc/os-release

Впрочем, в мане или на просторах сети полно информации о дэдэшечке, не будем повторяться.

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

> dd if=/dev/zero of=/tmp/testfile bs=1G count=1

1073741824 bytes (1,1 GB, 1,0 GiB) copied, 10,0035 s, 107 MB/s

Теперь пробежимся по значимым опциям.

bs = размер блока. Т.к dd работает именно поблочно, то это будет размером его атомарной операции, считать n байт => записать n байт, ни больше ни меньше. Поддерживается указание арифметических операций, например bs=2x80x18b (размер дискеты). По умолчанию bs равен 512b

count = тут понятно, это просто счетчик.

status = progress – периодически отписываться в stderr о прогрессе.

iflag \ oflag = чтение или запись может производится особым образом.

  • direct – отправлять запросы минуя кэш (direct I/O)
  • dsync – использовать синхронное I/O (synchronized I/O), т.е. каждое последующее IO только после завершения предыдущего.

conv = обработка, которая происходит между in и out. В основном используется директива noerror, остальные весьма специфичны, и редко встречались или требовались мне на практике.

Таким образом, можно проводить тесты линейной записи или чтения.
Можно регулировать размер блока, который будет записан\прочитан.
Есть возможность писать, с просьбой ядру не использовать кэш.
Можно использовать синхронное I/O.

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

А нам надо видеть чем занимаются диски. Разбираемся дальше.


iotop

Аналог top, только типо для дисков.

Можно наблюдать список процессов, и различные показатели по столбцам: thread ID, IO приоритет, пользователя, интенсивность свопа и некий процент IO.

IO priority – be\rt\id означают класс процесса (best-effort, real-time, idle), а значение от 0 до 7 приоритет относительно других процессов (меньшие значения подразумевают больший приоритет). Эти значения можно изменить с помощью утилиты ionice, но помните, что приоритеты смотрит I/O scheduler, упоминаемый в предыдущей статье. И умеют это только “весовые” виды планировщиков, т.е. CFQ, BFQ и Kyber (насчет него неточно). Также, приоритеты не работают для асинхронной записи.
Swap – так как процессу отводится некоторая часть всей очереди на ввод-вывод, то процент от этой части, являющийся свопингом будет отображен в этом значении.
I/O – работает по тому же принципу что и swap, но отображает время затрачиваемое на ожидание ввода-вывода.

Также в верхней части экрана есть значения {Total,Actual} DISK {READ,WRITE}. Как написано в мане, Total read\write подразумевает весь обмен между процессами и дисковой подсистемой ядра.

Немного аналитики
Мне показалось непонятной формулировка “kernel block device subsystem” я решил хотя бы минимально, так сказать, прощупать границы. Выяснилось, что это точно не уровень VFS, т.к. запись в tmpfs c o_direct не отображается в iotop. Так что судя по всему подразумевается bio.

Actual read\write – обмен между дисковой подсистемой ядра и драйверами оборудования.
Таким образом значения Total и Actual могут ощутимо отличаться как раз таки из-за кэша и планировщика.

Ключи
-a – iotop будет показывать аккумулированные данные с момента своего запуска. В таком режиме информация получается более наглядной.
-o – показывать только те процессы, у которых есть активность.
-d – интервал обновления.
-P – показывать не треды а процессы.

На самом деле, каких-то действительно полезных аналитических данных через iotop у меня не получалось добыть. Главное причина тому – нет разделения по блочным устройствам, показываются данные по всей системе целиком. Но в роли подспорья утилита однозначно хороша.


iostat

Является частью пакета sysstat.

Тулза для мониторинга блочных устройств.
Если просто просто ее запустить без ключей, то получим средние значения по всем дискам с момента загрузки системы.

Если же запускать в формате iostat число1 число2, то механизм работы будет следующим: число1 – это как часто опрашивать статистику, число2 – количество опросов. iostat 2 10 – опросить каждые 2 секунды,
всего 10 раз .

В верхней строчке идет статистика по CPU.
В принципе, все это можно увидеть в top:
%user – загрузка процессами выполняющимися в userpspace.
%nice – тот же userspace, но то что выполняется с настройками nice – определяющими приоритет у дискового шедулера
%system – то что кушают ядерные процессы
%iowait – процент времени которые CPU ждет ответа от дисковой подсистемы
%steal (может отсутствовать) – как правило, встречается в виртуализации, когда виртуальные машины бьются друг с другом за ресурсы
%idle – CPU отдыхает

Значения:

tps – количество I/O запросов устройству. Судя по ману, здесь отображаются запросы уже после их комбинирования планировщиком.
Без дополнительных ключей, остальные столбцы, думаю, понятны – запись и чтение в секунду, и сколько всего было записано\прочитано.

Другое дело с ключиком :
rrqm/s и wrqm/s – количество объединенных запросов чтения и записи соответственно.
r/s и w/s – количество i/o на чтение и запись в секунду.
rkB/s и wkB/s – чтение и запись в КБ\сек.
avgrq-sz – средний размер отправленных запросов (в секторах).
avgqu-sz – средняя длина очереди.
await, r_await, w_await – время запроса в очереди, плюс время на его обслуживание.
util – время в течении которого блочное устройство занято запросами на I/O в процентах.

В новых версиях iostat слегка изменились некоторые столбцы.
Добавились %rrqm и %wrqm – процент объединенных запросов.
areq-sz – средний размер запросов (бывший avgrq-sz, но в отличие от него не в секторах, а в килобайтах).
aqu-sz – средняя длина очереди (бывший avgqu-sz)
rareq-sz, wareq-sz – средний размер запроса для чтения\записи.

Кратко о ключах:

  • с – только статистика по CPU.
  • d – убрать статистику по CPU, только диски.
  • g – позволяет группировать диски и отображать по ним суммарные значения. например: iostat -g my_disk_group sda sdb sdc. Получим статистику по трем дискам и их сумму в группе my_disk_group.
  • H – вместе с -g уберет из вывода отдельные диски, оставив только суммарные значения по группе.
  • h – human readable, но, как по мне, удобств не добавляет.
  • j { ID | LABEL | PATH | UUID | … } – вывести название диска в указанном формате, по умолчанию LABEL.
  • k и m – отображать значения в килобайтах или мегабайтах соответственно.
  • N – использовать имена для device mapper томов. Удобно для LVM.
  • p – отображать разделы
  • t – штамп времени каждого отчета
  • x – расширенная статистика.
  • y – не показывать первый отчет, который показывает общую статистику с момента загрузки.
  • z – не показывать устройства с неизменными значениями (типо неактивные).

/proc/sys/vm/block_dump

Можно включить отладку сброса dirty pages на диск.

echo 1 > /proc/sys/vm/block_dump

Сообщения о том, данные какого процесса сбрасываются можно увидеть в dmesg или логах ядра (количество сообщений пропорционально интенсивности записи, будьте готовы к срачу в логах)


Домашняя работа

А теперь на основании теоретических знаний из первой статьи и практических инструментов из текущей, попробуйте понять что происходит с линейной записью в этих четырех случаях, и почему получаются именно такие результаты? Какое узкое место системы в данном случае проявляется? Запись производится на один диск, файловая система XFS.

> dd if=/dev/zero of=/tmp/testfile bs=4k count=200000 
...
819200000 байт (819 MB, 781 MiB) скопирован, 5,26288 s, 156 MB/s 
> dd if=/dev/zero of=/tmp/testfile bs=781M count=1 oflag=direct
...
818937856 байт (819 MB, 781 MiB) скопирован, 8,7302 s, 93,8 MB/s
> dd if=/dev/zero of=/tmp/testfile bs=4k count=200000 oflag=direct 
...
819200000 байт (819 MB, 781 MiB) скопирован, 39,5175 s, 20,7 MB/s
> dd if=/dev/zero of=/tmp/testfile bs=4k count=2000 oflag=dsync 
...
8192000 байт (8,2 MB, 7,8 MiB) скопирован, 60,7651 s, 135 kB/s

В последнем примере специально количество блоков меньше в сто раз, потому что иначе очень долго ждать 🙂

Попробуйте эти команды у себя (на ext4 результаты отличаться не будут), удостоверьтесь что результат не из головы.

Ответ будет в отдельном посте или в следующей части. Всех благ!