I/O Scheduling Optimization บน Linux — Scheduler, fio Benchmark และ cgroups

I/O Scheduling คือกระบวนการที่ Kernel ใช้จัดลำดับการเข้าถึง Storage Device เพื่อให้ได้ IOPS และ Throughput สูงสุด I/O Scheduler ที่เหมาะสมขึ้นอยู่กับประเภท Storage (HDD, SSD, NVMe) และ Workload (Database, Web Server, Log Processing) การเลือกและปรับ Scheduler ที่ถูกต้องสามารถลด Latency และเพิ่ม Throughput ได้อย่างมีนัยสำคัญ

บทความนี้อธิบาย I/O Scheduler ที่มีใน Linux Kernel ปัจจุบัน ทั้ง none, mq-deadline, BFQ และ Kyber พร้อมวิธีวัด I/O Performance ด้วย fio, iotop, iostat การปรับค่า Queue Depth, Read-ahead และการตั้งค่า cgroups สำหรับควบคุม I/O ของแต่ละ Process

I/O Scheduler ใน Linux Kernel ปัจจุบัน

# ดู I/O Scheduler ที่มีในระบบ
cat /sys/block/sda/queue/scheduler
# ตัวอย่าง Output: [none] mq-deadline kyber bfq
# ค่าในวงเล็บ [] = Scheduler ที่ใช้งานอยู่ปัจจุบัน

