プログラマのための IT 教科書

監視とオンコール

この部の 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・メモリ・コネクション数)は、 原因を調べる時に見る補助指標であって、健康状態そのものではありません。 この区別が、次のアラート設計に直結します。

ラベルにユーザーIDを入れない

メトリクスは「ラベルの組み合わせの数」だけ時系列データが作られます。

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 は「どこまで壊れてよいか」の合意

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つの害があります。

  1. 障害を隠すようになる(報告が遅れ、被害が拡大する)
  2. 本当の原因(仕組みの欠陥)が放置される

責めるべきなのは、「注意していれば防げた」という状態を許していた仕組みです。 人は必ずミスをします。それを前提に設計されていなかったことが原因です。

「確認漏れ」で止めない

原因が「確認漏れ」「見落とし」で終わっているポストモーテムは、 まだ原因に到達していません。そこから一段掘ります。

なぜ確認漏れが起きた?  → 確認項目が30個あり、手動だから
なぜ手動なのか?        → 自動でチェックする仕組みが無いから
→ 対策: その確認を CI で自動化する

「なぜ」を数回繰り返して、人の注意力ではなく仕組みに着地させます。

書く項目

項目内容
概要何が起きたか、3行で
影響期間、影響したユーザー数・リクエスト数、金銭影響
タイムライン発生・検知・対応・復旧の時刻(記録係の出番)
根本原因仕組みとして何が欠けていたか
検知どうやって気づいたか。ユーザーからの報告で気づいたなら、それ自体が課題
うまくいったことロールバックが 3分で終わった、など(良かった点も必ず書く)
再発防止具体的なアクション。担当者と期限をつける

「発生から検知までの時間」と「検知から復旧までの時間」を分けて書くと、 どちらを改善すべきかがはっきりします。

再発防止を「気をつける」で終わらせない

これがポストモーテムの成否を分けます。

❌ 弱い再発防止✅ 強い再発防止
今後は気をつける型で表現して、コンパイルで落とす(TypeScript の型・Go)
レビューを厳しくするCI のチェックを追加する(CI/CD)
手順書に注意書きを足す手順そのものを自動化する
定期的に目視で確認するアラートを追加して自動検知する
該当箇所を直した同じ形のバグが他にないか横断的に探した

判定基準はシンプルです。

その対策は、当事者が全員退職しても機能しますか?

機能しないなら、それは対策ではなく決意表明です。 そして再発防止のアクションは、チケット化して期限を切らないと、必ず実行されません。

オンコールの現実

ローテーション

オンコールは、時間外にアラートを受ける当番です。 週替わりで回すのが一般的で、多くの場合は一次担当と二次担当(エスカレーション先)を置きます。

健全な運用の条件は、だいたいこのあたりです。

  • 1週間で人を起こすアラートは数件まで。毎晩鳴るならアラート設計が壊れている
  • 当番中は大きな開発タスクを持たない(いつ中断されるか分からないため)
  • 対応した分の代休や手当がある
  • 当番中はリスクの高いデプロイをしない(金曜夕方のデプロイを避けるのはこのため)

引き継ぎ

当番を交代する時、口頭で「特にありません」で済ませないでください。

  • 未解決のまま持ち越しているアラート
  • 一時的に抑止(ミュート)しているアラート、とその解除予定
  • 進行中の大きな変更・移行作業
  • 今週鳴ったアラートと、その対応

一時ミュートの解除忘れは、実際によくある事故です。 「うるさいから止めた」まま次の当番に伝わらず、 本物の障害を誰も知らないまま数時間経過します。

runbook

runbook は、アラート1本につき1枚用意する対応手順書です。 「深夜に叩き起こされた、そのアラートに詳しくない人」が読者だと思って書きます。

項目内容
このアラートの意味何を検知したのか
ユーザー影響放置するとどうなるか
最初に見る場所ダッシュボードのリンク、確認コマンド
よくある原因と対処過去の事例。「まずロールバックを検討」など
やってはいけないこと「この Pod は消さない(データが飛ぶ)」
エスカレーション先手に負えない時に誰を呼ぶか、呼んでよい基準
runbook は障害の直後に書く

最も良いタイミングは、対応が終わった直後です。手順を覚えているうちに書きます。

新人にとって、これは最高の学習機会でもあります。 自分が対応した障害の runbook を書けば、その仕組みを一番深く理解できますし、 チームへの明確な貢献にもなります。

そして、runbook を書けないアラートは、そもそも鳴らすべきではありません。 対応方法が書けないということは、鳴った人も何もできないということです。

