FlowingDev

Cron 式の解説:スケジューラが使うタイムトラベル言語

バックアップからニュースレターまで、自動化タスクをいつ実行するかコンピュータに指示する、小さくても強力なスケジューリング言語、cron 式の仕組みを学びましょう。

ツールを試す: Cron式パーサー

ひとことで言うと

cron 式とは、繰り返し実行されるスケジュールを定義する短い文字列のこと。要するに、コンピュータに自動化タスクを いつ 実行すべきかを伝えるものです。

これが解決する問題

デジタル暗黒時代(1970年代)に遡ると、コンピュータに何かを自動的にやらせたい場合、例えば深夜0時に一時ファイルをクリーンアップさせたい、なんてときは、クリエイティブになる必要がありました。ループで実行され、時間をチェックしてはしばらくスリープする、といった不格好なスクリプトを書くしかありませんでした。それはごちゃごちゃしていて、非効率で、壊れやすいものでした。

そこに登場したのが、初期の Unix の聖地、ベル研究所で生まれたデーモン(バックグラウンドプロセス)である cron です。その名前はギリシャ神話の時間神クロノスにちなんだもので、これ以上ないくらいギークなネーミングです。cron の仕事はシンプルながらも画期的でした。実行すべきコマンドとその時間のリストを読み込み、それを実行する、というものです。タスク自動化における「設定したら、あとはおまかせ」なシステムだったのです。

cron に いつ タスクを実行させるかを伝えるには、言語が必要でした。英語のように冗長な言語ではなく、コンピュータが即座にパースできるようなものです。その言語こそが cron 式です。これは、「毎週月曜の午前9時」「毎月1日と15日」「10分ごと」といった、考えうるあらゆる繰り返しスケジュールを、標準化された予測可能な方法で記述するという根本的な問題を解決します。これがなければ、開発者たちは皆、スケジューリングという車輪の再発明をすることになり、あなたのサーバーは自作の sleep() コマンドでカオスな状態になっていたでしょう。

内部の仕組み

cron 式は、知らない人にとっては */15 9-17 * * 1-5 のような、意味不明な文字列の羅列に見えるかもしれません。しかし、これは魔法ではなく、高度に構造化されたミニ言語なのです。一度シンタックスを覚えてしまえば、プロのようにスケジュールを読み書きできるようになります。

終末の5フィールド(とその仲間たち)

標準的な cron 式は、スペースで区切られた5つのフィールドで構成されています。各フィールドは時間の単位を表します。正しい瞬間を合わせるために回す5つのダイヤルセットだと考えてください。

フィールド 許可される値 許可される特殊文字
分 0-59 * , - /
時 0-23 * , - /
日 1-31 * , - / ? L W
月 1-12 or JAN-DEC * , - /
曜日 0-7 or SUN-SAT * , - / ? L #

最近の cron 実装では、6番目のフィールドとして年(1970-2099)や、先頭に秒(0-59)のフィールドが追加されることもありますが、古典的な5フィールド形式が最も一般的です。

曜日の注意点: 日曜日には 0 と 7 の両方が受け入れられることがよくあります。どちらか一方の規約(例:0=日曜、6=土曜)に統一するのが賢明です。

特殊文字:秘密の言語

cron 式の真の力は、数値を修飾する一握りの特殊文字からもたらされます。

  • * (「毎」):ワイルドカードです。「時」のフィールドにアスタリスクがあると、「毎日毎時」を意味します。すべてのフィールドにアスタリスク (* * * * *) を指定すると、「毎月毎日毎時毎分...」となります。もうお分かりですね。

  • , (「と」):カンマはリストの区切り文字として機能します。「日」のフィールドに 1,15 とあれば、「月の1日 と 15日」を意味します。

  • - (「〜まで」):ハイフンは範囲を定義します。「時」のフィールドに 9-17 とあれば、「午前9時から午後5時までの毎時」を意味します。

  • / (ステップ):スラッシュは間隔を指定するために使います。「分」のフィールドに */10 とあれば、「10分ごと」を意味します。範囲と組み合わせることもできます。0-30/5 は「最初の30分間で5分ごと」(つまり、:00, :05, :10, :15, :20, :25, :30)を意味します。

  • ? (「どっちでもいい」):これはトリッキーですが重要です。「日」と「曜日」の両方を同時に指定することはできません。なぜなら、それらは矛盾する可能性があるからです。「月の13日 かつ 金曜日に実行」と指定したのに、13日が水曜日だったらどうなるでしょう? ? は、「片方を指定したので、もう片方は無視してください」と伝えることでこの問題を解決します。スケジュールを * * 13 * ?(13日に実行、曜日は問わない)または * * ? * 5(毎週金曜日に実行、日付は問わない)のように設定します。

  • L (「最終」):L は「日」のフィールドでは「月の最終日」(1月31日、2月28日/29日など)を意味します。「曜日」のフィールドでは、「月の最後のX曜日」を意味します。例えば、5L は「月の最後の金曜日」です。

  • W (「平日」):「日」のフィールドの 15W は「15日に最も近い平日」を意味します。15日が土曜日なら、ジョブは14日の金曜日に実行されます。15日が日曜日なら、16日の月曜日に実行されます。

  • # (「N番目」):これは「月の第3金曜日」のような指定に使います。そのための式は * * ? * 5#3 となります。

