コンテンツにスキップ
タイプアンロック
ステージ本番環境
難易度中程度
ジョブタスクを自動化
対象バックエンド開発者
対象開発チーム
サービスCron
サービスコンテナ
Hoody の利点コンテナ経済学
Hoody の利点HTTP ネイティブ
タイプアンロック
ステージ本番環境
難易度中程度
ジョブタスクを自動化
対象バックエンド開発者
対象開発チーム
サービスCron
サービスコンテナ
Hoody の利点コンテナ経済学
Hoody の利点HTTP ネイティブ
タイプアンロック
ステージ本番環境
難易度中程度
ジョブタスクを自動化
対象バックエンド開発者
対象開発チーム
サービスCron
サービスコンテナ
Hoody の利点コンテナ経済学
Hoody の利点HTTP ネイティブ
タイプアンロック
ステージ本番環境
難易度中程度
ジョブタスクを自動化
対象バックエンド開発者
対象開発チーム
サービスCron
サービスコンテナ
Hoody の利点コンテナ経済学
Hoody の利点HTTP ネイティブ
CRON · GIT · ブランチごとのスケジュール

ブランチごとの crontab を、コードと一緒にデプロイ

`.hoody/crontab` をジョブの隣にチェックインします。デプロイスクリプトが `main`、`feature/billing-v2`、あるいは任意のプレビューブランチ用にコンテナを起動するとき、そのファイルを新しいコンテナの Cron API に PUT します。スケジュールはブランチとともに出荷され、ブランチが消えると一緒に消えます。

Cron API を読む

`.hoody/crontab` がどのように本物のスケジュールになるか

すべてのブランチコンテナは Hoody Cron を実行します。デプロイスクリプトはチェックインされた crontab を読み、それを新しいコンテナの raw-crontab エンドポイントに PUT します。コンテナは、ファイルが記述するスケジュールを実行します — それ以上でも以下でもありません。

deploy.sh · CI から push
shell · クライアント
#!/bin/sh
# このブランチ用の新しいコンテナをプロビジョニングします。
BRANCH=$(git branch --show-current)
CTR=$(hoody containers create --from main-snapshot)

# コンテナの crontab をリポジトリ内のものに置き換えます。
curl -X PUT --data-binary @.hoody/crontab \
  -H "Content-Type: text/plain" \
  https://$CTR-cron-1.hoody.com/users/root/crontab

# 完了。ブランチのスケジュールはそのコンテナの中に存在します。
echo "deployed $BRANCH → $CTR"
PUT /users/root/crontab
cron · サーバー
# Hoody Cron raw-crontab エンドポイント — ファイル全体をアトミックに置き換えます。
PUT /users/root/crontab HTTP/1.1
Host: ctr_4d72b9-cron-1.hoody.com
Content-Type: text/plain

0 2 * * * /srv/jobs/billing-rollup-v2.sh
*/15 * * * * /srv/jobs/sync-stripe.py
@hourly curl -fsS http://localhost/healthz
*/5 * * * * /srv/jobs/diff-v1-v2.sh

HTTP/1.1 200 OK
# 200 OK: cron デーモンがリロードし、スケジュールは 1 秒以内に有効化されます。

crontab は、cron サーバーが記憶する状態ではなく、ブランチが運ぶデータです。コンテナを削除すれば、片付けるべきエントリは何も残りません — ファイルはディスクとともに消えました。

やめられる 3 つのこと

スケジュールがリポジトリ内のファイルになると、3 つのカテゴリの作業が消えます。

バージョニング

スケジュールはコードと同じ diff に乗る

`billing-rollup.sh` を v2 に変更すると、新しいスケジュールも同じプルリクエストに乗ります。レビュアーはスクリプトのすぐ隣で cron 行を見ます。1 つのコミットを取り消せば、スケジュールも一緒に元に戻ります。

ティアダウン

ブランチを削除すれば cron も消える

ブランチコンテナはエフェメラルです。ブランチをマージまたはクローズすると、コンテナを破棄します。crontab はその中に存在していたため、スケジュールは清掃係なしで消えます — 古いエントリを抱えた共有 cron サーバーは存在しません。

アイソレーション

実験はステージングでは発火しない

`experiment/llm-rollups` の毎時実験ジョブは、自分のファイルシステムを持つ自分のコンテナで動きます。ステージングの cron デーモンには見えませんし、本番の cron デーモンにも見えません。ジョブ自体の中に `if BRANCH_ENV` のガードはありません。