# ตรวจสอบทุก Block Device
for dev in /sys/block/*/queue/scheduler; do
    echo "$dev: $(cat $dev)"
done

# ดูว่าเป็น HDD หรือ SSD (rotational=1 คือ HDD, rotational=0 คือ SSD)
cat /sys/block/sda/queue/rotational

# Schedulers ที่มีในปัจจุบัน (Linux 5.x+):
# none     — ไม่มี Reordering, I/O ส่งตรงไปยัง Driver
#            เหมาะ: SSD, NVMe, Virtual Disk บน VPS
# mq-deadline — กำหนด Deadline ให้แต่ละ I/O Request ป้องกัน Starvation
#            เหมาะ: HDD ทั่วไป, Mixed Read/Write Workload
# bfq      — Budget Fair Queuing ให้ Fairness สูง มี Latency ต่ำสำหรับ Interactive
#            เหมาะ: Desktop, Media Editing, Low-latency workloads
# kyber    — Low-latency Scheduler ออกแบบสำหรับ Fast NVMe Storage
#            เหมาะ: High-performance NVMe, IOPS-intensive workloads

เปลี่ยน I/O Scheduler

# เปลี่ยนชั่วคราว (Reset เมื่อ Reboot)
echo none | sudo tee /sys/block/sda/queue/scheduler
echo mq-deadline | sudo tee /sys/block/sdb/queue/scheduler

# ตรวจสอบ
cat /sys/block/sda/queue/scheduler

# เปลี่ยนถาวรสำหรับ SSD ทุกตัว (ผ่าน udev rule)
sudo tee /etc/udev/rules.d/60-io-scheduler.rules <<'EOF'
# ใช้ none สำหรับ SSD/NVMe/Virtual Disk
ACTION=="add|change", KERNEL=="sd[a-z]|nvme[0-9]n[0-9]", ATTR{queue/rotational}=="0", ATTR{queue/scheduler}="none"
# ใช้ mq-deadline สำหรับ HDD
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="1", ATTR{queue/scheduler}="mq-deadline"
EOF

sudo udevadm control --reload-rules
sudo udevadm trigger

วัด I/O Performance ด้วย fio

fio (Flexible I/O Tester) เป็นเครื่องมือมาตรฐานสำหรับ Benchmark I/O Performance รองรับ Pattern การทดสอบหลากหลายทั้ง Sequential Read/Write และ Random IOPS

# ติดตั้ง fio
sudo apt install fio -y        # Ubuntu/Debian
sudo dnf install fio -y        # RHEL/Rocky

# Test 1: Sequential Read (ทดสอบ Throughput)
fio --name=seq-read \
    --ioengine=libaio \
    --iodepth=32 \
    --rw=read \
    --bs=1M \
    --direct=1 \
    --size=1G \
    --numjobs=1 \
    --filename=/tmp/fio-test \
    --output-format=normal

# Test 2: Sequential Write
fio --name=seq-write \
    --ioengine=libaio \
    --iodepth=32 \
    --rw=write \
    --bs=1M \
    --direct=1 \
    --size=1G \
    --numjobs=1 \
    --filename=/tmp/fio-test

# Test 3: Random Read IOPS (4K Block — คล้าย Database)
fio --name=rand-read-4k \
    --ioengine=libaio \
    --iodepth=64 \
    --rw=randread \
    --bs=4k \
    --direct=1 \
    --size=1G \
    --numjobs=4 \
    --filename=/tmp/fio-test

# Test 4: Mixed Random Read/Write (70% Read, 30% Write)
fio --name=mixed-rw \
    --ioengine=libaio \
    --iodepth=32 \
    --rw=randrw \
    --rwmixread=70 \
    --bs=4k \
    --direct=1 \
    --size=1G \
    --numjobs=4 \
    --filename=/tmp/fio-test

# ลบไฟล์ทดสอบ
rm -f /tmp/fio-test

ตรวจสอบ I/O ด้วย iostat และ iotop

# iostat — ดู I/O Stats ของ Block Device
# ติดตั้ง: sudo apt install sysstat / sudo dnf install sysstat
iostat -xz 1          # แสดง Extended Stats ทุก 1 วินาที

# ค่าสำคัญใน iostat:
# %util      — % เวลาที่ Disk ทำงาน (ใกล้ 100% = Disk เต็มกำลัง)
# await      — เวลาเฉลี่ยที่รอ I/O (ms) — สูงเกิน = Latency ปัญหา
# r/s, w/s   — Read/Write Requests ต่อวินาที
# rkB/s, wkB/s — Read/Write Throughput (KB/s)
# r_await, w_await — Latency แยกระหว่าง Read และ Write

# iotop — ดู I/O ตาม Process
sudo iotop                    # Interactive mode
sudo iotop -o                 # แสดงเฉพาะ Process ที่มี I/O
sudo iotop -b -n 5 -o         # Batch mode 5 ครั้ง

# ดู I/O ของ Process เฉพาะ
sudo iotop -p $(pgrep mysql)

# วิเคราะห์ I/O ผ่าน /proc
cat /proc/diskstats              # Raw Disk Statistics
cat /proc/$(pgrep mysql)/io      # I/O ของ MySQL Process

Queue Depth และ Read-ahead Optimization

# Queue Depth (nr_requests) — จำนวน I/O Request ที่รอใน Queue ได้
cat /sys/block/sda/queue/nr_requests
# Default: 64 (หรือ 128 สำหรับบางระบบ)

# เพิ่ม Queue Depth สำหรับ SSD/NVMe ที่รับ Parallel I/O ได้ดี
echo 128 | sudo tee /sys/block/sda/queue/nr_requests
echo 1024 | sudo tee /sys/block/nvme0n1/queue/nr_requests

# สำหรับ HDD ให้ลด Queue Depth เพื่อลด Seek Time
echo 32 | sudo tee /sys/block/sda/queue/nr_requests

# Read-ahead — จำนวน Data ที่ Pre-read ล่วงหน้า (512-byte sectors)
blockdev --getra /dev/sda       # ดูค่าปัจจุบัน (default: 256 = 128KB)

# เพิ่มสำหรับ Sequential Workload (เช่น Log Analysis, Video Streaming)
sudo blockdev --setra 4096 /dev/sda   # 2MB Read-ahead

# ลดสำหรับ Random Workload (เช่น Database)
sudo blockdev --setra 128 /dev/sda    # 64KB Read-ahead

# ตั้งค่าถาวรผ่าน udev
sudo tee /etc/udev/rules.d/60-blockdev.rules <<'EOF'
# SSD: Queue 128, Read-ahead 256
ACTION=="add", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="0", RUN+="/sbin/blockdev --setra 256 /dev/%k"
# HDD: Queue 64, Read-ahead 2048
ACTION=="add", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="1", RUN+="/sbin/blockdev --setra 2048 /dev/%k"
EOF

I/O Throttling ด้วย cgroups

cgroups (Control Groups) ช่วยจำกัดและจัดสรร I/O Bandwidth ให้แต่ละ Process หรือ Service เพื่อป้องกัน I/O-intensive Process รบกวน Process อื่น

# cgroup v2 I/O Controller (systemd จัดการ cgroups โดยอัตโนมัติ)
# ดู I/O ของ Service ต่าง ๆ
systemctl show nginx.service | grep -i io
cat /sys/fs/cgroup/system.slice/nginx.service/io.stat

# จำกัด I/O Bandwidth ของ Service ผ่าน systemd
# แก้ไข /etc/systemd/system/mysql.service.d/io-limit.conf
sudo mkdir -p /etc/systemd/system/mysql.service.d
sudo tee /etc/systemd/system/mysql.service.d/io-limit.conf <<'EOF'
[Service]
# จำกัด Read/Write ที่ 100MB/s สำหรับ /dev/sda (major:minor ดูจาก ls -la /dev/sda)
IOReadBandwidthMax=/dev/sda 100M
IOWriteBandwidthMax=/dev/sda 100M

# หรือจำกัด IOPS
IOReadIOPSMax=/dev/sda 10000
IOWriteIOPSMax=/dev/sda 5000
EOF

sudo systemctl daemon-reload
sudo systemctl restart mysql

# ตรวจสอบ I/O Weight ของ Service (0-10000, default=100)
systemctl set-property mysql.service IOWeight=200
systemctl set-property nginx.service IOWeight=100

Filesystem-level I/O Optimization

# Mount Options ที่ช่วย I/O Performance

# noatime — ไม่อัพเดต Access Time เมื่ออ่านไฟล์ (ลด Write I/O มาก)
# ตรวจสอบ Mount Options ปัจจุบัน
cat /proc/mounts | grep " / "

# เพิ่ม noatime ใน /etc/fstab
# ก่อน:  UUID=xxx / ext4 defaults 0 1
# หลัง:  UUID=xxx / ext4 defaults,noatime 0 1
sudo nano /etc/fstab

# Apply โดยไม่ต้อง Reboot
sudo mount -o remount,noatime /

# Filesystem ที่เหมาะกับ I/O Performance:
# ext4: ดีสำหรับ General Purpose, รองรับ Journal
# xfs: ดีสำหรับ Large Files, High-concurrency Write
# btrfs: รองรับ Snapshot, Compression, RAID แต่ Overhead สูงกว่า

# ตรวจสอบ Filesystem ปัจจุบัน
df -T

Script ตรวจสอบ I/O Health

#!/bin/bash
# /usr/local/bin/io-health-check.sh
# ตรวจสอบ I/O Health และ Scheduler

echo "=== I/O Health Check: $(hostname) at $(date) ==="
echo ""

# ดู Scheduler ของทุก Block Device
echo "--- I/O Schedulers ---"
for dev in /sys/block/*/queue/scheduler; do
    name=$(echo "$dev" | cut -d/ -f4)
    sched=$(cat "$dev" 2>/dev/null)
    rot=$(cat "/sys/block/${name}/queue/rotational" 2>/dev/null)
    type=$( [ "$rot" = "0" ] && echo "SSD/NVMe" || echo "HDD" )
    echo "  ${name} (${type}): ${sched}"
