MENU

cronからsystemdタイマーへの移行|ジョブ再現性検証・失敗時通知・コンカレント制御設計

目次

なぜ今、cronからsystemdタイマーへ移行するのか

cron は30年以上にわたり Linux の定期ジョブ実行を支えてきましたが、現行の運用環境では次のような限界が顕在化しています。まずログが syslog や /var/log/cron にしか残らず、ジョブの標準出力・終了コードを体系的に追えません。次に、ジョブの依存関係(「Aが終わってからBを起動する」「ネットワークが上がってから実行する」)を cron だけで記述する手段がありません。さらに失敗しても再実行やアラートが自動では起きないため、無言で失敗するサイレントエラーが発生しやすい構造です。

systemd タイマーはこれらを構造的に解決します。journald による出力の完全記録、Requires= や After= による依存解決、OnFailure= による失敗ハンドリング、そして Type=oneshot との組み合わせによる多重起動防止が、ユニットファイル1枚で宣言できます。ディストリビューションの標準管理単位でもあるため、2026年現在 RHEL 9・Ubuntu 22.04/24.04・Debian 12 いずれでも追加パッケージなしで利用できます。

基本の移行手順——.service と .timer ユニットの作成

cron エントリ 0 3 * * * /usr/local/bin/backup.sh を systemd タイマーへ移行する想定例を示します。

.service ユニットの書き方

まずジョブ本体を記述する .service ファイルを /etc/systemd/system/backup.service として保存します。

[Unit]
Description=Daily backup job
After=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
User=backup
StandardOutput=journal
StandardError=journal

Type=oneshot はプロセス終了まで systemd がアクティブ状態を保持する指定です。ログはすべて journald へ流れます。User= でジョブ実行ユーザーを明示しておくと、root で動かしていた cron ジョブの権限を最小化できます。

.timer ユニットの書き方と有効化

次に /etc/systemd/system/backup.timer を作成します。

[Unit]
Description=Daily backup timer

[Timer]
OnCalendar=*-*-* 03:00:00
RandomizedDelaySec=300
Persistent=true

[Install]
WantedBy=timers.target

RandomizedDelaySec=300 は、同一インフラ上で多数のジョブが同時起動することで生じるタイマー集中負荷を分散させる目的で有効です。Persistent=true はシステムが停止していて起動タイミングを逃した場合、次回起動時に遅延実行する設定です。cron の anacron 相当の振る舞いを systemd で再現できます。

有効化と起動は次のコマンドで行います。

sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer
systemctl list-timers --all

ジョブ再現性を検証する

「スケジュール通りに実行されているか」「ジョブが正常終了しているか」の2軸で再現性を確認します。

次回実行時刻の確認には systemctl list-timers を使います。NEXT 列に次回予定が、LAST 列に前回実行が、PASSED 列に経過時間が表示されます。設定した OnCalendar= の構文が正しいか事前確認するには systemd-analyze calendar が有効です。

systemd-analyze calendar "*-*-* 03:00:00"

実行ログの確認は journalctl で行います。

# 直近20行を確認
journalctl -u backup.service -n 20

# 今日分だけ絞り込む
journalctl -u backup.service --since today

# リアルタイムで流す
journalctl -u backup.service -f

終了コードは journalctl の出力末尾に code=exited, status=0/SUCCESS の形式で記録されます。ステータスが SUCCESS 以外の場合はその前後の出力を追うことで原因を絞り込めます。systemctl status backup.service でも直近の実行結果と最終ログ行が確認できるため、素早いトリアージに有効です。

失敗時の通知を仕組みとして設計する

cron では失敗してもサイレントになりやすい一方、systemd タイマーは OnFailure= ディレクティブで「サービスが失敗したときに別のユニットを起動する」仕組みを持っています。

代表的なパターンは「通知専用 .service を OnFailure= で呼び出す」設計です。backup.service の [Unit] セクションに次の行を追加します。

[Unit]
Description=Daily backup job
After=network-online.target
OnFailure=notify-failure@%n.service

%n はユニット名に展開される specifier です。通知側ユニット /etc/systemd/system/notify-failure@.service の想定例を示します。

[Unit]
Description=Send failure notification for %i

[Service]
Type=oneshot
ExecStart=/usr/local/bin/notify-slack.sh "%i failed"

notify-slack.sh の実装はチームの通知チャネル(Slack Incoming Webhook・メール・PagerDuty など)に合わせて用意します。重要なのは通知ロジックをジョブ本体から切り離すことで、backup.service の変更なしに通知先を差し替えられる設計になる点です。なお、通知ユニット自体が失敗した場合の無限ループを避けるため、notify-failure@.service には OnFailure= を設定しないのが一般的です。

