RHEL9ではlogrotateがanacronではなくsystemdのtimer unitで制御されている話。

はじめに

こんにちは。今日はlogrotateのお話です。

RHEL9ではlogrotateの実行タイミングはanacronではなくlogrotate.timerというsystemdのTimer unitを利用して制御されています。

今回の記事では、実際にlogrotate.timerを編集して実行時間を変えることができるかどうかを検証することが狙いとなります。

logrotate.timerによるlogrotateの制御

loglotateとは

さて、本題に入る前にlogrotateとは何だったかをおさらいしましょう。以下はmanの一部抜粋です。

logrotate(8) – Linux man page

logrotate is designed to ease administration of systems that generate large numbers of log files. It allows automatic rotation, compression, removal, and mailing of log files. Each log file may be handled daily, weekly, monthly, or when it grows too large.

参考:
https://linux.die.net/man/8/logrotate

翻訳すると以下の通りです。

logrotate は、大量のログ ファイルを生成するシステムの管理を容易にするように設計されています。ログ ファイルの自動ローテーション、圧縮、削除、メール送信が可能になります。各ログ ファイルは、毎日、毎週、毎月、またはサイズが大きくなりすぎたときに処理できます。

こうした多機能で管理者の負担を軽減してくれる素敵logrotateですが、どのタイミングで実行されるかを制御する必要があります。

その制御を担うのが今回の記事で取り上げるsytemdのlogrotate.timerというユニットです。

※ systemdの仕組みについて、次の書籍が分かりやすかったです。各unitに対する詳しい説明はありませんが、概要の把握にはぴったりだと感じました。 -> [バージョン8&9両対応! Red Hat Enterprise Linux完全ガイド ]

logrotate.timerの検証

以下では設定ファイルの確認からunitファイルの編集、結果の確認まで順番に見ていきたいと思います。

まず、デフォルトのunitファイルを確認してみます。
次のように、/usr/lib/systemd/system/配下にある、logrotateに関するunitファイルはlogrotate.timerとlogrotate.serviceの2つです。

[root@ip-10-0-2-46 ~]# ls -lrt /usr/lib/systemd/system/ | grep logrotate
-rw-r--r--. 1 root root  191 Oct 14  2019 logrotate.timer
-rw-r--r--. 1 root root  870 Aug 21  2020 logrotate.service

中身を見てみます。
まず、logrotate.timeユニットの方からです。

[root@ip-10-0-2-46 ~]# cat /usr/lib/systemd/system/logrotate.timer
[Unit]
Description=Daily rotation of log files
Documentation=man:logrotate(8) man:logrotate.conf(5)

[Timer]
OnCalendar=daily
AccuracySec=1h
Persistent=true

[Install]
WantedBy=timers.target

日次で実行することを意味するOnCalendar=dailyが設定されていることが分かります。
ちなみに、Persistent=trueは後述するanacronと同様の動作をする設定になります。

参考: systemd.timer(5) — Linux manual page
https://man7.org/linux/man-pages/man5/systemd.timer.5.html

Persistent=
Takes a boolean argument. If true, the time when the service unit was last triggered is stored on disk. When the timer is activated, the service unit is triggered immediately if it would have been triggered at least once during the time when the timer was inactive. Such triggering is nonetheless subject to the delay imposed by RandomizedDelaySec=. This is useful to catch up on missed runs of the service when the system was powered down. Note that this setting only has an effect on timers configured with OnCalendar=. Defaults to false.

翻訳:

Persistent= ブール値の引数を受け取ります。 true の場合、サービス ユニットが最後にトリガーされた時刻がディスクに保存されます。タイマーがアクティブ化されると、タイマーが非アクティブな間にサービス ユニットが少なくとも 1 回トリガーされていた場合、サービス ユニットはすぐにトリガーされます。ただし、このようなトリガーは、RandomizedDelaySec= によって課される遅延の影響を受けます。これは、システムの電源がオフになったときにサービスが実行されなかった場合にそれを取り戻すのに役立ちます。この設定は、OnCalendar= で構成されたタイマーにのみ影響することに注意してください。デフォルトは false です。

