第27章:Dockerfile HEALTHCHECK:コンテナ自身に健康判定を持たせる 🧪📦
Dockerの「プロセスは動いてるけど、実はもう応答してない…😇」を見抜くための仕組みが Dockerfile の HEALTHCHECK です。
コンテナの中でコマンドを定期実行して、成功/失敗で healthy / unhealthy を付けます。(Docker Documentation)
① 今日のゴール 🎯✨
HEALTHCHECKを Dockerfile に書ける ✍️docker psで(healthy)/(unhealthy)を見てニヤッとできる 😎docker inspectで「なぜ失敗したか」を追える 🔎(Docker Documentation)
② 図(1枚)🖼️(脳内イメージでOK)
Docker Engine
│ (一定間隔で)
▼
[HEALTHCHECK コマンド] ← コンテナ内で実行
│
▼
http://127.0.0.1:3000/health にアクセス
│
├─ 200 OK → exit 0 → healthy ✅
└─ 500 / タイムアウト / 例外 → exit 1 → unhealthy ❌
③ 手を動かす(手順 8つ)🛠️💨
以下は「第25章で /health がある」前提で進めます(もし無ければ、最後に最小の /health 例も置いておきます🧯)。
手順1:healthcheck 用スクリプトを作る 🧾
scripts/healthcheck.mjs を追加します。
// scripts/healthcheck.mjs
const url = process.env.HEALTHCHECK_URL ?? "http://127.0.0.1:3000/health";
// healthcheck 自体にも timeout はあるけど、アプリ側でも短く失敗させると気持ちいい🙂
const timeoutMs = Number(process.env.HEALTHCHECK_TIMEOUT_MS ?? "2000");
const controller = new AbortController();
const t = setTimeout(() => controller.abort(), timeoutMs);
try {
const res = await fetch(url, { signal: controller.signal });
clearTimeout(t);
// OKなら exit 0(healthy)
if (res.ok) process.exit(0);
// NGなら exit 1(unhealthy)
console.error(`healthcheck failed: ${res.status} ${res.statusText}`);
process.exit(1);
} catch (e) {
clearTimeout(t);
console.error(`healthcheck error: ${e?.message ?? e}`);
process.exit(1);
}
ポイント:
- exit code が命:
0=healthy / 1=unhealthy / 2は予約で使うな(Docker Documentation) - 出力(stdout/stderr)は
docker inspectで見れる(ただし保存は最大4096 bytes)(Docker Documentation)
手順2:Dockerfile に HEALTHCHECK を足す 🧩
(例:Nodeの実行イメージに scripts/ をコピーして使う)
## --- runtime stage のイメージ例(あなたのDockerfileに合わせてOK) ---
## 例: FROM node:24-alpine
WORKDIR /app
## 既存:distやnode_modulesをコピーしてるはず
## COPY --from=build /app/dist ./dist
## COPY package*.json ./
## RUN npm ci --omit=dev
## ✅ 追加:healthcheck スクリプト
COPY scripts ./scripts
## ✅ 追加:Dockerfile HEALTHCHECK
HEALTHCHECK --interval=10s --timeout=3s --start-period=15s --retries=3 \
CMD ["node", "/app/scripts/healthcheck.mjs"]
## 既存:起動コマンド
## CMD ["node", "/app/dist/server.js"]
HEALTHCHECK のオプション(超重要)👇
--interval:何秒ごとにチェックするか(既定 30s)(Docker Documentation)--timeout:1回のチェックの制限時間(既定 30s / 超えたら失敗扱いで強制終了)(Docker Documentation)--retries:何回連続で失敗したら unhealthy にするか(既定 3)(Docker Documentation)--start-period:起動直後の猶予(この間の失敗はカウントしない)(Docker Documentation)--start-interval:start-period 中だけチェック間隔を変える(Docker Engine 25.0+)(Docker Documentation)
また、Dockerfile内に書ける HEALTHCHECK は 1つだけで、複数書いたら最後だけ有効です。(Docker Documentation)
手順3:ビルド 🏗️
docker build -t obs-mini-api:hc .
手順4:起動 ▶️
docker run -d --name obs-api -p 3000:3000 obs-mini-api:hc
手順5:docker ps で health 表示を見る 👀✨
docker ps
期待する雰囲気(例):
- 起動直後:
(health: starting)🐣 - 成功後:
(healthy)✅ - 失敗が続くと:
(unhealthy)❌
※ health は「通常の status」に追加で付くもの、というのがポイントです。(Docker Documentation)
手順6:docker inspect で “なぜ失敗したか” を見る 🔎🧠
まずはステータスだけ👇
docker inspect --format '{{.State.Health.Status}}' obs-api
次にログ(直近の実行結果)👇
docker inspect --format '{{range .State.Health.Log}}{{println .End " exit=" .ExitCode " " .Output}}{{end}}' obs-api
docker inspect はDockerオブジェクトの詳細をJSON等で見れるコマンドです。(Docker Documentation)
(この章では “Health の中身を覗く” がメイン🎁)
手順7:わざと壊して unhealthy を見る 😈💥
一番わかりやすいのは「/health が 500 を返す状態」にすること。
例:/health が “ファイルがあったら不健康” にする(デモ用でOK)👇
src/server.ts などに、こういう分岐を入れます:
import fs from "node:fs";
app.get("/health", (req, res) => {
if (fs.existsSync("/tmp/unhealthy")) {
return res.status(500).json({ ok: false });
}
return res.json({ ok: true });
});
起動したまま、コンテナ内でファイルを作る👇
docker exec obs-api sh -lc "touch /tmp/unhealthy"
しばらくして docker ps を見ると unhealthy になってるはず!😆
戻す👇
docker exec obs-api sh -lc "rm -f /tmp/unhealthy"
手順8:掃除 🧹
docker rm -f obs-api
④ つまづきポイント(3つ)🪤😵💫
-
“curl が無い問題” 公式例は
curl -f http://localhost/ || exit 1が多いけど、イメージによっては curl が入ってません。(Docker Documentation) → 今回は Node のfetchで回避(依存を増やさない👍) -
/health が重い(DB確認とか) healthcheck は「軽く・速く」が正義💨 重いと
timeoutで落ちて、無駄に unhealthy 連発します。 -
start-period を入れてないせいで起動直後に即unhealthy 起動に時間がかかるアプリは、
--start-periodを入れて “助走時間” を確保しよう🏃♂️➡️(Docker Documentation) (Engine 25.0+なら--start-intervalで start-period 中だけ細かく監視もできるよ)(Docker Documentation)
⑤ ミニ課題(15分)⏳🎮
A. チューニング遊び 🎚️
--interval=3sにして、healthy/unhealthy の切り替わり速度を体感する⚡--retries=5にして「粘り強さ」を変える🧘
B. start-period 実験 🐢
- アプリ起動をわざと遅くする(例:起動時に
await new Promise(r=>setTimeout(r, 8000))) start-periodあり/なしで、起動直後の挙動がどう変わるか観察👀
⑥ AIに投げるプロンプト例(コピペOK)🤖📋
Dockerfile HEALTHCHECK を追加したい。
Node(fetch) で /health を叩く healthcheck.mjs を作って。
- 2秒でタイムアウト
- 成功は exit 0、失敗は exit 1
- 失敗時は理由を console.error に出す
/health エンドポイントに「/tmp/unhealthy があれば 500」を入れて、
docker exec で unhealthy を再現できるようにしたい。Express の例を書いて。
(AI拡張:GitHub Copilot / OpenAI Codex などにそのまま投げてOK🤖✨)
おまけ:HEALTHCHECK NONE って何?🙋♂️
ベースイメージが healthcheck を持ってる時に、継承を無効化できます。(Docker Documentation) (「このイメージの healthcheck、うちの用途だと邪魔…」って時に便利👍)
最小の /health がまだ無い場合(超ミニ)🧯
app.get("/health", (req, res) => res.json({ ok: true }));
次の第28章で、この health を **Compose の起動順(healthy になるまで待つ)**につなげて、「起動直後の接続失敗ログが減って気持ちいい」状態にしていきます 😆⏳