監視とオンコール
この部の 5 / 5 章 ・ 全体で 74 / 76 章 ・ 読了目安 45 分
- 障害時にどこから見るかの順番を持つ
- 意味のあるアラートと無意味なアラートを区別できる
- 再発防止を人の注意力に頼らない形で書ける
前章までで、作ったものをクラウドに載せ、自動でデプロイできるようになりました。
最後に残るのは、それが動き続けているかをどう知り、壊れた時にどう動くかです。
サービスは必ず壊れます。優秀なチームとそうでないチームの差は、 壊れるかどうかではなく、壊れたことに何分で気づき、何分で戻せるか にあります。
監視と可観測性は別のもの
「落ちたら知る」から「なぜ落ちたか調べられる」へ
監視(モニタリング) は、あらかじめ決めた質問に答える仕組みです。 「サーバーは生きているか」「エラー率は 1% を超えていないか」—— 質問を先に決めておき、その答えを見張ります。
可観測性(オブザーバビリティ) は、まだ思いついていない質問に答えられる状態のことです。 「なぜ、火曜の 14 時だけ、特定のバージョンのアプリからの決済だけが遅いのか」—— こんな質問を事前に用意しておくことはできません。
| 監視 | 可観測性 | |
|---|---|---|
| 答えるもの | 決めておいた質問 | その場で生まれた質問 |
| 典型的な形 | 死活監視、閾値アラート | ログ・メトリクス・トレースの掘り下げ |
| 分かること | 壊れたこと | 壊れた理由 |
| 足りないと | 気づけない | 気づけるが、原因が分からない |
障害対応で本当に苦しいのは、後者が欠けている時です。 「落ちているのは分かる。でも、どこが原因か分からない」という状態が、 深夜の 3 時間を溶かします。
アラートは後から追加できますが、「調べられるかどうか」はコードを書いた時点で決まっています。
trace_id をログに入れていない、エラーを握り潰している(読まれるコードを書く)、
ctx を下流に渡していない(マイクロサービスの実務)——
これらは障害が起きてから直しても、今起きている障害の役には立ちません。
読まれるコードを書くの「これ、本番で落ちたらどう調べる?」という問いは、 この章のための準備でした。
3本柱: ログ・メトリクス・トレース
可観測性を支えるデータは、大きく3種類あります。 同じものを3回見ているのではなく、それぞれ答えられる質問が違います。
| ログ | メトリクス | トレース | |
|---|---|---|---|
| 見るもの | 個別の事象 | 傾向と閾値 | どこで時間を使ったか |
| 答える質問 | 「このリクエストで何が起きた?」 | 「いつから、どれくらい悪い?」 | 「なぜ遅い? 誰が犯人?」 |
| 粒度 | 1件ずつ | 集計値(率・分位数) | 1リクエストの経路 |
| 苦手なこと | 全体傾向が見えない。量が多くコストが高い | 個別の原因が分からない。「なぜ」に答えられない | 全リクエストは保存できない(サンプリング) |
見る順番を持つ
障害時に迷わないための順番です。
1. メトリクス 何が、いつから、どれくらい悪いか(範囲を絞る)
↓
2. トレース その中の遅い/失敗したリクエストで、どこが犯人か
↓
3. ログ 犯人のサービスで、実際に何が起きていたか
広い → 狭い の順です。逆をやると溺れます。
最初からログを grep し始めると、1分間に数万行流れるログの海で、
何を探しているのかすら分からなくなります。
トレースの読み方(N+1、直列実行、伝播漏れ)はマイクロサービスの実務で扱ったので、 ここでは残りの2本を深掘りします。
構造化ログ
grep できるログを書く
ログは「動かすために出す」のではなく、後から検索・集計するために出します。
// 検索できない: あとから userId で絞り込めない
logger.info(`user ${userId} ordered item ${itemId} in ${ms}ms`);
// 検索できる: キーと値で出す
logger.info({ event: 'order_created', userId, itemId, durationMs: ms }, 'order created');文字列に埋め込んだ瞬間、その値は検索対象ではなく、ただの文章の一部になります。
durationMs > 1000 で絞り込む、userId で集計する、といったことが一切できません。
TypeScript の例
import pino from 'pino';
const logger = pino({ level: process.env.LOG_LEVEL ?? 'info' });
export function handleOrder(req: Request, ctx: RequestContext) {
// リクエスト単位の共通フィールドを固定した子ロガーを作る
const log = logger.child({
traceId: ctx.traceId,
userId: ctx.userId,
route: 'POST /orders',
});
const start = performance.now();
try {
const order = createOrder(req.body);
log.info({ event: 'order_created', orderId: order.id, durationMs: performance.now() - start });
return order;
} catch (error) {
log.error({ event: 'order_failed', durationMs: performance.now() - start, err: error });
throw error;
}
}ポイントは logger.child() で共通フィールドを固定すること です。
毎回 traceId を書き忘れる余地をなくします。
Go の例
標準ライブラリの log/slog で同じことができます。
// 起動時に一度だけ作る(JSON で標準出力へ)
s.logger = slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo}))
func (s *Server) CreateOrder(ctx context.Context, req *pb.CreateOrderRequest) (*pb.Order, error) {
start := time.Now()
// ctx から trace_id を取り出して固定する(マイクロサービスの実務の伝播がここで効く)
log := s.logger.With(
slog.String("trace_id", traceIDFrom(ctx)),
slog.String("user_id", userIDFrom(ctx)),
slog.String("rpc", "CreateOrder"),
)
order, err := s.orders.Create(ctx, req)
if err != nil {
log.ErrorContext(ctx, "order failed",
slog.String("error", err.Error()),
slog.Int64("duration_ms", time.Since(start).Milliseconds()),
)
return nil, err
}
log.InfoContext(ctx, "order created",
slog.String("order_id", order.Id),
slog.Int64("duration_ms", time.Since(start).Milliseconds()),
)
return order, nil
}出力はこうなります。GKE なら標準出力に1行1 JSON で出すだけで、 ログ基盤側が構造化フィールドとして扱ってくれます。
{"time":"2026-04-01T10:05:12Z","level":"INFO","msg":"order created","trace_id":"abc123","user_id":"u_42","rpc":"CreateOrder","order_id":"o_7","duration_ms":842}何を入れるか
| フィールド | なぜ必要か |
|---|---|
trace_id(相関ID) | 1リクエストの全経路を一列に並べられる。最重要 |
user_id(内部ID) | 「一部のユーザーだけ」の切り分けができる |
duration_ms | 遅延の犯人を後から集計できる |
service / version | どのバージョンから壊れたかが分かる |
event | イベント名で集計できる(order_created の件数など) |
| エラーの種類・コード | 「どのエラーが増えたか」を数えられる |
ログは長期保存され、開発者以外も含む多くの人が閲覧できます。 一度出したものは、後から消すのが非常に困難です。
- パスワード、アクセストークン、API キー、Cookie、
Authorizationヘッダ - クレジットカード番号
- 氏名・メールアドレス・電話番号・住所などの個人情報
- リクエストボディ・レスポンスボディの丸ごと出力
最後の項目が実際には一番よくやる事故です。
logger.info('request', req.body) と書いた瞬間、
そこに何が入るかは将来のフィールド追加次第になります。
出すフィールドは常に明示的に選んでください。 ユーザーを識別したいなら、メールアドレスではなく内部 ID を使います。
ログレベルとコスト
| レベル | 使う場面 | アラート |
|---|---|---|
error | 人が対応すべき異常 | 繋がることがある |
warn | 異常ではないが注視したい(リトライした、上限に近い) | 繋がない |
info | 業務上の重要イベント(注文作成、決済完了) | 繋がない |
debug | 開発時の詳細。本番では出さない | — |
ログは無料ではありません。 1リクエストにつき 10 行出すサービスが秒間 1000 リクエストを捌けば、 1日で 8 億行になります。保存料金も検索速度も現実的でなくなります。
- 正常系は1リクエスト1行を目安にする(入口か出口のどちらか)
- ループの中でログを出さない
- 詳細が必要なら、
debugを環境変数で一時的に上げられるようにしておく
メトリクスの読み方
平均を見ない
平均レスポンスタイム: 120ms ← 一見すると健康
p50: 80ms 半分のユーザーは 80ms 以内
p95: 200ms 20人に1人は 200ms 以上
p99: 4200ms 100人に1人は 4秒以上待たされている
平均は、少数の重症者を大多数の健康な人で薄めて隠します。 見るべきは分位数(パーセンタイル)です。 p99 が 4 秒なら、100 リクエストするユーザーはほぼ確実に一度は遭遇しています。
最低限見る指標
リクエストを受けるサービスなら、この3つ(RED)で足ります。
| 指標 | 意味 |
|---|---|
| Rate | 秒間リクエスト数。急に減ったら「呼ばれていない」障害 |
| Errors | エラー率。5xx や gRPC の非 OK コードの割合 |
| Duration | レイテンシの p50 / p95 / p99 |
リソース側(CPU・メモリ・コネクション数)は、 原因を調べる時に見る補助指標であって、健康状態そのものではありません。 この区別が、次のアラート設計に直結します。
メトリクスは「ラベルの組み合わせの数」だけ時系列データが作られます。
http_requests_total{route="/orders", status="200"} → 数十系列
http_requests_total{route="/orders", user_id="u_42", ...} → ユーザー数だけ系列が増える
これをカーディナリティ爆発と呼びます。 監視基盤のメモリを食い潰し、監視のせいで監視が落ちます(実際によくある障害です)。
ラベルに入れてよいのは、取りうる値が少なく、増えないものだけです。 route、status、method、リージョン、バージョンなど。 ユーザー ID・注文 ID・URL のパス変数はログかトレースに置きます。
SLI / SLO / エラーバジェット
100% を目指さない理由
「可用性は高いほど良い」は、運用の現場では正しくありません。
99% → 月に約 7時間 停止してよい
99.9% → 月に約 43分
99.99% → 月に約 4分 (人間が気づいて対応する時間はない。自動化が必須)
99.999% → 月に約 26秒
9 を1つ増やすたびに、必要なコストと複雑さは跳ね上がります。 そしてあなたのサービスが 100% でも、ユーザーの回線や端末は 100% ではありません。 到達できない目標を掲げると、次の2つが同時に起きます。
- 常に「未達成」なので、数字が意味を持たなくなる
- 障害を恐れてリリースが止まる
3つの言葉
| 用語 | 意味 | 例 |
|---|---|---|
| SLI | 測る指標そのもの | 「200 を返したリクエストの割合」 |
| SLO | その指標の目標値 | 「直近30日で 99.9% 以上」 |
| SLA | 顧客との契約(違反すると返金など) | 「99.5% を下回ったら利用料を返金」 |
SLI は「ユーザーが体験する品質」で選びます。 CPU 使用率は SLI になりません(ユーザーは CPU を体験しません)。
| 種類 | SLI の書き方 |
|---|---|
| 可用性 | 成功したリクエスト数 ÷ 全リクエスト数 |
| レイテンシ | 300ms 以内に返ったリクエストの割合 |
| 鮮度 | 5分以内に反映されたデータの割合 |
エラーバジェット
SLO を 99.9% にするということは、0.1% は壊れてよいと決めたということです。 この 0.1% がエラーバジェット(誤差の予算) です。
30日 = 43,200分
0.1% = 43.2分 ← 今月、これだけ壊れてよい
予算という言い方をするのは、使う対象だからです。
| バジェットの残り | 取る行動 |
|---|---|
| 十分に残っている | リリース速度を上げてよい。多少のリスクを取ってよい |
| 使い切りそう | 新機能を止め、信頼性の改善に人を割く |
| 使い切った | 機能リリースを凍結し、原因を潰す |
これで「攻めるか守るか」の議論が、感情論ではなく数字の話になります。 「最近障害が多い気がする」ではなく「今月バジェットの 80% を使った」と言えます。
SLO の本質は技術指標ではなく、チーム間の約束です。
「1分の停止でも許されない」と全員が思い込んでいる状態では、 誰も怖くてデプロイできず、変更のたびに深夜作業になります。
逆に「月43分までは許容する」と事前に合意されていれば、 5分の障害は「予算内の想定された出来事」になります。
だから SLO は、開発者だけで決めるものではありません。 プロダクト側と一緒に決めます。新人のうちは、 自分のチームの SLO が何%で、今月どれだけ使っているかを一度聞いてみてください。
アラート設計
人を起こす条件はひとつだけ
今すぐ人が動かないと、状況が悪化する
これに当てはまらないものは、アラートで人を起こしてはいけません。 「知っておくと良さそう」「念のため」は、すべてダッシュボードかチケットに送ります。
| 通知の重さ | 使う場面 |
|---|---|
| 電話・ページャー(人を起こす) | ユーザーが今困っている。放置すると悪化する |
| Slack 通知(営業時間内に見る) | 悪化しているが、まだユーザー影響が出ていない |
| ダッシュボード / チケット | 傾向として気になる。後で調べればよい |
症状で鳴らす、原因で鳴らさない
これがアラート設計で最も重要な原則です。
| ❌ 原因で鳴らす | ✅ 症状で鳴らす |
|---|---|
| CPU 使用率が 80% を超えた | エラー率が 5分間 5% を超えている |
| Pod が再起動した | p95 レイテンシが SLO を割っている |
| メモリ使用率が高い | 注文の完了数が普段の半分以下 |
| ディスク使用率が 70% | エラーバジェットの消費が速すぎる |
原因側で鳴らすと、2つの方向に失敗します。
- 偽陽性: CPU が 90% でも、ユーザーは何も困っていないかもしれない(起こされ損)
- 偽陰性: CPU が 20% でも、デッドロックで全リクエストが失敗しているかもしれない(気づけない)
ユーザーが困っているかどうかだけがアラートの基準です。 リソース指標は、鳴った後に原因を探すために見ます。
例外は予測可能な枯渇です。「ディスクが 4 時間後に満杯になる」のように、 放置すれば確実に障害になり、かつ今なら安全に対処できるものは鳴らす価値があります。
アラート疲れが最大の敵
無意味なアラートが1日20件届く
↓
「またか」と思って中身を見なくなる
↓
通知をミュートする
↓
本物の障害を見逃す
鳴りすぎるアラートは、鳴らないアラートより危険です。 「一応鳴らしておこう」で追加した1本が、監視システム全体の信頼を削ります。
対処は追加ではなく削除です。
- 過去1ヶ月、一度も対応が発生しなかったアラートは消す
- 鳴るたびに「様子見」で終わるアラートは、閾値か対象が間違っている
- 1つの障害で 10 本鳴るなら、依存関係でまとめる(グルーピング)
アラートに書いておくこと
深夜に起きた人が、寝ぼけた頭でも動けるようにします。
| 項目 | 例 |
|---|---|
| 何が起きているか | 「注文 API のエラー率が 12%(閾値 5%)」 |
| 影響 | 「注文が完了できないユーザーがいる」 |
| runbook へのリンク | 対応手順書(後述) |
| ダッシュボードへのリンク | 該当のグラフに直接飛べる URL |
「CPU 使用率が80%を超えたら担当者に電話する」というアラートがあります。何が問題ですか。
障害対応の順番
アラートが鳴りました。何から手を付けるかには、決まった順番があります。
① 復旧 ユーザーへの影響を止める(原因究明より先)
② 影響範囲 誰が、何を、どれくらいできなくなっているか
③ 情報共有 分かっていることを、分かっている範囲で伝える
④ 原因調査 落ち着いてから、根本原因を突き止める
① 復旧が最優先
新人が最も間違えやすいのがここです。 原因が分からなくても、復旧はできます。
| 緩和手段 | 効く場面 |
|---|---|
| ロールバック | 直前のデプロイが怪しい時(最も多い) |
| フィーチャーフラグを切る | 新機能だけが壊れている時 |
| トラフィックを旧バージョンに戻す | カナリアリリース中 |
| スケールアウト | 負荷が原因の時 |
| 該当機能の一時停止 | 一部を切り捨てて全体を守る |
「なぜ壊れたか完全に理解してから直そう」とすると、 その間ずっとユーザーは使えないままです。 原因調査はログとメトリクスが残っていれば後からできます。ユーザーの時間は戻りません。
ただし、復旧前に証拠を残すことは意識してください。 Pod を消す前にログを退避する、該当時刻のダッシュボードのスクリーンショットを撮る—— これを怠ると、原因が永久に分からなくなります。
② 影響範囲の把握
「壊れています」だけでは誰も動けません。数字で言います。
- いつから壊れているか
- 全ユーザーか、一部か(どの一部か)
- どの機能が、どの程度使えないか(完全に不可か、遅いだけか)
- お金・データに影響があるか(二重課金やデータ欠損は最優先)
③ 情報共有
障害中は、分かっていることと分かっていないことを分けて共有します。
✅ 良い共有
「10:05 から注文 API のエラー率が 12%。原因は未特定。
10:00 のデプロイをロールバック中。10:20 に再度状況を共有します。」
❌ 悪い共有
「調査中です」(何も伝わらない)
「たぶん DB が原因です」(憶測が事実として広まる)
次にいつ報告するかを毎回言うのがコツです。 これがあるだけで、周りが「今どうなっている?」と聞きに来る回数が激減し、 対応する人が調査に集中できます。
役割を分ける
障害が大きい時は、1人が全部やろうとすると必ず破綻します。
| 役割 | やること |
|---|---|
| 指揮(インシデントコマンダー) | 全体の判断と優先順位付け。自分では手を動かさない |
| 作業 | 実際に調査・復旧を行う |
| 記録・連絡 | タイムラインを書き、関係者に共有する |
新人はまず記録係を任されることが多いです。 これは雑用ではありません。後のポストモーテムの質は、この記録で決まります。
障害対応中、新人が最も貢献できるのは技術力ではなく発言です。
「エラーが増え始めたのは 10:05 に見えます」
「その時刻に注文サービスのデプロイがあったようです」
「自分の手元でも同じエラーが再現します」
「今、何を確認すればいいですか?」
ベテランは自分の仮説に集中していて、視野が狭くなっています。 「これ関係ないかもしれないんですが」で始めて構いません。 その一言が原因特定を 30 分縮めることは珍しくありません。
逆に絶対にやってはいけないのは、黙って本番環境を触ることです。 良かれと思った操作が、他の人の調査を壊し、原因を分からなくします。 何かする前に「〜してもいいですか」と必ず声を出してください。
切り分けの型
原因を探す時、闇雲にログを読んではいけません。 3つの質問を順に埋めるだけで、候補は劇的に絞れます。
① いつから
メトリクスのグラフで、線が折れた時刻を特定します。 これが分からないと、他のすべての質問に答えられません。
- 急に折れた → 変更(デプロイ・設定・切り替え)が原因のことが多い
- じわじわ悪化 → リソースの枯渇・データ量の増加・メモリリーク
- 周期的 → バッチ処理・キャンペーン・時報的なアクセス集中
② 全員か、一部か
一部なら、何で切ると分かれるかを探します。ここが最大のヒントです。
| 切り口 | 分かること |
|---|---|
| リージョン・ゾーン | インフラ側の局所障害 |
| アプリのバージョン | 新バージョンの不具合 |
| 特定のエンドポイント | その処理固有の問題 |
| 特定のユーザー層 | データ依存(特定の値で落ちる) |
| Pod 単位 | 一部のインスタンスだけ壊れている |
「全員」なら共通の依存(DB・認証・ネットワーク)、 「一部」ならその境目に原因があります。
③ 直前に何が変わったか
これが圧倒的に当たります。 本番障害の原因の大半は、「誰かが何かを変えた」直後に起きています。
| 変わったもの | 確認先 |
|---|---|
| アプリのデプロイ | CI/CD の実行履歴(CI/CD) |
| 設定変更 | ConfigMap / Secret / 環境変数の変更履歴 |
| フィーチャーフラグ | フラグの切り替えログ |
| インフラ変更 | Terraform の apply 履歴、手動操作の監査ログ |
| 依存サービス | 相手チームのデプロイ、外部 API のステータスページ |
| データ | 大量投入バッチ、テーブルの急な増加 |
| 時間経過そのもの | 証明書・トークンの期限切れ、月初/月末の処理 |
「自分たちは何も変えていません」は、多くの場合こう言い換えられます。
- 他のチームがデプロイした
- 設定だけ変えた(コードは変えていない、と思っている)
- データ量が閾値を越えた(インデックスが効かなくなる規模になった、データベース)
- 証明書が期限切れになった(変えていないから壊れたパターン)
- クラウド側が変わった(ノードの入れ替え、メンテナンス)
調査で行き詰まったら、「変更履歴を時系列で並べる」 に戻ってください。 折れたグラフの時刻と、変更ログの時刻を並べるだけで見つかることが本当に多いです。
ポストモーテム
障害が収まった後に書く振り返りの文書です。 「事後検死」という意味で、ここでの死体はコードであって人ではありません。
非難しない(ブレームレス)
❌ 「A さんが確認を怠ってデプロイしたため障害が発生した」
✅ 「テストが通れば本番に出せる状態だったため、この不具合を検出できなかった」
人を責めても再発は防げません。むしろ2つの害があります。
- 障害を隠すようになる(報告が遅れ、被害が拡大する)
- 本当の原因(仕組みの欠陥)が放置される
責めるべきなのは、「注意していれば防げた」という状態を許していた仕組みです。 人は必ずミスをします。それを前提に設計されていなかったことが原因です。
「確認漏れ」で止めない
原因が「確認漏れ」「見落とし」で終わっているポストモーテムは、 まだ原因に到達していません。そこから一段掘ります。
なぜ確認漏れが起きた? → 確認項目が30個あり、手動だから
なぜ手動なのか? → 自動でチェックする仕組みが無いから
→ 対策: その確認を CI で自動化する
「なぜ」を数回繰り返して、人の注意力ではなく仕組みに着地させます。
書く項目
| 項目 | 内容 |
|---|---|
| 概要 | 何が起きたか、3行で |
| 影響 | 期間、影響したユーザー数・リクエスト数、金銭影響 |
| タイムライン | 発生・検知・対応・復旧の時刻(記録係の出番) |
| 根本原因 | 仕組みとして何が欠けていたか |
| 検知 | どうやって気づいたか。ユーザーからの報告で気づいたなら、それ自体が課題 |
| うまくいったこと | ロールバックが 3分で終わった、など(良かった点も必ず書く) |
| 再発防止 | 具体的なアクション。担当者と期限をつける |
「発生から検知までの時間」と「検知から復旧までの時間」を分けて書くと、 どちらを改善すべきかがはっきりします。
再発防止を「気をつける」で終わらせない
これがポストモーテムの成否を分けます。
| ❌ 弱い再発防止 | ✅ 強い再発防止 |
|---|---|
| 今後は気をつける | 型で表現して、コンパイルで落とす(TypeScript の型・Go) |
| レビューを厳しくする | CI のチェックを追加する(CI/CD) |
| 手順書に注意書きを足す | 手順そのものを自動化する |
| 定期的に目視で確認する | アラートを追加して自動検知する |
| 該当箇所を直した | 同じ形のバグが他にないか横断的に探した |
判定基準はシンプルです。
その対策は、当事者が全員退職しても機能しますか?
機能しないなら、それは対策ではなく決意表明です。 そして再発防止のアクションは、チケット化して期限を切らないと、必ず実行されません。
オンコールの現実
ローテーション
オンコールは、時間外にアラートを受ける当番です。 週替わりで回すのが一般的で、多くの場合は一次担当と二次担当(エスカレーション先)を置きます。
健全な運用の条件は、だいたいこのあたりです。
- 1週間で人を起こすアラートは数件まで。毎晩鳴るならアラート設計が壊れている
- 当番中は大きな開発タスクを持たない(いつ中断されるか分からないため)
- 対応した分の代休や手当がある
- 当番中はリスクの高いデプロイをしない(金曜夕方のデプロイを避けるのはこのため)
引き継ぎ
当番を交代する時、口頭で「特にありません」で済ませないでください。
- 未解決のまま持ち越しているアラート
- 一時的に抑止(ミュート)しているアラート、とその解除予定
- 進行中の大きな変更・移行作業
- 今週鳴ったアラートと、その対応
一時ミュートの解除忘れは、実際によくある事故です。 「うるさいから止めた」まま次の当番に伝わらず、 本物の障害を誰も知らないまま数時間経過します。
runbook
runbook は、アラート1本につき1枚用意する対応手順書です。 「深夜に叩き起こされた、そのアラートに詳しくない人」が読者だと思って書きます。
| 項目 | 内容 |
|---|---|
| このアラートの意味 | 何を検知したのか |
| ユーザー影響 | 放置するとどうなるか |
| 最初に見る場所 | ダッシュボードのリンク、確認コマンド |
| よくある原因と対処 | 過去の事例。「まずロールバックを検討」など |
| やってはいけないこと | 「この Pod は消さない(データが飛ぶ)」 |
| エスカレーション先 | 手に負えない時に誰を呼ぶか、呼んでよい基準 |
最も良いタイミングは、対応が終わった直後です。手順を覚えているうちに書きます。
新人にとって、これは最高の学習機会でもあります。 自分が対応した障害の runbook を書けば、その仕組みを一番深く理解できますし、 チームへの明確な貢献にもなります。
そして、runbook を書けないアラートは、そもそも鳴らすべきではありません。 対応方法が書けないということは、鳴った人も何もできないということです。
人を起こすことの重さ
最後に、この章で一番覚えておいてほしいことです。
アラートを1本追加するということは、誰かの睡眠を奪う権利を作るということです。
そのアラートで起こされるのは、あなたかもしれないし、 明日から当番に入る同僚かもしれません。
だから、アラートを追加する時は必ず自問してください。
- これが鳴った時、人は今すぐ何かできるか
- 何もできないなら、なぜ起こすのか
- 朝まで待てるなら、なぜ電話なのか
この3つに答えられないアラートは、作らないのが正解です。
新人が最初にやること
- 自分のサービスのダッシュボードを平常時に見ておく — 障害中に初めて見るグラフは読めません
trace_idを全ログに入れる — 調査できるかどうかがここで決まる- アラートが鳴ったら、まず声を出す — 黙って本番を触らない
- 「いつから/全員か/直前に何が変わったか」を聞く — 切り分けの型を口癖にする
- 対応した障害の runbook を書く — 一番の学習になり、チームにも残る
実務の落とし穴まとめ
- 文字列連結のログ — 後から
userIdやdurationで絞り込めない - ログに個人情報・トークン・ボディ丸ごと — 消せない事故になる
- 平均レイテンシだけ見る — p99 の重症者が平均に埋もれる
- メトリクスのラベルにユーザーID — カーディナリティ爆発で監視基盤が落ちる
- リソース指標でアラート — 起こされ損と見逃しの両方が起きる
- 「念のため」のアラート追加 — アラート疲れで本物を見逃す
- 原因究明を復旧より先にやる — その間ずっとユーザーは使えない
- 復旧を急いで証拠を消す — 原因が永久に分からなくなる
- 再発防止が「今後は気をつける」 — 対策ではなく決意表明
- ミュートの解除忘れ — 誰も気づかないまま障害が進行する
まとめ
- 監視は「落ちたことを知る」、可観測性は「なぜ落ちたか調べられる」。後者はコードを書く時点で決まる
- 3本柱は役割が違う。メトリクス(傾向)→ トレース(犯人)→ ログ(詳細) の順で広い方から見る
- ログは構造化して出す。
trace_idは最重要。個人情報・トークン・ボディ丸ごとは出さない - 平均ではなく p95 / p99 を見る。メトリクスのラベルに高カーディナリティな値を入れない
- SLO は「どこまで壊れてよいか」の合意。エラーバジェットが攻めと守りの判断を数字にする
- アラートは「今すぐ人が動かないと悪化する」時だけ。症状で鳴らし、原因では鳴らさない
- アラート疲れが最大の敵。増やすより減らす方が難しく、価値がある
- 障害対応は ① 復旧 → ② 影響範囲 → ③ 情報共有 → ④ 原因調査。新人はまず気づいたことを声に出す
- 切り分けは「いつから / 全員か一部か / 直前に何が変わったか」。直前の変更が原因の割合が圧倒的
- ポストモーテムは人ではなく仕組みを直す。「気をつける」は対策ではない
- アラートを1本足すことは、誰かの睡眠を奪う権利を作ること
公式ドキュメント
迷ったら一次情報に戻ってください。
| 対象 | リンク |
|---|---|
| Google SRE Book(全文無料) | https://sre.google/books/ |
| OpenTelemetry | https://opentelemetry.io/docs/ |
| Prometheus | https://prometheus.io/docs/ |
| Google Cloud Observability | https://cloud.google.com/stackdriver/docs?hl=ja |
章末問題
注文 API のエラー率が急上昇したとアラートが鳴りました。調べると、30分前に注文サービスのデプロイが行われています。まず何をすべきですか。
これで第8部の主要な内容は終わりです。
道具の使い方から始まり、チームでの進め方、Web の仕組み、言語、 サービスの繋ぎ方、安全なコード、そして動かし続ける方法まで来ました。
ここまでの内容は、現場で困らないための最低限の共通言語です。 これを全部覚えている必要はありません。 必要になった時に「そういえばあの章にあった」と思い出せれば十分です。
そして最後に。
分からないことは、分からないと言ってください。
障害対応でも、レビューでも、設計の議論でも、 一番価値があるのは、知っているふりをしないことです。 エンジニアとしての立ち振る舞いで触れたこの姿勢が、結局のところ、 技術書 20 冊分より速くあなたを成長させます。
これで第8部は終わりです。 最後の第9部では、ここまでの章を1本のサービスにつないで、実際に動かします。