Konfiguracja prostego monitoringu serwera narzędziami systemowymi
Skrypt kontrolny, timer systemd i przetestowany kanał powiadomień wystarczą dla pojedynczego serwera root. Ten poradnik pokazuje, co monitorować, jak sprawdzić każdy krok i od kiedy opłaca się duża skrzynka narzędziowa.
Serwer nie zgłasza się sam, kiedy coś idzie nie tak. Działa dalej, dopóki nie przestanie działać, a pierwszą informację zwrotną dostajesz od klienta albo od siebie samego, jeśli akurat spojrzysz na maszynę. Ten poradnik buduje najmniejszy monitoring, który kładzie temu kres: skrypt kontrolny, timer systemd i jedną drogę powiadomień. Bez bazy szeregów czasowych, bez dashboardu, bez dodatkowego otwartego portu.
Systemami odniesienia są Debian 13 (trixie), Debian 12 (bookworm), Ubuntu 24.04 LTS oraz Ubuntu 22.04 LTS. Tam, gdzie te cztery systemy się różnią, jest to wyraźnie zaznaczone. Wszystkie polecenia napisano z myślą o pracy jako root; jako zwykły użytkownik poprzedzasz każde polecenie słowem sudo. Ten poradnik rozwija krok 8 z listy kontrolnej dla nowego serwera root.
Co w ogóle warto monitorować
Najczęstszym błędem przy pierwszym monitoringu nie jest zbyt mała liczba mierzonych wartości, tylko zbyt duża. Kto zbiera 40 wskaźników, nie ogląda żadnego z nich. Sens ma wyłącznie to, co potrafi zatrzymać pracę serwera i na co jesteś w stanie zareagować. Zostaje siedem punktów.
| Wskaźnik | Dlaczego trafia na listę | Skąd bierze się wartość | Sensowny próg |
|---|---|---|---|
| Miejsce na dysku | Najczęstsza przyczyna awarii, której nikt nie widzi nadchodzącej | df --output=pcent,target | od 85 procent |
| Inody | Dysk pozornie wolny, a mimo to „No space left on device” | df --output=ipcent,target | od 85 procent |
| Wolna pamięć RAM | OOM killer rzadko trafia akurat w ten proces, który sam byś poświęcił | MemAvailable w /proc/meminfo | poniżej 200 MB |
| Obciążenie systemu | Pokazuje zator niezależnie od tego, czy winna jest CPU, czy dysk | trzecie pole w /proc/loadavg | średnia z 15 minut powyżej dwukrotności liczby rdzeni |
| Usługi w stanie failed | Usługa, która padnie w nocy, inaczej pozostaje martwa aż do rana | systemctl is-system-running | wszystko poza running |
| Wygasanie certyfikatu | Z dnia na dzień odcina każdego odwiedzającego, a nie tylko część | openssl x509 -checkend | poniżej 21 dni pozostałej ważności |
| Dostępność z zewnątrz | Jako jedyna kontrola odpowiada na pytanie, czy serwer jeszcze istnieje | drugi host, curl | dwie nieudane próby z rzędu |
Na liście nie ma obciążenia CPU w procentach: serwer, który ciągnie 100 procent, bo pracuje na nim koder wideo, robi dokładnie to, do czego go postawiono. Z tego samego powodu brakuje przepustowości sieci oraz liczby procesów. Jedno i drugie pomaga w szukaniu przyczyny, ale nie nadaje się na alarm, bo nie istnieje wartość, od której koniecznie musiałbyś zadziałać.
Droga odwrotu, zanim powstanie pierwszy plik
Monitoring to operacja czysto odczytowa i w normalnych warunkach nie jest w stanie niczego zepsuć. Trzy rzeczy mimo wszystko potrafią.
Skrypt, który naprawia, zamiast meldować. Pomysł jest kuszący: skoro nginx padł, niech skrypt po prostu uruchomi go ponownie. Wychodzi z tego usługa, która startuje co dziesięć minut, działa pół sekundy i przykrywa prawdziwą przyczynę. A skrypt, który przy pełnym dysku samodzielnie kasuje pliki, kiedyś skasuje coś, co było potrzebne. Pierwsza wersja wyłącznie czyta i nie wywołuje ani systemctl restart, ani rm, ani kill.
Otwarte interfejsy sieciowe. Eksportery metryk nasłuchujące na wszystkich adresach to jedna z najczęstszych niezamierzonych publikacji danych na pojedynczych serwerach. Opisana tu metoda nie otwiera żadnego portu i nie wymaga reguły firewalla.
Sama droga alarmu. Monitoring, którego powiadomienia nigdy nie przetestowano, nie jest monitoringiem, tylko dobrym samopoczuciem. Test znajdziesz niżej i nie jest on opcjonalny.
Jak dostać się na serwer bez SSH
Serwery root z KVM oraz serwery dedykowane nie mają ani IPMI, ani iDRAC. Drogą dostępu, kiedy SSH przestaje odpowiadać, jest konsola VNC w panelu klienta. Nie wisi ona na stosie sieciowym systemu gościa, więc nie zablokuje jej ani reguła firewalla, ani przeciążona usługa SSH. Zaloguj się tam raz zawczasu i upewnij się, że znasz hasło roota.
Wyłącznik
Jeśli sam monitoring stanie się problemem, na przykład zacznie wysyłać alarmy co minutę, potrzebujesz dwóch poleceń. Zapamiętaj je, zanim zaczniesz:
systemctl disable --now kh-monitor.timer
systemctl mask kh-monitor.service
Pierwsze natychmiast zatrzymuje timer i sprawia, że nie wróci przy kolejnym starcie. Drugie to hamulec bezpieczeństwa: zamaskowanej usługi nie da się już przypadkiem uruchomić ręcznie, a cofasz to poleceniem systemctl unmask kh-monitor.service. Metoda jest mało ryzykowna przede wszystkim dlatego, że powstają wyłącznie nowe pliki; wycofanie zmian sprowadza się do skasowania tych plików. Mimo wszystko trzymaj otwartą drugą sesję SSH, dopóki pracujesz na systemie.
Najpierw sprawdź wskaźniki ręcznie
Zanim skrypt zacznie cokolwiek oceniać, każdą wartość powinieneś raz zobaczyć na własne oczy. Inaczej później nie rozstrzygniesz, czy alarm jest uzasadniony, czy po prostu twój próg nie ma sensu.
Miejsce na dysku i inody
df -h
df --output=pcent,target -x tmpfs -x devtmpfs -x squashfs -x overlay
df -i
Wykluczenia są konieczne. Na Ubuntu 22.04 i 24.04 snap montuje swoje pakiety jako obrazy squashfs tylko do odczytu, a te stoją na stałe na 100 procentach. Bez -x squashfs twój monitoring już od pierwszego przebiegu melduje pełny dysk, codziennie, w nieskończoność. To samo dotyczy punktów montowania overlay należących do Dockera.
Dwie właściwości warto znać. df nie przyjmuje -P i --output jednocześnie i przerywa pracę komunikatem o wzajemnie wykluczających się opcjach. A na ext4 fabrycznie pięć procent jest zarezerwowane dla roota, dlatego df melduje już 100 procent, podczas gdy root nadal może pisać. Co robić po takim alarmie, opisuje artykuł Pełny dysk w Linuksie.
Kontrola inodów nie jest tematem pobocznym. Katalog z milionami maleńkich plików sesji albo plików cache potrafi zużyć wszystkie inody, podczas gdy df -h pokazuje mnóstwo wolnego miejsca. Zapisy kończą się wtedy błędem No space left on device, a wyjaśnienie, które samo się nasuwa, jest tym błędnym.
Pamięć RAM
free -m
awk '/^MemAvailable:/ { printf "%d MB\n", $2 / 1024 }' /proc/meminfo
Kolumna free na zdrowym systemie Linux jest prawie zawsze mała, bo kernel wykorzystuje nieużywaną pamięć jako cache plików. Wiarygodna jest tylko kolumna available, czyli MemAvailable: ilość pamięci, którą może dostać nowa aplikacja bez przenoszenia czegokolwiek do swapa. Alarmuj na tę wartość, nigdy na free.
Czy było już wcześniej ciasno, zdradzi log kernela:
journalctl -k -b --grep "Out of memory"
Każde trafienie to proces, który kernel zabił, bo skończyła się pamięć. Jak zareagować, nie zakładając swapa na oślep, opisuje artykuł Out of Memory i poprawna konfiguracja swapa.
Obciążenie systemu
nproc
cat /proc/loadavg
uptime
Pierwsze trzy pola w /proc/loadavg to średnie z jednej, pięciu i piętnastu minut. Dwie rzeczy bywają regularnie źle rozumiane. Po pierwsze obciążenie w Linuksie nie jest czystą miarą CPU: liczą się do niego także procesy czekające na dostęp do dysku. Obciążenie 20 na czterech rdzeniach może oznaczać, że CPU się pali, albo że zaciął się nośnik danych. Po drugie wartość jednominutowa nie nadaje się do alarmów, bo każdy przebieg backupu na chwilę podbija ją w górę. Bierz średnią z 15 minut, a próg ustaw względem liczby rdzeni.
Jeśli twój kernel udostępnia statystykę zatorów, będzie ona bardziej wymowna, bo rozdziela CPU, wejście i wyjście oraz pamięć. Dostępna jest nie wszędzie:
test -d /proc/pressure && cat /proc/pressure/io || echo "brak statystyki zatorów w tym kernelu"
Usługi
systemctl is-system-running
systemctl --failed --no-pager
systemctl is-active nginx
systemctl is-system-running to najkrótsza sensowna kontrola całości. Zwraca running, kiedy żadna jednostka nie jest w stanie błędu, oraz degraded, gdy choć jedna w nim jest. Kod wyjścia wynosi odpowiednio 0 albo coś różnego od 0.
Dwie pułapki. Stan degraded utrzymuje się, dopóki po naprawie nie zresetujesz go poleceniem systemctl reset-failed; jedno zadanie, które raz zawiodło, inaczej podtrzymuje ten komunikat tygodniami. A przy sprawdzaniu pojedynczych usług systemy odniesienia się różnią: na Ubuntu 24.04 SSH startuje przez aktywację gniazda, ssh.service jest tam w spoczynku inactive, mimo że SSH działa bez zarzutu. Kto monitoruje tam ssh.service, dostaje stały fałszywy alarm. Na Ubuntu 24.04 monitorować należy ssh.socket, a na Debianie 12, Debianie 13 i Ubuntu 22.04 przeciwnie: ssh.service.
Wygasanie certyfikatu
openssl x509 -enddate -noout -in /etc/letsencrypt/live/example.com/fullchain.pem
openssl x509 -checkend 1814400 -noout -in /etc/letsencrypt/live/example.com/fullchain.pem
Ciekawsze jest drugie polecenie. -checkend oczekuje liczby sekund, 1814400 to 21 dni. Jeśli certyfikat wygasa w tym terminie, polecenie wypisuje Certificate will expire i zwraca kod wyjścia 1, w przeciwnym razie Certificate will not expire oraz 0. Katalogi /etc/letsencrypt/live i /etc/letsencrypt/archive są czytelne wyłącznie dla roota.
Ta kontrola ma lukę, którą wiele poradników przemilcza: sprawdza plik na dysku, a nie certyfikat, który wydaje twój serwer WWW. Jeśli odnowienie przejdzie, ale przeładowanie serwera WWW się nie powiedzie, plik jest nowy, a wydawany klucz stary. Kontrola pliku nie melduje niczego, podczas gdy odwiedzający widzą już ostrzeżenie o certyfikacie. Ten przypadek wyłapuje dopiero spojrzenie z zewnątrz:
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -enddate
-servername nie jest opcjonalne, gdy tylko na jednym adresie IP leży kilka certyfikatów. Bez tego dodatku dostajesz domyślny certyfikat serwera i sprawdzasz niewłaściwą domenę.
Skrypt kontrolny
Wszystkie kontrole razem składają się na skrypt, który nie robi nic poza czytaniem, porównywaniem i meldowaniem w razie błędu. Potrzebuje curl, a dla drogi przez webhooka także jq. W minimalnych instalacjach Debiana brakuje obu:
apt update
apt install -y curl jq
cat > /usr/local/sbin/kh-monitor <<'EOF'
#!/bin/bash
set -u
DISK_WARN="${DISK_WARN:-85}"
INODE_WARN="${INODE_WARN:-85}"
MEM_MIN_MB="${MEM_MIN_MB:-200}"
LOAD_FACTOR="${LOAD_FACTOR:-2}"
CERT_DAYS="${CERT_DAYS:-21}"
UNITS="${UNITS:-ssh nginx}"
STATE_DIR="${STATE_DIRECTORY:-/var/lib/kh-monitor}"
problems=""
add() { problems+="- ${1}"$'\n'; }
notify() {
printf '%s | %s\n' "$1" "$(printf '%s' "$2" | tr '\n' ' ')"
if [ -n "${WEBHOOK_URL:-}" ]; then
printf '%s\n%s' "$1" "$2" | jq -Rs '{text: .}' \
| curl -fsS -m 10 -o /dev/null -H 'Content-Type: application/json' \
--data-binary @- "$WEBHOOK_URL"
fi
if [ -n "${MAILTO:-}" ]; then
printf '%s\n' "$2" | mail -s "$1" "$MAILTO"
fi
}
while read -r pcent target; do
pcent="${pcent%\%}"
case "$pcent" in ''|*[!0-9]*) continue ;; esac
[ "$pcent" -ge "$DISK_WARN" ] && add "Dysk ${target} zajęty w ${pcent} procentach"
done < <(df --output=pcent,target -x tmpfs -x devtmpfs -x squashfs -x overlay | tail -n +2)
while read -r ipcent target; do
ipcent="${ipcent%\%}"
case "$ipcent" in ''|*[!0-9]*) continue ;; esac
[ "$ipcent" -ge "$INODE_WARN" ] && add "Inody na ${target} zajęte w ${ipcent} procentach"
done < <(df --output=ipcent,target -x tmpfs -x devtmpfs -x squashfs -x overlay | tail -n +2)
mem_avail=$(awk '/^MemAvailable:/ { printf "%d", $2 / 1024 }' /proc/meminfo)
[ "${mem_avail:-0}" -lt "$MEM_MIN_MB" ] && add "tylko ${mem_avail} MB wolnej pamięci RAM"
cores=$(nproc)
load15=$(awk '{ print $3 }' /proc/loadavg)
awk -v l="$load15" -v c="$cores" -v f="$LOAD_FACTOR" 'BEGIN { exit !(l > c * f) }' \
&& add "Obciążenie w średniej z 15 minut ${load15} przy ${cores} rdzeniach"
sysstate=$(systemctl is-system-running)
[ "$sysstate" = "running" ] || add "systemd melduje stan ${sysstate}"
for unit in $UNITS; do
systemctl is-active --quiet "$unit" || add "Usługa ${unit} jest w stanie $(systemctl is-active "$unit")"
done
for cert in /etc/letsencrypt/live/*/fullchain.pem; do
[ -r "$cert" ] || continue
openssl x509 -checkend $(( CERT_DAYS * 86400 )) -noout -in "$cert" >/dev/null 2>&1 \
|| add "Certyfikat ${cert} wygasa za mniej niż ${CERT_DAYS} dni"
done
mkdir -p "$STATE_DIR"
now=$(printf '%s' "$problems" | sha256sum | cut -d' ' -f1)
before=$(cat "${STATE_DIR}/last" 2>/dev/null || true)
printf '%s' "$now" > "${STATE_DIR}/last"
if [ -z "$problems" ]; then
[ -n "${HEARTBEAT_URL:-}" ] && curl -fsS -m 10 -o /dev/null "$HEARTBEAT_URL"
[ -n "$before" ] && [ "$now" != "$before" ] \
&& notify "Odwołanie alarmu $(hostname -s)" "Wszystkie kontrole znowu bez zastrzeżeń."
exit 0
fi
[ "$now" = "$before" ] && exit 0
notify "Ostrzeżenie $(hostname -s)" "$problems"
EOF
Cztery miejsca zasługują na wyjaśnienie.
- Pętle czytają z
< <( ... ), a nie z potoku. Potok przenosi pętlę do osobnej podpowłoki, a komunikaty zebrane w środku zniknęłyby podone. Ten błąd jest podstępny, bo skrypt przebiega bezbłędnie i tylko nigdy niczego nie melduje. - Progi wczytują się ze środowiska. Każdą granicę możesz nadpisać na potrzeby pojedynczego wywołania. Na tym opiera się opisany niżej test alarmu.
- Stan zapisywany jest jako suma kontrolna. Meldunek wychodzi tylko wtedy, gdy lista problemów się zmieniła. Inaczej pełny dysk wysyła ci co dziesięć minut tę samą wiadomość, a po dwóch dniach wyłączasz monitoring.
- Pętla certyfikatów trafia w pustkę, jeśli katalog Let's Encrypt nie istnieje. Wzorzec pozostaje nierozwinięty,
[ -r "$cert" ]zawodzi, a przebieg zostaje pominięty.
Sprawdź to teraz, zanim do gry wejdzie systemd:
chmod 700 /usr/local/sbin/kh-monitor
bash -n /usr/local/sbin/kh-monitor && echo "Składnia ok"
/usr/local/sbin/kh-monitor; echo "Kod wyjścia $?"
Na zdrowym serwerze trzecie polecenie nie wypisuje nic poza Kod wyjścia 0. Jeśli widzisz komunikat, to albo coś jest nie w porządku, albo któryś próg nie pasuje, na przykład dlatego, że w UNITS stoi usługa, której tu nie ma.
Timer systemd
Cronjob też by wystarczył. Timer ma jednak cztery konkretne zalety: nie startuje drugiej instancji, dopóki działa pierwsza, nadrabia przebieg opuszczony podczas wyłączenia serwera, jego wyjście trafia do journala i wyłącza się go jednym poleceniem. Jednostka usługi nie potrzebuje sekcji [Install], bo aktywuje ją nie start systemu, tylko timer.
cat > /etc/systemd/system/kh-monitor.service <<'EOF'
[Unit]
Description=Krótka kontrola stanu serwera
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/kh-monitor
EnvironmentFile=-/etc/default/kh-monitor
StateDirectory=kh-monitor
SyslogIdentifier=kh-monitor
Nice=10
IOSchedulingClass=idle
EOF
cat > /etc/systemd/system/kh-monitor.timer <<'EOF'
[Unit]
Description=Uruchamia kh-monitor regularnie
[Timer]
OnCalendar=*:0/10
RandomizedDelaySec=60
Persistent=true
[Install]
WantedBy=timers.target
EOF
StateDirectory=kh-monitor zakłada /var/lib/kh-monitor z odpowiednimi uprawnieniami i ustawia zmienną STATE_DIRECTORY, po którą sięga skrypt. Poprzedzający minus w EnvironmentFile=-/etc/default/kh-monitor oznacza, że brak pliku nie jest błędem. RandomizedDelaySec=60 rozprasza moment startu, a Persistent=true nadrabia przy rozruchu przebieg opuszczony w czasie wyłączenia.
Timer automatycznie aktywuje usługę o tej samej nazwie, wiersz Unit= nie jest potrzebny:
systemd-analyze verify /etc/systemd/system/kh-monitor.service
systemd-analyze calendar '*:0/10'
systemctl daemon-reload
systemctl start kh-monitor.service
systemctl enable --now kh-monitor.timer
Kontrola powodzenia:
systemctl list-timers kh-monitor.timer --no-pager
journalctl -u kh-monitor.service -n 20 --no-pager
Lista musi pokazać wiersz z najbliższym terminem wykonania. Jeśli zostaje pusta, timer nie jest aktywny. systemd-analyze calendar znajduje literówki w wyrażeniu czasowym: przelicza je na postać normalną i podaje najbliższy termin. Szczegóły o plikach jednostek i ich obrazach błędów znajdziesz w artykule Tworzenie usługi systemd.
Kanał powiadomień
Tutaj wykłada się większość samodzielnie zbudowanych systemów monitoringu. Nadajnik alarmu działa na tym samym serwerze, który nadzoruje, więc pada razem z nim. Melduje przez to niezawodnie wszystko poza tym jednym przypadkiem, który naprawdę się liczy.
Odpowiedzią jest sygnał życia, czyli heartbeat: serwer melduje się po każdym udanym przebiegu u zewnętrznego odbiorcy, a kiedy meldunek nie przychodzi, to odbiorca podnosi alarm. Skrypt powyżej robi dokładnie to, jeśli ustawisz HEARTBEAT_URL. Awaria sieci, zawieszony system plików i system po crashu wyglądają dla odbiorcy tak samo, a o wszystkich trzech chcesz wiedzieć.
Dane dostępowe należą do osobnego pliku, a nie do skryptu. Adres webhooka jest sekretem: kto go ma, może wysyłać wiadomości w twoim imieniu:
cat > /etc/default/kh-monitor <<'EOF'
WEBHOOK_URL=https://przyklad.example/hooks/xxxxxxxx
HEARTBEAT_URL=https://przyklad.example/heartbeat/xxxxxxxx
UNITS="ssh nginx"
EOF
chmod 600 /etc/default/kh-monitor
Cudzysłowy wokół UNITS są ważne: systemd poradziłby sobie i bez nich, ale przy opisanym niżej przebiegu testowym plik wczytuje shell, a tam nginx bez cudzysłowów zostałby potraktowany jako polecenie. Wpisuj wyłącznie jednostki, które w tym systemie istnieją, na Ubuntu 24.04 więc ssh.socket zamiast ssh.
Kto woli e-mail, potrzebuje drogi wysyłki. Pełnoprawny serwer pocztowy to przesada, wystarczy zwykły przekaźnik:
apt install -y msmtp msmtp-mta bsd-mailx
Konfigurujesz to w /etc/msmtprc przy użyciu danych dostępowych istniejącej skrzynki. Plik zawiera hasło i należy mu się chmod 600, inaczej msmtp odmówi pracy z odwołaniem do uprawnień pliku. Dwa uczciwe ograniczenia: e-mail z dopiero co przydzielonego adresu IP serwera często ląduje w folderze spamu albo zostaje odrzucony, a wiadomość, którą zobaczysz dopiero przy następnym zajrzeniu do skrzynki, jest przy awarii po prostu za wolna. Do alarmów praktyczniejszym wyborem jest doręczanie push.
Wywołaj alarm raz na próbę
Ten krok nie jest opcjonalny. Wymuś meldunek, ustawiając bezsensownie jeden z progów na czas pojedynczego wywołania:
set -a; . /etc/default/kh-monitor; set +a
DISK_WARN=0 /usr/local/sbin/kh-monitor
rm -f /var/lib/kh-monitor/last
Wiadomość musi teraz faktycznie do ciebie dotrzeć, a nie tylko wylądować w journalu. Trzeci wiersz kasuje zapisany stan, żeby testowy alarm nie stłumił kolejnego prawdziwego przebiegu. Jeden efekt uboczny jest zamierzony: jeśli doręczenie zawiedzie, kh-monitor.service kończy się błędem i pojawia się w systemctl --failed. Cichy kanał powiadomień byłby najgorszym możliwym błędem w monitoringu.
Widok z zewnątrz
Heartbeat mówi ci, że serwer żyje. Nie mówi, że twoja strona odpowiada. Do tego potrzebna jest kontrola z innej lokalizacji, według tego samego wzorca ze skryptem i timerem, tyle że na drugim hoście:
curl -fsS -m 10 -o /dev/null -w '%{http_code} %{time_total}\n' https://example.com/
O wartości tej kontroli decydują cztery punkty. Po pierwsze: sprawdzaj prawdziwą usługę, a nie sam ICMP. Serwer, który odpowiada na ping, podczas gdy serwer WWW wisi w nieskończonej pętli, przy kontroli pingiem uchodzi za zdrowy. Po drugie: -f każe curlowi zawieść przy kodach błędów HTTP, bez tego przełącznika także strona błędu liczy się jako sukces. Po trzecie: alarmuj dopiero po dwóch nieudanych próbach z rzędu, inaczej każde krótkie zakłócenie sieci zgłosi awarię. Po czwarte: wywołanie co minutę z tego samego adresu może trafić na ograniczenie liczby zapytań albo zostać uznane przez oprogramowanie blokujące za atak; wpisz adres hosta wykonującego kontrolę jako wyjątek.
Bez drugiego serwera zostaje hostowana usługa kontrolna. Darmowe oferty sprawdzają zwykle co pięć minut, o awarii dowiadujesz się więc z odpowiednim opóźnieniem. Dla pojedynczego serwera to wystarczy i jest lepsze niż alternatywa, czyli nie dowiedzieć się wcale.
Typowe błędy i ich rozwiązania
Failed to start kh-monitor.service: Unit kh-monitor.service not found.: po założeniu albo zmianie jednostki brakuje systemctl daemon-reload. Drugą najczęstszą przyczyną jest zły katalog; własne jednostki należą do /etc/systemd/system/.
The unit files have no installation config: wywołałeś systemctl enable kh-monitor.service zamiast kh-monitor.timer. Usługa świadomie nie ma sekcji [Install], aktywuje się timer.
code=exited, status=203/EXEC: systemd nie zdołał uruchomić pliku. Albo nie zgadza się ścieżka w ExecStart, albo brakuje chmod 700, albo plik edytowano pod Windowsem i ma końce wierszy ze znakiem powrotu karetki. Pomaga na to sed -i 's/\r$//' /usr/local/sbin/kh-monitor.
Syntax error: redirection unexpected: skrypt uruchomiono przez sh zamiast bash. Na Debianie i Ubuntu /bin/sh to powłoka dash, a ta nie zna ani < <( ... ), ani += dla łańcuchów znaków. Pierwszy wiersz musi brzmieć #!/bin/bash.
bash: mail: command not found: nie zainstalowano żadnego programu pocztowego. Polecenie mail pochodzi zależnie od systemu z bsd-mailx albo mailutils, a te dwa różnią się przełącznikami. Trzymaj się -s dla tematu wiadomości.
curl: (22) The requested URL returned error: 404: adres webhooka jest błędny albo został skasowany po drugiej stronie. Bez -f curl przełknąłby ten błąd po cichu.
curl: (60) SSL certificate problem: certificate has expired: przy kontroli z zewnątrz to nie błąd narzędzia, tylko szukany wynik. Przy własnym webhooku ten sam komunikat wskazuje na złą godzinę na serwerze wykonującym kontrolę.
Certificate will expire: zwykłe wyjście openssl x509 -checkend z kodem wyjścia 1. Sprawdź, czy odnawianie nadal działa i czy serwer WWW jest po nim przeładowywany.
Failed to parse calendar specification: wyrażenie za OnCalendar= jest nieprawidłowe. Przetestuj je osobno przez systemd-analyze calendar, zanim trafi do jednostki.
Warning: Stopping kh-monitor.service, but it can still be activated by: kh-monitor.timer: zatrzymałeś usługę zamiast timera. Usługa i tak działa tylko przez sekundy, wyłączyć trzeba timer.
Alarm przy każdym przebiegu, chociaż nic się nie zmieniło: stan nie jest zapisywany. Sprawdź, czy w jednostce stoi StateDirectory=kh-monitor oraz czy /var/lib/kh-monitor/last istnieje i jest zapisywalny.
Brak alarmu, chociaż coś jest wyraźnie zepsute: wywołaj wymuszony meldunek opisany wyżej. Jeśli i on nie dotrze, winna jest droga doręczenia, a nie same kontrole.
Różnice między czterema systemami
| System | Jednostka SSH, którą trzeba monitorować | Punkty montowania squashfs | Gdzie lądują komunikaty |
|---|---|---|---|
| Debian 13 (trixie) | ssh.service, pojedyncze obrazy korzystają z ssh.socket | z reguły brak | tylko journal, w minimalnych instalacjach brakuje rsysloga |
| Debian 12 (bookworm) | ssh.service | z reguły brak | journal, rsyslog zależnie od wariantu instalacji |
| Ubuntu 24.04 LTS | ssh.socket | zwykle obecne, potrzebne wykluczenie | journal i rsyslog |
| Ubuntu 22.04 LTS | ssh.service | zwykle obecne, potrzebne wykluczenie | journal i rsyslog |
We wszystkich czterech systemach identyczne jest to, że systemd-analyze, StateDirectory= i RandomizedDelaySec= są dostępne, a skrypt oraz pliki jednostek działają bez zmian.
Kiedy opłaca się duża skrzynka narzędziowa
To, co masz teraz, ma wyraźne granice. Nie zapisuje historii, nie sprawdzisz więc, czy zużycie pamięci rośnie od trzech tygodni. Nie zna korelacji między wieloma hostami. I nie ma ani eskalacji, ani reguł dyżuru, ani możliwości wyciszenia znanego alarmu na dwie godziny.
Dokładnie te punkty odpowiadają na pytanie o przesiadkę. Budowa systemu metryk z dashboardem opłaca się, gdy tylko spełniony jest jeden z nich:
- Prowadzisz więcej niż garstkę serwerów i chcesz widzieć je obok siebie.
- Potrzebujesz historii i trendów, na przykład do planowania pojemności albo po to, by odpowiedzieć liczbami na skargę o powolne działanie.
- Dyżur dzieli między siebie kilka osób, potrzebne są więc stopnie eskalacji i wyciszanie.
- Musisz udokumentować dostępność wobec osób trzecich.
Jeśli nie pasuje żaden z nich, taka budowa dla pojedynczego serwera jest zwykle złym interesem. Kolektor, baza danych, dashboard i eksporter zajmują razem bez trudu kilkaset megabajtów pamięci RAM na dokładnie tej maszynie, której wolną pamięć mają nadzorować. Do tego dochodzi kolejny otwarty port oraz drugie oprogramowanie, które trzeba aktualizować. Kluczowy punkt pozostaje bez zmian: jeśli kolektor działa na tym samym serwerze, to o awarii tego serwera nie powiadomi ani trochę lepiej niż skrypt. Przy przesiadce kolektor należy postawić na innym hoście, a eksporter wiąże się z 127.0.0.1 albo jest ograniczony firewallem do adresu tego hosta.
Droga pośrednia sprawdza się dobrze: timer i skrypt zostają, bo pokrywają przypadek alarmowy, a system metryk dochodzi wtedy, gdy potrzebujesz historii. Jedno nie wyklucza drugiego.
Wycofanie zmian
Jeśli chcesz się tej metody znowu pozbyć, wystarczy pięć wierszy:
systemctl disable --now kh-monitor.timer
rm -f /etc/systemd/system/kh-monitor.timer /etc/systemd/system/kh-monitor.service
rm -f /usr/local/sbin/kh-monitor /etc/default/kh-monitor
rm -rf /var/lib/kh-monitor
systemctl daemon-reload
Kontrola końcowa
Sześć kontroli, które pokazują stan faktyczny, a nie ten, na który liczysz:
systemctl list-timers kh-monitor.timer --no-pagerpodaje najbliższy termin wykonania.journalctl -u kh-monitor.service --since "1 hour ago" --no-pagerpokazuje przebiegi w oczekiwanych odstępach.- Wymuszony alarm przez
DISK_WARN=0dociera do ciebie, a nie tylko do journala. - Po poleceniu
reboottimer działa dalej, bez żadnego działania z twojej strony. - Heartbeat się odzywa: zatrzymaj timer i odczekaj, czy odbiorca podniesie alarm.
systemctl --failed --no-pagernie wypisuje niczego, a w szczególności niekh-monitor.service.
Punkt piąty jest najbardziej niewygodny i najważniejszy. Monitoring, w którym przypadek alarmowy nigdy nie nastąpił, jest tylko przypuszczeniem. Dopiero gdy celowo wywołasz awarię i otrzymasz wiadomość, łańcuch od serwera aż do twojego telefonu jest dowiedziony w całości.
Najczęstsze pytania
Czy dla pojedynczego serwera root naprawdę potrzebuję serwera metryk z dashboardem?
Dlaczego timer systemd, a nie cronjob?
Mój monitoring stale melduje pełny dysk, chociaż wolnego miejsca jest dość. Z czego to wynika?
Skąd się dowiem, że serwer padł całkowicie?
Którą jednostkę SSH trzeba monitorować?
Jak uniknąć tego, że co dziesięć minut dostaję to samo ostrzeżenie?
Certyfikat na dysku jest ważny, a odwiedzający i tak widzą ostrzeżenie. Jak to możliwe?
Jak szybko wyłączyć monitoring, kiedy zaczyna przeszkadzać?
2026 KernelHost GmbH. Wszelkie prawa zastrzeżone. Ten poradnik jest chroniony prawem autorskim. Publikowanie go w innych serwisach, w całości, we fragmentach lub w zmienionej formie, wymaga naszej pisemnej zgody. Cytaty z podaniem źródła i z linkiem są jak najbardziej mile widziane.

