Przejdź do treści
Lekcja 25 z 117

Kurs › Moduł 2 › Lekcja 25

Priorytety, limity i profile tuned

⏱ ok. 13 min czytania· moduł 2: Procesy i jak działa system

Po tej lekcji: kursant umie dać procesowi niższy lub wyższy priorytet na procesorze i dysku, ustawić limity zasobów dla użytkownika i przełączyć profil wydajności serwera.

Wstęp

Codziennie o czternastej na serwerze rusza kopia zapasowa. I codziennie o 14:01 dzwoni księgowość, że „system muli”. Kopia musi się zrobić. Księgowość musi pracować. Kupić drugi serwer? Przesunąć kopię na noc, kiedy i tak ktoś coś odpala?

A można inaczej: powiedzieć kopii „ty jesteś ważna, ale mniej ważna niż ludzie”. Jedną komendą. Dziś nauczymy się ustawiać kolejkę do procesora i do dysku, ograniczać, ile czego może zjeść jeden użytkownik, i przełączać serwer w tryb „sport” albo „eko”.

nice: kto pierwszy do kuchenki

Pamiętasz, że proces to kucharz realizujący zamówienie? Teraz wyobraź sobie kuchnię z jedną kuchenką i dziesięcioma kucharzami. Ktoś musi decydować, kto kiedy gotuje. Robi to jądro, a konkretnie jego planista. I każdy kucharz ma przypiętą plakietkę z liczbą: jak bardzo jest miły, czyli jak chętnie przepuszcza innych.

Ta liczba to nice, po polsku po prostu „uprzejmość”. Zakres od -20 do 19.

  • 19 — najmilszy kucharz świata. „Ależ proszę, pan pierwszy”. Gotuje, kiedy nikt inny nie chce.
  • 0 — normalny człowiek. Tyle dostaje każdy proces na start.
  • -20 — kucharz, który wpycha się bez kolejki, bo „ja tylko na chwilę”.

Czyli im wyższa liczba, tym niższy priorytet. Tak, to jest odwrotnie, niż podpowiada intuicja. Linux nie pyta, jak ważny jesteś. Pyta, jak bardzo jesteś skłonny ustąpić.

nice -n 10 ./kopia.sh

Uruchamia skrypt z uprzejmością 10. Samo nice ./kopia.sh, bez -n, też daje 10, bo to wartość domyślna komendy.

Zobaczmy, czy to w ogóle coś daje. Dwie identyczne pętle, które mielą procesor bez sensu, przypięte do tego samego rdzenia. Jedna normalna, druga z nice 19:

taskset -c 0 bash -c 'while :; do :; done' &
taskset -c 0 nice -n 19 bash -c 'while :; do :; done' &
sleep 5
ps -o pid,ni,%cpu,time,cmd -p 7234,7235
  PID  NI %CPU     TIME CMD
 7234   0 93.2 00:00:04 bash -c while :; do :; done
 7235  19  1.3 00:00:00 bash -c while :; do :; done

Dziewięćdziesiąt trzy procent kontra jeden. Ten sam program, ta sama kuchenka. Różni je tylko plakietka. taskset -c 0 przypina proces do rdzenia numer zero. Bez tego każda pętla dostałaby swój rdzeń i obie miałyby po sto procent, a ja nie miałbym czym się chwalić. Pamiętaj potem o kill na obu, bo inaczej wentylator Ci podziękuje.

W top widzisz to w dwóch kolumnach: NI to uprzejmość, a PR to priorytet w skali jądra. Dla zwykłych procesów PR to 20 plus NI, więc nice 19 daje PR 39.

renice: zmiana plakietki w trakcie

Kopia już się mieli, a nie odpaliłeś jej z nice? Nie trzeba jej zabijać. Zmieniasz plakietkę kucharzowi w trakcie gotowania:

renice -n 15 -p 704
704 (process ID) old priority 10, new priority 15

-p to PID. Zamiast tego możesz podać -u jan, czyli wszystkie procesy użytkownika jan, albo -g dla grupy procesów.

I teraz ważna zasada. Zwykły użytkownik może swojego kucharza tylko uspokoić, czyli podnieść liczbę. Obniżyć już nie:

renice -n 5 -p 9081
renice: failed to set priority for 9081 (process ID): Permission denied

To samo z nice -n -5: „cannot set niceness: Permission denied”. Wpychanie się w kolejkę wymaga roota, czyli sudo. Logiczne. Gdyby każdy mógł dać sobie -20, wszyscy by mieli -20 i wrócilibyśmy do punktu wyjścia, tylko z większą ilością krzyku.

ionice: kolejka do magazynu

