W redakcji. Artykuł jest wersją roboczą, nie jest jeszcze wersją finalną i może ulec zmianie.
Pomiary energii mają sens wtedy, gdy odpowiadają na konkretne pytanie: ile pobiera cały host, czy dana VM powoduje wybudzenia procesora albo czy zmiana konfiguracji rzeczywiście coś poprawiła. Opisujemy metodę z labu, nie uniwersalną receptę dla każdego serwera.
Co można wykonać po lekturze: przygotować kontrolowany pomiar na hoście KVM, wybrać właściwe źródło danych, porównać stan bezczynny z obciążeniem oraz sprawdzić, czy problem leży w aplikacji gościa, definicji VM czy samym hoście. Przykładowe nazwy i dane są zanonimizowane.
Spis treści
- Czy ten pomiar ma sens dla danego hosta KVM?
- Czym różnią się W, Wh i kWh oraz host od VM?
- Kiedy użyć watomierza, BMC/IPMI, baterii lub RAPL?
- Jak przygotować porównywalny i bezpieczny pomiar?
- Jaki wynik dał zanonimizowany eksperyment z dziesięcioma VM?
- Jak odczytać RAPL bez zmiany konfiguracji hosta?
- Jak znaleźć VM lub emulowane urządzenie, które zużywa CPU?
- Jak bezpiecznie przetestować zmianę definicji VM?
- Co wynika z wyniku, a czego pojedynczy odczyt nie dowodzi?
- Jak przeprowadzić kolejne pomiary?
Czy pomiar energii ma sens dla danego hosta KVM?
Pomiar jest użyteczny, gdy prowadzi do decyzji. Może odpowiedzieć na przykład, czy host laboratoryjny pracujący całą dobę rzeczywiście zużywa mniej po zmianie definicji VM, czy warto przenieść zadanie z laptopa na serwer albo która maszyna powoduje nieoczekiwane wybudzenia procesora. Nie zastępuje monitoringu dostępności, testu wydajności ani planowania pojemności.
Najwięcej daje na hostach z wieloma bezczynnymi VM, małym obciążeniem tła i długim czasem pracy. Przy serwerze, którego obciążenie stale zmienia się wraz z ruchem użytkowników, pojedynczy wynik jest tylko obserwacją. Wtedy potrzebny jest dłuższy szereg danych, zebrany w porównywalnych przedziałach i zestawiony z metrykami aplikacji.
Co dokładnie mierzymy: moc, energię, host czy pojedynczą VM?
Moc wyrażamy w watach (W). Jeden wat oznacza tempo zużywania energii równe jednemu dżulowi na sekundę. Odczyt 5 W mówi więc, ile energii urządzenie pobiera w danej chwili albo w krótkim oknie uśredniania. Energia jest sumą mocy w czasie: watogodzina (Wh) to praca urządzenia o mocy 1 W przez godzinę, a 1 kWh to 1000 Wh. Host pobierający stale 3,3 W przez rok zużyje w przybliżeniu 28,9 kWh: 3,3 × 24 × 365 / 1000.
Proces QEMU i VM nie są osobnym licznikiem energii. VM zużywa czas CPU, pamięć i I/O na wspólnym hoście, a do poboru całej maszyny dochodzą dyski, sieć, wentylatory, kontrolery oraz straty zasilacza. Dlatego odczyt CPU gościa odpowiada na pytanie „co robi aplikacja w VM”, a watomierz przy gniazdku odpowiada na pytanie „ile energii pobiera cały zestaw”. Oba wyniki są przydatne, lecz nie należy ich ze sobą utożsamiać.
Kiedy użyć watomierza, BMC/IPMI, baterii lub RAPL?
| Źródło danych | Na jakie pytanie odpowiada | Najważniejsze ograniczenie |
|---|---|---|
| watomierz przy gniazdku | ile energii pobiera kompletny host razem z zasilaczem | trzeba utrzymać ten sam zasilacz, ekran i peryferia; rozdzielczość tanich modeli może ukryć małą różnicę |
| BMC/IPMI | ile pobiera serwer, jeśli kontroler udostępnia czujnik mocy | zakres, częstotliwość próbkowania i dokładność zależą od producenta oraz modelu serwera |
power_now baterii laptopa | jaką moc pobiera laptop z baterii w danym stanie | nie jest to pobór z gniazdka; podczas ładowania odczyt może być zerowy lub nieprzydatny |
| RAPL (Running Average Power Limit) | jak zmienia się energia pakietu CPU, a na części platform także domeny psys | to licznik dostępny tylko na części procesorów; nie obejmuje automatycznie całego hosta ani strat zasilacza |
Watomierz jest punktem odniesienia dla kosztu zasilania. RAPL i turbostat najlepiej pokazują, czy zmiana wpływa na procesor i stany bezczynności. Bateria może być użytecznym przybliżeniem całej płyty w laptopie, ale nie wymusza się jej rozładowania na hoście produkcyjnym tylko po to, aby uzyskać pomiar. Ten tryb może zakończyć pracę hosta po wyczerpaniu akumulatora.
Jak przygotować porównywalny i bezpieczny pomiar?
Najpierw zapisujemy warunki, a dopiero potem zmieniamy jedną rzecz. Host powinien pracować z tym samym źródłem zasilania, profilem energetycznym, stanem ekranu, siecią i zestawem urządzeń USB. Na laptopie znaczenie mają również poziom baterii, ładowarka, temperatura, zamknięcie klapy i podświetlenie. Na każdym hoście wynik zmienią C-states procesora, turbo, praca wentylatorów, zadania okresowe, cache dysków i ruch sieciowy.
- Wybieramy okno laboratoryjne albo VM, której bezpiecznie dotyczy eksperyment. Nie generujemy sztucznego obciążenia na produkcji bez ustalonego limitu i planu wycofania.
- Notujemy wersję jądra, aktywne VM, źródło zasilania i temperaturę otoczenia. Te dane są potrzebne, aby później wyjaśnić różnicę.
- Odczekujemy, aż host osiągnie stan ustalony, a następnie wykonujemy kilka równie długich pomiarów kontrolnych i kilka pomiarów po zmianie. Porównujemy medianę lub przedział, nie najniższy pojedynczy odczyt.
- Między serią kontrolną i serią po zmianie nie aktualizujemy systemu, nie podłączamy monitora ani nie uruchamiamy innych testów. Jeżeli to niemożliwe, wynik opisujemy jako niewiarygodny.
Na hoście KVM administrator root, albo użytkownik mający dostęp do libvirt i sudo, może zapisać stan wyjściowy poniższymi poleceniami. Wszystkie są odczytowe: nie zmieniają konfiguracji ani stanu VM. Sukces oznacza pełny identyfikator hosta, wersję jądra i listę domen, które później da się zestawić z wynikami pomiaru.
hostnamectl
uname -r
virsh list --all
systemctl list-timers --all
Na jakim środowisku wykonaliśmy przykład i czego on nie obejmuje?
Wszystkie liczby niżej pochodzą z pomiarów laboratoryjnych wykonanych 17 września 2026 roku. Lab to zestaw maszyn wirtualnych do testowania zmian przed wdrożeniem, opisany w artykule Lab do testów na Terraform i OpenTofu. Nazwę modelu urządzenia, adresy i nazwy domen celowo zastąpiliśmy danymi ogólnymi.
| Element | Stan |
|---|---|
| Host | laptop laboratoryjny, procesor Intel 4 rdzenie / 8 wątków, 38 GiB pamięci, dysk NVMe, sieć wyłącznie po Wi-Fi |
| System hosta | Debian 13.7, jądro 6.12 |
| Wirtualizacja | KVM (od angielskiego Kernel-based Virtual Machine, czyli wirtualizacja wbudowana w jądro), QEMU 10.0.13 (emulator, który uruchamia maszyny i udaje ich urządzenia), libvirt 11.3.0 |
| Definicje maszyn | Terraform 1.16.3, provider dmacvicar/libvirt 0.8.3 |
| Obciążenie | 10 działających VM: 6 bezczynnych systemów testowych, VM z pulpitem, serwer kopii zapasowych, k3s i host Dockera; jedenasta VM była wyłączona |
| Metoda | licznik energii części platformy w procesorze (RAPL psys), moc całej płyty z akumulatora jako kontrola oraz zużycie CPU z procesów QEMU |
Czego nie mierzyliśmy: poboru z gniazdka (nie mieliśmy licznika energii, więc straty zasilacza są poza pomiarem i rzeczywisty rachunek jest wyższy), wpływu zmian na czas ciężkich zadań oraz zachowania na innych laptopach. Tam, gdzie wniosek wynika z rozumowania, a nie z pomiaru, piszemy to wprost.
Kontrola laboratoryjna: przy dziesięciu uruchomionych VM narzędzie power-probe.py odczytało w oknie 10 s RAPL psys = 3,33 W. Bateria była wtedy podłączona do zasilania i nie rozładowywała się, więc nie stanowiła niezależnego pomiaru całej płyty. Ten odczyt potwierdza zakres historycznego eksperymentu, ale nie jest pomiarem poboru z gniazdka.
Jaki wynik dał eksperyment z hostem KVM przed zmianami i po nich?
Poniższy przykład pokazuje moc całej płyty laptopa odczytaną z power_now baterii w ustalonych oknach pomiarowych. Nie jest to pobór z gniazdka, więc nie obejmuje strat zasilacza. Stan „po zmianach” obejmuje wyłączenie HPET w bezgłowych VM, usunięcie nieużywanej grafiki, preferencję energetyczną procesora i wygaszanie konsoli. Porównujemy te same dziesięć VM, a nie różne zestawy maszyn.
| Stan | Moc płyty |
|---|---|
| Host bez maszyn, ekran aktywny (klapa zamknięta) | 1,49-1,59 W |
| Host bez maszyn, konsola wygaszona | 1,07-1,10 W |
| 10 maszyn, konfiguracja wyjściowa | 5,29-5,90 W |
| 10 maszyn, po zmianach (także po restarcie hosta) | 3,25-3,39 W |
Przy dziesięciu VM to spadek o około 2,2 W, czyli o około 40 procent. W skali roku oznacza to około 49 kWh przed zmianami i około 29 kWh po nich, licząc samą płytę, bez strat zasilacza. Wynik nie mówi, że każdy host KVM oszczędzi dokładnie 2,2 W. Pokazuje, że w tym zestawie największy koszt powodowała konfiguracja bezczynnych VM. Na zmianę złożyły się:
- wyłączenie emulowanego zegara HPET (od angielskiego High Precision Event Timer) w maszynach i usunięcie z nich grafiki, której nikt nie ogląda - największy efekt i zarazem największe zaskoczenie,
- preferencja energetyczna procesora ustawiona na
balance_power, - wygaszanie konsoli po 60 sekundach.
Ciekawsze od listy jest to, czego na niej nie ma. Wyłączanie Bluetooth, zarządzanie energią urządzeń PCI czy wyłączenie watchdoga dają ułamki wata albo nic - sprawdziliśmy to pomiarem i opisujemy niżej.
Jak odczytać RAPL na hoście bez zmiany konfiguracji?
Procesory Intela mogą udostępniać liczniki energii RAPL (od angielskiego Running Average Power Limit). Domena psys bywa użytecznym wskaźnikiem części platformy, lecz nie występuje na każdym procesorze i nie jest pomiarem z gniazdka. Na hoście KVM administrator root wykonuje poniższy blok tylko po to, aby znaleźć licznik. Polecenie jest odczytowe; powodzenie oznacza wypisanie ścieżki do energy_uj. Nie zakładamy numeru katalogu, ponieważ zależy on od platformy.
P=
for d in /sys/class/powercap/intel-rapl*:*; do
[ "$(cat "$d/name" 2>/dev/null)" = psys ] && P="$d/energy_uj"
done
[ -n "$P" ] || { echo "brak domeny psys"; exit 1; }
echo "$P"
Licznik podaje energię w mikrodżulach, więc moc to różnica dwóch odczytów podzielona przez czas. Plik energy_uj może czytać tylko root (na naszym hoście ma uprawnienia 0400). Dostęp ograniczył commit jądra 949dd0104c49.
Na hoście KVM uruchamiamy następny blok jako root, na przykład po sudo -i. Jest odczytowy: czeka 60 sekund i wylicza średnią moc z tego okresu, nie zmieniając VM ani ustawień hosta. Dodatni wynik w watach oznacza sukces. W dłuższej serii trzeba obsłużyć przejście licznika przez zero, dlatego blok uwzględnia max_energy_range_uj.
P=
for d in /sys/class/powercap/intel-rapl*:*; do
[ "$(cat "$d/name" 2>/dev/null)" = psys ] && P="$d/energy_uj"
done
[ -n "$P" ] || { echo "brak domeny psys"; exit 1; }
m=$(cat "${P%/energy_uj}/max_energy_range_uj")
a=$(cat "$P"); t0=$(date +%s.%N)
sleep 60
b=$(cat "$P"); t1=$(date +%s.%N)
[ "$b" -lt "$a" ] && b=$((b + m)) # licznik przeszedł przez zero
awk -v a="$a" -v b="$b" -v t0="$t0" -v t1="$t1" \
'BEGIN { printf "%.2f\n", (b - a) / (t1 - t0) / 1000000 }' # waty
Tak wyglądał host z dziesięcioma działającymi VM, mierzony oknami po 60 sekund. Najpierw i na końcu rejestrowaliśmy bieg jałowy. Pomiędzy nimi uruchomiliśmy obciążenie CPU wyłącznie w odizolowanej VM laboratoryjnej; nie uruchamialiśmy go na produkcji.
| Stan | psys (część platformy) | pakiet procesora |
|---|---|---|
| bieg jałowy | 2,93 W | 1,08 W |
| osiem wątków obciążenia | 20,31 W | 13,61 W |
| powrót do biegu jałowego | 2,92 W | 1,07 W |
Jeżeli do kontroli czujnika potrzebne jest znane obciążenie, w testowej VM można uruchomić stress-ng już zainstalowany w tym gościu. Polecenie obciąża osiem wątków przez dwie minuty i samo kończy pracę; sukces oznacza zakończenie z kodem 0. Nie należy kopiować go na VM obsługującą ruch użytkowników bez wcześniejszego limitu i zgody na test.
sudo stress-ng --cpu 8 --timeout 120s --metrics-brief
Licznik reaguje na obciążenie i wraca do wcześniejszego zakresu, więc nadaje się do porównań „przed i po”. Jedno zastrzeżenie: psys to nie jest pełny rachunek. Obejmuje część platformy związaną z procesorem, pamięcią i przetwornicami, ale nie wszystko, co siedzi na płycie. Na tym laptopie czytał 0,7-1 W mniej niż moc faktycznie pobierana z akumulatora w tym samym oknie czasowym (dwa pomiary: 4,07 wobec 5,06 W oraz 2,59 wobec 3,28 W). Poza licznikiem zostają między innymi karta Wi-Fi, dysk, kontroler wbudowany i straty przetwornic. Pobór z gniazdka jest jeszcze wyższy o straty zasilacza.
Uwaga na btop. Monitor btop w wersji 1.3.2 pokazuje przy baterii wartość z jednostką „W”, która watami nie jest. W kodzie tej wersji liczy ją jako energy_now / 1000000, czyli podaje zapas energii akumulatora w watogodzinach. Na naszym hoście to 43,75 - i dokładnie tyle wynosi energy_full naładowanej baterii. W gałęzi głównej projektu poprawiono to na power_now. Jeżeli Twój „pobór mocy” nie zmienia się pod obciążeniem i podejrzanie przypomina pojemność baterii, to nie jest pomiar mocy.
Kiedy pomiar z baterii obejmuje całą płytę laptopa?
Żeby sprawdzić, czego psys nie widzi, potrzebny jest pomiar obejmujący więcej niż CPU. Akumulator może go dać, gdy laptop rzeczywiście pracuje z baterii i sterownik udostępnia power_now. Najpierw na hoście laboratoryjnym sprawdzamy wyłącznie dostępne dane. Ten blok jest odczytowy; sukces oznacza nazwę baterii, stan Discharging oraz liczbę w mikrowatach, którą można przeliczyć na waty.
ls /sys/class/power_supply/
B=/sys/class/power_supply/BAT0
cat "$B/status"
cat "$B/power_now"
Nie każdy laptop poda użyteczną wartość. Podczas ładowania odczyt może wynosić zero albo opisywać przepływ do baterii, a model kontrolera decyduje o częstotliwości odświeżania i dokładności. W naszym labie laptop mógł na krótko wymusić rozładowanie mimo podłączonego zasilacza przez charge_behaviour. Zgodnie z opisem interfejsu jądra wartość force-discharge oznacza właśnie taki tryb.
To nie jest polecenie dla hosta produkcyjnego ani hosta dostępnego wyłącznie przez SSH. Użyliśmy go tylko przy fizycznie dostępnym laptopie laboratoryjnym, z naładowaną baterią, podłączonym zasilaczem i timerem przywracającym normalny tryb. Skutek poleceń w kolejnym bloku jest zapisywalny: bateria zaczyna się rozładowywać. Warunkiem bezpiecznego zakończenia jest powrót auto i stan inny niż Discharging.
# Tylko fizycznie nadzorowany host laboratoryjny.
B=/sys/class/power_supply/BAT0
# Bezpiecznik: za 60 minut wróć do ładowania, nawet jeśli zerwie się SSH.
sudo systemd-run --unit=power-guard --on-active=60min \
/bin/sh -c "echo auto > $B/charge_behaviour"
echo force-discharge | sudo tee $B/charge_behaviour
cat $B/status # Discharging
cat $B/power_now # mikrowaty: 5060000 to 5,06 W
echo auto | sudo tee $B/charge_behaviour
sudo systemctl stop power-guard.timer
Bezpiecznik nie jest przesadą: zapomniany tryb rozładowania przy pracy na odległość może wyłączyć host. Wynik warto potwierdzić drugą drogą, przez ubytek energy_now w czasie. W naszym pięciominutowym oknie odczyt power_now 5,06 W i wyliczenie z ubytku energii 5,03 W były zbliżone, a psys w tym samym czasie pokazał 4,07 W. To wyjaśnia, dlaczego licznik platformy nie może zastąpić pomiaru całej płyty.
Liczby w tabelach dotyczących płyty pochodzą z tego laboratoryjnego pomiaru baterii. Odczyt jest uśredniany przez układ pomiarowy i ma szum: dwa pomiary tej samej konfiguracji dały 5,29 W i 5,90 W. Dlatego przy małych różnicach korzystaliśmy także ze stabilniejszych liczników procesora, a serie układaliśmy naprzemiennie: kontrola, zmiana, kontrola, zmiana.
Moc pakietu procesora i udział stanów uśpienia odczytuje turbostat z pakietu linux-cpupower. Na hoście KVM uruchamiamy go jako root; jest odczytowy i przez dwie minuty zbiera próbkę. Sukces oznacza wiersz z wartościami mocy oraz udziałem stanów C, a brak kolumny oznacza, że platforma jej nie udostępnia.
sudo turbostat --quiet --show Busy%,PkgWatt,RAMWatt,Pkg%pc8,Pkg%pc10 \
--interval 120 --num_iterations 1
Kolumny Pkg%pc8 i Pkg%pc10 to udział czasu, w którym cały procesor był w dwóch najgłębszych stanach uśpienia. To one tłumaczą większość wyników poniżej. Każdy pomiar trwał 90-180 sekund i w tym czasie na hoście nie uruchamialiśmy nic innego; jeden pomiar, podczas którego równolegle działał menedżer pakietów, odrzuciliśmy.
Dlaczego pusty host nie wchodził w najgłębszy stan uśpienia procesora?
Pierwszy pomiar zrobiliśmy przed instalacją czegokolwiek: 1,49-1,59 W. Procesor spędzał 92 procent czasu w stanie PC8, ale do najgłębszego PC10 nie schodził ani razu. Przyczynę znaleźliśmy w katalogu /sys/class/drm: wewnętrzny panel eDP (od angielskiego Embedded DisplayPort) miał stan DPMS (od angielskiego Display Power Management Signaling) On, a podświetlenie w sysfs stało na maksimum, mimo zamkniętej klapy. Konsola tekstowa nigdy się nie wygaszała.
Tak ma być z definicji. Dokumentacja parametrów jądra mówi o consoleblank wprost: wartość 0 wyłącza wygaszanie i jest domyślna. Na komputerze, który ma pracować bez monitora, to ustawienie po prostu kosztuje. Zmierzyliśmy to naprzemiennie, po 120 sekund:
| Stan | Moc płyty | Procesor | PC8 | PC10 |
|---|---|---|---|---|
| konsola wygaszona | 1,10 W | 0,20 W | 28% | 67% |
| panel aktywny | 1,49 W | 0,31 W | 92% | 0% |
| konsola wygaszona | 1,07 W | 0,19 W | 29% | 67% |
Różnica wynosiła około 0,4 W, czyli ponad jedną czwartą mocy pustego hosta. Czy panel faktycznie świecił pod zamkniętą klapą, nie sprawdzaliśmy; istotne jest to, że aktywny potok wyświetlania nie pozwalał procesorowi zejść do PC10. Na bezgłowym hoście laboratoryjnym trwałą zmianę zapisaliśmy w osobnym pliku GRUB. Jest to zmiana zapisywalna, która zacznie działać po restarcie.
# /etc/default/grub.d/power.cfg
GRUB_CMDLINE_LINUX_DEFAULT="$GRUB_CMDLINE_LINUX_DEFAULT consoleblank=60"
sudo update-grub
# doraźnie, bez restartu:
sudo sh -c 'TERM=linux setterm --blank force </dev/tty1 >/dev/tty1'
Administrator na hoście uruchamia potem sudo update-grub; sukces sprawdzamy po restarcie przez obecność consoleblank=60 w /proc/cmdline. Aby wycofać zmianę, usuwamy plik /etc/default/grub.d/power.cfg, uruchamiamy ponownie update-grub i planujemy restart. Wciśnięcie klawisza na lokalnej konsoli nadal ją wybudza. Przy działających VM procesor i tak rzadko dochodził do PC10 (0,5-1,1 procent czasu), więc ta zmiana ma znaczenie głównie wtedy, gdy VM są wyłączone.
Jak znaleźć bezczynną VM lub emulowane urządzenie, które zużywa CPU hosta?
Po uruchomieniu dziesięciu maszyn host pobierał 5,29-5,90 W, procesor 2,80-2,85 W, a do stanu PC8 nie schodził już wcale. Rozkład zużycia CPU na procesy QEMU wyglądał tak (100 procent to jeden wątek hosta): k3s około 50, pulpit KDE około 28 (jak się później okazało, jego serwer graficzny padał wtedy w pętli), a każdy z bezczynnych systemów testowych 5-6 procent. Te ostatnie nie robiły nic. Wewnątrz gościa było około 50 przerwań na sekundę i 100 procent bezczynności.
Proces QEMU ma osobne wątki: po jednym na każdy procesor wirtualny (vCPU, od angielskiego virtual CPU) oraz wątek główny, który obsługuje emulowane urządzenia. Na hoście KVM administrator może odczytać czas wątków jednej laboratoryjnej VM z /proc. Blok jest odczytowy; przed uruchomieniem zastępujemy deb13 unikalnym fragmentem nazwy własnej domeny. Sukces oznacza listę wątków i rosnące liczniki czasu CPU w dwóch próbkach.
p=$(pgrep -f 'guest=deb13,')
for t in /proc/$p/task/*; do
echo "$(cat $t/comm): $(sed 's/.*) //' $t/stat | awk '{print $12+$13}')"
done
# dwa odczyty w odstępie, różnica / (CLK_TCK * sekundy) = udział CPU
Nazwę wątku obcinamy przez sed 's/.*) //', bo może zawierać spację: wątki procesorów wirtualnych QEMU nazywają się CPU 0/KVM, CPU 1/KVM i tak dalej. Po obcięciu czas użytkownika (utime) i czas w jądrze (stime) są w polach 12 i 13. Pierwsza wersja tego artykułu liczyła awk '{print $14+$15}' na całej linii i dla takich wątków sumowała niewłaściwe pola, pomijając stime. Na labie 29 września 2026 roku linia wątku vCPU zaczynała się tak: 3463 (CPU 0/KVM) S 1 3451 3451 0 -1 138412224 32930 0 0 0 118638 22844. awk traktuje (CPU i 0/KVM) jako dwa pola, więc zamiast utime i stime (118638 i 22844 tików) dodawał pole cmajflt (tu 0) i utime.
Wynik dla bezczynnego Debiana 13: oba vCPU po 0,3 procent, wątek grafiki 0,3 procent, a wątek główny 4,65 procent. Wartości dla vCPU pochodzą z pierwszej wersji skryptu, więc obejmują tylko czas użytkownika i mogą być zaniżone. Nazwy wątku grafiki z tamtego pomiaru nie zapisaliśmy, więc nie wiemy, czy dotyczyło go to samo. Wątek główny nazywa się qemu-system-x86, bez spacji, więc jego wynik jest poprawny. Maszyna prawie nie pracowała, pracował emulator. Co dokładnie robił, pokazał strace:
Do krótkiej diagnozy laboratorium użyliśmy także strace. Dołączenie śledzenia nie zmienia konfiguracji VM, ale samo obserwowanie procesu może nieznacznie wpłynąć na jego czas pracy; dlatego traktujemy je jako wskazówkę, a nie pomiar mocy. Nie dołączamy go do procesu produkcyjnego w czasie incydentu bez oceny wpływu.
$ sudo timeout 10 strace -c -p $p
% time seconds usecs/call calls errors syscall
76,99 0,062275 12 5005 ppoll
22,94 0,018559 3 5000 ioctl
$ sudo timeout 2 strace -tt -p $p # lista deskryptorów skrócona
13:20:36.170975 ioctl(15, KVM_IRQ_LINE_STATUS, 0x7fff134cf4a0) = 0
13:20:36.171126 ioctl(15, KVM_IRQ_LINE_STATUS, 0x7fff134cf4a0) = 0
13:20:36.171170 ppoll([...], 79, {tv_sec=0, tv_nsec=0}, NULL, 8) = 0 (Timeout)
13:20:36.171242 ppoll([...], 79, {tv_sec=0, tv_nsec=3649479}, NULL, 8) = 0 (Timeout)
13:20:36.175092 ioctl(15, KVM_IRQ_LINE_STATUS, 0x7fff134cf4a0) = 0
13:20:36.175189 ioctl(15, KVM_IRQ_LINE_STATUS, 0x7fff134cf4a0) = 0
Co 4 milisekundy, czyli 250 razy na sekundę, wątek główny budzi się i dwa razy zmienia stan linii przerwania. Tak wygląda zegar sprzętowy emulowany w przestrzeni użytkownika. Po stronie gościa odpowiedź była w sysfs: jako zapasowe źródło zdarzeń zegarowych (/sys/devices/system/clockevents/broadcast/current_device) jądro wybrało hpet.
Jak emulowany zegar HPET wybudzał QEMU?
HPET to zegar sprzętowy, który QEMU emuluje programowo. Nasze domeny miały w definicji tylko <clock offset='utc'/>, bez żadnych ustawień zegarów, więc HPET był obecny. Tymczasem virt-install 4.1.0 dla gościa Debian 12 generuje taki blok (sprawdzone przez --print-xml, które niczego nie tworzy):
<clock offset="utc">
<timer name="rtc" tickpolicy="catchup"/>
<timer name="pit" tickpolicy="delay"/>
<timer name="hpet" present="no"/>
</clock>
RTC (od angielskiego Real-Time Clock) i PIT (od angielskiego Programmable Interval Timer) to dwa pozostałe zegary, a ich opcje opisuje rozdział o zegarach w dokumentacji formatu domeny libvirt. Ponieważ ten blok zmienia trzy rzeczy naraz, rozdzieliliśmy je osobnym testem na dwóch VM Ubuntu, obok trzeciej z pełną poprawką:
| Wariant | CPU hosta | Wywołania systemowe QEMU na 10 s |
|---|---|---|
HPET obecny, zmienione tylko tickpolicy | 5,78% | 10 005 |
| wyłączony tylko HPET | 0,20% | 68 |
| pełny blok (kontrola) | 0,98% | - |
Odpowiada za to sam HPET. Z 10 tysięcy wywołań systemowych w 10 sekund zostało 68. Jeśli VM powstają własnym XML, skryptem albo narzędziem do infrastruktury jako kodu, najpierw sprawdzamy faktycznie wdrożoną definicję. Na hoście KVM poniższe polecenie jest odczytowe; zastępujemy deb13 nazwą domeny. Sukces oznacza pokazanie bloku <clock>, który należy porównać z definicją źródłową.
virsh dumpxml deb13 | grep -A4 '<clock'
Czy bezgłowa VM ma grafikę, której nikt nie używa?
W tym samym wyniku był jeszcze wątek grafiki. Nasze maszyny testowe nie mają pulpitu i kod nie deklaruje im żadnej grafiki. A jednak każda miała protokół SPICE (od angielskiego Simple Protocol for Independent Computing Environments, czyli system zdalnego ekranu dla maszyn wirtualnych) i kartę cirrus. Źródło widać w kodzie providera: domyślna definicja domeny w pliku domain_def.go wersji 0.8.3 zawiera już grafikę SPICE, a dokumentacja zasobu domeny nazywa cirrus domyślną kartą libvirt. Brak bloku graphics w kodzie nie oznacza więc braku grafiki.
Trzy bezczynne Debiany zmierzyliśmy w tych samych 120 sekundach:
| Maszyna | HPET | SPICE i karta graficzna | CPU hosta |
|---|---|---|---|
deb11 | tak | tak | 5,09% |
deb12 | nie | tak | 1,76% |
deb13 | nie | nie | 0,06% |
Po wyłączeniu HPET grafika wciąż kosztowała około 1,7 punktu procentowego rdzenia na maszynę. Bez obu dodatków bezczynna maszyna przestaje być widoczna w zużyciu hosta.
Jak bezpiecznie przetestować zmianę definicji VM w Terraform?
Provider 0.8.3 nie ma argumentu do ustawiania zegarów ani do usunięcia domyślnej grafiki. Ma za to blok xml z polem xslt: arkusz XSLT (od angielskiego XSL Transformations, czyli język przekształcania dokumentów XML), który przekształca wygenerowaną definicję, zanim trafi do libvirt. Mieliśmy już taki arkusz do poprawiania formatu dysków danych, więc zamieniliśmy go w szablon z jedną zmienną. Jego aktualną wersję można obejrzeć w repozytorium, w pliku terraform/modules/vm-group/domain.xsl.tftpl:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes"/>
<!-- kopiuj wszystko bez zmian -->
<xsl:template match="@*|node()">
<xsl:copy><xsl:apply-templates select="@*|node()"/></xsl:copy>
</xsl:template>
<!-- dyski *.qcow2: provider wpisuje każdy plik jako raw -->
<xsl:template match="/domain/devices/disk[@device='disk'][source[contains(@file, '.qcow2')]]/driver/@type">
<xsl:attribute name="type">qcow2</xsl:attribute>
</xsl:template>
<!-- zegary bez HPET, niezależnie od tego, co wygenerował provider -->
<xsl:template match="/domain">
<xsl:copy>
<xsl:apply-templates select="@*|node()[not(self::clock)]"/>
<clock offset="utc">
<timer name="rtc" tickpolicy="catchup"/>
<timer name="pit" tickpolicy="delay"/>
<timer name="hpet" present="no"/>
</clock>
</xsl:copy>
</xsl:template>
%{ if headless ~}
<!-- maszyny bez pulpitu: bez grafiki -->
<xsl:template match="/domain/devices/graphics"/>
<xsl:template match="/domain/devices/video"/>
<xsl:template match="/domain/devices/audio"/>
%{ endif ~}
</xsl:stylesheet>
W zasobie domeny arkusz dostaje każda maszyna, a zmienna mówi, czy ma pulpit:
resource "libvirt_domain" "vm" {
# ...
xml {
xslt = templatefile("${path.module}/domain.xsl.tftpl", {
headless = each.value.profile != "desktop"
})
}
}
Maszyna z pulpitem zachowuje SPICE i swoją kartę graficzną, ale też traci HPET. Konsola szeregowa działa u wszystkich jak wcześniej. Przed wdrożeniem sprawdziliśmy wynik szablonu poleceniem xsltproc na prawdziwych definicjach trzech maszyn, a potem virt-xml-validate wobec schematu libvirt.
Cztery rzeczy, które warto wiedzieć przed apply:
- Zmiana
xsltodtwarza domenę. Plan pokazałmust be replaceddla wszystkich jedenastu maszyn. Dyski są osobnymi zasobami i zostały nietknięte, ale sama maszyna jest usuwana i tworzona od nowa. Wyłączyliśmy więc maszyny łagodnie przedapply, a nie po. - Dotyczy to każdej zmiany treści szablonu, także komentarza. Do
xslttrafia wyrenderowany tekst, a nie znaczenie arkusza. - Potrzebny jest
xsltprocna maszynie, na której działa Terraform. Bez niegoapplykończy się błędemexec: "xsltproc": executable file not found in $PATH. - Konfiguracja startowa nie uruchomiła się ponownie. Dysk z danymi cloud-init był ten sam, więc każdy gość ma jeden katalog instancji, a moduły jednorazowe zgłaszają w logu
already ran(sprawdzone na dziewięciu maszynach, z pominięciem wyłączonej i tej bez agenta).
Tak wyglądał efekt przy dziesięciu maszynach, jeszcze przy domyślnej preferencji energetycznej procesora:
| Moc płyty | Procesor (RAPL) | Zajętość CPU hosta | CPU procesów QEMU | PC8 | |
|---|---|---|---|---|---|
| przed | 5,29-5,90 W | 2,80-2,85 W | 23% | 148-150% | 0% |
| po | 4,17 W | 1,71 W | 7,3% | 47% | 23% |
Jedno zastrzeżenie, żeby liczby nie mówiły więcej, niż wiemy. W pomiarach „przed” dwie maszyny Ubuntu były świeżo po pierwszym starcie i zużywały po 17-18 procent CPU, a później 0-1 procent. Część spadku w kolumnie QEMU (około 35 z około 100 punktów) wynika więc z tego, a nie z poprawki. Czysty efekt samej poprawki pokazują tabele z dwóch poprzednich sekcji: około 5 punktów procentowych rdzenia na każdą bezczynną maszynę.
Kiedy preferencja balance_power ma sens dla procesora hosta?
Procesor pracuje pod sterownikiem intel_pstate w trybie aktywnym, z HWP (od angielskiego hardware-managed P-states, czyli sprzętowym sterowaniem częstotliwością). W tym trybie jądro nie wybiera częstotliwości samo, tylko podpowiada procesorowi preferencję - EPP (od angielskiego Energy-Performance Preference). Opisuje to dokumentacja intel_pstate. Domyślnie na naszym hoście było balance_performance. Zmierzyliśmy balance_power przed poprawką XSLT i po niej:
| 10 maszyn | EPP | Moc płyty | Procesor | Średnia częstotliwość w pracy |
|---|---|---|---|---|
| przed poprawką | balance_performance | 5,29-5,90 W | 2,80-2,85 W | 1084 MHz |
| przed poprawką | balance_power | 4,98 W | 1,89 W | 919 MHz |
| po poprawce | balance_performance | 4,17 W | 1,71 W | 1312 MHz |
| po poprawce | balance_power | 3,25-3,39 W | 1,12-1,20 W | 1014-1049 MHz |
Na samym procesorze pobór spadł o około 0,6 W. Kosztem jest nieco dłuższa praca przy niższej częstotliwości: zajętość CPU hosta wzrosła z 7,3 do 9,0-9,1 procent. Tryb turbo pozostaje dostępny. Czasu ciężkich zadań, na przykład aktualizacji wszystkich VM naraz, nie mierzyliśmy. Dlatego nie ustawiamy tej preferencji bez wcześniejszego benchmarku ważnego dla danego hosta.
Najpierw na hoście KVM odczytujemy bieżące wartości. Ten blok jest odczytowy; sukces oznacza jednolitą wartość EPP oraz governor dla wszystkich CPU albo pokazuje, że host wymaga osobnej analizy.
cat /sys/devices/system/cpu/cpu*/cpufreq/energy_performance_preference | sort | uniq -c
cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor | sort | uniq -c
Na odizolowanym hoście laboratoryjnym można następnie wykonać tymczasowy test. Poniższy blok jest zapisywalny i zmienia preferencję wszystkich CPU; przed wykonaniem zapisujemy pierwotną wartość z poprzedniego odczytu. Powodzenie oznacza, że drugi odczyt zwraca balance_power. Aby wycofać test, zamiast balance_power zapisujemy wcześniej odnotowaną wartość, na przykład balance_performance.
echo balance_power | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/energy_performance_preference
# wycofanie przykładu:
echo balance_performance | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/energy_performance_preference
Na stałe ustawia ją jedna linia dla systemd-tmpfiles, ale dopiero po pomiarze i benchmarku. Reguła w zapisuje wartość do pliku przy starcie i przyjmuje wzorzec ścieżki, więc obejmuje wszystkie wątki naraz. Na badanym hoście nie było TLP ani demona profili energetycznych, które mogłyby ją nadpisać.
Preferencję da się tak zmieniać przy governorze powersave, który na naszym hoście był ustawiony na wszystkich ośmiu wątkach. Przy governorze performance sterownik intel_pstate z HWP wpisuje do EPP wartość 0 i, według tej samej dokumentacji, odrzuca zapis innej wartości przez sysfs, więc reguła poniżej nic by nie zmieniła. Governor sprawdzamy razem z wynikiem reguły.
# /etc/tmpfiles.d/power.conf
w /sys/devices/system/cpu/cpu*/cpufreq/energy_performance_preference - - - - balance_power
sudo systemd-tmpfiles --create /etc/tmpfiles.d/power.conf
cat /sys/devices/system/cpu/cpu*/cpufreq/energy_performance_preference | sort | uniq -c
cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor | sort | uniq -c # oczekiwane: powersave
Jak oszacować wpływ jednej VM bez mylenia go z poborem całego hosta?
Po obu zmianach sprawdziliśmy, gdzie zostały waty. Kolejne grupy VM wstrzymywaliśmy poleceniem virsh suspend: VM zachowuje pamięć i stan, ale jej procesory przestają pracować. To jest zmiana stanu usługi, dlatego wykonaliśmy ją wyłącznie na VM laboratoryjnych, po sprawdzeniu że nikt z nich nie korzysta. Na produkcji bezpieczniejszy jest najpierw odczyt CPU QEMU, a takie wstrzymanie wymaga uzgodnionego okna.
# Host KVM, tylko VM laboratoryjna:
virsh suspend nazwa-vm
virsh domstate nazwa-vm # oczekiwane: paused
virsh resume nazwa-vm # wycofanie; oczekiwane: running
| Stan | Moc płyty | Procesor | Różnica |
|---|---|---|---|
| wszystkie 10 maszyn działa | 3,39 W | 1,12 W | |
| wstrzymane 6 systemów testowych | 3,20 W | 1,08 W | około 0,2 W za sześć |
| + wstrzymana maszyna z k3s | 2,36 W | 0,72 W | około 0,85 W |
| + wstrzymany pulpit KDE | 1,52 W | 0,31 W | około 0,85 W |
| + wstrzymane Proxmox Backup Server i Docker | 1,23 W | 0,21 W | około 0,3 W |
| wszystkie wznowione (kontrola) | 3,25 W | 1,12 W |
W tym labie sześć bezczynnych systemów testowych kosztuje razem tyle, ile mieści się w szumie pomiaru. Nie dowodzi to, że każda bezczynna VM jest bezpłatna energetycznie. Resztę poboru powodowały głównie dwie VM. Pierwsza to k3s: 32-36 procent wątku hosta, a wewnątrz gościa najwięcej zużywał proces k3s-server.
Druga, pulpit KDE, okazała się awarią, a nie kosztem pulpitu. Gdy sprawdziliśmy procesy w środku, serwer X padał tam w pętli, około 11 razy na minutę (szczegóły w artykule Lab na osobnym hoście). Po naprawie zmierzyliśmy tę maszynę jeszcze raz, w trzech stanach po 120 sekund:
| Stan (10 maszyn, pulpit po naprawie) | Moc płyty | Procesor | CPU hosta dla pulpitu |
|---|---|---|---|
| pulpit z ekranem logowania | 3,21 W | 1,02 W | 39% |
| pulpit wstrzymany | 3,00 W | 0,84 W | 1% |
| pulpit działa, menedżer logowania zatrzymany | 2,82 W | 0,85 W | 3% |
Działający ekran logowania zajmuje więcej CPU niż pętla awarii (39 procent wątku wobec 11-22), a mimo to kosztuje mniej energii: około 0,2-0,4 W wobec około 0,85 W. Wyjaśnienie, którego nie weryfikowaliśmy osobno: równomierna praca pozwala procesorowi utrzymać niską częstotliwość, a ciągłe uruchamianie serwera od nowa - nie. Dla maszyny z pulpitem, która przez większość czasu czeka, najtańszy jest stan bez menedżera logowania, uruchamianego dopiero wtedy, gdy pulpit jest potrzebny.
Które zmiany nie obniżyły poboru energii w tym labie?
Dwie zmiany wyglądały obiecująco i obie odrzuciliśmy po pomiarze:
| Zmiana | Wynik |
|---|---|
kvm.halt_poll_ns=0 - host nie czeka aktywnie na wybudzenie bezczynnego procesora wirtualnego (domyślnie na tym jądrze 200 000 ns) | procesor 2,80 W wobec 2,80-2,85 W, zużycie CPU procesów QEMU bez zmian |
zarządzanie energią urządzeń PCI w czasie pracy (power/control=auto, sugestia powertop) plus kernel.nmi_watchdog=0, czyli wyłączony watchdog NMI (od angielskiego Non-Maskable Interrupt) | dwie pary kontrola/zmiana: 3,30/3,47 W i 3,30/3,29 W, procesor bez zmian |
Pierwsza nie zadziałała, bo problemem nie było czekanie procesorów wirtualnych, tylko wątek główny emulatora - co widać było już w podziale na wątki. Druga może dawać coś poniżej progu naszego pomiaru, ale nie potrafimy tego wykazać, więc nie wdrażamy. Kartę Wi-Fi celowo wyłączyliśmy z testu zarządzania energią, bo to jedyne łącze hosta.
Czego świadomie nie zmienialiśmy, mimo że narzędzia to sugerowały?
Świadomie nie wdrażaliśmy:
- wyłączania Bluetooth, kamery, czytnika linii papilarnych, czytnika kart i usypiania USB - to ułamki wata za cenę dodatkowej konfiguracji,
- wydłużenia interwału zapisu buforów na dysk (
vm.dirty_writeback_centisecs=1500, kolejna sugestiapowertop) - dłuższe okno utraty danych przy awarii, bez mierzalnego zysku przy tym obciążeniu, - wyłączenia trybu turbo - dotyczy tylko szczytów, a wydłużyłoby ciężkie zadania,
- KSM (od angielskiego Kernel Samepage Merging, czyli scalania identycznych stron pamięci) - samo skanowanie pamięci zużywa CPU, a pamięci mamy dość,
- przejścia z Wi-Fi na kabel - nie mierzyliśmy, wymaga okablowania.
Bez żadnych zmian dobrze działały już:
- dysk NVMe - APST (od angielskiego Autonomous Power State Transition) był włączony, a po 100 ms bezczynności dysk przechodzi do stanu o poborze 4 mW (
nvme get-featurez pakietunvme-cli), - oszczędzanie energii karty Wi-Fi (
iw dev ... get power_savezwracałoon), - ASPM (od angielskiego Active State Power Management) w stanie L1 na łączu dysku i większości portów PCI Express,
- usypianie kodeka dźwięku po 10 sekundach,
- pamięć - liczniki RAPL pokazywały 0,4-0,5 W bez względu na stan, więc przy 38 GiB nie było tu czego odzyskiwać.
Jak sprawdzić, czy ustawienia przetrwały restart hosta?
Po wdrożeniu zrestartowaliśmy host. Po starcie /proc/cmdline zawierał consoleblank=60, wszystkie osiem wątków miało preferencję balance_power, panel przeszedł w DPMS Off po minucie, a systemctl --failed był pusty. Poprawka XSLT znajduje się w definicjach domen, więc nie zależy od restartu hosta. Pomiar po ponownym uruchomieniu VM dał 3,39 W, a procesor 1,15 W, czyli zakres zbliżony do stanu przed restartem.
Co wynika z wyniku, a czego pojedynczy odczyt nie dowodzi?
Wynik 5,29-5,90 W → 3,25-3,39 W pokazuje, że w tym samym laboratorium konfiguracja bezczynnych VM miała większe znaczenie niż drobne ustawienia hosta. Nie dowodzi, że każda zmiana HPET da 40 procent oszczędności, że identycznie zachowa się inny procesor ani że tyle samo spadnie rachunek za energię. Pomiary baterii nie obejmują strat zasilacza, a RAPL nie obejmuje automatycznie wszystkich urządzeń hosta.
Różnicę mniejszą od rozrzutu serii traktujemy jako brak potwierdzonego efektu. Jeżeli moc rośnie po godzinie pracy, przyczyną może być temperatura, turbo, wentylator, cache, zadanie okresowe lub bateria, a nie ostatnia zmiana w XML. Dlatego wiarygodny wniosek wymaga powrotu do stanu kontrolnego albo powtórzenia pomiaru w innym dniu. Dopiero zgodny wynik z kilku serii uzasadnia trwałą zmianę konfiguracji.
Jak przeprowadzić kolejny pomiar hosta KVM krok po kroku?
- Wybierz źródło danych zgodne z pytaniem: watomierz dla całego poboru, BMC/IPMI dla serwera, bateria tylko w kontrolowanym labie laptopa, a RAPL dla trendu CPU/platformy.
- Sprawdź, czy host z maszynami schodzi w ogóle do głębokich stanów uśpienia. Zero procent w PC8 to sygnał, że coś budzi procesor bez przerwy.
- Rozbij zużycie CPU procesów QEMU na wątki. Wysoki wątek główny przy bezczynnych procesorach wirtualnych to emulowane urządzenie, a nie praca gościa.
- Sprawdź blok
<clock>w definicjach maszyn. Brak<timer name='hpet' present='no'/>kosztował nas około 5 procent rdzenia na każdą bezczynną maszynę. - Sprawdź, czy maszyny bez pulpitu nie mają grafiki, której nie zamawiałeś. Brak bloku w kodzie nie znaczy brak urządzenia.
- Pamiętaj, że zmiana arkusza XSLT w providerze libvirt odtwarza domeny. Wyłącz maszyny przed
apply. - Na hoście bez monitora ustaw
consoleblank, bo jądro domyślnie nie wygasza konsoli. - Preferencję
balance_powerustaw trwale i wiedz, jak ją podnieść na czas ciężkiej pracy. - Zanim zaczniesz wyłączać drobne urządzenia, sprawdź, które maszyny kosztują najwięcej. U nas dwie z dziesięciu brały połowę.
- Każdą zmianę, która nie dała mierzalnego efektu, cofnij, zamiast zostawiać „bo nie szkodzi”.
Najbardziej pouczające w tym ćwiczeniu jest to, gdzie była oszczędność. Laptop sam w sobie był już dobrze ustawiony: dysk, Wi-Fi i łącza PCI Express zasypiały bez naszej pomocy. Waty uciekały w definicjach maszyn, które nikt nie oglądał, bo były generowane przez narzędzie. Mierzenie na poziomie wątków emulatora zajęło kwadrans i dało więcej niż wszystkie ustawienia hosta razem.
Chcesz wiedzieć, na co naprawdę idzie moc Twoich serwerów?
Mierzymy, zanim zmienimy: od liczników procesora po wątki emulatora. Pomagamy utrzymać infrastrukturę wirtualną, która nie płaci za urządzenia, których nikt nie używa.
Powiązany artykuł
Lab na osobnym hoście: od czystego Debiana 13 do jedenastu maszyn
Jak powstały maszyny, których zużycie energii tu mierzymy.
Zobacz też