第15章:ヘルスチェック:死活と準備の2段階 🩺✅
この章は「公開してから詰むポイント」を先に潰す回だよ😆 ヘルスチェックがちゃんと入ると、デプロイ後に落ちても“勝手に戻る”サービスに近づく👍
15.1 今日のゴール 🎯✨
最終的にこうなるのがゴール👇
- ✅ アプリに /healthz(生きてる?)を実装できる
- ✅ 必要なら /readyz(今トラフィック受けて平気?)も作れる
- ✅ startup / liveness / readiness を「いつ・何を見てるか」で説明できる
- ✅ Cloud Run でヘルスチェック設定して、落ちたら復旧できるようにする
(この3種類の考え方は Kubernetes の用語として有名で、Cloud系もだいたい同じノリで来るよ)(Kubernetes)
15.2 まず“3種類”を超ざっくり理解しよう 🧠💡
🍳 たとえ:飲食店で考える
- startup(開店準備できた?):厨房の火入れ、仕込み、レジ起動…「開店前」チェック
- readiness(今、お客さん入れていい?):混みすぎ・材料切れ・回線不調…「今は新規客ムリ」
- liveness(店、死んでない?):店員が固まって動かない(デッドロック)とか「店として終わってる」
定義をちゃんと見るとこう👇
- liveness は「再起動すべきか」を判断
- readiness は「トラフィック受けていいか」を判断(ダメなら外される)
- startup は「起動が遅い子を殺さないため」に最初だけ見て、成功するまで他を止める (Kubernetes)
15.3 “2段階”って結局なに?🩺✅➡️🔁
この教材の「2段階」はこう覚えると強い💪
- 準備OK?(startup / readiness)
- 生きてる?(liveness)
特に Google の Cloud Run だと、startup probe を入れると 成功するまで liveness / readiness を走らせないので、起動が遅いアプリが事故りにくくなるよ。(Google Cloud Documentation)
15.4 エンドポイント設計:まずはこの2本でOK 🧩🪄
✅ /healthz(浅いチェック)
目的:死活(liveness)用
- 200 を返せばOK(本文は “ok” で十分)
- DBや外部APIに触らない(重くすると自爆する💥)
例:
- Node のイベントループが生きてる
- “プロセスが固まってない”程度の確認
✅ /readyz(少し深いチェック)
目的:準備(readiness)用
- 依存先(DB/Redis/外部API)が必要ならここで確認
- ただし「毎回重い問い合わせ」はダメ🙅♂️ → “疎通できるか”程度にする(キャッシュでもOK)
15.5 TypeScript(Express例)で実装してみよう 🛠️✨
ここでは最低限の構成でいくよ(まず動くのが正義👑)
① health ルートを作る(浅い)🩺
// src/health.ts
import type { Request, Response } from "express";
export function healthz(_req: Request, res: Response) {
// 超軽量:プロセスが動いてるならOK
res.status(200).type("text/plain").send("ok");
}
② readiness を作る(必要なら)🚦
「初期化が完了したか」だけでも readiness っぽくなるよ👍 (DBがある人は、あとでここに“軽い疎通”を足す)
// src/ready.ts
import type { Request, Response } from "express";
let isReady = false;
// 例:起動後の初期化が終わったら true にする想定
export function markReady() {
isReady = true;
}
export function readyz(_req: Request, res: Response) {
if (!isReady) {
return res.status(503).type("text/plain").send("not ready");
}
return res.status(200).type("text/plain").send("ready");
}
③ app に組み込む 🌐
// src/app.ts
import express from "express";
import { healthz } from "./health";
import { readyz, markReady } from "./ready";
const app = express();
app.get("/healthz", healthz);
app.get("/readyz", readyz);
// 例:起動時の初期化(本当はDB接続など)
async function bootstrap() {
// ここで「重い初期化」をやる想定
await new Promise((r) => setTimeout(r, 500)); // ダミー
markReady();
}
bootstrap().catch((e) => {
// 起動に失敗したらログ出して落ちる(中途半端に生きない)
console.error(e);
process.exit(1);
});
const port = Number(process.env.PORT ?? "8080");
app.listen(port, "0.0.0.0", () => {
console.log(`listening on ${port}`);
});
15.6 ローカルで確認(Windows)🧪🪟
① コンテナ起動(例)
docker build -t myapp .
docker run --rm -p 8080:8080 -e PORT=8080 myapp
② 動作確認
PowerShell でも curl は使えるよ👍
curl -i http://localhost:8080/healthz
curl -i http://localhost:8080/readyz
15.7 Cloud Run での設定イメージ(重要)☁️⚙️
Cloud Run のヘルスチェックは HTTP/TCP/gRPC で設定できて、HTTP なら HTTP/1 のエンドポイントをアプリ側に用意する必要があるよ(Cloud Run のデフォルトは HTTP/2 じゃない点に注意)。また、HTTP のヘルスチェック用エンドポイントは 外部から到達可能なので、普通の公開APIと同じ発想で扱うのが大事。(Google Cloud Documentation)
✅ 設定おすすめ(最初のテンプレ)📌
- startup:
/healthz(起動が遅いならここを厚めに) - liveness:
/healthz(軽いのでこれでOK) - readiness:
/readyz(依存先があるならここ)
Cloud Run の liveness は、規定回数(failureThreshold * periodSeconds)失敗すると コンテナを SIGKILL で落として、新しいインスタンスを起動して復旧させる動き。(Google Cloud Documentation)
⚠️ readiness は “Preview” 扱い(2026-02 時点)
readiness probes は Preview として案内されていて、いくつか制限も書かれてるよ。例えば session affinity を有効にしていると、readiness が落ちても同じインスタンスに送られ続ける…など。(Google Cloud Documentation)
💰 地味だけど大事:プローブにもCPU課金がある
Cloud Run は「プローブ実行時はCPUが割り当てられる」「CPU/メモリ使用分は課金対象(ただしリクエスト課金は無し)」と明記されてる。だからヘルスチェックは軽く作るのが正義👑(Google Cloud Documentation)
15.8 “重すぎないヘルスチェック”の鉄則 🧯😇
/healthz でやっていいこと ✅
- 固まってないか(即レスできるか)
- 返す情報は最小(
okだけ)
/healthz でやっちゃダメ 🙅♂️
- DBに毎回クエリ
- 外部APIへアクセス
- 重い計算(暗号化、画像処理…)
/readyz の現実的なライン ✅
- DBが必須なら「接続が生きてるか」程度(短いタイムアウト)
- 外部API必須なら「最近成功した結果を数秒キャッシュ」して返す、みたいな工夫
15.9 よくある詰まり Top5 😵💫🧨
- パスが違う(設定は
/healthzなのにアプリが/healthとか) - PORT違い(Cloud Run 側の設定ポートとコンテナの待受ポートがズレる)
- 重すぎてタイムアウト(Timeout は period を超えられない)(Google Cloud Documentation)
- readiness を “いつまでも503” にしてる(初期化完了フラグを立て忘れ)
- readiness の仕様を誤解(「落ちたら再起動」じゃなく「一時的にトラフィック止める」側)(Kubernetes)
15.10 ミニ課題(やると一気に腹落ち)📘✨
- ✅ 課題1:/healthz を 100回叩いても遅くならない(軽いことが勝ち)🏃♂️💨
- ✅ 課題2:/readyz を、起動直後だけ 503 → 数秒後に 200 に変える(「準備」の感覚)🚦
- ✅ 課題3:擬似的に “固まった” 状態を作る(例:無限ループを仕込む)→ liveness の価値を体感 😇 ※本番では絶対やらないやつ!ローカルだけね!😂
15.11 AIに投げるコピペ用プロンプト集 🤖📎
- 「この Express + TypeScript に /healthz と /readyz を追加して。/healthz は軽く、/readyz は初期化完了フラグで 503→200 にして」
- 「Cloud Run の startup / liveness / readiness を、このアプリ構成だとどう割り当てるのが安全?推奨値(period/timeout/failureThreshold)も理由つきで」
- 「ヘルスチェックが重くなる典型パターンを列挙して、避け方をコード例込みで」
おまけ:Dockerfile の HEALTHCHECK って使うべき?🐳🧩
ローカル(Docker単体/Compose)では、Dockerfile の HEALTHCHECK が効く場面があるよ。 Docker の HEALTHCHECK には interval/timeout/start-period/retries などがあり、Docker Engine 25.0+ では start-interval も使える、と公式に書かれてる。(Docker Documentation)
ただし Cloud Run 側は Cloud Run のプローブ設定が主役なので、まずは「HTTPエンドポイントでのヘルスチェック」を固めるのが一番コスパ良い👍(Google Cloud Documentation)
次の章に進む前に、/healthz と /readyz の2本が手元で安定して返る状態にしておくと、後でめちゃくちゃ楽になるよ😆🩺✨