Procesor to nie wszystko. Kopia zapasowa najbardziej męczy dysk. Wyobraź sobie, że kuchnia ma jeden magazyn i jedne drzwi. Nawet najmilszy kucharz, który wynosi z magazynu sto worków ziemniaków, blokuje drzwi wszystkim innym.

Do tego jest ionice, priorytet dostępu do dysku. Ma trzy klasy:

  • klasa 1, realtime — wchodzi do magazynu pierwszy, zawsze. Tylko root. Używaj ostrożnie, bo może zagłodzić resztę.
  • klasa 2, best-effort — normalna kolejka. Priorytety od 0 do 7, gdzie 0 to najważniejszy. Domyślnie 4.
  • klasa 3, idle — wchodzi tylko wtedy, kiedy przez chwilę nikt inny nie chciał. Idealne dla kopii zapasowych.
ionice -c 3 nice -n 19 tar czf /backup/dom.tar.gz /home

To jest klasyka: kopia, która ustępuje i na procesorze, i na dysku. Sprawdzenie i zmiana dla działającego procesu:

ionice -p 1150
ionice -c 3 -p 1150
ionice -p 1150
none: prio 0
idle

„none” oznacza, że proces nigdy nie prosił o konkretną klasę, więc jądro wylicza ją z jego nice.

ulimit: regulamin dla jednej osoby

Priorytet mówi, kto pierwszy. A limit mówi, ile maksymalnie. Jak na siłowni: karnet pozwala na dwie godziny dziennie i jedną szafkę. Nie ważne, jak bardzo chcesz trzecią szafkę.

W Linuksie każdy proces ma taki regulamin i dziedziczy go po rodzicu. Pokazuje go ulimit -a:

ulimit -a
core file size              (blocks, -c) 0
file size                   (blocks, -f) unlimited
max locked memory           (kbytes, -l) 8192
open files                          (-n) 1024
max user processes                  (-u) 32022
stack size                  (kbytes, -s) 8192
...

Najczęściej w pracy admina spotkasz dwa: -n, czyli ile plików naraz może mieć otwartych proces, i -u, ile procesów może mieć użytkownik. Liczby u Ciebie będą inne, bo zależą od systemu i ilości pamięci.

Każdy limit ma dwie wartości:

  • soft, miękki — obowiązuje teraz. Sam możesz go podnieść, ale tylko do twardego.
  • hard, twardy — sufit. Zwykły użytkownik może go tylko obniżyć, nigdy podnieść.
ulimit -Sn
ulimit -Hn
1024
524288

Co się dzieje, kiedy proces dobije do limitu? Zróbmy to specjalnie. Ustawiam limit na cztery otwarte pliki. Trzy zajmują już klawiatura, ekran i głośnik błędów, czyli deskryptory 0, 1 i 2:

bash -c 'ulimit -n 4; exec 3</etc/hostname; echo "trzeci otwarty"; exec 4</etc/hosts'
trzeci otwarty
bash: line 1: /etc/hosts: Too many open files

„Too many open files”. Zapamiętaj ten komunikat. Zobaczysz go w logach serwera WWW albo bazy danych, kiedy przyjdzie więcej klientów niż zwykle, i wtedy już będziesz wiedział, że to nie dysk, tylko regulamin.

Dla procesu, który już działa, ulimit nic nie pomoże, bo działa tylko na bieżący Bash i jego dzieci. Do cudzego procesu jest prlimit:

prlimit --pid 1234 --nofile
RESOURCE DESCRIPTION              SOFT HARD UNITS
NOFILE   max number of open files  512  512 files

/etc/security/limits.conf: regulamin na stałe

ulimit żyje tyle, co sesja. Na stałe limity dla użytkowników wpisujesz do /etc/security/limits.conf albo, lepiej, do osobnego pliku w /etc/security/limits.d/. Format: kto, typ, co, ile.

sudo nano /etc/security/limits.d/90-kurs.conf
kursant25  soft  nofile  4096
kursant25  hard  nofile  8192
@kursanci  hard  nproc   200

Nazwa użytkownika albo @grupa, albo gwiazdka dla wszystkich. Typ soft, hard albo myślnik dla obu naraz. Sprawdzamy, logując się jako ten użytkownik:

su - kursant25 -c 'ulimit -Sn; ulimit -Hn'
4096
8192

Ten plik czyta moduł PAM o nazwie pam_limits, a PAM wkracza tylko przy logowaniu: login, su, sudo, SSH. Dwie konsekwencje. Po pierwsze, zmiana działa od następnego logowania, nie od razu. Po drugie, usługi uruchamiane przez systemd w ogóle się nie logują, więc limits.conf ich nie dotyczy. Dla usługi ustawiasz LimitNOFILE= w jej pliku jednostki. Do tego wrócimy w lekcji o systemd. I jeszcze jedno: limity dla grup i gwiazdki nie obejmują roota. Dla roota trzeba go wpisać z nazwy.

