EQVPS
ダッシュボード始める

VPSでcronとsystemdタイマーを使ってタスクをスケジュールする方法

バックアップ、クリーンアップ、スクリプトを定期実行します。5分でわかるcronと、ジョブを黙って壊す3つの落とし穴、そしてログ、停止中に逃した実行の補完、正確な時刻のためのsystemdタイマー。以下の挙動はすべて新しいUbuntu 24.04サーバーで検証しました。

サーバーの価値は、あなたが寝ている間に働くことにあります。バックアップ、ログのローテーション、証明書の更新、9時のレポート。Linuxにはそのためのスケジューラーが2つあります。cronは40年の歴史があり、1行で済みます。systemdタイマーは小さなファイルが2つ必要ですが、その代わりにログ、停止後の補完、正確な時刻が手に入ります。このガイドではUbuntu 24.04で両方を扱い、すべての挙動を当社イメージの新しいサーバーで確認しました。cronのジョブが何も言わずに失敗する3つのミスも含みます。

sudoを使えるユーザーが必要です。Ubuntuのセットアップガイドで作成できます。

1. 5分でわかるcron

cronは当社のUbuntuとDebianのイメージにインストール済みで、動いています。ユーザーごとにジョブの表があり、自分の表はこれで編集します。

crontab -e

各行は5つの時刻フィールドとコマンドです。

# min hour day month weekday  command
  30  3    *   *     *        /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1
  */5 *    *   *     *        /usr/local/bin/check.sh
  0   9    *   *     1-5      /usr/local/bin/report.sh
  @reboot                     /usr/local/bin/start-tunnel.sh

つまり、毎晩03:30、5分ごと、平日の09:00、そして起動のたびに1回です。*/5は5つごと、1-5は範囲、1,15はリストです。サーバーのタイムゾーンが使われ、当社のイメージではUTCです。ホスト名とタイムゾーンのガイドで一度設定すれば、cronはそれに従います。

何が実行されたかはjournalctl -u cronで確認します。実行ごとにユーザーとコマンドが記録され、3つの落とし穴もそこに現れます。

落とし穴1:出力が消える。 cronはジョブの出力をユーザーにメールします。当社のイメージにはメールサーバーがないため、journalに(CRON) info (No MTA installed, discarding output)と記録され、出力は失われました。最初の例のように>> /var/log/backup.log 2>&1で自分でリダイレクトしてください。

落とし穴2:パーセント記号。 crontabでは%がコマンドを終わらせ、残りを標準入力に変えます。date +%F > /tmp/todayをスケジュールしたところ、journalには実際に実行された内容CMD (date +)が残っていました。ファイルは作られず、エラーも届きませんでした。\%と書くか、コマンドをスクリプトにしてそのスクリプトをスケジュールしてください。

落とし穴3:環境の違い。 cronはジョブをbashではなく/bin/shで、ホームディレクトリから、最小限の環境で実行します。Ubuntu 24.04のPATHは評判より良く、rootでも一般ユーザーでも/usr/local/binと/snap/binを含むシステムの完全なパスでした。それでも足りないのはホームディレクトリ以下のすべてで、~/.local/bin、nvmのnode、pipx、cargoです。ジョブではフルパスを使うか、行を. $HOME/.profile;で始めてください。

ユーザーではなくシステムに属するジョブは、ユーザーという項目を1つ加えたファイルとして/etc/cron.d/に置きます。

30 3 * * * root /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1

Ubuntuは/etc/cron.hourly、cron.daily、cron.weekly、cron.monthlyの中身も実行します。毎時17分、06:25、日曜日の06:47、毎月1日の06:52です。実行可能なスクリプトを置けば、ほかと一緒に実行されます。

2. systemdタイマー

タイマーは2つのユニットでできています。作業をするサービスと、いつ実行するかを決めるタイマーです。まずスクリプトを作り、次にユニットを作ります。

sudo nano /usr/local/bin/backup.sh
sudo chmod +x /usr/local/bin/backup.sh
sudo nano /etc/systemd/system/backup.service
[Unit]
Description=Nightly backup

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
sudo nano /etc/systemd/system/backup.timer
[Unit]
Description=Run the backup every night at 03:30

[Timer]
OnCalendar=*-*-* 03:30
Persistent=true
AccuracySec=1m

[Install]
WantedBy=timers.target

有効にするのはサービスではなくタイマーです。

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

list-timersは次回の実行、それまでの残り時間、前回の実行を表示します。ログの設定は不要で、journalctl -u backup.serviceに開始、終了、出力の各行がタイムスタンプ付きで残ります。今すぐジョブを実行するにはsudo systemctl start backup.serviceです。スケジュールを信頼する前のスクリプトのテストにも使えます。

カレンダーの書式。 OnCalendarは曜日 年-月-日 時:分:秒と読み、*は任意の値です。使う前に式を確かめましょう。

systemd-analyze calendar "Mon..Fri *-*-* 03:00" --iterations 2