コンカレント制御——多重起動を防ぐ設計

前のジョブがまだ実行中なのに次のタイマーが発火するケースは、cron でも起きる古典的な問題です。systemd タイマーで Type=oneshot を指定すると、既存のサービスインスタンスが起動中の場合は新規起動をキューイングする動作になります。しかしキューの蓄積が問題になるケースでは、より明示的な制御が必要です。

実行中は後続の起動リクエストをスキップしたい場合、ExecCondition=(systemd 243 以降)が活用できます。

[Service]
Type=oneshot
ExecCondition=/bin/bash -c "! systemctl is-active --quiet backup.service"
ExecStart=/usr/local/bin/backup.sh

ExecCondition= は条件が成立しないときサービスを失敗なしにスキップできます。RHEL 9・Ubuntu 22.04 以降で標準搭載されており、スキップの意図が明示的に読み取れる点が利点です。

タイマー側でコンカレント制御を行う別のアプローチとして、絶対時刻(OnCalendar=)から実行完了後の相対間隔(OnActiveSec=)へ切り替える方法があります。

[Timer]
# 絶対時刻指定ではなく、前回実行完了から24時間後に次を起動
OnActiveSec=86400
OnBootSec=300

この設定ではジョブが長引いても次のジョブが追い越さない設計になります。処理時間が可変な重めのバッチ処理では、この切り替えを検討する価値があります。RuntimeMaxSec= でジョブの最大実行時間を上限として設定し、ハングした際に自動 kill する対策もあわせて検討すると運用の安全性が高まります。

2026年現行環境での注意点と差分

2026年時点の主要ディストリビューション環境でとくに注意が必要な差分を整理します。

  • RHEL 9 / Rocky Linux 9 / AlmaLinux 9:systemd 252 ベース。ExecCondition= は使用可能。cronie(cron デーモン)はデフォルトインストールされますが、依存がなければ削除を検討できます。SELinux ポリシーが有効な環境では、カスタムシェルスクリプトを ExecStart= で呼ぶ場合に restorecon でラベルを修正しないと Permission Denied になるケースがあります。
  • Ubuntu 24.04 LTS:systemd 255 ベース。ユーザーセッションタイマー(systemctl --user)が安定しており、root 権限不要のジョブ移行に活用できます。ただしユーザーセッションはログアウトで停止するため、サーバー用途では linger を有効化する必要があります(loginctl enable-linger <user>)。
  • Debian 12 (Bookworm):systemd 252 ベース。タイマーの精度は AccuracySec= のデフォルトが 1min のままです。秒単位の精度が必要な場合は AccuracySec=1s を明示的に指定してください。
  • コンテナ・クラウドネイティブ環境:Kubernetes など systemd が動かない環境では、CronJob リソースや Argo Workflows など別の仕組みを使います。systemd タイマーはホスト OS 上のスケジューリングに特化しているため、コンテナオーケストレーション層との設計上の境界を明確にしておく必要があります。

cron から systemd タイマーへの移行は、既存のジョブ定義を1対1で変換するだけでなく、ログ・通知・コンカレント制御をユニットファイルで宣言的に管理できる機会です。運用ディストリビューションのバージョンと使用する systemd 機能の対応を事前に確認した上で、段階的に移行を進めることで安定した運用につながります。

「コマンドは打てる。次は”現場で通用する型”を最短で身につけたい」——そんなあなたへ。

ネットの断片的なTipsをコピペするだけでは、体系的な運用スキルはなかなか積み上がりません。リナックスマスター.JPの無料メルマガ「リナマガ」では、現場で実際に使うLinuxサーバー運用の考え方を、順を追って実務目線でお届けしています。いま登録された方には、安全なサーバー構築の「型」をまとめた『Linuxサーバー構築入門マニュアル(図解60P)』を完全無料でプレゼント中です。

>> 図解60P『Linux入門マニュアル』を無料で受け取る
(メルマガ登録は10秒・いつでも解除OK)

※ 「独学の時間がもったいない」「プロから現場の技術を最短で学びたい」という本気の方には、2日で実務レベルが身につく初心者向けハンズオンセミナーもご用意しています。

PR・広告

[試して理解]Linuxのしくみ 増補改訂版(Amazon)

プロセス・権限・ファイルシステムなどLinux内部を図解で理解できる一冊。運用判断の勘所に。

Amazonで見る

※ Amazonのアソシエイトとして、当サイトは適格販売により収入を得ています。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

目次