人を起こすことの重さ

最後に、この章で一番覚えておいてほしいことです。

アラートを1本追加するということは、誰かの睡眠を奪う権利を作るということです。

そのアラートで起こされるのは、あなたかもしれないし、 明日から当番に入る同僚かもしれません。

だから、アラートを追加する時は必ず自問してください。

  1. これが鳴った時、人は今すぐ何かできるか
  2. 何もできないなら、なぜ起こすのか
  3. 朝まで待てるなら、なぜ電話なのか

この3つに答えられないアラートは、作らないのが正解です。

新人が最初にやること

  1. 自分のサービスのダッシュボードを平常時に見ておく — 障害中に初めて見るグラフは読めません
  2. trace_id を全ログに入れる — 調査できるかどうかがここで決まる
  3. アラートが鳴ったら、まず声を出す — 黙って本番を触らない
  4. 「いつから/全員か/直前に何が変わったか」を聞く — 切り分けの型を口癖にする
  5. 対応した障害の runbook を書く — 一番の学習になり、チームにも残る

実務の落とし穴まとめ

  1. 文字列連結のログ — 後から userId や duration で絞り込めない
  2. ログに個人情報・トークン・ボディ丸ごと — 消せない事故になる
  3. 平均レイテンシだけ見る — p99 の重症者が平均に埋もれる
  4. メトリクスのラベルにユーザーID — カーディナリティ爆発で監視基盤が落ちる
  5. リソース指標でアラート — 起こされ損と見逃しの両方が起きる
  6. 「念のため」のアラート追加 — アラート疲れで本物を見逃す
  7. 原因究明を復旧より先にやる — その間ずっとユーザーは使えない
  8. 復旧を急いで証拠を消す — 原因が永久に分からなくなる
  9. 再発防止が「今後は気をつける」 — 対策ではなく決意表明
  10. ミュートの解除忘れ — 誰も気づかないまま障害が進行する

まとめ

  • 監視は「落ちたことを知る」、可観測性は「なぜ落ちたか調べられる」。後者はコードを書く時点で決まる
  • 3本柱は役割が違う。メトリクス(傾向)→ トレース(犯人)→ ログ(詳細) の順で広い方から見る
  • ログは構造化して出す。trace_id は最重要。個人情報・トークン・ボディ丸ごとは出さない
  • 平均ではなく p95 / p99 を見る。メトリクスのラベルに高カーディナリティな値を入れない
  • SLO は「どこまで壊れてよいか」の合意。エラーバジェットが攻めと守りの判断を数字にする
  • アラートは「今すぐ人が動かないと悪化する」時だけ。症状で鳴らし、原因では鳴らさない
  • アラート疲れが最大の敵。増やすより減らす方が難しく、価値がある
  • 障害対応は ① 復旧 → ② 影響範囲 → ③ 情報共有 → ④ 原因調査。新人はまず気づいたことを声に出す
  • 切り分けは「いつから / 全員か一部か / 直前に何が変わったか」。直前の変更が原因の割合が圧倒的
  • ポストモーテムは人ではなく仕組みを直す。「気をつける」は対策ではない
  • アラートを1本足すことは、誰かの睡眠を奪う権利を作ること

公式ドキュメント

迷ったら一次情報に戻ってください。

対象リンク
Google SRE Book(全文無料)https://sre.google/books/
OpenTelemetryhttps://opentelemetry.io/docs/
Prometheushttps://prometheus.io/docs/
Google Cloud Observabilityhttps://cloud.google.com/stackdriver/docs?hl=ja

章末問題

注文 API のエラー率が急上昇したとアラートが鳴りました。調べると、30分前に注文サービスのデプロイが行われています。まず何をすべきですか。

これで第8部の主要な内容は終わりです。

道具の使い方から始まり、チームでの進め方、Web の仕組み、言語、 サービスの繋ぎ方、安全なコード、そして動かし続ける方法まで来ました。

ここまでの内容は、現場で困らないための最低限の共通言語です。 これを全部覚えている必要はありません。 必要になった時に「そういえばあの章にあった」と思い出せれば十分です。

そして最後に。

分からないことは、分からないと言ってください。

障害対応でも、レビューでも、設計の議論でも、 一番価値があるのは、知っているふりをしないことです。 エンジニアとしての立ち振る舞いで触れたこの姿勢が、結局のところ、 技術書 20 冊分より速くあなたを成長させます。

これで第8部は終わりです。 最後の第9部では、ここまでの章を1本のサービスにつないで、実際に動かします。

読み終わったら記録しておくと、目次で進み具合が分かります。