переналаштування logrotate

| щоденник, комп'ютери, linux, сервер

рік тому я налаштував собі невеличкий сервер журналів, та й забув про нього. ну як забув — насправді ні, але руки не доходили дивитися ті журнали, та то й добре — значить, не було потреби, все працювало як слід. так? а не зовсім, бо на самому сервері журналів тим часом робилося чорт зна що: logrotate наплодив величезну кількість порожніх файлів у теках деяких (не всіх) хостів… досяг ліміту довжини назви файлу (255 символів для ext4) й почав спамити помилками у власному журналі. виявилося це випадково, коли мені заманулося погратися з переглядачем журналів lnav; довелося ремонтувати.

ось такий срач (server — машина в мережі, старий сервер):

> ls -lh /var/log/hosts/server
...
server.log.1.1.1.1.1.1.1.1.1.2
server.log.1.1.1.1.1.1.1.1.2
server.log.1.1.1.1.1.1.1.2
server.log.1.1.1.1.1.1.2
server.log.1.1.1.1.1.2
server.log.1.1.1.1.2
server.log.1.1.1.2
server.log.1.1.2
server.log.1.2
server.log.2

те саме в теках hosts/roku, router… але не micro, ap2? найцікавіший випадок — vpn (окрема віртуалка з одним сервісом):

ls -lh /var/log/hosts/vpn
total 76M
-rw-r----- 1 root adm  21K Feb 27  2023 debug.log
-rw-r----- 1 root adm 1.1M Dec 11 10:30 ovpn-server.log
-rw-r----- 1 root adm    0 Nov  5 00:00 ovpn-server.log.1
-rw-r----- 1 root adm    0 Nov  6 00:00 ovpn-server.log.1.1
-rw-r----- 1 root adm    0 Nov  7 00:00 ovpn-server.log.1.1.1
-rw-r----- 1 root adm    0 Nov  8 00:00 ovpn-server.log.1.1.1.1
-rw-r----- 1 root adm    0 Nov  9 00:00 ovpn-server.log.1.1.1.1.1
-rw-r----- 1 root adm    0 Nov 10 00:00 ovpn-server.log.1.1.1.1.1.1
-rw-r----- 1 root adm    0 Nov 11 00:00 ovpn-server.log.1.1.1.1.1.1.1
-rw-r----- 1 root adm    0 Nov 12 00:00 ovpn-server.log.1.1.1.1.1.1.1.1
-rw-r----- 1 root adm    0 Nov 13 00:00 ovpn-server.log.1.1.1.1.1.1.1.1.1
-rw-r----- 1 root adm    0 Nov 14 00:00 ovpn-server.log.1.1.1.1.1.1.1.1.1.1
-rw-r----- 1 root adm    0 Nov 15 00:00 ovpn-server.log.1.1.1.1.1.1.1.1.1.1.1
-rw-r----- 1 root adm    0 Nov 16 00:00 ovpn-server.log.1.1.1.1.1.1.1.1.1.1.1.1
-rw-r----- 1 root adm    0 Nov 17 00:00 ovpn-server.log.1.1.1.1.1.1.1.1.1.1.1.1.1
-rw-r----- 1 root adm    0 Nov 18 00:00 ovpn-server.log.1.1.1.1.1.1.1.1.1.1.1.1.1.1
-rw-r----- 1 root adm    0 Nov 19 00:00 ovpn-server.log.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1
-rw-r----- 1 root adm    0 Nov 20 00:00 ovpn-server.log.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1
-rw-r----- 1 root adm    0 Nov 21 00:00 ovpn-server.log.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1
-rw-r----- 1 root adm    0 Nov 22 00:00 ovpn-server.log.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1
-rw-r----- 1 root adm    0 Nov 23 00:00 ovpn-server.log.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1
-rw-r----- 1 root adm    0 Nov 24 00:00 ovpn-server.log.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1
-rw-r----- 1 root adm    0 Nov 25 00:00 ovpn-server.log.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1
-rw-r----- 1 root adm    0 Nov 26 00:00 ovpn-server.log.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1
-rw-r----- 1 root adm    0 Nov 27 00:00 ovpn-server.log.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1
-rw-r----- 1 root adm    0 Nov 28 00:00 ovpn-server.log.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1
-rw-r----- 1 root adm    0 Nov 29 00:00 ovpn-server.log.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1
-rw-r----- 1 root adm    0 Nov 30 00:00 ovpn-server.log.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1
-rw-r----- 1 root adm    0 Dec  1 00:00 ovpn-server.log.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1
-rw-r----- 1 root adm    0 Dec  2 00:00 ovpn-server.log.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1
-rw-r----- 1 root adm    0 Dec  3 00:00 ovpn-server.log.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1
-rw-r----- 1 root adm    0 Dec  4 00:00 ovpn-server.log.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1
-rw-r----- 1 root adm    0 Dec  5 00:00 ovpn-server.log.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1
-rw-r----- 1 root adm    0 Dec  6 00:00 ovpn-server.log.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1
-rw-r----- 1 root adm    0 Dec  7 00:00 ovpn-server.log.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1
-rw-r----- 1 root adm    0 Dec  8 00:00 ovpn-server.log.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1
-rw-r----- 1 root adm    0 Dec  9 00:00 ovpn-server.log.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1
-rw-r----- 1 root adm    0 Dec 10 00:00 ovpn-server.log.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1
-rw-r----- 1 root adm    0 Dec 11 00:00 ovpn-server.log.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1
-rw-r----- 1 root adm  51M Nov  4 17:57 ovpn-server.log.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1
-rw-r----- 1 root adm  24M Dec 11 19:17 vpn.log

