Espressobin: uspávání rotačního HDD pomocí hdparm
Vytvořeno: 8.6.2020
Poznámka k aktuálnosti: Toto je dobový článek. Postup vychází z konkrétního nasazení na Espressobinu s Nextcloudem a nemusí odpovídat dnešnímu stavu systemd, NextcloudPi ani nástrojů kolem storage.
Důležité: Tento článek se týká klasického rotačního HDD. Pro SSD a NVMe se podobné uspávání přeshdparmběžně nepoužívá.
hdparm umožňuje nastavit standby režim rotačního disku. V mém případě šlo o HDD připojený k Espressobinu, kde jsem chtěl snížit hlučnost a zbytečné opotřebení mechaniky. Samotné nastavení ale komplikovalo několik souběžných problémů: Advanced Power Management, procesy probouzející disk a nepřesnosti při sledování /proc/diskstats.
Základní nastavení přes hdparm
Uspání po 30 minutách idle času odpovídalo hodnotě -S 241:
hdparm -S 241 /dev/sda
Okamžité uspání disku:
hdparm -y /dev/sda
Pro sledování stavu se hodilo průběžné ověřování:
while : ; do /sbin/hdparm -C /dev/sda; sleep 1; done
Nebo:
watch -n 1 hdparm -C /dev/sda
Později jsem zjistil důležitou věc: dotaz hdparm -C /dev/sda mění statistiky v /proc/diskstats, takže může rušit vlastní logiku pro vyhodnocení nečinnosti disku.
Advanced Power Management
Na některých systémech stačilo použít jen -S. Na Espressobinu se disk neuspával vůbec, takže jsem zkoušel i Advanced Power Management přes parametr -B.
Například:
hdparm -B 127 /dev/sda
Nebo kombinaci:
hdparm -B 50 -S 241 /dev/sda
V praxi se ale uspávání chovalo nevyzpytatelně. Disk se někdy neuspal vůbec a jindy usínal dřív, než jsem čekal. Na této cestě jsem nakonec rezignoval.
Vlastní skript nad /proc/diskstats
Nakonec se mi osvědčilo nečekat, že se disk bude uspávat jen podle interní logiky hdparm, ale pravidelně kontrolovat změny v /proc/diskstats a při nečinnosti ručně spustit hdparm -y.
Původní jednodušší verze skriptu:
#!/bin/bash # Get new state from diskstats devcode=$(findfs UUID=hFAc5i-yMtV-UC83-jF99-UnqA-ck7f-qYlnzo | cut -c 6-8) NEWstate=$(cat /proc/diskstats | grep $devcode) echo $NEWstate > /run/diskNEWstate.txt # compare md5 sums md5new=$(md5sum /run/diskNEWstate.txt | sed 's/ .*//') md5old=$(md5sum /run/diskOLDstate.txt | sed 's/ .*//') # if no changes, power down if [ "$md5new" = "$md5old" ]; then hdparm -y /dev/disk/by-id/ata-ST4000VN008-2DR166_ZDH8BP5S echo "going to sleep" fi # Write current state to file echo $NEWstate > /run/diskOLDstate.txt
Skript byl spouštěný přes cron každých 30 minut. Když se obsah /proc/diskstats od minulého běhu nezměnil, disk se uspal.
Vylepšená verze skriptu
Později jsem zjistil, že i u uspaného disku se mění některé sloupce v /proc/diskstats. Proto jsem skript rozšířil o odstranění sloupců, které nemělo smysl sledovat:
#!/bin/bash # Get new state from diskstats devcode=$(findfs UUID=hFAc5i-yMtV-UC83-jF99-UnqA-ck7f-qYlnzo | cut -c 6-8) NEWstate=$(cat /proc/diskstats | grep $devcode) del_column () { NEWstate=$(echo $NEWstate | sed -r 's/(\s+)?\S+//'$1) } del_column 14 del_column 13 del_column 7 del_column 6 del_column 4 echo $NEWstate > /run/diskNEWstate.txt # compare md5 sums md5new=$(md5sum /run/diskNEWstate.txt | sed 's/ .*//') md5old=$(md5sum /run/diskOLDstate.txt | sed 's/ .*//') # if no changes, power down if [ "$md5new" = "$md5old" ]; then hdparm -y /dev/disk/by-id/ata-ST4000VN008-2DR166_ZDH8BP5S echo "going to sleep" else #if changes date >> /etc/scripts/sleep.log fi # Write current state to file echo $NEWstate > /run/diskOLDstate.txt
Automatické nastavení po restartu
Pokud se použije čistě hdparm, je dobré pamatovat na to, že nastavení po restartu mizí. U systemd systému jsem to tehdy řešil přes vlastní službu:
# /usr/lib/systemd/system/sda-spindown.service [Unit] Description=Set HDD spindown [Service] Type=oneshot ExecStart=/sbin/hdparm -B 241 /dev/sdb RemainAfterExit=yes [Install] WantedBy=multi-user.target
Aktivace služby:
systemctl daemon-reload
systemctl enable sda-spindown.service
systemctl start sda-spindown.service
Co disk probouzelo
Nejvíc času zabralo hledání procesů, které disk probouzely. V mém případě šlo hlavně o údržbu Nextcloudu a monitoring stavu disku v NextcloudPi.
První krok bylo omezit frekvenci Nextcloud cronu. Místo každých 15 minut jsem ho nechal běžet jen jednou denně:
crontab -e -u www-data
12 10 * * * php -f /var/www/nextcloud/cron.php
Druhý problém byl v NextcloudPi: zapnutá volba automatického monitoringu zdraví HDD probouzela disk prakticky pořád. Tuto volbu jsem vypnul.