次はlogrotate.serviceユニットの方を見てみます。

[root@ip-10-0-2-46 ~]# cat /usr/lib/systemd/system/logrotate.service
[Unit]
Description=Rotate log files
Documentation=man:logrotate(8) man:logrotate.conf(5)
RequiresMountsFor=/var/log
ConditionACPower=true

[Service]
Type=oneshot
ExecStart=/usr/sbin/logrotate /etc/logrotate.conf

# performance options
Nice=19
IOSchedulingClass=best-effort
IOSchedulingPriority=7

# hardening options
#  details: https://www.freedesktop.org/software/systemd/man/systemd.exec.html
#  no ProtectHome for userdir logs
#  no PrivateNetwork for mail deliviery
#  no NoNewPrivileges for third party rotate scripts
#  no RestrictSUIDSGID for creating setgid directories
LockPersonality=true
MemoryDenyWriteExecute=true
PrivateDevices=true
PrivateTmp=true
ProtectClock=true
ProtectControlGroups=true
ProtectHostname=true
ProtectKernelLogs=true
ProtectKernelModules=true
ProtectKernelTunables=true
ProtectSystem=full
RestrictNamespaces=true
RestrictRealtime=true

logrotate.serviceには、ExecStart=/usr/sbin/logrotate /etc/logrotate.confとある通り、実際に起動するプログラムとその設定ファイルがそれぞれ記載されていることが分かります。

つまり、.timerユニットでタイミングを設定して、.serviceユニットで何を実行するのかを指定しているというわけですね。

では、いよいよこのユニットファイルをカスタムして、1時間に1回実行されるように変更したいと思います。

なお、ユニットファイルをカスタムする時は、以下の引用に言及されている通り/etc/systemd/system/ディレクトリ配下にunitファイルを作成します。

システムにインストールされるサービスは、/usr/lib/systemd/system/ ディレクトリーに保存されるデフォルトのユニットファイルと共に提供されます。システム管理者はこのファイルを直接変更できないため、カスタマイズは /etc/systemd/system/ ディレクトリーの設定ファイルに制限される必要があります。

参考:
https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/7/html/system_administrators_guide/sect-managing_services_with_systemd-unit_files#sect-Managing_Services_with_systemd-Unit_File_Modify

また、unit名については.timerと.serviceとの間で統一する必要があります。

参考: systemd.timer(5) — Linux manual page(前掲)

For each timer file, a matching unit file must exist, describing the unit to activate when the timer elapses. By default, a service by the same name as the timer (except for the suffix) is activated. Example: a timer file foo.timer activates a matching service foo.service. The unit to activate may be controlled by Unit= (see below).

翻訳:

各タイマー ファイルには、タイマーの経過時にアクティブ化するユニットを記述する、一致するユニット ファイルが存在する必要があります。デフォルトでは、タイマーと同じ名前 (サフィックスを除く) のサービスがアクティブ化されます。例: タイマー ファイル foo.timer は、一致するサービス foo.service をアクティブにします。

以上2つの注意点をおさえたうえで、デフォルトのunitファイルをコピーします。

cp /usr/lib/systemd/system/logrotate.timer /etc/systemd/system/
cp /usr/lib/systemd/system/logrotate.service /etc/systemd/system/

OnCalendar=dailyをOnCalendar=hourlyに変更してみます。
参考: 時間指定方法について
https://takuya-1st.hatenablog.jp/entry/2020/04/24/032822

sed -i s/OnCalendar=daily/OnCalendar=hourly/ /etc/systemd/system/logrotate.timer

変更の反映にはdeamon reloadしてあげる必要がありますが、先に変更前のlogrotate予定時刻を確認しておきます。