done
echo ""

# ดู I/O Stats สรุป
echo "--- Disk I/O Summary (1 second) ---"
iostat -x 1 1 2>/dev/null | grep -v "^$\|^Linux\|^Device" | \
    awk 'NR>1 && $1 ~ /^[sv]d|nvme/ {
        printf "  %-10s reads/s=%-8.1f writes/s=%-8.1f await=%-8.2f %%util=%.1f%%\n",
               $1, $4, $5, $10, $NF
    }'
echo ""

# ดู Top I/O Process
echo "--- Top I/O Processes ---"
sudo iotop -b -n 1 -o 2>/dev/null | grep -v "^Total\|^Actual\|^TID" | head -6
echo ""

echo "=== End of I/O Health Check ==="

สรุป

I/O Scheduler ที่เหมาะสมสำหรับ VPS ที่ใช้ SSD หรือ Virtual Disk คือ none ซึ่งส่ง I/O ตรงไปยัง Driver โดยไม่มี Overhead สำหรับ HDD แนะนำ mq-deadline เพื่อป้องกัน Starvation การวัด I/O Performance ด้วย fio ก่อนและหลัง Tune ช่วยยืนยันผลลัพธ์ Queue Depth ควรเพิ่มสำหรับ NVMe และลดสำหรับ HDD การใช้ Mount Option noatime ลด Write I/O ได้มากโดยไม่มีผลเสีย และ cgroups ช่วยจำกัด I/O ของ Service ที่มี I/O สูงเพื่อป้องกันการรบกวน Service อื่น

แนะนำบริการ DE

การปรับ I/O Scheduler และ Queue Depth ต้องการสิทธิ์ Root และการเข้าถึง Block Device โดยตรง Cloud VPS ของ DE ให้ Root Access เต็มรูปแบบ รองรับการปรับค่า I/O Scheduler, cgroups I/O Throttling และการ Tune Filesystem Mount Options เพื่อให้ Storage ทำงานได้อย่างมีประสิทธิภาพสูงสุด

สำหรับผู้ที่ต้องการโฮสต์เว็บไซต์โดยไม่ต้องปรับ I/O เอง Cloud Hosting ของ DE มี Storage ที่ปรับแต่งมาสำหรับ Web Hosting โดยเฉพาะ รองรับการอ่านเขียนไฟล์เว็บไซต์ได้อย่างรวดเร็วโดยไม่ต้องตั้งค่าเพิ่มเติม