共有 crontab とブランチ crontab

標準の「ops が管理する 1 つの crontab」モデルとブランチに紐づくモデルは、反対方向に失敗します。同じジョブ、まったく異なる影響範囲です。

観点共有 CRONTABブランチ CRONTAB
信頼できる出典
ops の wiki + ansible ロールスケジュールはスクリプトとは別のリポジトリに存在
リポジトリ内の .hoody/crontabcron 行が呼び出すスクリプトのすぐ隣
ジョブの追加
コードをマージ → ops に連絡 → cron ホストに SSH2 つのシステムが手作業で一致する必要あり
ファイルを編集、push、デプロイ1 つの diff、1 つのマージ、1 つのデプロイ
ブランチ分離
if [ "$ENV" = staging ]; then …すべてのジョブがすべての環境を知っている
ブランチごとに 1 コンテナスクリプト内に env フラグなし
クリーンアップ
行を削除するのを覚えておく古いエントリが何年も積み上がる
ブランチ削除 = cron 削除ファイルシステムが消えれば、スケジュールも消える
実験
本番 crontab しか存在しないどのテストも本番で発火するリスクがある
スパイクブランチ = スパイク crontabそのコンテナでのみ発火

違いは機能ではありません — スケジュールがどこに存在するかです。ブランチが運ぶファイルか、ブランチが借りる共有テーブルの行か。

Hoody Cron が実際に提供するもの

コンテナごとの Cron は本物の REST サーフェスです — 3 つのエンドポイントファミリ、標準の cron 構文、ユーザーごとの完全な分離。数値は Cron API 仕様によるもので、捏造したベンチマークではありません。

  1. コンテナごとの CRONTAB1

    各ブランチコンテナは自分のユーザーごとの crontab を持ちます。ファイル全体を PUT し、GET で取り戻し、アトミックに置き換えます。裏に共有スケジュールテーブルはありません。

  2. エンドポイントファミリ3

    Raw crontab (GET/PUT)、マネージドエントリ (UUID と `expires_at` 付きの POST/PATCH/DELETE)、ユーザーごとのリスト。デプロイスクリプトが必要とするものを選んでください。

  3. フィールド CRON 表現5

    標準の `min hour day month dow` に加えてマクロ: `@hourly`、`@daily`、`@weekly`、`@monthly`、`@yearly`。`.hoody/crontab` がすでに使っているのと同じ構文です。

Hoody Cron API より: 各コンテナの cron サービス URL 上で GET/PUT /users/[user]/crontab および POST/PATCH/DELETE /users/[user]/entries。

スケジュールは、それを動かすコードの隣、同じコンテナの中、同じブランチに存在します。

before · 覚えておくべき 2 つのシステムafter · リポジトリ内の 1 つのファイル
以前の状態スケジュールは ops/ansible に · コードは app/ に · 一致しなかったPR をマージしてから、cron ホストを更新するチケットを起票
現在の状態PUT @.hoody/crontab → cron-1.[branch].hoody.com1 つの PUT、1 つのコンテナ、1 つのスケジュール、1 つのブランチ
Cron API を読む

これが置き換えるもの

cron スケジュールがかつて存在していた 6 つの場所、どれもコードの隣ではありませんでした。ブランチに紐づく crontab は、それらすべてを不要にします。

  • 共有本番 crontabすべてのチームが調整する必要があった、cron ホスト上の 1 つのファイル
  • 手動 cron 設定同期コードのマージとは別に適用される Ansible ロール / Puppet マニフェスト
  • main に固定された GitHub Actions スケジュールデフォルトブランチに紐づくスケジュール、フィーチャー作業やプレビューには見えない
  • 「マージしたら cron を更新するのを忘れずに」人間のチェックリスト項目 — あなたと古いエントリの間に立つ唯一のもの
  • 別の cron 設定リポジトリ実際のコードを持つリポジトリに遅れる以外に仕事がない 2 つ目のリポジトリ
  • Atlas/Liquibase のスケジュール済みマイグレーションスケジュールが存在する場所が他になかったため、マイグレーションツールがその役を担う

システム間でスケジュールを同期するのをやめましょう。crontab をチェックインしてください。ブランチに運ばせましょう。

Cron ドキュメントを読む

他のユースケースを読む