cgroups w skrócie: budżet działu

ulimit to regulamin dla jednej osoby. Ale co, jeśli dział marketingu ma razem dostać najwyżej dwadzieścia procent kuchenki, niezależnie od tego, ilu kucharzy zatrudni? Każdy kucharz z osobna jest grzeczny, a razem zjadają wszystko.

Do tego jądro ma cgroups, czyli grupy kontrolne. Procesy trafiają do grup, a grupa ma wspólny budżet: procesor, pamięć, liczbę procesów, dysk. Na systemach z systemd każda usługa siedzi w swojej grupie, a grupy są poukładane w drzewo: osobno usługi systemowe, osobno sesje użytkowników.

systemd-cgls
systemd-cgtop

Pierwsze pokazuje drzewo, drugie to top, tylko dla grup zamiast procesów. A limit ustawiasz tak:

sudo systemctl set-property httpd.service CPUQuota=20% MemoryMax=1G

CPUQuota=20% to najwyżej jedna piąta jednego rdzenia. MemoryMax=1G to twardy sufit pamięci: kiedy grupa go przekroczy i nie da się nic odzyskać, zabójca pamięci wkracza tylko w tej grupie, a nie w całym serwerze. Zmiana działa od razu i zostaje na stałe, chyba że dodasz --runtime. Jednorazowe polecenie w takiej klatce odpalisz przez systemd-run:

sudo systemd-run --scope -p MemoryMax=500M -p CPUQuota=50% ./kopia.sh

Więcej o grupach przy kontenerach, bo kontener to w dużej mierze właśnie cgroups plus parę innych sztuczek jądra.

tuned-adm: tryb eko i tryb sport

Wiele samochodów ma przełącznik: eko, normal, sport. Ten sam silnik, inne ustawienia skrzyni, gazu, zawieszenia. Serwery też tak mają, a przełącznik nazywa się TuneD. To usługa, która jednym poleceniem zmienia kilkadziesiąt ustawień jądra, dysków i procesora pod konkretne zastosowanie. Na RHEL to ważna rzecz, jest w wymaganiach egzaminu RHCSA.

[RHEL] Zwykle jest już na pokładzie. Jeśli nie:

sudo dnf install tuned
sudo systemctl enable --now tuned

[Ubuntu] Pakiet jest w repozytorium universe: sudo apt install tuned. Na Ubuntu to raczej ciekawostka, ale działa tak samo.

Cztery komendy, które trzeba znać:

tuned-adm list
tuned-adm active
tuned-adm recommend
sudo tuned-adm profile virtual-guest
Available profiles:
- balanced                    - General non-specialized tuned profile
- latency-performance         - Optimize for deterministic performance at the cost of increased power consumption
- powersave                   - Optimize for low power consumption
- throughput-performance      - Broadly applicable tuning that provides excellent performance across a variety of common server workloads
- virtual-guest               - Optimize for running inside a virtual guest
...

list pokazuje dostępne profile. active mówi, który jest włączony. recommend podpowiada, co TuneD uważa za najlepsze dla tej maszyny. Na maszynie wirtualnej zwykle odpowie virtual-guest. profile przełącza. Do tego tuned-adm verify sprawdza, czy system faktycznie ma ustawienia z profilu, a tuned-adm off wyłącza całe strojenie.

Profile mieszkają w /usr/lib/tuned/, każdy w swoim folderze z plikiem tuned.conf. Własne kładziesz do /etc/tuned/, i jeśli nazwa jest taka sama, wygrywa Twoja wersja. Zajrzyjmy do środka:

cat /usr/lib/tuned/virtual-guest/tuned.conf
[main]
summary=Optimize for running inside a virtual guest
include=throughput-performance

[sysctl]
vm.dirty_ratio = 30
vm.swappiness = 30

Żadnej magii. Profil dziedziczy po throughput-performance i dokłada dwie wartości jądra. To te same ustawienia, które za kilka lekcji będziemy zmieniać ręcznie przez sysctl. TuneD robi to za Ciebie i pilnuje, żeby się trzymało.

Jak to działa pod spodem

Wszystko, co dziś robiliśmy, to tylko wpisy w strukturze procesu w jądrze. nice i renice wołają funkcję systemową setpriority, a planista przy każdym wyborze „kto teraz gotuje” bierze pod uwagę wagę wynikającą z nice. ionice woła ioprio_set, a decyzję podejmuje planista I/O dysku. ulimit i prlimit to setrlimit i prlimit: jądro zapisuje dwie liczby, soft i hard, a potem przy każdym otwarciu pliku czy tworzeniu procesu po prostu porównuje. Przekroczysz, dostajesz błąd, jak EMFILE, czyli nasze „Too many open files”.