[root@ip-10-0-2-46 ~]# systemctl list-timers
NEXT                        LEFT          LAST                        PASSED       UNIT                         ACTIVATES
Sun 2023-12-03 05:31:06 JST 2min 37s left Sun 2023-12-03 05:26:06 JST 2min 22s ago nm-cloud-setup.timer         nm-cloud-setup.service
Sun 2023-12-03 05:59:39 JST 31min left    Sun 2023-12-03 04:28:08 JST 1h 0min ago  dnf-makecache.timer          dnf-makecache.service
Mon 2023-12-04 00:00:00 JST 18h left      Sun 2023-12-03 05:00:08 JST 28min ago    logrotate.timer              logrotate.service
Mon 2023-12-04 03:40:08 JST 22h left      Sun 2023-12-03 03:40:08 JST 1h 48min ago systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service

4 timers listed.
Pass --all to see loaded but inactive timers, too.

出力結果から、2023-12-04 00:00:00予定ということが分かりました。

変更を反映してみます。

systemctl daemon-reload
systemctl restart logrotate.service

変更反映後の予定時刻を確認してみます。

[root@ip-10-0-2-46 ~]# systemctl list-timers
NEXT                        LEFT       LAST                        PASSED       UNIT                         ACTIVATES
Sun 2023-12-03 05:31:06 JST 37s left   Sun 2023-12-03 05:26:06 JST 4min 22s ago nm-cloud-setup.timer         nm-cloud-setup.service
Sun 2023-12-03 05:38:08 JST 7min left  Sun 2023-12-03 04:28:08 JST 1h 2min ago  dnf-makecache.timer          dnf-makecache.service
Sun 2023-12-03 06:00:00 JST 29min left Sun 2023-12-03 05:00:08 JST 30min ago    logrotate.timer              logrotate.service
Mon 2023-12-04 03:40:08 JST 22h left   Sun 2023-12-03 03:40:08 JST 1h 50min ago systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service

4 timers listed.
Pass --all to see loaded but inactive timers, too.

2023-12-03 06:00:00 予定となり、予定時刻が変更されたのが確認できました。

ただし、これでお終いではありません。
logrotate.timerはlogrotate.serviceをトリガーしますが、logrotate.serviceは実質的に/usr/sbin/logrotate /etc/logrotate.confを実行しているだけでした。

つまり、logrotate.timerのおかげで1時間に1回/usr/sbin/logrotateが実行されるようになりましたが、実際にログがローテーションされるかどうかは/etc/logrotate.conf(もしくは個別に設定している/etc/logrotate.d/ディレクトリ配下のファイル)の内容に依存しています。

そこでlogrotate.confの内容を確認してみます。

[root@ip-10-0-2-46 ~]# cat /etc/logrotate.conf
# see "man logrotate" for details

# global options do not affect preceding include directives

# rotate log files weekly
weekly

# keep 4 weeks worth of backlogs
rotate 4

# create new (empty) log files after rotating old ones
create

# use date as a suffix of the rotated file
dateext

# uncomment this if you want your log files compressed
#compress

# packages drop log rotation information into this directory
include /etc/logrotate.d

# system-specific logs may be also be configured here.

weeklyになっていますので、このままではlogrotateが実行されてもローテーションはされません。

そのためweeklyをhourlyに変更します。

sed -i s/weekly/hourly/ /etc/logrotate.conf

実際にログがローテされているか/var/log/messagesを例に確認してみます。

[root@ip-10-0-2-46 ~]# ls -lrt /var/log/messages*
-rw-------  1 root root  15324 Dec  3 05:59 /var/log/messages-2023120306
-rw-------  1 root root    351 Dec  3 06:00 /var/log/messages

ちゃんとできてましたね!!!

※/var/log/messagesのログローテについては、デフォルトで/etc/logrotate.d/rsyslogにおいて個別に追加設定されていますが、時間設定はされていません。デフォルトの設定を変更していない今回に限っては、/etc/logrotate.d/rsyslogの設定内容は関係ありません。

余談: anacronによるlogrotateの制御

systemdのteimerユニットによるlogrotateの制御についてみてきました。

しかし、よりよく理解するためには以前のやり方がどうであったか、比較の対象についても合わせて知ることが時として肝要かと思いますので、以下ではanacronでlogrotateを実行する場合について見ていきます。

anacronとは

manページには以下のように書かれてあります。

anacron(8) – Linux man page

