PABLO.ENGINEERING
// WRÓĆ DO WSZYSTKICH WPISÓW
// 4 min

Higiena logów na VPS: Jak opanować rosnący dysk, zoptymalizować crony i nie obudzić się o 3 w nocy z pełnym dyskiem

#linux#devops#vps#cron#docker#systemd#observability

Kiedy na małym serwerze VPS uruchamiasz flotę kilkunastu zadań w tle, watchdogów i kontenerów Docker, największym wrogiem stabilności nie jest brak pamięci RAM, lecz cichy rozrost niesformatowanych logów. Bez ścisłej dyscypliny rotacji i monitoringu, serwer prędzej czy później zapełni dysk do zera w środku nocy.

Problem: Niekontrolowany rozrost plików i milczące awarie zadań crona

Podczas regularnego audytu stanu serwera zauważyłem kilka niebezpiecznych zjawisk:

  • Milczące błędy crona: Standardowe wpisy w crontabie zrzucały wyjście do plików bez dat i bez kodów wyjścia (exit code). Gdy skrypt padał, w logu pojawiał się tylko urywek błędu bez informacji, którego dnia i o której godzinie do tego doszło.
  • Dysk zapychany przez kontenery i journald: Dziennik systemowy systemd-journald zajął ponad 2.1 GB, a kontenery Docker (Umami i AOC) działały z domyślnym sterownikiem logowania bez żadnego limitu wielkości plików.
  • Pętla budzenia brokera co 5 sekund: Skrypt brokera budził się co 5 sekund (ok. 60 wybudzeń na 5 minut) tylko po to, by sprawdzić pustą kolejkę zadań, generując niepotrzebne obciążenie I/O i zapisy w logach.
  • Śmieci w katalogu /tmp: W /tmp zalegało prawie 1.8 GB tymczasowych artefaktów i pobranych plików HTML, których żaden proces nie czyścił.

Co zrobiłem: Kompleksowa higiena logów i optymalizacja procesów

Zamiast doraźnego kasowania plików, wprowadziłem trwałe reguły systemowe na poziomie całego środowiska.

1. Dedykowany wrapper crona z kodami wyjścia (run_logged.sh)

Stworzyłem lekki skrypt powłoki /usr/local/bin/run_logged.sh, który przechwytuje każde wywołanie crona, dodaje precyzyjny znacznik czasu ISO, identyfikator procesu (PID) oraz raportuje kod zakończenia (rc):

#!/usr/bin/env bash
# run_logged.sh - transparentny wrapper z logowaniem ISO i exit code
LOG_FILE="$1"; shift
QUIET_OK=false
if [ "$1" = "--quiet-ok" ]; then QUIET_OK=true; shift; fi

TMP_OUT=$(mktemp)
"$@" > "$TMP_OUT" 2>&1
RC=$?
TS=$(date -u +"%Y-%m-%dT%H:%M:%SZ")

if [ $RC -ne 0 ] || [ "$QUIET_OK" = false ] || [ -s "$TMP_OUT" ]; then
  sed "s/^/[$TS] [$$] /" "$TMP_OUT" >> "$LOG_FILE"
  echo "[$TS] [$$] rc=$RC" >> "$LOG_FILE"
fi
rm -f "$TMP_OUT"
exit $RC

Przepisałem 17 zadań w crontabie roota oraz 4 w /etc/cron.d/, dodając flagę --quiet-ok. Dzięki temu ciche sukcesy nie produkują pustych wpisów, a każda awaria zostawia jasny, łatwy do odszukania ślad.

2. Twarde limity dla Dockera i systemd-journald

Ograniczyłem maksymalną przestrzeń dyskową, jaką mogą zająć logi systemowe i kontenerowe:

  • W konfiguracjach Docker Compose dodałem sekcję logging z ograniczeniem max-size: 10m i max-file: 3.
  • W /etc/systemd/journald.conf ustawiłem SystemMaxUse=2G, a następnie zredukowałem obecny dziennik za pomocą polecenia journalctl --vacuum-size=1.5G, odzyskując od ręki kilkaset megabajtów.
  • Skonfigurowałem reguły logrotate dla 14 logów crona (retencja 30 dni) i 6 logów watchdogów (retencja 90 dni).

3. Zmniejszenie częstotliwości brokera z 5s do 15s

Przeanalizowałem zachowanie brokera zadań i wydłużyłem interwał sprawdzania kolejki z 5 do 15 sekund. Liczba wybudzeń procesu spadła z ok. 60 do zaledwie 16 na każde 5 minut, co obniżyło zużycie procesora i ograniczyło zapisy do bazy bez zauważalnego wpływu na responsywność asystentów.

4. Bezpieczne sprzątanie /tmp z zachowaniem kopii

Zamiast ryzykować usunięcie plików, które mogły być jeszcze otwarte przez działające procesy, stworzyłem zarchiwizowaną paczkę z manifestem i zredukowałem rozmiar /tmp z 1.8 GB do 45 MB.

Dlaczego to ważne (Use Case)

  • Stabilność bez niespodzianek: Serwer ma przewidywalne, stałe zużycie dysku. Logi nigdy nie urosną do rozmiaru, który zablokuje bazę SQLite czy procesy systemowe.
  • Błyskawiczna diagnostyka awarii: Każde zdarzenie w logach ma spójny format [ISO-TS] [PID] treść oraz rc=kod. Wystarczy jedno polecenie grep rc= /var/log/*.log, by w sekundę znaleźć wszystkie błędy z minionej nocy.
  • Oszczędność zasobów na małym VPS: Mniej pustych wybudzeń i cichsze logi oznaczają niższe zużycie procesora, mniej operacji wejścia/wyjścia (I/O) i dłuższą żywotność dysku NVMe.

Czego się nauczyłem

  1. Brak polityki retencji to dług techniczny: Każdy proces zapisujący do pliku tekstowego bez zdefiniowanego limitu rozmiaru to przyszły incydent produkcyjny.
  2. Milczący sukces to czysty log: Stosowanie podejścia --quiet-ok pozwala odsiać szum tła. Logi powinny krzyczeć tylko wtedy, gdy coś poszło nie tak.
  3. Transparentny wrapper jest lepszy niż modyfikacja 20 skryptów: Zamiast dopisywać obsługę błędów w każdym skrypcie Pythona i basha, jeden prosty wrapper run_logged.sh na poziomie crona natychmiast zabezpieczył cały ekosystem.