Linuxでジョブをスケジューリングする:cronとsystemd timerの比較

Linuxシステム管理において「いつ」ジョブが実行されるかは「何」を実行するかと同じくらい重要です。 最も古くから使われているのはcron、最近のディストリビューションではsystemdが標準になったことで、systemd timerを使うケースも増えています。 この記事では両者を比較し、実際にどのような場面でどちらを使うと良いかを整理します。


1. なぜcronとsystemd timerを比較するのか?



多くのLinuxサーバーでは次のような要件があります。

  • 毎朝3時にバックアップを実行
  • 5分ごとにヘルスチェックスクリプトを実行
  • 毎週日曜日にログを圧縮・整理
  • 起動後1分で一度だけ実行する初期化作業

従来はすべてcronで処理していましたが、最近では多くのディストリビューションがsystemdをデフォルトのinitシステムとして採用し、systemd timerが強力な代替手段として登場しています。


2. cronの基本概念

2.1 cronとは?

cronは古いUnix/Linuxスケジューラです。 決められた時間・周期でコマンドを実行し、設定は主にcrontabファイルで管理します。

2.2 cron設定の場所

  • システム全体: /etc/crontab, /etc/cron.d/
  • ユーザー別: crontab -eで編集されるper-user crontab

例として、毎朝3時にバックアップスクリプトを実行する設定:

0 3 * * * /usr/local/bin/backup.sh

フィールドの意味は次の通りです。

分  時  日  月  曜日  コマンド
0   3   *   *   *    /usr/local/bin/backup.sh

2.3 cronのメリット

  • 長年にわたり検証されたツール
  • ほとんどのLinux/Unixシステムで同じ概念
  • 設定文法がシンプルで例が豊富
  • systemd以前のシステムでも使用可能

2.4 cronの限界

  • サービスと分離されているため「どのサービスに関連するジョブか」を追跡しにくい
  • 状態・ログ確認が不便(syslog、メールなどに分散)
  • 依存関係管理(他のサービスが先に起動している必要がある等)が弱い
  • 「起動後5分で一度だけ実行」など相対時間ベースのスケジューリングが曖昧

3. systemd timerの基本概念



3.1 systemd timerとは?

systemd timerはsystemdのタイマーユニット(.timer)です。 特定の条件(時間、起動後経過時間等)に応じて別のsystemdサービスを実行します。

つまり構造は常にペアで存在します。

  • myjob.service → 実際に実行するタスクを定義
  • myjob.timer → いつ実行するかを定義

3.2 サービス + タイマーの基本例

毎朝3時にバックアップを実行する例をsystemd timerで書くと次のようになります。

(1) サービスユニット: /etc/systemd/system/backup.service

[Unit]
Description=Daily backup job

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh

(2) タイマーユニット: /etc/systemd/system/backup.timer

[Unit]
Description=Run daily backup at 3:00

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

[Install]
WantedBy=timers.target

有効化:

sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer

3.3 systemd timerのメリット

  • サービスと明確に結びつく: どのサービスがどのタイマーで実行されるか構造がはっきり
  • ログ管理が容易: journalctl -u backup.serviceなどでログを確認
  • 柔軟なトリガー条件:
  • OnCalendar=: cronに似た時間ベースの表現
  • OnBootSec=: 起動後X秒後
  • OnUnitActiveSec=, OnUnitInactiveSec=など: 最後の実行後一定時間
  • 依存関係処理: 「ネットワークが起動した後にのみ実行」などをサービスのAfter=network-online.target等で表現可能
  • systemdエコシステムと統合: systemctl list-timers, systemctl statusで状態確認

3.4 systemd timerのデメリット

  • cronに比べ学習曲線がある(サービス+タイマーの2ファイルが必要)
  • systemd環境に依存(古いシステムやnon-systemd環境では使用不可)
  • 単純な1行ジョブには「やや過剰」に感じることがある

4. cron vs systemd timer: 機能比較