Wszystko to się dziedziczy. Dziecko dostaje kopię nice i limitów rodzica. Dlatego nice ./kopia.sh obejmuje też każdy tar i gzip, które skrypt uruchomi.

cgroups działają inaczej: to nie cecha procesu, tylko katalog w specjalnym systemie plików /sys/fs/cgroup. Proces jest w grupie, bo jego PID jest wpisany do pliku cgroup.procs w tym katalogu. Limity to też pliki, na przykład memory.max. A systemctl set-property po prostu je za Ciebie zapisuje. Wszystko jest plikiem, nawet budżet działu marketingu.

Ściąga flag

KomendaFlagaCo robi
nice-n Nuruchamia z uprzejmością N (bez -n: 10)
renice-n N -p PIDzmienia nice działającego procesu
renice-u userzmienia nice wszystkich procesów użytkownika
ionice-c 1 / 2 / 3klasa: realtime / best-effort / idle
ionice-n 0–7priorytet w klasie, 0 najważniejszy
ionice-p PIDpokazuje lub zmienia klasę procesu
ulimit-awszystkie limity bieżącej powłoki
ulimit-n / -uotwarte pliki / liczba procesów
ulimit-S / -Hlimit miękki / twardy
prlimit--pid PID --nofilelimity cudzego procesu
systemctlset-property UNIT X=Ylimit cgroups dla usługi
systemd-run--scope -p X=Ypolecenie w klatce z limitem
tuned-admlist / active / recommendprofile / aktywny / polecany
tuned-admprofile NAZWAprzełącza profil
tuned-admverify / offsprawdza / wyłącza strojenie

⚠ Typowy błąd

Admin chce na chwilę zmniejszyć limit plików, żeby coś przetestować:

ulimit -n 512
ulimit -n 20000
bash: ulimit: open files: cannot modify limit: Operation not permitted

I to nawet jako root w tej samej sesji. Dlaczego? Bo ulimit -n 512 bez -S ani -H ustawia oba limity naraz, miękki i twardy. Obniżyłeś sobie sufit. A sufitu w górę zwykły proces nie podniesie. To jak oddanie kluczy od siłowni przez okienko i pytanie, czy można je dostać z powrotem. Nie można, trzeba nowy karnet, czyli nowa sesja.

Jak robić dobrze: ulimit -Sn 512. Tylko miękki. Wtedy wracasz do starej wartości kolejnym ulimit -Sn.

Drugi klasyk: „wpisałem limit do limits.conf, a baza danych dalej ma 1024”. Baza działa jako usługa systemd, więc PAM jej nie dotyka. Limit trzeba wpisać do jednostki usługi. Jak, zobaczymy w lekcji dwudziestym ósmym.

✎ Zadanie

  1. Jako zwykły użytkownik uruchom w tle nice -n 15 yes > /dev/null &. Sprawdź w ps jego wartość NI.
  2. Spróbuj zmienić mu nice na 5, potem na 19. Zanotuj, co się stało i dlaczego.
  3. Sprawdź swój miękki i twardy limit otwartych plików.
  4. Na maszynie z RHEL (albo Ubuntu z zainstalowanym tuned) sprawdź aktywny profil TuneD i ten, który system poleca.
  5. Nie zapomnij zabić yes. Serio.
Pokaż rozwiązanie
nice -n 15 yes > /dev/null &
ps -o pid,ni,comm -p $!
renice -n 5 -p 9081
renice -n 19 -p 9081
  PID  NI COMMAND
 9081  15 yes
renice: failed to set priority for 9081 (process ID): Permission denied
9081 (process ID) old priority 15, new priority 19

Z 15 na 5 nie wolno, bo to wpychanie się w kolejkę, a do tego trzeba roota. Z 15 na 19 wolno, bo uprzejmości nikt nikomu nie zabrania. Limity plików:

ulimit -Sn; ulimit -Hn
1024
524288

Twoje liczby mogą być inne, ważne, że miękki jest mniejszy albo równy twardemu. A na RHEL tuned-adm active na maszynie wirtualnej odpowiada zwykle Current active profile: virtual-guest, a tuned-adm recommend to samo. Jeśli dostałeś „Cannot talk to TuneD daemon via DBus”, to usługa nie działa: sudo systemctl enable --now tuned. I mam nadzieję, że yes już nie żyje. kill %1 i spokojny sen.

Co dalej

Umiemy już powiedzieć procesom, kto ma pierwszeństwo. Ale skąd wiedzieć, że serwerowi czegoś brakuje? W następnej lekcji zaglądamy pod maskę: pamięć, swap, obciążenie, dysk pod presją i zabójca, który przychodzi, kiedy pamięci zabraknie.