Logi na VPS rosły bez końca. Ustawiłem limity i wyłapuję ciche błędy crona
Na moim małym serwerze VPS działa kilkanaście zadań w tle, kilka strażników i kontenery Docker. Najbardziej zaskoczyło mnie to, że problemem nie była pamięć RAM, tylko logi. Rosły po cichu, aż dysk zaczął się zapełniać.
Co znalazłem: dysk pęka, a zadania padają bez śladu
Zadania w cronie zapisywały wynik do plików bez dat i bez kodów wyjścia. Gdy skrypt padał, w logu zostawał tylko urwany fragment błędu i nie dało się ustalić, kiedy to się stało.
Największe pliki to nie aplikacje, tylko zapisy systemowe. Dziennik systemowy journald zajmował ponad 2 GB, kontenery Docker nie miały żadnego limitu, a w /tmp leżało prawie 1,8 GB plików, których nikt nie czyścił. Do tego strażnik kolejki budził się co 5 sekund, żeby sprawdzić pustą kolejkę, czyli około 60 razy w ciągu 5 minut.
Dlaczego to ważne
Serwer, który co noc ma pełny dysk, zawodzi w najgorszym możliwym momencie. Baza danych przestaje zapisywać, a ja dowiaduję się o tym rano. Bez dat i kodów wyjścia nawet znalezienie przyczyny zajmuje dużo czasu. Im mniej zbędnego zapisu i wybudzeń, tym mniej obciążenia dla dysku i procesora.
Jak to naprawiłem
- Napisałem mały wrapper
run_logged.sh, który uruchamia każde zadanie crona i dopisuje do logu czas, numer procesu oraz kod wyjścia. Flaga--quiet-okpomija wpisy po udanych zadaniach. Przepiąłem na niego 21 zadań, zamiast przerabiać każdy skrypt osobno. - Ustawiłem limity: kontenery Docker przechowują po 3 pliki po 10 MB, a
journaldmaksymalnie 2 GB. Logi crona trzymam 30 dni, logi strażników 90 dni. - Strażnik kolejki budzi się co 15 sekund zamiast co 5. Wybudzeń jest teraz około 16 na 5 minut zamiast 60.
- Pliki z
/tmpnajpierw spakowałem razem z listą zawartości, a dopiero potem usunąłem oryginały. Katalog zmniejszył się z 1,8 GB do 45 MB, a w razie potrzeby da się wszystko odtworzyć.
Czego się nauczyłem
- Zadanie bez polityki przechowywania logów to przyszła awaria. Każdy plik, który rośnie, potrzebuje limitu.
- Logi powinny krzyczeć tylko wtedy, gdy coś poszło źle. Zapis udanych przebiegów to szum.
- Jeden wrapper na poziomie crona zabezpiecza wszystkie zadania naraz. Poprawianie każdego skryptu osobno zajęłoby dużo czasu i łatwo byłoby coś przeoczyć.