全部まとめてみよう

いくつかの一般的な式を解読してみましょう:

# 毎晩深夜0時に実行
0 0 * * *
  • 分フィールドの 0:0分(正時)に。
  • 時フィールドの 0:0時(深夜)に。
  • 他のフィールドの * * *:毎週、毎月、毎日。
# 平日(月〜金)の毎朝8:30に実行
30 8 * * 1-5
  • 分フィールドの 30:30分に。
  • 時フィールドの 8:午前8時に。
  • 日と月の * *:問わない。
  • 曜日の 1-5:月曜日から金曜日まで。
# 平日の業務時間中(午前9時〜午後5時)に15分ごとに実行
*/15 9-17 * * 1-5
  • */15: 15分ごと。
  • 9-17: 9時、10時、11時、12時、13時、14時、15時、16時、17時に。
  • 1-5: 月曜日から金曜日まで。

実社会での事例

深夜バックアップ事件

あるスタートアップのデータベースは急速に成長していました。リードシステム管理者は毎日のバックアップが必要だと分かっていましたが、日中にバックアップスクリプトを実行すると、アプリケーション全体が亀のように遅くなってしまいました。ユーザーは文句を言い、潜在的な売上も失われました。解決策?単純な cron ジョブです。彼女はリソースを大量に消費するバックアップスクリプトを、サイトのトラフィックがほぼゼロになる午前2時に実行するようにスケジュールしました。0 2 * * * という式は、一人のユーザーにも迷惑をかけることなくデータを安全に保つ、会社のサイレントヒーロー(縁の下の力持ち)となったのです。 教訓: リソースを大量に消費するタスクは、パフォーマンスを維持するためにオフピーク時にスケジュールしよう。

忘れられたニュースレター事件

小さな e コマースサイトが、売上を伸ばすために毎週火曜の朝に「今週のお買い得情報」メールを送りたがっていました。何ヶ月もの間、Dave というマーケティング担当者が午前10時に手動で「送信」ボタンをクリックする役目を担っていました。しかしある週、Dave は病気で休みました。ニュースレターは送信されず、その火曜日の売上は散々なものでした。リード開発者が介入し、プロセスを自動化しました。彼女はニュースレターを送信するスクリプトを書き、それを 0 10 * * 2 という cron ジョブに接続しました。それ以来、誰がオフィスにいようといまいと、ニュースレターは時計のように正確に送信されるようになりました。 教訓: 繰り返し発生する時間的制約のあるタスクは自動化し、信頼性を向上させ、ヒューマンエラーをなくそう。

古くなったキャッシュ事件

あるニュースサイトは速報性を誇りにしていましたが、ホームページの表示がしばしば遅く感じられました。パフォーマンスのためにコンテンツはキャッシュされていましたが、キャッシュがクリアされるのは開発者が手動で行ったときだけでした。これは、新しい記事が表示されるまでに数時間かかることがある、ということを意味します。ある開発者は、サイトのキャッシュを5分ごとに自動的にクリアして再構築する cron ジョブをセットアップしました。*/5 * * * * という式のおかげで、サイトは劇的に速く かつ より最新の状態に保たれるようになりました。新しいコンテンツが数分以内に表示されることが保証されたからです。 教訓: キャッシュの無効化のような定期的な「ハウスキーピング(お掃除)」タスクに cron を使い、システムを新鮮で高性能に保とう。