正規化された形と次の日時が表示されます。確認した式の例です。

OnCalendar意味
*-*-* 03:30毎日03:30
Mon..Fri *-*-* 09:00平日の09:00
*:0/1515分ごと
weekly月曜日の00:00
*-*-01 04:00毎月1日の04:00
*-*-* 03:00 Europe/Kyivキーウ時間の03:00。テストでは01:00 UTCに実行

知っておきたい3つの設定。 Persistent=trueは前回の実行をディスクに記録し、サーバーの停止中やタイマーの停止中に逃した実行を補完します。2分ごとのタイマーを止め、予定の実行が過ぎるのを待ってから再開したところ、逃した実行は同じ秒のうちに始まりました。Persistentがなければ、次の予定を待つだけです。

驚きはAccuracySecです。systemdはデフォルトで、起こす回数を減らすためにタイマーを1分の幅でまとめるので、最初の実行は07:42:00ではなく07:42:18でした。AccuracySec=1sにすると、その後の実行は07:46:00と07:48:00に秒単位で正確に揃いました。分が重要なときに設定し、夜間バックアップならデフォルトのままで構いません。

RandomizedDelaySec=10mはあえて逆のことをします。開始を一定の幅に散らし、10台のサーバーが同じ秒に同じバックアップ先へ殺到しないようにします。

1回だけのジョブならファイルは不要です。

sudo systemd-run --on-active=30m --unit=cleanup-once /usr/local/bin/cleanup.sh

1回だけ動いて消える一時的なタイマーが作られ、それまではsystemctl list-timersに表示されます。

3. どちらを使うか

cronsystemdタイマー
設定1行ファイル2つ
ログ出力をリダイレクトした場合のみjournalctl -u name.service
サーバー停止中に逃した実行失われるPersistent=trueで起動時に実行
次の時刻にまだジョブが動いている2つ目が起動する1つ目が終わるまでスキップ
正確な時刻分単位AccuracySec=1sで秒単位
ステータスなしlist-timers、systemctl status

1年後に見ても分かる簡単な1行ならcronを使います。失敗に気づく必要があるもの、重複してはいけないもの、再起動で逃しても実行すべきものにはタイマーを使います。バックアップ、証明書の更新、データベースのクリーンアップなどです。cronのジョブが重なる可能性があるなら、flock -n /tmp/backup.lock /usr/local/bin/backup.shで包めば、2つ目はすぐに終了します。

どちらのスケジューラーもジョブを起動したら終わりです。常に動き続け、クラッシュ後に復帰すべきプログラムには、スケジュールではなくサービスが必要です。systemdサービスの作り方と、WindowsならWindows Serverでスクリプトを24時間動かす方法を参照してください。最初のタイマーにはresticによるバックアップのガイドのものがおすすめです。自動セキュリティ更新はUbuntuに最初から入っているタイマーapt-daily-upgrade.timerで、新しいサーバーで動く17個のうちの1つです。

よくある質問

ターミナルでは動くのにcronでは動かないのはなぜですか?

よくある理由は3つです。cronはジョブをbashではなくshで実行するので、bash専用の構文は失敗します。nvm、pipx、cargoなどホームディレクトリに入れたツールはcronのPATHに含まれないため、フルパスを使ってください。そしてコマンド行の%はそこでコマンドを終わらせます。当社のサーバーではdate +%Fがdate +になりました。\%と書くか、コマンドをスクリプトに移してください。

cronの出力はどこに行きますか?

デフォルトではメールです。当社のイメージにはメールサーバーがないため、cronは'No MTA installed, discarding output'と記録し、出力は失われます。ジョブに>> /var/log/myjob.log 2>&1を付けるか、出力が自動でjournalに入るsystemdタイマーに切り替えてください。

cronと比べたsystemdタイマーの利点は?

journalctlでのログ、まだ実行中のジョブの2つ目を起動しないこと、Persistent=trueでサーバー停止中に逃した実行を補完すること、そして次回と前回の実行を示すステータス行です。簡潔さではcronが勝ります。ファイル2つではなく1行です。

タイマーが18秒遅れて動きました。なぜですか?

systemdはデフォルトで、システムを起こす回数を減らすためにタイマーを1分の幅でまとめます。これがAccuracySecの設定です。2分ごとのタイマーは07:42:00ではなく07:42:18に動きました。AccuracySec=1sにすると07:46:00ちょうどに動きました。正確な分が重要なときに設定してください。

cronとタイマーはどのタイムゾーンを使いますか?

サーバーのタイムゾーンで、当社のイメージではUTCです。timedatectlで一度設定すれば(ホスト名とタイムゾーンのガイドを参照)、cronもタイマーもそれに従います。タイマーは独自のゾーンも持てます。OnCalendar=*-*-* 03:00 Europe/Kyivはテストで01:00 UTCに実行されました。

コメント

まだコメントはありません。最初になりましょう。

コメントを残す

コメントは表示される前にモデレートされます。