それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

Linuxのデフォルトのタイマーがわかりにくい。cron/systemd.timerの優先関係

伝統的に、cron/atq によってタイマー・スケジュール実行は管理されています。

現代のLinuxでは、systemd.timer が追加されています。いまのLinuxでは両方が存在します。

両方が存在するために、タイマーの設定ファイルが systemd.timer / cron に同時に存在しています。

デフォルトのcron を見る。

takuya@:~$ ls  /etc/cron.*
/etc/cron.d:
anacron  certbot  e2scrub_all  php  popularity-contest

/etc/cron.daily:
0anacron  apt-compat       cracklib-runtime  exim4-base  man-db   popularity-contest
apache2   automysqlbackup  dpkg              logrotate   mlocate

/etc/cron.hourly:

/etc/cron.monthly:
0anacron

/etc/cron.weekly:
0anacron  man-db

デフォルトの systemd.timerを見る。

takuya@:~$ systemctl status *.timer | grep enabled
     Loaded: loaded (/lib/systemd/system/phpsessionclean.timer; enabled; vendor preset: enabled)
     Loaded: loaded (/lib/systemd/system/anacron.timer; enabled; vendor preset: enabled)
     Loaded: loaded (/lib/systemd/system/man-db.timer; enabled; vendor preset: enabled)
     Loaded: loaded (/lib/systemd/system/apt-daily-upgrade.timer; enabled; vendor preset: enabled)
     Loaded: loaded (/lib/systemd/system/apt-daily.timer; enabled; vendor preset: enabled)
     Loaded: loaded (/lib/systemd/system/e2scrub_all.timer; enabled; vendor preset: enabled)
     Loaded: loaded (/lib/systemd/system/exim4-base.timer; enabled; vendor preset: enabled)
     Loaded: loaded (/lib/systemd/system/logrotate.timer; enabled; vendor preset: enabled)
     Loaded: loaded (/lib/systemd/system/fwupd-refresh.timer; enabled; vendor preset: enabled)
     Loaded: loaded (/lib/systemd/system/fstrim.timer; enabled; vendor preset: enabled)

思い切り、被ってないか?

2つを見比べると、少し気づいたことがあります。同じ名前の設定ファイルが存在します。anacron とか logroatateとか。

両方に設定が存在するもの

これらは、systemdにもcron にも登場します。つまり被ってます。

  • anacron
  • certbot
  • e2scrub
  • php/phpsessionclean
  • exim4-base
  • man-db
  • logrotate
  • popularity-contest

cron だけにあるもの

  • apt-compat
  • cracklib-runtime
  • apache2
  • automysqlbackup
  • dpkg
  • mlocate

systemd だけにあるもの

  • fstrim
  • fwup-refresh

被ってるものはどうなってるのか

被っていたら、正しくジョブが実行されないはずです。どうなってるのでしょうか。

anacron の例

よくこれで、重複実行されないよね。とおもったので調べました。

anacrom.timer の中身

[Unit]
Description=Trigger anacron every hour

[Timer]
OnCalendar=*-*-* 07..23:30
RandomizedDelaySec=5m
Persistent=true

[Install]
WantedBy=timers.target

timer 実体は anacron.service にありますね。 service/timer のペア運用を知らないと気づかないね、初見殺し。

takuya@:~$ cat /lib/systemd/system/anacron.service
[Unit]
Description=Run anacron jobs
After=time-sync.target
# By default, anacron will not run when no AC power is connected to system.
# If you are using systemd and want to run anacron even when running on
# battery, you should create the following file with the specified content
# and then call "systemctl daemon-reload":
#    /etc/systemd/system/anacron.service.d/on-ac.conf:
#        [Unit]
#        ConditionACPower=
# See /usr/share/doc/anacron/README.Debian for detailed information.
ConditionACPower=true
Documentation=man:anacron man:anacrontab

[Service]
EnvironmentFile=/etc/default/anacron
ExecStart=/usr/sbin/anacron -d -q $ANACRON_ARGS
IgnoreSIGPIPE=false
KillMode=mixed
# Use SIGUSR1 to stop gracefully
KillSignal=SIGUSR1

[Install]
WantedBy=multi-user.target

そしてこれが、cron.d

takuya@:~$ cat /etc/cron.d/anacron
# /etc/cron.d/anacron: crontab entries for the anacron package

SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

30 7-23 * * *   root    [ -x /etc/init.d/anacron ] && if [ ! -d /run/systemd/system ]; then /usr/sbin/invoke-rc.d anacron start >/dev/null; fi

cron.d/anacron では /run/systemd/system のチェックし、cronは無視される。具体的には、systemd-initがpid=1環境であれば 、cron では実行スキップ

互換性重視のために、ファイルを残している。そのような意図が見えます。

動かないけど、cronは起動するよ

でも、これってcron 自体は実行起動しますよね。testコマンドだけで負荷もないから気にしないけど。

e2scrub の場合

systemdが存在したら実行するようです。

でもこれも、cron 自体は起動しますよね?負荷は気にはならないけど、実行されるという事実が気になる。

takuya@:~$ cat /etc/cron.d/e2scrub_all
30 3 * * 0 root test -e /run/systemd/system || SERVICE_MODE=1 /usr/lib/x86_64-linux-gnu/e2fsprogs/e2scrub_all_cron
10 3 * * * root test -e /run/systemd/system || SERVICE_MODE=1 /sbin/e2scrub_all -A -r

mlocate の場合

mlocate の場合は etc/cron.daily にあるので単純にスクリプトです。

cron の中身不要じゃね?

消したい・・・消したいけど、dist-upgrade時に /etc/の中身変わってるよと聞かれるのがうざい。

systemd timer より、cron.dailyは可読性がいい。

systemd timer でみるより、/etc/cron.daily/logrotate のほうが可読性が高く、何をしているのかよく分かる。

#!/bin/sh

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

# this cronjob persists removals (but not purges)
if [ ! -x /usr/sbin/logrotate ]; then
    exit 0
fi

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

mlocate は実行されないかも?

mlocate は、cron にだけ存在する。しかし、cron.daily/mlocateではsystemd側では動作しない。

mlocate は systemd では mask されている。

takuya@:~$ sudo systemctl status mlocate.service
● mlocate.service - Update a database for mlocate
     Loaded: loaded (/lib/systemd/system/mlocate.service; static)
     Active: inactive (dead)
       Docs: man:updatedb.mlocate(8)
             man:updatedb.conf(5)
takuya@:~$ sudo systemctl status mlocate.timer
● mlocate.timer
     Loaded: masked (Reason: Unit mlocate.timer is masked.)
     Active: inactive (dead)
    Trigger: n/a

しかし、cron.daily では mlocate は systemd環境下で何も実行しないよう構成されていました。

takuya@:~$ cat  /etc/cron.daily/mlocate
#! /bin/bash

set -e

# skip in favour of systemd timer
if [ -d /run/systemd/system ]; then
    exit 0
fi
# 中略
flock --nonblock /run/mlocate.daily.lock $NOCACHE $IONICE nice /usr/bin/updatedb.mlocate

cron記述では、systemdを優先する条件分岐があり、systemd.timerではマスクされる。これでは、何も実行されませんね。どうなってるのでしょうか。

タイマーは見直したほうがいい。

systemd信用できない。