よくある間違いと落とし穴

  • タイムゾーンのトラブル: 「間違った時間に実行される」という古典的な問題です。cron ジョブは、ほとんどの場合、サーバーのシステム時刻を使って実行されます。それは UTC かもしれないし、あなたが今いる場所とは違うタイムゾーンかもしれません。あなたの時間で午前 9 時にジョブをスケジュールしても、サーバー時間では午後2時に実行される、なんてこともありえます。常にサーバーのタイムゾーンを意識してください。

  • 「日」vs「曜日」の衝突: 初心者がやりがちな間違いは、「日」と「曜日」の両方のフィールドに値を設定してしまうことです(例:* * 1 FRI)。ほとんどの cron デーモンはこれを OR 条件で解釈します:「月の1日に実行する、または いずれかの金曜日に実行する」。これはあなたが望む動作であることは稀でしょう。「月の最初の金曜日」に実行したい場合は、* * ? * 5#1 のような式を使うか、より複雑なスクリプトを書く必要があります。曖昧さを避けるために ? 文字を使いましょう。

  • 出力リダイレクトの忘れ: デフォルトでは、スクリプトが標準出力や標準エラーに出力するものはすべて、crontab を所有するユーザーにメールで送信されます。これは便利に聞こえますが、あっという間にメールボックスが役に立たない通知でいっぱいになってしまいます。ベストプラクティスは、出力を明示的に処理することです。ログファイルに送信する(>> /var/log/myjob.log 2>&1)か、気にしないなら破棄しましょう(> /dev/null 2>&1)。

  • ジョブのオーバーラップ: ジョブを * * * * * に設定すると、「毎分のはじめにこのジョブの新しいインスタンスを開始する」という意味になります。もしあなたのジョブが実行に90秒かかるなら、実行が重なってしまい、競合状態、リソースの枯渇、その他あらゆる種類のカオスを引き起こす可能性があります。

  • ミニマリストな環境: あなたが普段使っているインタラクティブシェルは、$PATH のような便利な環境変数で満たされています。一方、cron ジョブは、何もない、最小限の環境で実行されます。コマンドラインから完璧に実行できるスクリプトが、cron では node や python のようなプログラムを見つけられずに失敗することがあります。解決策は、すべてのコマンドに絶対パスを使う(例:node の代わりに /usr/bin/node)か、crontab ファイルの先頭で PATH 変数を設定することです。

なぜ注目すべきか

cron は昔ながらのシステム管理者だけが使うものだと思っているかもしれません。しかし、その DNA はあらゆるところに存在します。

  • バックエンド & DevOps: データベースのメンテナンスやログのローテーションからコードのデプロイまで、バックグラウンドジョブをスケジュールするためのデファクトスタンダードです。
  • Web 開発: 毎日のメールレポートを送信したり、キャッシュをクリアしたり、サイトマップを生成したりする必要がありますか?cron はあなたのためのツールです。
  • クラウドプラットフォーム: AWS Lambda のスケジュールされたイベントや Google Cloud Scheduler のようなサービスは、トリガーを定義するために cron 式を使用します。このシンタックスは、最もモダンな「サーバーレス」環境においてさえ、スケジューリングのための世界共通言語なのです。

cron 式の読み書きを学ぶことは、基本的なスキルです。これは自動化を解き放つ鍵であり、より堅牢で、信頼性が高く、自己完結したシステムを構築することを可能にします。これは、一度知ってしまえば、あらゆるところで使い道が見つかる、そんなちょっとした知識の一つなのです。

もっと深く

  • Wikipedia: cron - cron ユーティリティ自体の歴史と概要。
  • crontab(5) - Linux man page - ファイル形式とそのシンタックスに関する、正統で技術的なリファレンス。
  • Crontab Guru - cron 式を平易な英語に翻訳してくれる、インタラクティブなオンラインエディタ。自分の書いた式をチェックするのに非常に価値があります。
  • The Open Group Base Specifications: crontab - crontab が何をすべきかを定義する POSIX 標準。
  • Quartz CronTrigger Tutorial - 秒や追加の特殊文字を含む拡張 cron 式フォーマットを使用する、人気の Java ベーススケジューラについての詳細な解説。

理論はOK。さあ手を動かそう — 100%ブラウザ内で。

ツールを試す: Cron式パーサー