4-го листопада журнал ovpn-server.log виріс до 50 мб, і далі logrotate намагався щодня (?) зберегти попередній файл, додавши одиничку до назви. в інших теках це (50 мб?) трапилося раніше, тому файлів наплодилося більше; деякі з них не нульової довжини.

найперше — ідентифікую «проблемні» теки, в кожній створюю один «архівний» файл .old і скидаю туди всі файли з цифровими суфіксами, а самі файли видаляю:

> sudo -i
> cd /var/log/hosts/server
> touch server.log.old
> for file in *.log.*; do cat "${file}" >> server.log.old && rm "${file}" && echo -n "." || echo -n "x"; done; echo

перебір *.log.* не дуже коректний і «зламається» на самому server.log.old, але && не дасть його видалити, тож для моєї задачі вистачає:

> ls -lh
total 124M
-rw-r----- 1 root adm   23M Dec 11 20:17 server.log
-rw-r--r-- 1 root root 102M Dec 11 20:15 server.log.old

далі треба знайти, яка заковика в конфігурації logrotate спричинила до цього. оскільки «вражено» лише підтеки /val/log/hosts/, за котру відповідає файл налаштувань /etc/logrotate.d/remote — причина має бути саме там:

> cat /etc/logrotate.d/remote
/var/log/hosts/* /var/log/hosts/*/* {
  size 50M
  rotate 3
  notifempty
  missingok
}

от же ж бовдур! звісно ж, що /var/log/hosts/*/* намагатиметься процесувати — а отже додати суфікс і збереги, — не лише .log, але й .log.1, і .log.1.1 тощо. чому я конфігурував цю маячню? гадаю, мене ввів в оману ось цей приклад з man logrotate:

/var/log/news/* {
    monthly
    rotate 2
    olddir /var/log/news/old
    missingok
    postrotate
        kill -HUP 'cat /var/run/inn.pid'
    endscript
    nocompress
}

але ж тут olddir явно переміщує старі журнали до окремої підтеки, тоді як я не додав цього, та й не хотів би зайве ускладнювати і без того глибоку структуру тек. як би не було, виправляю свою конфігурацію:

/var/log/hosts/*/*.log {
  size 50M
  rotate 3
  notifempty
  missingok

наостанок налаштував ще ping на healthcheck.io для logrotate. тепер має бути гаразд. за тиждень перевірю, а тим часом можна «погратися» з lnav, нарешті.

поновлення (2023-12-12). для щоденного пінгу healthcheck, я додав два curl’и до /etc/cron.daily/logrotate:

/usr/sbin/logrotate /etc/logrotate.conf
EXITVALUE=$?
if [ $EXITVALUE != 0 ]; then
    /usr/bin/logger -t logrotate "ALERT exited abnormally with [$EXITVALUE]"
    # Notify healthcheck.io on failed logrotate
    /usr/bin/curl -fsS --retry 3 https://hc-ping.com/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/fail >/dev/null
else
    # Update healthcheck.io with healthy ping
    /usr/bin/curl -fsS --retry 3 https://hc-ping.com/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx >/dev/null
fi
exit $EXITVALUE

але вночі вони не спрацювали. чому? тому що цей скрипт спершу перевіряє, чи присутній systemd:

# skip in favour of systemd timer
if [ -d /run/systemd/system ]; then
    exit 0
fi

я забув поглянути, чи є таймер logrotate. а він є:

> systemctl status *timer
> cat /lib/systemd/system/logrotate.timer
...
> cat /lib/systemd/system/logrotate.service
...
[Service]
Type=oneshot
ExecStart=/usr/sbin/logrotate /etc/logrotate.conf
...

трошки вдосконалюю logrotate.service, додавши ExecStopPost (документація systemd, healthcheck):

[Service]
Type=oneshot
ExecStart=/usr/sbin/logrotate /etc/logrotate.conf
ExecStopPost=/usr/bin/curl -fsS --retry 3 https://hc-ping.com/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/${EXIT_STATUS} >/dev/null

можна спробувати:

> sudo systemctl daemon-reload
> sudo systemctl start logrotate.service

дашборд healthcheck.io показує свіжий зелений пінг.