4.1 大まかな比較表

項目 cron systemd timer
設定場所 /etc/crontab, crontab -e /etc/systemd/system/*.service/etc/systemd/system/*.timer
実行対象 任意のコマンド/スクリプト systemdサービス
時間表現 cron式(5フィールド) OnCalendar, OnBootSec等多様
起動後X秒/分/時間 @reboot+遅延(sleep等)で代用は可能だが、素直には書きにくい OnBootSec, OnStartupSecで直接サポート
失敗/再試行管理 別実装必要 サービスユニット設定で一部可能
ログ確認 syslog、メール等 journalctl -u <service>
依存性(ネットワーク/ファイルシステム) 基本サポートなし Unitオプション(After=, Requires=等)で表現
システム統合度 低い 非常に高い(systemd全体と連携)
移植性(他OSへ) 比較的高い systemdに依存

5. 例で見る違い

5.1 毎朝3時のバックアップ: cron vs timer

cron版

0 3 * * * /usr/local/bin/backup.sh

systemd timer版

# backup.service
[Unit]
Description=Daily backup job

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
# backup.timer
[Unit]
Description=Run daily backup at 3:00

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

[Install]
WantedBy=timers.target

5.2 起動後5分で一度だけ実行

cronでは@reboot + sleeprc.localなどのトリックが必要です。

systemd timerで実装

# init-job.service
[Unit]
Description=Run custom init job after boot

[Service]
Type=oneshot
ExecStart=/usr/local/bin/init-job.sh
# init-job.timer
[Unit]
Description=Run init job 5 minutes after boot

[Timer]
OnBootSec=5min
AccuracySec=1min

[Install]
WantedBy=timers.target

これで、起動後5分経過するとinit-job.serviceが一度だけ実行されます。


6. どちらを選ぶべきか?

6.1 cronを継続して使うと良いケース

  • 既に動作しているcronジョブが多く、systemd統合が必須でない場合
  • 非常にシンプルな定期ジョブが数個だけある小規模サーバー
  • systemd以外の環境(コンテナ、BSD、非常に古いOSなど)も考慮する場合

6.2 systemd timerを使うと良いケース

  • 既にサービスがほとんどsystemdで管理されている場合
  • ジョブ失敗時のログ/状態を一元管理したい場合
  • 「起動後X時間後」や「最後の実行後Y時間後」など相対時間ベースのスケジューリングが必要な場合
  • 特定サービス(例: DB、ネットワーク)が起動した後にのみ実行したいジョブがある場合
  • CI/CDパイプライン、プロダクション環境などで一貫した管理/観測性が必要な場合

7. 既存cronジョブをsystemd timerへ移行する際のヒント

実際のマイグレーションは次の流れで考えると楽です。

  1. 現在のcronジョブ一覧を整理 * crontab -l * /etc/crontab, /etc/cron.d/* を確認
  2. 各ジョブを「サービス + タイマー」に分解 * 「何を実行するか?」 → .service * 「いつ実行するか?」 → .timer
  3. 時間表現を変換 * 0 3 * * *OnCalendar=*-*-* 03:00:00 * */5 * * * *OnCalendar=*:0/5(5分ごと等、複数表現が可能)
  4. テスト * systemctl start myjob.service で手動実行を確認 * systemctl start myjob.timersystemctl list-timers でスケジュール確認
  5. 既存cronを無効化 * 新しいタイマーが安定したら、該当cronエントリはコメントアウトまたは削除

8. まとめ:共存も可能

整理すると:

  • cronは依然として単純で移植性の高いスケジューラ
  • systemd timerはモダンLinux環境でサービス中心の管理、観測性、柔軟なトリガーを提供する強力なツール

どちらか一方が「正解」というわけではありません。既存のcronジョブを無理に全部移行する必要はありませんが、新規に作成するスケジュールやサービスと強く結びつくジョブはsystemd timerを検討すると良いでしょう。

image