さよならCron Job:より高度なタスクスケジューリングを実現するsystemd Timerへの移行

Linux tutorial - IT technology blog
Linux tutorial - IT technology blog

クイックスタート:5分で最初のタスクを実行する

すぐに試してみたいですか?systemdの威力を実感するために、1分ごとにログを自動記録するスクリプトを作成する手順を紹介します。

ステップ1:スクリプトの準備

# スクリプト管理用ディレクトリを作成
sudo mkdir -p /opt/scripts

# シンプルなスクリプトファイルを作成
sudo bash -c 'cat <<EOF > /opt/scripts/hello-timer.sh
#!/bin/bash
echo "タスク実行時刻: \$(date)"
EOF'

# 実行権限を付与
sudo chmod +x /opt/scripts/hello-timer.sh

ステップ2:Serviceの定義

このファイルは具体的な実行内容を記述します。/etc/systemd/system/hello-timer.serviceにファイルを作成してください:

[Unit]
Description=定期ログ出力スクリプト

[Service]
Type=oneshot
ExecStart=/opt/scripts/hello-timer.sh

ステップ3:Timerの設定

このファイルはタイマー(スケジューラ)の役割を果たします。/etc/systemd/system/hello-timer.timerを作成します(ファイル名はServiceファイルと一致させる必要があります):

[Unit]
Description=1分ごとにhello-timerを実行

[Timer]
OnCalendar=*:*:00
Unit=hello-timer.service

[Install]
WantedBy=timers.target

システムを有効化して実行を開始します:

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

確認するには、次のコマンドを使用します:<a href="https://itfromzero.com/ja/linux/linux%e3%81%ae%e5%8a%b9%e7%8e%87%e7%9a%84%e3%81%aa%e3%83%87%e3%83%90%e3%83%83%e3%82%b0%ef%bc%9ajournalctl%e3%81%a8dmesg%e3%81%ab%e3%82%88%e3%82%8b%e8%a9%b3%e7%b4%b0%e3%82%ac%e3%82%a4%e3%83%89.html">journalctl</a> -u hello-timer.service。ログが1分ごとに規則正しく表示されます。

なぜCronをやめてsystemd Timerに移行したのか?

Crontabは小規模な個人タスクには非常に便利です。しかし、4GB RAMを搭載したUbuntu 22.04サーバー群で数十個のバックアップジョブを同時に管理していた際、Cronでは制御が難しくなりました。

最大の課題はロギング(Logging)です。Cronでは、スクリプトのエラーが音もなく消えてしまうことがよくあります。>> /var/log/app.log 2>&1のように手動でログをリダイレクトする必要がありますが、これは非常に手間です。systemdなら、すべてが自動的にjournaldに送られます。コマンド一つで、実行履歴やエラーの痕跡をすべて確認できます。

次に依存関係(Dependency)の制御です。MySQLが起動した後、あるいはネットワークが疎通した後にのみスクリプトを実行したい場合、Cronでは標準的にこれを実現できません。systemdなら、サービス間の関係を非常に厳密に管理できます。

特に重要なのがリソース制限です。バックアップスクリプトがCPUを100%消費し、Webサーバーがダウンするという事態に遭遇したことがあります。systemdでは、Serviceファイル内にMemoryLimit=500MCPUQuota=30%を記述するだけで、システムを保護するためにリソースを即座に制限できます。

systemd Timerの構造を深く理解する

秘訣は、Service(何をやるか)とTimer(いつやるか)が分離されていることにあります。

1. Serviceファイル (.service)

スクリプトの実行方法に焦点を当てます:

  • Type=oneshot: ログのクリーンアップやデータベースのバックアップなど、実行して終了するジョブに適しています。
  • User=www-data: セキュリティリスクを軽減するため、rootではなく特定のユーザー権限でスクリプトを実行します。

2. Timerファイル (.timer)

柔軟な時間設定を行います:

  • モノトニックタイマー(相対時間): 起動時や前回の実行時からの経過時間で計算します。例:OnUnitActiveSec=1h(1時間ごとに再実行)。
  • リアルタイムタイマー(カレンダー時間): Cronのようにスケジュールに従って実行しますが、より可読性が高いです。

いくつかのOnCalendarの例:

  • *-*-* 03:00:00: 毎日午前3時ちょうど。
  • Mon *-*-* 00:00:00: 毎週月曜日の深夜0時。
  • *:0/20: 20分おき。

自動リトライによるスマートなエラー処理

これは非常に価値のある機能です。ネットワークの不安定さが原因でクラウドへのデータ同期スクリプトが失敗した場合、systemdが自動的にリトライしてくれます。

Serviceファイルに追加する設定例:

[Service]
ExecStart=/opt/scripts/sync-cloud.sh
Restart=on-failure
RestartSec=60s
StartLimitBurst=5

この設定は、エラーが発生した場合に60秒待機してからリトライすることを意味します。5回連続で失敗して初めて、完全に停止し警告を送信します。

覚えておくと便利な管理コマンド

タイマーを管理するために、以下の3つのコマンドをよく使用します:

  • systemctl list-timers: ジョブの一覧を表示し、次のジョブがいつ実行されるかを正確に把握します。
  • systemctl status task.timer: タイマーの状態を確認します。
  • journalctl -u task.service -f: ログをリアルタイムで監視し、迅速にデバッグします。

トラブルを避けるための実践的な経験則

本番環境でのトラブルシューティングを経て得られた、3つの重要な注意点です:

  1. 絶対パスを使用する: systemdはpython3がどこにあるかを知りません。/usr/bin/python3 /path/to/script.pyのように記述してください。
  2. Persistent機能を活用する: バックアップ実行時刻の午前2時にサーバーが停止していた場合、午前7時に起動した際、Persistent=trueが設定されていれば、タイマーは即座に未実行分を実行します。Cronはデフォルトでそのジョブをスキップします。
  3. ファイル名を一致させる: 管理を容易にし、システムが自動的に関連付けを理解できるように、常にapp-sync.serviceapp-sync.timerのように同じ名前に設定してください。

2つのファイルを作成するのは、Crontabの1行よりも手間に感じるかもしれません。しかし、systemd Timerが提供する透明性とリソース管理能力は、それだけの価値が十分にあります。

Share: