こんにちは!日々のシステムの安定稼働、本当にお疲れ様です。
WebサーバーのAPIがエラーを出したらすぐにアラートが飛ぶ仕組みを作っている人は多いですが、「夜間バッチがしれっと途中で止まっていた」「KubernetesのCronJobがOOMKilled(メモリ不足)で沈黙していたのに、誰も気づかなかった」……そんな冷や汗をかくような経験はありませんか?
Webアプリケーションの監視が完璧でも、裏でこっそり動くバックグラウンドジョブが「サイレント障害(エラーログすら残さず、ただ沈黙する障害)」を起こすと、翌朝にユーザーからの問い合わせで発覚するという最悪の事態につながります。
今回は、Sentryの隠れた(いや、実は最強の)機能である「Sentry Crons(ハートビート監視)」を使って、バッチ処理や定期ジョブの死活監視を完全に手中に収める方法を、優しく丁寧にお伝えします。これをマスターすれば、朝出社したときの「バッチ動いてるかな…?」という不安なドキドキから完全に解放されますよ。
—
1. なぜ「ログ監視」や「死活監視」だけでは不十分なのか?
まずは、バックグラウンドジョブ監視の難しさを少しだけ整理しましょう。
一般的な監視手法として、サーバーのCPU使用率やプロセスの生存確認(Pingなど)があります。しかし、これらは「プロセスが生きているか」はわかっても、「ジョブが正常に最後まで完了したか」までは教えてくれません。
また、バッチスクリプトの中でエラー起因の例外キャッチを忘れていると、Sentryにエラーすら飛ばずにプロセスが異常終了してしまうこともあります。
そこで登場するのが、Heartbeat(心音)方式の監視です。
「私は今から処理を始めます(Check-in: in_progress)」
「無事に処理が終わりました(Check-in: ok)」
という信号(心音)を、ジョブ側から定期的にSentryへ送り続けるのです。もし、決まった時間になっても「終わったよ」の信号が来なければ、Sentryが「あれ、心音が止まったぞ?」と検知して、Slackやメールに緊急アラートを飛ばしてくれます。
—
2. Sentry Cronsの仕組みと基本概念
Sentry Cronsのセットアップは拍子抜けするほど簡単ですが、事前に知っておくべき概念が2つあります。
1. Monitor Slug(モニター名): ジョブを一意に識別する名前(例: `nightly-billing-batch`)。
2. Check-in(チェックイン): ジョブの状態をSentryに伝えるアクション。主に以下の3つのステータスを送ります。
- `in_progress`: ジョブが開始された(オプションですが、タイムアウト検知に超重要!)
- `ok`: ジョブが正常に完了した
- `error`: ジョブが異常終了した
それでは、実際にLinuxのCronとKubernetesのCronJobでの具体的な実装例を見ていきましょう。
—
3. 実装ガイド①:Linux Cronでの実践
まずは、古典的かつ最も広く使われているLinuxのCron環境での実装です。
シェルスクリプトからSentry公式のCLIツール(`sentry-cli`)を使って、ジョブの開始と終了を通知します。
ステップ1: 準備(sentry-cliの導入と認証)
バッチを実行するサーバーに`sentry-cli`をインストールし、プロジェクトの認証を通しておきます。
sentry-cliのインストール(公式推奨スクリプト)
curl -sL https://sentry.io/get-sentry-cli | bash
認証トークンの設定(環境変数として渡すのが安全です)
export SENTRY_AUTH_TOKEN=”あなたのSentry統合用AuthToken”
export SENTRY_ORG=”あなたの組織名”
export SENTRY_PROJECT=”あなたのプロジェクト名”
ステップ2: ラッパーシェルスクリプトの作成
Cronに直接コマンドを書くのではなく、ラッパーとなるシェルスクリプトを作成し、その中でジョブの「開始(`in_progress`)」と「終了(`ok` / `error`)」をハンドリングします。
`run_batch_with_sentry.sh`
!/bin/bash
エラーが発生した時点でスクリプトを即座に終了させる
set -e
MONITOR_SLUG=”nightly-data-sync”
1. 処理の開始をSentryに通知 (in_progress)
CHECK_IN_ID=$(sentry-cli monitors check-in –monitor “$MONITOR_SLUG” –status in_progress –ci)
実際の重いバッチ処理(例: Pythonスクリプトの実行)
万が一ここでエラーが出ると、trapコマンドが発動します
python3 /path/to/my_heavy_batch.py
2. 処理の正常終了をSentryに通知 (ok)
sentry-cli monitors check-in –monitor “$MONITOR_SLUG” –status ok –check-in-id “$CHECK_IN_ID”
ここでポイントなのが、スクリプトが途中で失敗した場合のハンドリング(trap処理)です。以下のように書くことで、バッチがコケた瞬間にもSentryに「エラー終了」を伝えることができます。
!/bin/bash
set -e
MONITOR_SLUG=”nightly-data-sync”
CHECK_IN_ID=$(sentry-cli monitors check-in –monitor “$MONITOR_SLUG” –status in_progress –ci)
失敗時のトラップ設定
trap ‘sentry-cli monitors check-in –monitor “$MONITOR_SLUG” –status error –check-in-id “$CHECK_IN_ID”‘ ERR
バッチ処理実行
python3 /path/to/my_heavy_batch.py
正常終了時
sentry-cli monitors check-in –monitor “$MONITOR_SLUG” –status ok –check-in-id “$CHECK_IN_ID”
ステップ3: Linux Cronへの登録
あとは、このシェルスクリプトをCronに登録するだけです。
毎日深夜2時に実行
0 2 /path/to/run_batch_with_sentry.sh > /dev/null 2>&1
これで、もしバッチがメモリ不足で途中で死んでも、無限ループで固まっても、Sentryが即座に検知してくれます!
—
4. 実装ガイド②:Kubernetes (K8s) CronJobでの実践
現代的なインフラであるKubernetesをお使いですか?ご安心ください、K8sのCronJobともシームレスに連携できます。
K8sの場合は、`sentry-cli`を毎回入れる代わりに、Sentryが提供するHTTP API(Check-in Endpoint)を `curl` で叩くのが最も軽快でスマートです。
以下は、Kubernetesの `CronJob` マニフェストのサンプルです。
`k8s-cronjob.yaml`
apiVersion: batch/v1
kind: CronJob
metadata:
name: kubernetes-cleanup-job
spec:
schedule: “0 3 ” # 毎日深夜3時に実行
concurrencyPolicy: Forbid
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: cleanup-task
image: my-cleanup-image:latest
command: [“/bin/sh”, “-c”]
args:
- |
# 1. 処理開始の通知 (SentryのWebhookエンドポイントへPOST)
# 環境変数からSentryのDsnやSecretを取得します
CHECK_IN_RESPONSE=$(curl -s -X POST \
“https://sentry.io/api/0/organizations/YOUR_ORG/monitors/k8s-cleanup-monitor/checkins/” \
-H “Authorization: Bearer $SENTRY_MONITOR_TOKEN” \
-H “Content-Type: application/json” \
data='{“status”: “in_progress”}’)
# 返却された check_in_id を抽出(簡易的にjq等を使用、または無視してOK)
# 2. メインの処理を実行
if python3 /app/cleanup.py; then
# 3-A. 成功の通知
curl -s -X PUT \
“https://sentry.io/api/0/organizations/YOUR_ORG/monitors/k8s-cleanup-monitor/checkins/” \
-H “Authorization: Bearer $SENTRY_MONITOR_TOKEN” \
-H “Content-Type: application/json” \
-d ‘{“status”: “ok”}’
else
# 3-B. 失敗の通知
curl -s -X PUT \
“https://sentry.io/api/0/organizations/YOUR_ORG/monitors/k8s-cleanup-monitor/checkins/” \
-H “Authorization: Bearer $SENTRY_MONITOR_TOKEN” \
-H “Content-Type: application/json” \
-d ‘{“status”: “error”}’
exit 1
fi
env:
- name: SENTRY_MONITOR_TOKEN
valueFrom:
secretKeyRef:
name: sentry-secret
key: monitor-token
Kubernetesのポッド自体がクラッシュしたり、ノードが消失してPodが消滅したような場合でも、Sentry側で「設定されたスケジュール(`0 3 `)を過ぎたのに `ok` も `in_progress` も届かない!」と判断し、タイムアウト(Missing)アラートを確実に発報してくれます。
—
5. Sentryダッシュボードでの設定とHelloWorld動作確認
コードの準備ができたら、SentryのWebコンソール側でモニターを有効化しましょう。
1. Sentryのダッシュボードを開き、左メニューの [Crons] をクリックします。
2. 初めてチェックイン(`sentry-cli` または `curl`)を実行すると、ダッシュボード上に自動的に `nightly-data-sync` や `k8s-cleanup-monitor` が認識され、リストに現れます。
3. モニターの設定画面を開き、以下を調整します。
- Schedule Type: Cron (例: `0 2 `)
- Timezone: Asia/Tokyo (日本時間にするのを忘れずに!)
- Max Runtime: バッチが通常何分で終わるか(例: 30分を超えたら異常とみなす)
これで設定は完了です!
手動で一度バッチを実行(あるいはテスト実行)し、Sentryの画面上で「Green(正常完了)」のステータスが綺麗にプロットされるのを確認してください。この瞬間が、オブザーバビリティエンジニアにとって最も快感な瞬間です。
—
まとめ:今夜から「目隠しバッチ」を卒業しよう
今回は、Sentry Cronsを使ったバッチ処理・定期ジョブのハートビート監視について解説しました。
- プロセスが生きているかではなく、「ジョブが正常に完了したか」を監視する。
- 開始(`in_progress`)と終了(`ok` / `error`)をシグナルとして送る。
- 万が一サイレント障害が起きても、Sentryのタイムアウト検知があなたを救ってくれる。
バックグラウンド処理の監視は、後回しにされがちですが、システム全体の信頼性を担保する上ではWebサーバーと同じくらい(あるいはそれ以上に)重要です。
ぜひ今回のコードを参考に、あなたのシステムのバックグラウンドタスクに「心音」を宿してあげてください。毎日の平穏な朝が、あなたを待っていますよ!