systemd timer による定期実行
近年のディストリビューションでは cron の代わりに systemd の timer ユニットで定期実行を組む方法が広く使われます。timer は実行する処理を書いた service ユニットと、起動タイミングを書いた timer ユニットの2つで構成します。cron に比べてログが journal に統合され、依存関係の指定や、電源オフで逃した実行を後から補う Persistent 設定ができる点が利点です。systemctl list-timers で次回起動の予定を一覧できます。
近年の主要なディストリビューションでは、起動処理やサービス管理を一手に担う systemd が標準になっています。この systemd には定期実行のしくみも備わっており、cron の代わりに timer ユニットで定期処理を組む方法が広く使われるようになりました。考え方は cron と同じ「決めたタイミングで処理を動かす」ですが、systemd の世界に統合されることで、ログの一元管理・依存関係の指定・逃した実行の補完といった、素の cron には無い利点が得られます。新しくサーバを組むなら、定期実行は cron にするか systemd timer にするか、という選択肢を意識して持っておくと、設計の幅が広がります。とくに、すでに他のサービスを systemd で管理している環境なら、定期処理も timer に寄せると運用の見え方がそろって扱いやすくなります。
service と timer の2点セット
systemd timer の最大の特徴は、「何を実行するか」と「いつ実行するか」を別々のユニットファイルに分けて書く点です。前者を service ユニット(拡張子 .service)、後者を timer ユニット(拡張子 .timer)と呼び、この2つをペアで用意します。原則として同じ名前にそろえ、たとえば backup.service と backup.timer のように対応させると、timer 側が時間になったとき自動的に同名の service を起動してくれます。これらのファイルは、システム全体用なら /etc/systemd/system/ に、ユーザ個人用なら ~/.config/systemd/user/ に置きます。service には実際の処理を ExecStart=/usr/local/bin/backup.sh のように書き、一度だけ走って終わる処理なので Type=oneshot を指定するのが定番です。役割が2つのファイルに分かれることで、「実行内容の修正」と「スケジュールの変更」を独立して扱えるのが利点です。ファイルを新規に置いたり書き換えたりした後は、systemctl daemon-reload で systemd に読み直させる必要がある点も覚えておきましょう。
OnCalendar — カレンダー式の指定
timer ユニットでスケジュールを書く中心が OnCalendar= です。書式は 曜日 年-月-日 時:分:秒 の並びで、省略した部分や * の部分は「毎」として扱われます。たとえば OnCalendar=*-*-* 03:00:00 は毎日午前3時、OnCalendar=Mon..Fri 09:00 は平日の午前9時、OnCalendar=*-*-01 00:00:00 は毎月1日の午前0時を表します。daily・weekly・monthly・hourly といった分かりやすい短縮名も使え、OnCalendar=daily は毎日午前0時、OnCalendar=hourly は毎時0分の意味になります。複数の時刻を指定したいときは行を複数並べることもできます。書いた式が狙いどおりに解釈されるかは、systemd-analyze calendar "Mon..Fri 09:00" のように検証でき、その式が次に該当する日時まで表示してくれるので、登録前の確認に重宝します。cron 式が暗号的に見えるのに比べ、OnCalendar は曜日や日付が言葉に近く書けるため、後から読み返したときに意図が分かりやすいのも利点です。
OnCalendar 以外に、基準時刻からの相対指定もあります。OnBootSec= はシステム起動からの経過時間で、OnUnitActiveSec= はそのユニットが最後に動いてからの経過時間でトリガーします。OnUnitActiveSec=15min なら「前回実行から15分後」に繰り返し動くので、cron の */15 のような一定間隔の繰り返しを表現できます。起動直後に1回動かしたうえで以後一定間隔で回す、といった組み合わせも、これらを併用すれば書けます。
有効化と一覧、ログの確認
ユニットファイルを置くだけでは動きません。timer を有効にして起動する必要があり、systemctl enable --now backup.timer のように --now を付けると「次回のシステム起動時からの自動有効化」と「いますぐの起動」を同時に行えます。ここで有効化するのは service ではなく timer 側である点に注意してください。登録された timer の状況は systemctl list-timers で一覧でき、NEXT 列に次回起動の予定時刻、LAST 列に前回起動時刻、UNIT 列に対応する timer 名が並ぶので、「ちゃんと次が予約されているか」を目で確認できます。実行結果のログは、cron のようにメールやファイルへ自分で逃がす必要はなく、journal に自動で記録されます。journalctl -u backup.service のように unit を指定すれば、その処理の過去の実行ログだけをまとめて時系列で追えるのが、cron に対する大きな使い勝手の差です。失敗した回も含めて履歴がそろって残るので、原因の切り分けが格段にやりやすくなります。
Persistent と cron との違い
systemd timer がとりわけ強いのが、電源が落ちていた間に逃した実行を後から補える点です。timer ユニットに Persistent=true を設定しておくと、systemd は前回の実行時刻を記録し、もし予定時刻にマシンが停止していて実行を逃した場合、次に起動したときにその分をすぐ実行してくれます。常時起動でないノートPCや、間欠的に電源を入れるエッジ機器で「電源を入れていなかった日のバックアップが丸ごと飛ぶ」といった事態を防げるわけです。これは、逃した実行は黙って諦める素の cron には無い挙動で、同様の補完を cron 系で得たいときは anacron という別のしくみを併用する必要があります。まとめると、ログが journal に統合される・依存関係や条件を細かく書ける・Persistent で逃した実行を補える、という3点が systemd timer を選ぶ主な理由です。一方で、ちょっとした処理を1行でさっと仕込むだけなら crontab -e のほうが手軽、という住み分けになります。要件が素直なら cron、運用の作り込みや堅牢さを求めるなら systemd timer、と覚えておくと選択に迷いません。