Коли важке копіювання, резервне копіювання або індексація "забивають" диск, вся система починає гальмувати. Добра новина: у Linux можна акуратно притиснути I/O конкретних процесів, щоб робочий стіл і серверні сервіси залишались плавними. Сьогодні покажу, як це зробити через термінал Linux за допомогою ionice, cgroups v2 та systemd. Буде і трішки Linux моніторинг, і лайфхаки для реальних задач ✨
Що таке обмеження I/O і коли воно потрібне
I/O (input/output) — це читання та запис на диск. Якщо одна задача з'їдає все використання диска, інші програми страждають: менеджер вікон підфрізує, база повільно відповідає, а відео рветься. Обмеження I/O дозволяє:
- ставити пріоритети (важливе швидко, фонове — м'яко);
- задавати вагу (weight) для справедливого розподілу;
- встановлювати жорсткі ліміти на пропускну здатність (MB/s) або IOPS.
Для щоденної оптимізації продуктивності цього більш ніж достатньо.
Швидкий старт: ionice для однієї команди
ionice змінює I/O-пріоритет процесу. Найпростіше зробити фонову дію "лагідною" до системи:
# Запустити копіювання в класі idle (тільки коли диск вільний)
sudo ionice -c3 rsync -a /data/ /backup/
# Слабший, але не повний idle: best-effort з найнижчим пріоритетом
sudo ionice -c2 -n7 tar -czf archive.tgz /bigdir
# Перевірити пріоритет для PID
sudo ionice -p <PID>
Пам'ятайте: ionice працює лише з планувальниками I/O, які підтримують пріоритети (наприклад, BFQ). На NVMe-дисках за замовчуванням часто стоїть none або mq-deadline, і тоді ефекту може не бути — у такому разі переходьте до cgroups v2 + systemd нижче.
Точний контроль: cgroups v2 + systemd
Сучасний і надійний спосіб — використовувати контролер io у cgroups v2 через налаштування systemd. Так можна задати вагу, ліміти на MB/s або IOPS для будь-якого сервісу чи одноразового запуску.
Перевірка, що cgroups v2 увімкнені
mount | grep cgroup2
# Має з'явитися точка монтування /sys/fs/cgroup типу cgroup2
Разовий запуск з лімітами через systemd-run
Припустимо, ви хочете стиснути швидкість запису до 10 MB/s і читання до 20 MB/s на NVMe-диску для важкої команди:
sudo systemd-run --scope \
-p IOWeight=50 \
-p IOReadBandwidthMax=/dev/nvme0n1:20M \
-p IOWriteBandwidthMax=/dev/nvme0n1:10M \
dd if=/dev/zero of=/bigfile bs=1M count=20000 oflag=direct
Пояснення:
IOWeight=50— вага (1–10000). Менше — скромніше I/O.IOReadBandwidthMax/IOWriteBandwidthMax— жорсткі ліміти для пристрою.--scope— об'єднає процес у тимчасову cgroup з цими правилами.
Замість пропускної здатності можна лімітувати IOPS:
sudo systemd-run --scope \
-p IOReadIOPSMax=/dev/nvme0n1:200 \
-p IOWriteIOPSMax=/dev/nvme0n1:100 \
fio --name=test --rw=readwrite --bs=4k --size=2G --filename=/bigfile
Постійний сервіс із обмеженнями
Створимо unit, щоб кожен запуск мав ліміти автоматично.
sudo tee /etc/systemd/system/rsync-heavy.service >/dev/null <<'EOF'
[Unit]
Description=Rsync з обмеженням I/O
[Service]
Type=simple
ExecStart=/usr/bin/rsync -a /data/ /backup/
# Вага і ліміти для конкретного блочного пристрою
IOWeight=50
IOReadBandwidthMax=/dev/sda:30M
IOWriteBandwidthMax=/dev/sda:10M
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now rsync-heavy.service
Тепер rsync не заважатиме іншим задачам і не забиватиме канал.
Альтернативні способи і корисні трюки
Робота з сирими файлами cgroup v2
Якщо вам треба скриптувати без systemd, можна напряму писати до io.max. Спершу знайдіть major:minor вашого пристрою і створіть cgroup:
DEV=/dev/nvme0n1
MM=$(cat /sys/class/block/$(basename "$DEV")/dev) # напр., 259:0
CG=/sys/fs/cgroup/myio
sudo mkdir -p "$CG"
echo $$ | sudo tee "$CG/cgroup.procs" # додамо поточний шелл
# Ліміт 15 MB/s на читання і 8 MB/s на запис
echo "$MM rbps=15728640 wbps=8388608" | sudo tee "$CG/io.max"
Далі запускаєте свої команди у цьому шеллі — ліміти застосуються.
Поєднуйте з CPU nice
sudo nice -n 10 ionice -c3 tar -czf backup.tgz /vm-images
Так процеси будуть "скромні" і за CPU, і за I/O.
Перемикач планувальника I/O (для HDD)
На класичних HDD увімкніть BFQ для кращої справедливості:
DEV=sda
cat /sys/block/$DEV/queue/scheduler
# Напр.: [mq-deadline] kyber bfq
echo bfq | sudo tee /sys/block/$DEV/queue/scheduler
Увага: на NVMe це зазвичай недоречно; для постійної зміни використовуйте параметри ядра/udev-правила.
GUI-спосіб через Cockpit (для серверів і робочих станцій)
Графічних інструментів саме для I/O пріоритетів мало, але у Cockpit зручно керувати systemd-сервісами і додавати drop-in з параметрами I/O.
# Встановлення Cockpit (Debian/Ubuntu)
sudo apt install cockpit -y
sudo systemctl enable --now cockpit.socket
# Далі відкрийте https://<IP>:9090 у браузері
У Cockpit відкрийте ваш сервіс, створіть drop-in і додайте рядки на кшталт IOWeight=50, IOReadBandwidthMax=/dev/sda:20M, IOWriteIOPSMax=/dev/sda:100. Це простий спосіб без ручного редагування файлів. 📀
Моніторинг: перевіряємо, що ліміти працюють
Для Linux моніторинг I/O підійдуть:
# поточне I/O по процесах (root)
sudo iotop -oPa
# по PID з інтервалом 1с
pidstat -d 1
# в htop увімкніть стовпці IO_Read/IO_Write (F2 -> Columns)
htop
Додатково перевіряйте файли cgroup (io.max, io.stat) для вашого unit/скоупу.
FAQ
ionice не працює на моєму NVMe. Чому?
Більшість NVMe-дисків використовують планувальник none/ mq-deadline, які ігнорують I/O-пріоритети процесів. Використовуйте cgroups v2 + systemd з IO*Max параметрами — вони працюють незалежно від планувальника.
Отримую помилку "Unknown lvalue 'IOReadBandwidthMax'" у unit-файлі
Ваш systemd надто старий або система не в unified cgroup v2 режимі. Оновіть systemd до сучасної версії та переконайтесь, що контролер io активний (перезавантаження з параметром ядра systemd.unified_cgroup_hierarchy=1 може допомогти на старих дистрибутивах).
Як вказати правильний пристрій для *BandwidthMax/*IOPSMax?
Зазвичай це блочний пристрій-носій (наприклад, /dev/sda, /dev/nvme0n1), а не розділ. Подивіться дерево дисків:
lsblk -o NAME,PATH,SIZE,TYPE,MOUNTPOINT
Для надійності використовуйте стабільні шляхи на кшталт /dev/disk/by-id/.../
Чи сповільнить це всю систему?
Ні, лише цільові процеси/сервіси отримають нижчий I/O. Інші навпаки стануть чуйнішими.
Як переконатися, що ліміт застосовано?
Дивіться systemd-cgls і systemd-cgtop, читайте io.max/io.stat для відповідної cgroup, а також спостерігайте швидкість у iotop чи pidstat -d. У htop та top вмикайте I/O-статистику.
Працюєте в Docker?
Запускайте контейнери з лімітами, наприклад:
docker run --rm \
--device-read-bps /dev/sda:20mb \
--device-write-bps /dev/sda:10mb \
ubuntu dd if=/dev/zero of=/big bs=1M count=10000 oflag=direct
На системах з cgroups v2 ці прапорці мапляться на ті самі механізми io-контролера.
Порада від Kernelka
Зробіть маленький шорткат для важких речей. Наприклад, alias slow з вашими улюбленими параметрами systemd-run:
echo "alias slow='sudo systemd-run --scope -p IOWeight=50 -p IOWriteBandwidthMax=/dev/sda:10M'" | tee -a ~/.bashrc
source ~/.bashrc
slow rsync -a /data/ /backup/
І ще: запускайте масові операції у нічні години за допомогою таймерів systemd — так ви менш за все відчуєте їх вплив на повсякденну роботу.
Підсумок
- ionice — швидкий спосіб знизити пріоритет I/O для однієї команди.
- cgroups v2 + systemd — точні й стабільні ліміти на MB/s або IOPS для сервісів і скоупів.
- Використовуйте Linux моніторинг інструменти (
iotop,pidstat,htop) для перевірки. - Для HDD подумайте про планувальник BFQ; для NVMe — робіть ставку на cgroups v2.
- Поєднуйте з
niceі автоматизуйте запуск — це покращить оптимізація продуктивності системи.

Прокоментувати
На сайті відображається лише твоє ім'я та коментар. Електронна пошта зберігається виключно для зв'язку з тобою за потреби та в жодному разі не передається стороннім особам.