Anacron is used to execute commands periodically, with a frequency specified in days. Unlike cron(8), it does not assume that the machine is running continuously. Hence, it can be used on machines that aren’t running 24 hours a day, to control regular jobs as daily, weekly, and monthly jobs.

参考:
https://linux.die.net/man/8/anacron

翻訳すると以下の通りです。

Anacron は、日単位で指定された頻度でコマンドを定期的に実行するために使用されます。 cron(8) とは異なり、マシンが継続的に実行されていることを前提としていません。したがって、24 時間稼働していないマシンでも使用して、定期的なジョブを日次、週次、月次のジョブとして制御できます。

このanacronですが、例えば、AmazonLinux2でもlogrotateを実行するために利用されています。
以下では、設定の内容を確認してみます。

Amazon Linux 2の場合

Amazon Linux 2のanacrontabを見てみると、1日1回、コマンドとして/etc/cron.dailyを実行する設定がなされてあります。

[root@ip-10-0-2-89 ~]# cat /etc/anacrontab
# /etc/anacrontab: configuration file for anacron

# See anacron(8) and anacrontab(5) for details.

SHELL=/bin/sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
# the maximal random delay added to the base delay of the jobs
RANDOM_DELAY=45
# the jobs will be started during the following hours only
START_HOURS_RANGE=3-22

#period in days   delay in minutes   job-identifier   command
1       5       cron.daily              nice run-parts /etc/cron.daily
7       25      cron.weekly             nice run-parts /etc/cron.weekly

実際に、/etc/cron.dailyを見てみるとディレクトリになっており、その配下にlogrotateというファイルがあることが分かります。

[root@ip-10-0-2-89 ~]# ls -lrt /etc/cron.daily
total 12
-rwx------ 1 root root 219 Jul 27  2018 logrotate
-rwx------ 1 root root 208 Jul 27  2018 mlocate
-rwxr-xr-x 1 root root 618 Apr 29  2019 man-db.cron

さらにその中身を見てみると、設定ファイル/etc/logrotate.confを引数に、/usr/sbin/logrotateを起動するシェルスクリプトであることが分かります。

[root@ip-10-0-2-89 ~]# cat /etc/cron.daily/logrotate
#!/bin/sh

/usr/sbin/logrotate -s /var/lib/logrotate/logrotate.status /etc/logrotate.conf
EXITVALUE=$?
if [ $EXITVALUE != 0 ]; then
    /usr/bin/logger -t logrotate "ALERT exited abnormally with [$EXITVALUE]"
fi
exit 0

つまり、Amazon Linux2においては、/usr/sbin/logrotateを起動するためのシェルスクリプトが日次で実行されるよう/etc/anacrontabによって定義されているということです。

ちなみにRHEL9を覗いてみると以下の通りになっており、anacrontab自体は存在しますが、cron.daily/ディレクトリ配下にはlogrotateのスクリプトが存在しません。

# anacrontabは存在するが...。
[root@ip-10-0-2-89 ~]# cat /etc/anacrontab
# /etc/anacrontab: configuration file for anacron

# See anacron(8) and anacrontab(5) for details.

SHELL=/bin/sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
# the maximal random delay added to the base delay of the jobs
RANDOM_DELAY=45
# the jobs will be started during the following hours only
START_HOURS_RANGE=3-22

#period in days   delay in minutes   job-identifier   command
1       5       cron.daily              nice run-parts /etc/cron.daily
7       25      cron.weekly             nice run-parts /etc/cron.weekly
@monthly 45     cron.monthly            nice run-parts /etc/cron.monthly

# logrotateスクリプトが存在しない。
[root@ip-10-0-2-46 ~]# ls -lrt /etc/cron.daily
total 4
-rwxr-xr-x. 1 root root 341 Oct 27 10:47 update-client-config-packages

もちろん、このようになっているのは、先述した通りRHEL9ではsystemdのtimerユニットを利用しているからですね。

さて、以上でsystemdとanacronによるlogrotateの相違点についても理解できたかと思います。

おわりに

いかがだったでしょうか。
それではまたお会いしましょう!!
あでゅー!!

Last modified: 2023-12-03

Author