Metoda rozwiązywania problemów
Po tej lekcji: kursant ma powtarzalną metodę na awarie, umie znaleźć przyczynę wolnego serwera, procesora na 100%, braku pamięci, pełnego dysku i pełnych i-węzłów, usługi, która nie wstaje, braku SSH i niedziałającej strony, a po wszystkim zapisuje zgłoszenie, zmianę i raport po awarii.
Wstęp
To cała treść zgłoszenia. Która strona? Od kiedy? Nie działa, czyli nie otwiera się, jest wolna, czy pokazuje błąd? Dla wszystkich, czy dla szefa na hotelowym Wi-Fi?
Tu są dwie drogi. Pierwsza: panika, restart wszystkiego, co się da, i modlitwa. Czasem nawet pomaga, tylko nikt potem nie wie dlaczego i za tydzień jest to samo. Druga: metoda. Ta lekcja jest o drugiej drodze. To lekcja setny, więc zbieramy w nim wszystko, co już umiesz, w jedną skrzynkę z narzędziami.
Metoda: jak lekarz na SOR-ze
Dobry lekarz nie zaczyna od operacji. Zaczyna od wywiadu, potem bada, stawia diagnozę, leczy i sprawdza, czy pomógł. Ty robisz to samo:
- Wywiad. Co dokładnie nie działa, od kiedy, kogo dotyczy i, najważniejsze pytanie w całej informatyce: co się ostatnio zmieniło? Aktualizacja, nowy wpis w konfiguracji, nowy kolega z uprawnieniami roota.
- Odtwórz objaw sam. „Nie działa” z ust użytkownika i błąd, który widzisz na własne oczy, to dwie różne rzeczy.
- Hipoteza od najprostszych. Najpierw sprawdzasz, czy kabel jest włożony, a dopiero potem błąd w jądrze.
- Jedna zmiana naraz. Zmienisz trzy rzeczy i zadziała, to nie wiesz, która pomogła, a dwie pozostałe mogą być nową bombą z opóźnionym zapłonem.
- Sprawdź, że naprawiłeś. Ten sam test, który pokazał awarię, teraz ma pokazać, że jej nie ma.
- Zapisz. Co, kiedy, czym znalazłeś, co zrobiłeś.
Na zasoby jest jeszcze ściąga od Brendana Gregga, inżyniera od wydajności, nazywa się metoda USE: dla każdego zasobu, czyli procesora, pamięci, dysku i sieci, sprawdź wykorzystanie (ile czasu był zajęty), nasycenie (ile pracy czeka w kolejce) i błędy. Cztery zasoby, trzy pytania, dwanaście kratek do odhaczenia.
Pierwsza minuta na serwerze
Zanim zaczniesz zgadywać, zrób obchód. Te komendy znasz z poprzednich modułów:
uptime
systemctl --failed
journalctl -p err -b --no-pager | tail -20
free -h
df -h
df -i
top
ss -tlnpOd jak dawna działa i jakie ma obciążenie, co się wywróciło, jakie błędy są w dzienniku od startu, pamięć, miejsce, i-węzły, kto je procesor, kto czego słucha. Minuta pracy, a w połowie przypadków wiesz już, co się stało.
Serwer jest wolny
Najpierw uptime:
02:49:25 up 12 min, 0 user, load average: 1.21, 0.68, 0.35Trzy liczby to średnie obciążenie z 1, 5 i 15 minut. Load average to kolejka do kas w Biedronce: ile osób stoi albo jest obsługiwanych. Ale sama liczba nic nie mówi, jeśli nie wiesz, ile jest kas. Liczbę kas daje nproc. Load 4 na serwerze z 16 rdzeniami to spacer. Load 4 na dwóch rdzeniach to kolejka do wyjścia ze sklepu. Trzy liczby czytasz jak trend: 1.21, 0.68, 0.35 to „coś się właśnie zaczęło”.
Potem vmstat, który w jednej linijce mówi, kto jest wąskim gardłem:
vmstat 1 3procs -----------memory---------- ---swap-- -----io---- -system-- -------cpu-------
r b swpd free buff cache si so bi bo in cs us sy id wa st gu
1 0 0 7061680 74940 749932 0 0 0 0 869 784 3 50 47 0 0 0r— procesy w kolejce do procesora. Stale więcej niż rdzeń: brakuje procesora.b— procesy zablokowane, zwykle czekające na dysk.si,so— swap. Jak nie są zerami, brakuje pamięci i system przekłada rzeczy do piwnicy.wa— procesor czeka na dysk. Kucharz z patelnią czeka na ziemniaki z piwnicy.st— hiperwizor zabiera Ci procesor na sąsiadów.
Jak winny jest dysk, iostat -x 1 z pakietu sysstat. Patrzysz na %util, czyli jak długo dysk był zajęty, i na r_await oraz w_await, czyli ile milisekund trwa odczyt i zapis. A historia z wczoraj? sar z lekcji o monitorowaniu.
CPU 100%
Symulujemy:
yes > /dev/null &
ps -eo pid,user,%cpu,%mem,etime,cmd --sort=-%cpu | head -3 PID USER %CPU %MEM ELAPSED CMD
14782 root 97.3 0.0 00:05 yesJest winowajca, PID, właściciel, od kiedy działa. W top to samo na żywo, sortowanie po procesorze to P. I teraz pytanie, które odróżnia admina od kogoś z kill -9 w ręku: czy to jest normalne? Kompresja kopii o 2:00 w nocy może jeść procesor, ma prawo. Proces PHP, który od trzech dni stoi na 100%, to raczej pętla bez wyjścia. A proces o dziwnej nazwie z /tmp, który je procesor, to kopacz kryptowalut i właśnie zmieniłeś zgłoszenie z „wydajność” na „bezpieczeństwo”.
Koniec zabawy: kill 14782. Najpierw grzeczna prośba o zamknięcie lokalu, SIGTERM. Wyłączenie prądu, SIGKILL, dopiero gdy grzecznie nie działa.
Brak pamięci
free -h
ps -eo pid,user,%mem,rss,cmd --sort=-%mem | head -5
journalctl -k | grep -i -E 'out of memory|oom'W free patrzysz na kolumnę available, nie free. Linux używa wolnej pamięci na podręczną pamięć plików i oddaje ją na żądanie, więc małe free to nie alarm.
Gdy pamięci naprawdę zabraknie, jądro wzywa OOM killera. To bramkarz w przepełnionym klubie: wybiera jednego gościa, zwykle tego, który zajmuje najwięcej miejsca, i wyprowadza go bez dyskusji. W dzienniku jądra zostaje wpis „Out of memory: Killed process” z numerem i nazwą ofiary. Klasyczny objaw: „baza danych sama się wyłączyła w nocy”. Sama się nie wyłączyła, wyprowadził ją bramkarz.
Co dalej: znaleźć żarłoka, sprawdzić, czy rośnie stale (wyciek pamięci, wtedy sar -r z historii pokaże równą rosnącą linię), ograniczyć mu pamięć w usłudze systemd (MemoryMax=), dodać RAM albo swap. Swap nie przyspiesza, ale daje czas, zanim bramkarz zacznie wyprowadzać.
Pełny dysk
df -h
sudo du -xh --max-depth=1 / | sort -h | taildu -x nie wchodzi na inne systemy plików, więc nie liczy /proc ani zamontowanych dysków. Schodzisz piętro niżej w największy folder i powtarzasz. Zwykle trafiasz na /var/log, /var/lib/docker albo czyjąś kopię bazy „na chwilę” sprzed dwóch lat.
A teraz zagadka, na której wyłożył się każdy admin. Zrobiłem mały dysk 96 MB i proces, który pisze do logu 80 MB. Kasuję log:
rm mnt/app.log
df -h mnt
du -sh mnt/dev/loop1 96M 81M 8.7M 91% /home/eryk/mnt
20K mntPlik skasowany, du mówi 20 KB, a df dalej 91%. Kto kłamie? Nikt. Proces dalej ma ten plik otwarty. rm zdjął tylko tabliczkę z drzwi, ale w środku ktoś dalej mieszka i płaci czynsz. Szukamy lokatorów bez tabliczki:
sudo lsof -nP +L1COMMAND PID USER FD TYPE DEVICE SIZE/OFF NLINK NODE NAME
sleep 15295 root 3w REG 7,1 83886080 0 12 /home/eryk/mnt/app.log (deleted)+L1 to „pliki z liczbą dowiązań mniejszą niż 1”, czyli skasowane, ale otwarte. NLINK 0, dopisek (deleted). Dwie naprawy. Elegancka: restart tej jednej usługi, która trzyma plik. Awaryjna, gdy restartu zrobić nie możesz: obcięcie pliku przez deskryptor procesu:
ls -l /proc/15703/fd/3
: > /proc/15703/fd/3
df -h mntl-wx------ 1 root root 64 Nov 4 02:49 /proc/15703/fd/3 -> /home/eryk/mnt/app.log (deleted)
/dev/loop1 96M 24K 89M 1% /home/eryk/mntNa przyszłość logów działających programów się nie kasuje, tylko obcina (: > plik) albo zostawia logrotate. Dziennik systemd przycinasz jego własnym narzędziem: sudo journalctl --vacuum-size=500M.
Pełne i-węzły
Drugi podstęp. Dysk ma miejsce, a system mówi „nie”:
touch: cannot touch 'mnt/nowy.txt': No space left on devicedf -h mnt
df -i mnt/dev/loop1 96M 44K 89M 1% /home/eryk/mnt
/dev/loop1 1008 1008 0 100% /home/eryk/mntMiejsca 89 MB, i-węzłów zero. I-węzeł to karta w katalogu bibliotecznym: każdy plik potrzebuje jednej karty, nawet pusty. Szuflada z kartami się skończyła, choć półki stoją puste. Kto zjadł karty?
sudo du --inodes -x -d1 /var | sort -n | tail1 ./lost+found
997 ./sesje
999 .Prawie zawsze winne są miliony malutkich plików: sesje PHP, kolejka poczty, pamięć podręczna aplikacji, której nikt nie sprząta. Usuwasz, a potem ustawiasz sprzątanie, na przykład find z -mtime w cronie.
Usługa nie wstaje
Trzy komendy, zawsze w tej kolejności:
systemctl status nginx
journalctl -u nginx -b --no-pager | tail -30
sudo nginx -tStatus powie, że nie działa i z jakim kodem wyjścia. Dziennik powie dlaczego. Test konfiguracji powie, w której linijce. Każdy poważny program ma taki test: nginx -t, apachectl configtest, sshd -t, named-checkconf, promtool check config. Literówka w SSH:
sudo sshd -t/etc/ssh/sshd_config: line 132: Bad configuration option: PermitRootLogn
/etc/ssh/sshd_config: terminating, 1 bad configuration optionsI w Apache:
AH00526: Syntax error on line 2 of /etc/apache2/sites-enabled/sklep.conf:
Invalid command 'Listne', perhaps misspelled or defined by a module not included in the server configurationPozostałe typowe powody, gdy konfiguracja jest dobra: port zajęty przez inny proces (ss -tlnp | grep :80), brak uprawnień do pliku albo folderu, pełny dysk, a na RHEL-u SELinux, który nie pozwala usłudze czytać pliku z nietypowego miejsca. Tam zaglądasz do sudo ausearch -m AVC -ts recent. Jeśli status mówi start-limit-hit, usługa padała tyle razy, że systemd przestał próbować. Po naprawie najpierw systemctl reset-failed nginx, potem start.
Brak SSH
SSH to most. Jak most się zawalił, sprawdzasz po kolei, od swojego brzegu:
- Czy serwer w ogóle żyje w sieci?
ping serwer(pamiętaj, że zapora może ping blokować). - Czy port odpowiada?
nc -zv serwer 22. „Connection refused” to dobra wiadomość w złym opakowaniu: serwer żyje i odpowiada, tylko na tym porcie nikt nie słucha, więc sshd nie działa. Jeśli za toncczeka i kończy się limitem czasu, odpowiedź zjadła zapora albo sieć po drodze. Odmówienie przez okienko i brak okienka to dwie różne diagnozy. - Jeśli port odpowiada, a logowanie nie działa:
ssh -v eryk@serwerpokaże, na którym etapie rozmowa się sypie. Klucz odrzucony? Złe uprawnienia~/.sshpo stronie serwera. - Dalej już tylko z konsoli: konsola w panelu chmury,
virsh consolena KVM, monitor i klawiatura w serwerowni. Tam:systemctl status sshd[RHEL] lubssh[Ubuntu],sshd -t,ss -tlnp | grep :22, zapora (firewall-cmd --list-allalboufw status) i dziennik logowań:/var/log/secure[RHEL] albo/var/log/auth.log[Ubuntu].
I dlatego od modułu o SSH powtarzam: zmieniasz sshd_config albo zaporę, to druga sesja zostaje otwarta, a sshd -t przed restartem.
Strona nie działa
Wracamy do szefa z trzema wykrzyknikami. Idziesz warstwami, od dna:
getent hosts sklep.example.com
nc -zv sklep.example.com 443
curl -sI https://sklep.example.com
sudo ss -tlnp | grep -E ':80|:443'
sudo tail -20 /var/log/nginx/error.logNazwa, port, odpowiedź, czy ktoś słucha, co mówi dziennik. Najwięcej mówi kod odpowiedzi w pierwszej linijce curl -I. Postawiłem małą stronę testową:
HTTP/1.0 200 OK
HTTP/1.0 404 File not found
curl: (7) Failed to connect to 127.0.0.1 port 8099 after 0 ms: Couldn't connect to server| Objaw | Co znaczy | Gdzie szukać |
|---|---|---|
nie ma odpowiedzi, Couldn't connect | nikt nie słucha albo zapora | ss -tlnp, systemctl status, zapora |
404 | serwer działa, pliku nie ma | DocumentRoot, ścieżki, wdrożenie |
403 | jest, ale nie wolno | uprawnienia plików, SELinux httpd_sys_content_t |
500 | aplikacja się wywróciła | log błędów serwera i aplikacji |
502, 504 | reverse proxy nie dogadał się z backendem | czy backend działa, proxy_pass, PHP-FPM |
| certyfikat wygasł | przeglądarka krzyczy, curl też | openssl s_client, certbot |
A jeśli u Ciebie działa, a u szefa nie? Wtedy problem jest po drodze: DNS, który jeszcze pamięta stary adres, Wi-Fi w hotelu albo jego przeglądarka. Też diagnoza.
Zgłoszenie, zmiana, dokumentacja, raport po awarii
Awaria naprawiona. Teraz część, którą wszyscy pomijają, a która odróżnia admina od strażaka.
Zgłoszenie (ticket). Każda awaria ma numer w systemie zgłoszeń, czy to Jira, czy GLPI, czy cokolwiek używa firma. W zgłoszeniu zapisujesz na bieżąco: kto zgłosił, objaw, co sprawdziłeś i co z tego wyszło. Wpis „sprawdziłem, nie działa” jest bezużyteczny. Wpis „nc -zv web01 443: Connection refused, nginx nie działa” jest złoty.
Zmiana (change). Każda zmiana na produkcji, także naprawa, ma pięć rzeczy: co zmieniasz, dlaczego, jak sprawdzisz, że działa, jak to cofniesz i kiedy. Plan wycofania to nie biurokracja. To spadochron, który pakujesz na ziemi, a nie w powietrzu. Do tego kopia pliku przed edycją (cp -a plik plik.bak-$(date +%F)) albo, jeszcze lepiej, konfiguracja w Gicie, o którym za chwilę w następnym module.
Dokumentacja. Jak coś naprawiałeś drugi raz, zapisz instrukcję. Trzeci raz robi ją kolega o trzeciej w nocy, z Twoją kartką w ręku zamiast Twojego numeru telefonu.
Raport po awarii (postmortem). Po większej awarii piszesz krótki dokument:
AWARIA: sklep niedostępny, 2026-11-04 08:12-09:05 (53 min)
WPŁYW: klienci nie mogli składać zamówień
OŚ CZASU:
08:12 monitoring: strona nie odpowiada
08:20 zgłoszenie od klienta
08:31 diagnoza: dysk / pełny w 100%, nginx nie może pisać logów
08:44 przyczyna: usunięty log aplikacji dalej otwarty (lsof +L1)
09:05 restart aplikacji, miejsce odzyskane, strona działa
PRZYCZYNA: logi aplikacji kasowane ręcznie zamiast rotacji
CO ZADZIAŁAŁO: alarm z monitoringu przyszedł przed klientem
CO NIE: alarm dysku na 90% wyłączony od miesiąca
DZIAŁANIA:
[ ] logrotate dla aplikacji - Eryk, do 2026-11-08
[ ] włączyć alarm dysku na 80% i 90% - Eryk, do 2026-11-06Najważniejsza zasada raportu: szukamy przyczyny, nie winnego. Jeśli za każdą pomyłkę ktoś dostaje po głowie, ludzie zaczynają ukrywać pomyłki, a ukryta pomyłka wraca jako większa awaria. Pytanie „dlaczego system pozwolił to zrobić?” jest lepsze niż „kto to zrobił?”. Odpowiadasz „dlaczego” kilka razy z rzędu, aż dojdziesz do czegoś, co da się naprawić na stałe.
Jak to działa pod spodem
Load average w Linuksie liczy procesy w stanie R, czyli gotowe do pracy lub pracujące, oraz w stanie D, czyli w nieprzerywalnym oczekiwaniu, zwykle na dysk albo zasoby sieciowe jak NFS. Dlatego wysoki load przy pustym procesorze to często dysk albo zawieszony udział sieciowy, a nie procesor. Kolejka w Biedronce rośnie też wtedy, gdy kasjerka czeka na kierownika z kluczykiem.
Plik w Linuksie to i-węzeł z danymi i jedna lub więcej nazw, czyli dowiązań. rm usuwa nazwę i zmniejsza licznik dowiązań. Jądro zwalnia i-węzeł i bloki dopiero wtedy, gdy licznik dowiązań spadnie do zera i żaden proces nie ma pliku otwartego. Dlatego df, który pyta system plików o wolne bloki, widzi zajęte miejsce, a du, który chodzi po nazwach w folderach, go nie widzi. Otwarte pliki procesu jądro pokazuje w /proc/PID/fd/ i to tamtędy lsof je znajduje.
OOM killer wybiera ofiarę według punktacji, którą widzisz w /proc/PID/oom_score. Najwięcej punktów ma ten, kto zajmuje najwięcej pamięci. Usługa może się osłonić, ustawiając OOMScoreAdjust= w pliku jednostki systemd, ale nie rób z tego nawyku. Ktoś musi wyjść z klubu, a jak wszyscy mają karty VIP, bramkarz wyprowadzi ochroniarza.
Ściąga flag
| Komenda | Flaga | Co robi |
|---|---|---|
uptime | czas pracy i load average 1/5/15 min | |
vmstat | 1 5 | procesor, pamięć, swap, dysk co sekundę |
iostat | -x 1 | obciążenie dysków, %util, r_await, w_await |
ps | -eo ... --sort=-%cpu / --sort=-%mem | najwięksi zjadacze procesora i pamięci |
systemctl | --failed | usługi, które padły |
journalctl | -p err -b | błędy od startu |
journalctl | -u USŁUGA -b | dziennik jednej usługi |
journalctl | -k | dziennik jądra (OOM, dyski) |
journalctl | --vacuum-size=500M | przycięcie dziennika |
df | -h / -i | miejsce / i-węzły |
du | -xh --max-depth=1 | kto zajmuje miejsce, bez innych dysków |
du | --inodes -x -d1 | kto zajmuje i-węzły |
lsof | -nP +L1 | skasowane, ale otwarte pliki |
nc | -zv HOST PORT | czy port odpowiada |
curl | -sI / -v | kod odpowiedzi / cała rozmowa |
ss | -tlnp | kto słucha na jakim porcie |
sshd, nginx | -t | test konfiguracji |
⚠ Typowy błąd
Literówka w konfiguracji Apache, a admin od razu:
sudo systemctl restart apache2Job for apache2.service failed because the control process exited with error code.
See "systemctl status apache2.service" and "journalctl -xeu apache2.service" for details.Przed chwilą strona działała na starej konfiguracji. restart zatrzymał działający serwer i nie umiał uruchomić nowego. Awaria zrobiona własnoręcznie, w trzy sekundy. Systemd uprzejmie podpowiada, gdzie szukać, i warto posłuchać: status i journalctl -xeu pokażą ten sam AH00526 z numerem linijki.
Zasada: najpierw test, potem reload. sudo apachectl configtest && sudo systemctl reload apache2. && sprawi, że przeładowanie wykona się tylko wtedy, gdy test przejdzie, a reload w Apache i Nginx wczytuje nową konfigurację bez wyłączania tego, co działa. Drugi wariant tego błędu to restart jako pierwsza reakcja na każdą awarię. Czasem pomaga, ale zawsze kasuje dowody: stan procesów, otwarte pliki, zapełnioną pamięć. Zanim zrestartujesz, zrób obchód z pierwszej minuty i zapisz, co widzisz.
✎ Zadanie
Na maszynie wirtualnej zepsuj i napraw trzy rzeczy: 1) zapełnij dysk plikiem, który usuniesz, ale proces będzie go dalej trzymał otwarty; 2) zrób literówkę w konfiguracji Apache i zrestartuj usługę; 3) zamknij port 80 w zaporze. Dla każdej awarii zanotuj, jakimi komendami ją znalazłeś.
Pokaż rozwiązanie
Trzy awarie, trzy śledztwa. Najpierw dysk zapchany plikiem, który „nie istnieje”.
dd if=/dev/zero of=wielki.log bs=1M count=200
(exec 3<wielki.log; sleep 600) & # proces trzyma plik otwarty
rm wielki.log
df -h / # miejsce dalej zajęte
sudo lsof +L1 # otwarte pliki bez nazwy na dyskusleep 25945 eryk 3r REG 254,0 209715200 0 952579 /home/eryk/wielki.log (deleted)+L1 znaczy: pokaż pliki, które mają mniej niż jeden link, czyli zero nazw. Plik usunięty z katalogu, ale proces dalej trzyma go za rękę, więc jądro nie zwalnia miejsca. du go nie widzi, df go liczy. Naprawa: zrestartować proces albo, gdy nie wolno, opróżnić plik przez jego uchwyt:
sudo ls -l /proc/25945/fd | grep deleted
sudo truncate -s 0 /proc/25945/fd/3 # albo: : > /proc/25945/fd/3U mnie po tym lsof pokazał rozmiar 0. Druga awaria, literówka w Apache:
sudo systemctl restart apache2 # [RHEL] httpd
sudo systemctl status apache2
sudo journalctl -xeu apache2
sudo apachectl configtestAH00526: Syntax error on line 12 of /etc/apache2/sites-enabled/000-default.conf:
Invalid command 'DokumentRoot', perhaps misspelled or defined by a module not included in the server configurationApache podaje plik i numer linijki. Mało który program jest tak uprzejmy. Trzecia: zamknięty port 80. Z drugiej maszyny curl http://serwer/ wisi i kończy się po czasie. Na serwerze:
sudo ss -ltnp | grep ':80 ' # czy Apache w ogóle słucha?
curl -I http://localhost/ # lokalnie działa?
sudo ufw status numbered # [Ubuntu]
sudo firewall-cmd --list-all # [RHEL]Jeśli lokalnie działa, a z zewnątrz nie, to winny jest ochroniarz z listą okienek, czyli zapora. Notatki z tych trzech śledztw to Twój pierwszy raport po awarii. A dziś dowiesz się, gdzie takie notatki i skrypty trzymać, żeby nie zginęły.
Co dalej
W raporcie po awarii padło słowo, które zmienia życie admina: Git. W następnej lekcji zaczynamy nowy moduł: Git dla admina, czyli historia każdej zmiany w konfiguracji i skryptach, commit, diff, gałęzie, klucz SSH do GitLaba i GitHuba, a do tego etckeeper, który pilnuje /etc za Ciebie.