第12回で「定常状態を数値で持てているか」を確認し、第13回で「完了条件は設定値ではなく観測値で書く」と決め、第14回で「更新の完了条件は観測値」「中止する数値を先に決める」と繰り返しました。
3回続けて同じことを言っていますが、その観測値がどこから来るのかは、まだ設計していません。第12回から第14回までは指標を使う側で、本記事はその指標を定義する側です。
そして監視の設計書は、他の設計書と1つ違う性質を持ちます。「入れれば入れるほど良い」が成り立ちません。取り込んだ量がそのまま月額になり、通知の本数がそのまま人の可処分時間を削ります。Google SRE は「Paging a human is a quite expensive use of an employee’s time.」と書いています。
したがって本記事は、測る対象の一覧から始めません。何で人を起こすかから始めます。
目次
監視設計は「何を測るか」ではなく「何で人を起こすか」から書く
Google SRE は、監視システムが答えるべき問いを2つに整理しています。「Your monitoring system should address two questions: what’s broken, and why?」。前者が症状、後者が原因です。そして配分についても明言しています。「it’s better to spend much more effort on catching symptoms than causes」。
この区別を、監視設計書の構造にそのまま持ち込みます。
| 症状(what’s broken) | 原因(why) | |
|---|---|---|
| 例 | エラー率が上がった、応答が遅い | CPUが飽和している、接続が枯渇している |
| 通知の種類 | ページ(呼び出し) | チケット |
| 閾値の決め方 | エラーバジェットの消費速度 | 自分のサービスで劣化が始まる水準 |
| 使う場面 | いま対応するかの判断 | 対応が始まった後の切り分け |
よくある監視設計書は、この2列が混ざっています。CPU使用率が80%を超えたら呼び出すという設定は、右列の指標を左列の扱いにしたものです。CPUが80%でも応答が正常なら起こす理由はなく、CPUが40%でもエラー率が上がっていれば起こす理由があります。
本記事はリソース系の閾値を否定しません。置く列を変えるだけです。
通知を1本作る前に答える5つの問い
Google SRE が挙げている問いです。アラート台帳に行を1つ足すたびに、この5つを通します。
- この条件は、他の方法では検知できず、緊急で、対処可能で、いまユーザーに影響しているか
- この通知を「無害だから無視してよい」と判断する場面はあるか。あるなら、その場面をどう除外するか
- 本当にユーザーが影響を受けていると言い切れるか。テスト配備や意図的に切り離した経路を除外できているか
- 対処できるか。その対処は朝まで待てないか。自動化できないか
- 同じ事象で他の人も呼ばれていないか
4番目が最も効きます。Google SRE は「Every page response should require intelligence. If a page merely merits a robotic response, it shouldn’t be a page.」と書いています。決まった手順を実行するだけで済む通知は、自動化の対象であって、人を起こす対象ではありません。この線引きが、第16回で扱うオンコール体制の前提になります。
この記事で作る成果物
- ログ/メトリクス標準規約。開発チームとの合意文書であり、運用チームだけでは実装できないもの
- アラート台帳。1行が1つの通知で、症状か原因か、ページかチケットかを列に持つもの
- ダッシュボード定義。見る人と判断を先に書き、載せる指標を後から決めるもの
本記事ではクエリをほとんど書きません。ログの検索構文もPromQLの書き方も公式ドキュメントにあります。足りないのは、何を義務化し、何を捨て、どの数値で人を起こすかという判断基準です。
測る対象は、すでに決まっている
目標値は第3回から降りてくる。本記事の仕事は測定式にすること
可用性の目標は第3回で決めました。トポロジーを選ぶ際に、GKEのSLAがリージョンクラスタで99.95%、ゾーンクラスタで99.5%であること(許容されるダウンタイムは月21.6分と月216分)を確認し、それを土台にサービスの目標値を置いています。本記事で目標値を決め直すことはしません。ここでの仕事は、その目標値を測れる形に変換することです。
Cloud Monitoring の定義を借ります。SLIは「a measurement of performance」、SLOは「a target value for an SLI, measured over a period of time」、そしてエラーバジェットは「(1 − SLO goal) × (eligible events)」です。
この式が重要なのは、目標値を「分」ではなく「件数」に変換できるからです。30日間に3,000万リクエストを処理するサービスで99.95%を掲げるなら、許される失敗は15,000件です。この数え方に変えると、後段のアラート設計がそのまま計算になります。
SLIの型は2つあります。
- リクエストベース。成功したリクエスト数 ÷ 全リクエスト数
- ウィンドウベース。条件を満たした測定区間の数 ÷ 全区間数
そしてレイテンシのSLIは、公式の定義でも「閾値を下回った呼び出しの数 ÷ 全呼び出し数」です。平均応答時間ではありません。レイテンシを「平均何ミリ秒」で持つと、エラーバジェットの計算に接続できなくなります。「300ミリ秒以内に返った割合が99.9%」という形にしてください。
なおCloud Monitoring のサービスモニタリング機能には制約があります。1サービスあたりのSLOは500までで、「The highest value you can set is 99.9%, but you can use any lower value that is appropriate for your service.」と明記されています。コンプライアンス期間はカレンダー方式と、1日から30日までのローリング方式から選びます。
4つのゴールデンシグナルと、そこにある3つの落とし穴
測る対象の最小セットは決まっています。「The four golden signals of monitoring are latency, traffic, errors, and saturation.」——レイテンシ、トラフィック、エラー、飽和の4つです。
そのまま採用してよいのですが、実務で踏む落とし穴が3つあります。
- レイテンシは成功と失敗を分けて測る。バックエンドへの接続が切れて返した500応答は、非常に速く返ります。失敗を混ぜたまま平均を取ると、障害の最中にレイテンシが改善して見えます。ただし除外して終わりでもありません。原文は「a slow error is even worse than a fast error」と書いています。遅い失敗を別に測ってください
- 飽和は100%になる前に劣化する。原文は「many systems degrade in performance before they achieve 100% utilization, so having a utilization target is essential」。これが「CPU 80%」という数字の唯一の正当な使い道です。上限として与えられる値ではなく、自分のサービスで応答時間が崩れ始める水準を測って決める値です
- レイテンシの上昇は飽和の先行指標。原文は「Measuring your 99th percentile response time over some small window (e.g., one minute) can give a very early signal of saturation」。CPUの使用率より、99パーセンタイルの応答時間のほうが先に動きます
そして測り方にも指定があります。平均で持ってはいけません。原文の例が明快です。「average latency of 100 ms at 1,000 requests per second」でも「1% of requests might easily take 5 seconds」。対処は、レイテンシそのものではなくレイテンシ帯ごとのリクエスト数、つまりヒストグラムで収集すること、そして境界をおよそ指数的に(3倍程度の刻みで)置くことです。
この判断は、そのまま費用の判断になります。ヒストグラムのバケット数が、メトリクスの月額を決めます。その計算は後の章で扱います。
クラスタの中から見ている限り、クラスタが落ちたことは分からない
ここまでの指標は、すべてシステムの内部から取るものです。Google SRE の分類では白箱監視にあたります。これに対して、外から見た振る舞いを測るのが黒箱監視です。原文は「for paging, black-box monitoring has the key benefit of forcing discipline to only nag a human when a problem is both already ongoing and contributing to real symptoms」と書き、同時に「for not-yet-occurring but imminent problems, black-box monitoring is fairly useless」とも書いています。兆候の検知には使えず、実際に起きている障害の検知には向くということです。
Google Cloud では稼働時間チェックがこれにあたります。費用は1,000実行あたり$0.30、無料枠はプロジェクトあたり月100万実行で、3つのリージョンから実行すれば3実行と数えられます。
設計上の含意は2つです。
- 外形監視は、監視対象と同じ障害の影響を受けない場所に置く。クラスタ内のPodから自分自身を叩く構成は、クラスタが落ちると同時に止まります
- リージョン全体の停止を検知できるのは、この経路だけ。第17回で扱う災害復旧の発動判断は、ここからの入力で始まります
第3回で決めた3クラスタ構成に対して、外形監視をどこに置くかを1行で書いてください。本番クラスタの外側です。
ログ:stdout と JSON を義務化する
ファイルに書かせない。理由は3つある
ログ規約の1行目は出力先です。標準出力と標準エラーに書く、それ以外を認めない。理由は3つあります。
- ノードのストレージ消費が2倍になりうる。Kubernetes公式は「writing logs to a file and then streaming them to stdout can double how much storage you need on the node」と書いています
- サイドカーでログ収集エージェントを動かすと、
kubectl logsで読めなくなる。公式の記述は「you won’t be able to access those logs usingkubectl logsbecause they are not controlled by the kubelet」。第18回で開発者に渡す一次調査の手段を、こちらの都合で壊すことになります - ローテーションと保持をkubeletに任せられる。公式も「leave rotation and retention policies to the kubelet」と書いています。自前で
logrotateを持つ必要がなくなります
どうしてもファイルに書く実装を変えられない場合、公式が挙げている代替は出力先を /dev/stdout にすることです。転送用のサイドカーを立てるより先に、この選択肢を検討してください。
ノード側には3つの上限がある
標準出力に書かせたうえで、その先に3つの上限があります。ログ規約は、この上限の内側で設計します。
| 上限 | 既定値 | 超えると起きること |
|---|---|---|
| コンテナログのローテーション | containerLogMaxSize 10Mi / containerLogMaxFiles 5 | 古いファイルから消える |
kubectl logs が返す範囲 | 最新のログファイルのみ | 直近しか見えない |
| ロギングエージェントのスループット | ノードあたり毎秒100 KiB | 収集が追いつかない |
2番目は公式の例がそのまま使えます。「if a Pod writes 40 MiB of logs and the kubelet rotates logs after 10 MiB, running kubectl logs returns at most 10MiB of data」。ログを大量に出すアプリほど、その場で見える範囲が狭くなります。
3番目が最も見落とされます。GKEのロギングエージェントは「at least 100 KiB per second log throughput per node」で、負荷が低ければ毎秒500 KiB以上出ることもあります。これを超える場合は --logging-variant=MAX_THROUGHPUT を指定して「throughput as high as 10 MiB per second on nodes that have at least 2 unused CPU cores」まで上げられます(コントロールプレーンが1.23.13-gke.1000以降)。
毎秒100 KiBという数字を換算します。1行1 KiBのJSONログなら、ノードあたり毎秒100行です。読者の環境の値で置き換えてほしいのですが、例として1ノードに20 Podが乗り、それぞれが毎秒10行出力すれば、その時点で上限に達します。
ここが厄介なのは、上限に当たっていることが、ログを見ても分からない点です。「一部のログだけ届いていない」という形で現れます。設計書には、1ノードあたりの想定ログ量(行数 × 1行の平均バイト数)を計算した値を残してください。
なおGKEのノードシステム構成では containerLogMaxSize を10Miから500Mi、containerLogMaxFiles を2から10まで変更できますが、両者の積はノード総ストレージの1%を超えられません。そしてこの変更もロギングバリアントの変更も、ノードプールのノード再作成を伴います。第14回で扱ったノード更新と同じ扱いになるため、維持窓の中で実施してください。
JSONで出す理由は、読みやすさではない
JSON化の目的は整形ではありません。特定の名前のフィールドだけが、Cloud Logging の構造化フィールドに昇格するからです。認識されなかったフィールドは jsonPayload の中に残るだけになります。
| フィールド名 | 昇格先と効果 | 規約での扱い |
|---|---|---|
severity | ログの重大度 | 必須 |
message | 本文 | 必須 |
logging.googleapis.com/trace | トレースとの相関 | 必須 |
logging.googleapis.com/spanId | スパンとの相関 | 必須 |
logging.googleapis.com/trace_sampled | サンプリング済みかの表示 | 必須 |
httpRequest | メソッド、URL、応答コード、レイテンシ | 入口のサービスは必須 |
logging.googleapis.com/labels | ユーザー定義ラベル | 任意(第4回のラベル体系に揃える) |
logging.googleapis.com/sourceLocation | ソースコード位置 | 任意 |
logging.googleapis.com/operation | 長時間処理の相関 | 任意 |
logging.googleapis.com/insertId | 重複排除と順序 | 任意 |
この表の読み方を明確にします。severity を出していないアプリのログは、すべて同じ重大度として扱われます。そうなると「エラーだけ抽出する」も「エラーログの件数でアラートを出す」も成立しません。規約は「読みやすいJSONを出しましょう」ではなく、「この名前で出さないとこの機能が使えません」という形で書いてください。
JSONログには、テキストにはない失敗の形がある
ここは規約に必ず入れてください。GKEの公式ドキュメントは、こう書いています。
「Any LogEntry exceeding the size limit is dropped for jsonPayload logs and truncated for textPayload logs」
ログエントリの上限は256 KiB(監査ログは512 KiB)です。テキストなら切り詰められて残りますが、JSONは丸ごと破棄されます。
この挙動が問題になる場面は決まっています。スタックトレースやリクエストボディを1つのフィールドに入れる設計です。普段は問題なく動き、例外が発生してスタックが深くなったときだけ、そのログが存在しなくなります。最も知りたい場面で、最も知りたいログだけが消えます。
規約に入れる項目は3つです。
- 1エントリの最大サイズ。256 KiBを上限として、アプリ側で切り詰める責任を明記する(長大なフィールドは先頭何バイトまで、と決める)
- 重複キーを出さない。公式は「duplicated json keys are not supported」と書いています
- 予約語を使わない。
streamはGKEのログパイプラインの予約キーです
あわせて機微データの禁止も規約に入れてください。第5回でシークレット管理を設計しましたが、その値がログに出力された瞬間に、設計は無効になります。収集側では手当てできません。出力側の責任です。
ログの費用は、単価ではなくバケットの数で決まる
Cloud Logging の料金を確定値で示します。
| 項目 | 金額 | 備考 |
|---|---|---|
| 取り込み(ログバケットへの書き込み) | $0.50/GiB | 30日までの保存を含む。検索と分析に追加料金はかからない |
| 無料枠 | 月50 GiB/プロジェクト | |
| ネットワーク系のログ | $0.25/GiB | VPCフローログ、ファイアウォールログ、Cloud NATのログ |
| 30日を超える保持 | $0.01/GiB/月 | 既定の保持期間内なら保持費用はかからない |
| ルーティング | 無料 | 宛先側の費用は別途 |
_Required バケット | 無料 | 保持は固定400日。無効化も除外もできない |
ここに2つの落とし穴があります。
1つ目。同じログを複数のバケットに送ると、その回数だけ課金されます。公式は「if a log entry is routed to three log buckets that are in the same project, then that project is charged three times for the log entry」と書いています。「本番用」「監査用」「分析用」とバケットを分ける設計は、そのまま3倍の請求になります。
2つ目。無料のはずの監査ログをコピーすると課金されます。「If you route copies of logs that are automatically stored in the _Required bucket to another log bucket, then storage and retention pricing applies」。第5回で扱った監査要件から「管理操作の記録を長期保管したい」という話になったとき、素直に実装すると無料だったものが有料になります。_Required は既に400日保持しているので、それで足りるかを先に確認してください。
削減の手段は除外フィルタです。ただし手をつける順序があります。
- アプリ側の出力を直す。デバッグレベルのログを本番で出していないか。1リクエストで何行出しているか。ここが減れば、後続のどの段でも減ります
- Namespaceと重大度で除外する。開発用Namespaceのワークロードログ、正常系のアクセスログなど、価値の低いものを取り込む前に落とす
- 保持期間を見直す。効果は小さい($0.01/GiB/月)ので最後です
そして忘れられがちなのが、第3回で設計したCloud NATと第5回で設計したNetworkPolicyのロギングです。これらは$0.25/GiBの区分で、ワークロードのログとは別に積み上がります。NetworkPolicyのログについては第5回で「生成は無料だが保管費は発生する」と確認済みです。監視設計書のログの節では、アプリのログとネットワーク系のログを分けて見積もってください。
トレース:Trace ID は、伝播しなければ存在しない
入口で採番し、全ホップで転記する
トレースの設計は「導入するか否か」ではありません。どこで途切れるかの設計です。
Trace ID は入口で1回だけ採番し、以降のすべてのホップで転記することでしか成立しません。1つのサービスが転記を忘れると、そこから先が別のトレースとして記録されます。そして分断されたことは、トレースの画面を見ても分かりません。「そのリクエストは3ホップで完結した」ように見えるだけです。
採番する場所は、第3回で設計した外部公開の入口(Gateway)です。ここから先の全サービスが転記の対象になります。
ここで実務上の罠があります。Google Cloud には2種類のヘッダがあり、書式が違います。
traceparent(W3C) | X-Cloud-Trace-Context | |
|---|---|---|
| 区切り | ハイフン | スラッシュとセミコロン |
| 書式 | バージョン、trace-id、parent-id、trace-flags | TRACE_ID/SPAN_ID;o=OPTIONS |
| Trace ID | 16進数 | 32文字の16進数(128ビット) |
| Span ID | 16進数 | 符号なし64ビットの10進数 |
| サンプリング | trace-flags | o=0(未サンプル)/o=1(サンプル済み) |
同じスパンIDを、片方は16進数、もう片方は10進数で書きます。両方のヘッダが混在する構成では、変換を実装しない限りトレースが分断されます。公式は traceparent を優先することを推奨しています。
規約に書く項目は2つです。採用するヘッダを1つに決めること。そして変換が必要になる境界(ロードバランサやマネージドサービスを経由する箇所)を明示すること。「両方に対応する」という書き方は、実装者に判断を丸投げしているのと同じです。
ログとトレースをつなぐのは3つのフィールドだけ
トレースを入れる価値が最も出るのは、単体のトレース画面ではありません。エラーログから、そのリクエストの全経路へ移動できることです。
そのために必要なのは3つのフィールドだけです。
trace。推奨形式はトレースID、レガシー形式はprojects/PROJECT_ID/traces/TRACE_IDspanId。公式は「16-character hexadecimal encoding」と指定しています。IDが74のスパンなら000000000000004atraceSampled。そのログを書いた時点でトレースがサンプリング対象だったかを示します
ここで前節の罠が効いてきます。X-Cloud-Trace-Context から受け取ったスパンIDは10進数なので、ログに出すときは16進数へ変換する必要があります。この1行を規約に書いていないと、「Trace IDは入れたのにログとトレースがつながらない」という状態になります。
3つ揃って初めて、第16回の一次対応で「このエラーログから、そのリクエストが通った全サービスへ」という移動が成立します。1つでも欠けると成立しません。

費用と上限も押さえておきます。取り込みは100万スパンあたり$0.20、無料枠は請求先アカウントあたり月250万スパン、保持は30日です。費用より先に当たるのは1スパンあたりの属性32個・属性値256バイトという上限で(Telemetry API 経由なら属性1,024個・値64 KiB)、関係しそうな情報を全部スパンに入れる設計は成立しません。サンプリング率は費用ではなく再現性から決めます。下限は1件の障害を調べるのに必要なトレース件数 ÷ その時間帯のリクエスト件数で、第12回の観測窓・第13回の刻み幅と同じ構造です。母数の小さいサービスでは1%という率に意味がありません。なお取り込み経路はOTLP(Telemetry API)が現在の推奨で、GKEのマネージドOpenTelemetryは執筆時点でプレビュー段階のため、本番設計の前提には置きません。
メトリクス:設計するのは「集めるもの」ではなく「集めないもの」
GKEには無償の範囲があり、その外側は有効化した瞬間に課金が始まる
第6回で監視基盤として Google Cloud Managed Service for Prometheus を選定しました。その構成設計がここです。
まず線の位置を確認します。公式は「Cloud Monitoring does not charge for the ingestion of GKE system metrics.」と明記しています。GKEのシステムメトリクスは無償です。Google Cloud のメトリクス、Istioのメトリクス、Knativeのメトリクスも非課金の側にあります。
一方で、GKEが提供する観測パッケージは別です。「the metrics in the observability packages are charged by the number of samples ingested」。料金表にも「Metrics ingested by using Google Cloud Managed Service for Prometheus, including GKE control plane metrics」と書かれています。
| パッケージ | 課金 | 有効化を検討する理由 |
|---|---|---|
| システムメトリクス | 非課金 | 既定で有効。Autopilotでは無効化できない |
| コントロールプレーン(API_SERVER / SCHEDULER / CONTROLLER_MANAGER) | サンプル課金 | 第14回の非推奨API検出(apiserver_requested_deprecated_apis)はここ |
| kube state(POD / DEPLOYMENT / STATEFULSET / DAEMONSET / HPA / STORAGE ほか) | サンプル課金 | 第11回のレプリカ数やHPAの動作を追うならここ |
| cAdvisor / kubelet | サンプル課金 | コンテナ単位のリソース使用量 |
設計書には有効化したパッケージと、その理由を書いてください。第6回のADRと同じ形式で、有効化しなかったパッケージとその理由も残します。1年後に「なぜこれが取れていないのか」と問われたとき、判断だったのか漏れだったのかが区別できます。
サンプル課金の式が、そのまま設計の道具になる
サンプル数の数え方が公式に定義されています。この式が、そのまま設計判断の道具になります。
- スカラー型。時系列に書き込まれた1点につき1サンプル
- 固定バケットのディストリビューション型。1点につき2サンプル、加えて値が0でないバケット1つにつき1サンプル
- 指数バケット(動的バケット)のディストリビューション型。1点につき2サンプル、加えて値が0でないバケット1つにつき8分の1(切り上げ)
単価は取り込み量に応じた段階制で、500億サンプルまでが100万サンプルあたり$0.06、以降$0.048、$0.036、$0.024と下がります。無料枠はありません。
公式の料金例が、判断の材料としてそのまま使えます。
| 構成 | 15秒間隔 | 60秒間隔 |
|---|---|---|
| 100コンテナ × 1,000のスカラー時系列 | 月$1,051.20 | 月$262.80 |
| 1,000コンテナ × 1,000のスカラー時系列 | 月$9,009.60 | 月$2,628.00 |
| 100コンテナ × 1,000の100バケットのヒストグラム(25%が空) | 月$39,576.96 | 月$15,741.36 |
3つの行から、3つのレバーが読み取れます。
1つ目はスクレイプ間隔です。公式も「Changing the metric-scraping period from 15 seconds to 60 seconds can result in a 75% cost savings」と明記しています。前章で第11回のHPAに触れましたが、HPAの制御に使うメトリクスと、傾向を見るためのメトリクスは、同じ間隔である必要がありません。間隔を分けて設計してください。
2つ目はヒストグラムのバケット数です。先ほど「レイテンシは平均ではなくヒストグラムで持て」と書きました。その代償がこの行です。同じ100コンテナでも、スカラーなら月$1,051、100バケットのヒストグラムなら月$39,576。約37倍になります。
逃げ道は指数バケットです。非ゼロバケット1つにつき8分の1で数えられるため、同じバケット数でも桁が変わります。そしてゴールデンシグナルの節で引いた「境界をおよそ指数的に置く」という推奨と、この課金体系は一致しています。バケットの設計は、精度の問題であると同時に費用の問題です。
3つ目はカーディナリティです。公式は費用に効く要因として「the frequency with which the data is sampled and the cardinality of your data」を挙げ、その一方で「The values for the metric and resource labels that are part of your time series don’t contribute to your charges」とも書いています。ラベルの値そのものが加算されるのではなく、値の種類数が時系列の本数を決めることで費用になります。
ここで第4回の設計と衝突します。第4回でコスト按分のためのラベル体系を決めましたが、そのラベルをそのままメトリクスのラベルに持ち込むと、時系列の本数が掛け算で増えます。第4回でも「ラベルを増やすと按分コストが増える」と確認しましたが、メトリクス側ではその影響がさらに大きく出ます。公式の推奨は「Keep high-cardinality, low-value data local」——価値の低い高カーディナリティのデータは、マネージド側へ送らずローカルに留めることです。
収集対象を宣言するのは誰か
ここまでの費用の話には続きがあります。スクレイプ間隔を決めているのは、設計書ではなくマニフェストを書いた人です。
マネージド収集では、スクレイプ対象を2種類のカスタムリソースで宣言します。
PodMonitoring。公式の記述は「A PodMonitoring CR scrapes targets only in the namespace the CR is deployed in」。Namespace単位ですClusterPodMonitoring。全Namespaceにまたがる範囲に適用します
そしてスクレイプ間隔は、この宣言の endpoints の interval で決まります。開発チームが自分のNamespaceに PodMonitoring を置き、そこに5秒と書けば、その費用は運用チームの請求に乗ります。
したがって責務の分け方は、第2回のRACIと第3回のGateway APIの分割と同じ形になります。
| リソース | 書ける主体 | 根拠 |
|---|---|---|
ClusterPodMonitoring | 運用チーム | クラスタ全体に影響し、費用の責任も運用側にある |
PodMonitoring | 開発チーム(自分のNamespace) | 第3回のHTTPRouteと同じ位置づけ |
interval の下限 | 規約で運用チームが決める | 第5回のポリシーエンジンで強制できる |
なおマネージド収集はAutopilotでは1.25以降、Standardでは1.27以降で既定有効であり、Autopilotでは無効化できません。「入れるかどうか」は設計項目ではなく、「何を宣言するか」だけが設計項目です。
そしてこれらの設定は、すべて第8回で決めたIaCの管理対象です。手で作った監視設定は、後で消してよいかを判断できなくなります。第8回で確認した「問題は手動リソースの存在ではなく、どれが手動かを誰も把握していないこと」が、そのまま当てはまります。PodMonitoring はKubernetesのカスタムリソースなので第9回のGitOpsの同期対象、アラートポリシーとログシンクはクラウド側のリソースなので第8回のTerraformの管理対象という分かれ方になります。
保持期間は3つの系統でばらばらに決まっている
監視データの保持期間は、系統ごとに別の既定値を持ちます。設計書に転記しておかないと、必要になった時点で「そのデータはもうありません」となります。
| データ | 保持 | 備考 |
|---|---|---|
| Managed Service for Prometheus | 24か月(追加費用なし) | 粒度は1週間フル、その後5週間は1分、残りは10分 |
| Cloud Monitoring のカスタム/外部メトリクス | 24か月 | |
| Cloud Monitoring のその他のメトリクス | 6週間 | 系統によって4倍以上の差がある |
ログ(_Default・ユーザー定義) | 既定30日 | 1日から3,650日まで変更可 |
ログ(_Required) | 固定400日 | 変更不可・無料 |
| トレース | 30日 |
第5回で扱った監査関連の要件から「1年分の記録が必要」という話が出た場合、既定値のままでは満たせない系統があります。設計書には「保持期間」と「その期間になった根拠(要件)」を並べて書いてください。根拠のない保持期間は、後から短縮する判断ができません。
アラート:閾値ではなく、エラーバジェットの消費速度で決める
「5分継続したら鳴らす」をやめる
多くの現場が使っている「閾値を超えた状態がN分続いたら通知する」という方式について、Google SRE は明確に否定しています。「we do not recommend using durations as part of your SLO-based alerting criteria」。理由は2つです。
1つ目、深刻度に比例しません。継続時間を1時間に設定すると、100%の障害も0.2%の劣化も、同じ1時間後に通知されます。その1時間で、100%の障害は30日分のエラーバジェットの140%を消費します。
2つ目、一瞬でも閾値内に戻るとタイマーがリセットされます。公式が挙げている例が強烈です。10分ごとに5分間だけ100%のエラーが出るサービスでは、30日分のエラーバジェットの35%を消費しても、アラートが一度も鳴りません。
この2つは、いま動いている監視設定にそのまま当てはまります。「5分継続」という条件を書いた覚えがあるなら、上のシナリオで鳴るかどうかを確認してください。
バーンレート表をそのまま使う
代わりに使うのがバーンレートです。エラーバジェットを、SLOの期間に対してどれだけ速く消費しているかを表す倍率で、バーンレート1が「期間の終わりにちょうど使い切る速度」を意味します。
Google SRE の推奨パラメータは、99.9%のSLOに対して次のとおりです。
| 通知の種類 | 長い窓 | 短い窓 | バーンレート | 消費するエラーバジェット |
|---|---|---|---|---|
| ページ(呼び出し) | 1時間 | 5分 | 14.4 | 2% |
| ページ(呼び出し) | 6時間 | 30分 | 6 | 5% |
| チケット | 3日 | 6時間 | 1 | 10% |
短い窓は長い窓の12分の1が目安です。両方が同時に閾値を超えたときだけ通知します。
短い窓を併用する理由は、検知を速めるためではありません。復旧した後に鳴り続けないようにするためです。エラーが止まると短い窓は5分で閾値を下回りますが、長い窓が下回るには60分かかります。短い窓を条件に加えておくと、5分後に通知が止まります。
この表の価値は、3行しかないことです。サービスごとに窓とバーンレートを個別に決め始めると管理が破綻します。Google SRE も「Once you decide on your alerting parameters, apply them to all your services」と書いています。サービスごとに変えるのはSLOの目標値だけで、バーンレートの表は共通にしてください。
この方式が成立しない2つの場合
バーンレート方式には、機能しない領域が2つあります。先に確認しないと、自分のサービスで動かない設計を作ることになります。
1つ目は、トラフィックが少ないサービスです。公式の例では、1時間に10リクエストのシステムで1件失敗すると、その時間のエラー率は10%になります。99.9%のSLOに対してバーンレートは1,000、30日分のエラーバジェットの13.9%を1件で消費します。30日間に許される失敗が7件しかありません。社内向けシステムでは珍しくない規模です。
対処は4つあります。
- 合成トラフィックを生成する。実ユーザーの代わりに定期的なリクエストを流し、母数を作る
- 小さなサービスをまとめて1つのSLOにする。同じ障害要因を共有するもの(同じデータベースを使うなど)を束ねる
- 1件の失敗の影響を下げる。クライアント側のリトライ(指数バックオフとジッター)やフォールバック経路を用意する
- SLOの目標値を下げる。1件の失敗で人を起こす必要が本当にあるかを、利用部門と合意し直す
2つ目は、目標値が極端な場合です。99.999%を掲げると、全面障害は26秒でエラーバジェットを使い切ります。公式の指摘は「which is smaller than the metric collection interval of many monitoring services」——多くの監視サービスの収集間隔より短いということです。逆に90%のような緩い目標では、全面障害でも1時間で消費するのは1.4%にとどまり、2%を条件にしたページは決して鳴りません。
ここは第3回に戻す論点です。監視で守れる目標値かどうかは、目標値を決める段階で確認する必要があります。加えて前述のとおり、Cloud Monitoring のサービスモニタリングで設定できる最高値は99.9%と明記されています。99.99%を掲げる場合、この機能の枠外で測る設計が別途必要になります。
原因側の指標は、ページではなくチケットに置く
ここでCPU、メモリ、ディスクが登場します。削除するのではなく、チケット側の表に置きます。
閾値の決め方は前述のとおりです。80%は与えられる値ではなく、自分のサービスで応答時間が崩れ始める水準を測って得る値です。負荷テスト(第12回)でその水準が出ているなら、それを使ってください。出ていないなら、閾値はまだ決められません。
そしてKubernetesでは、ディスクが2種類あります。症状の出方が正反対なので、1つのアラートにまとめてはいけません。
| ノードのディスク | PersistentVolume | |
|---|---|---|
| 設計した回 | 第11回 | 第7回 |
| 枯渇したとき | kubeletがPodを退避する(既定のハード閾値は nodefs.available<10%、imagefs.available<15%) | Podは動き続け、書き込みだけが失敗する |
| PDBと猶予 | 尊重されない | 関係しない |
| 主な消費者 | コンテナログ、コンテナイメージ | アプリケーションのデータ |
左の列は、この記事の前半とつながっています。ノードのディスクを埋める主要因の1つがコンテナログです。containerLogMaxSize と containerLogMaxFiles の積がノード総ストレージの1%以内という制約も、ここに効いています。ログ設計とノードの飽和は、同じ設計書の中でつながっています。
そして飽和の指標には、持ち方の推奨があります。Google SRE は「It looks like your database will fill its hard drive in 4 hours」という例を挙げています。残量ではなく、枯渇までの時間で持つ。チケットの優先順位が決められるようになります。
なお第11回で扱ったHPAの制御に使うメトリクスは、ここでのアラートとは別物です。HPAが見ているのはMetrics APIが返す window の範囲であり、その窓より短いスパイクはHPAからは見えません。アラートの窓とHPAの窓を混同しないでください。
通知は即座には来ない。検知時間の下限を計算に入れる
アラートの設計で最も見落とされるのが、この遅延です。Cloud Monitoring の公式ドキュメントは2つの数値を書いています。
- 「most metrics aren’t visible for 60 seconds after collection」
- 「Alerting policy computations take an additional delay of up to 5 minutes and 30 seconds」
合計すると、再テスト期間をゼロにしても、通知が出るまでに最大6分30秒かかります。再テスト期間を設定すれば、その分が加算されます。
この数値は3か所に効きます。
- 第16回で定義する一次応答の時間。「15分以内に応答」と決めるとき、その15分に検知の6分30秒が含まれるのかを先に決めておく必要があります
- 第12回のカオステストの観測窓。下限は5分+起動時間、上限はGKEの自動修復のおよそ10分でした。アラートの検知時間は、この窓より長くなりうるため、実験中は指標を直接見る必要があります
- 第14回の更新中の中止判断。通知が届く頃には、ノードの更新は次の台へ進んでいます
もう1つ、設定項目として欠測データの扱いがあります。選択肢は3つで、既定は「開いているインシデントは開いたままにし、新規のインシデントは開かない」です。
この既定が意味することを確認してください。Podが全滅してメトリクスが1件も届かなくなったとき、新しいアラートは開きません。最も深刻な障害で通知が来ない設定になっています。データが来ないこと自体を異常として扱うなら、明示的に変更する必要があります。
アラートの本数そのものが費用になる
Cloud Monitoring の料金ページには、アラートに関する告知が載っています。「Starting no sooner than September 1, 2026, Cloud Monitoring will begin charging for alerting.」(料金表の有効日欄は2026年8月1日、事前通知は90日前と30日前)。価格は次のとおりです。
- アラートポリシー内のメトリクス参照1つにつき、月$0.35
- 条件のクエリが返した100万ポイントにつき$0.50
定義も明確です。条件に書いたメトリクス名1つが1メトリクス参照で、フィルタ方式の条件は1ポリシーに6つまで組み合わせられます。PromQLやMQLを使う条件は、クエリの中で使ったぶんだけ参照が数えられます。請求関連・割り当て・稼働時間チェックのメトリクスを使うポリシーと、ログベースのアラートは課金対象外です。実行間隔は多くの条件種別で30秒固定(PromQLの条件のみ変更可)です。
この告知は、本記事の主張と同じ方向を向いています。通知を増やすほど月額が増える構造になります。「とりあえず作っておいたアラート」を棚卸しする理由が、人の時間だけでなく金額としても現れることになります。
設計時の上限も控えておいてください。アラートポリシーはメトリクススコープあたり2,000(最大10,000まで引き上げ可)、1ポリシーの通知チャネルは16です。
メトリクスとログ以外の入力を、受け取る先を決めておく
第14回で「自動更新が停止する理由は監視項目に入れてください」と書きました。その具体化がここです。
ただし、これらはメトリクスでもログでもありません。インサイトとイベントという、別の経路で届きます。
| 入力 | 内容 | 受け取り方 |
|---|---|---|
| 非推奨APIのインサイト | 観測期間30日、呼び出し元のユーザーエージェントまで分かる。検出時は自動更新が一時停止する | インサイトの定期確認 |
| アップグレード関連のイベント | UpgradeAvailableEvent、UpgradeInfoEvent、SecurityBulletinEvent、UpgradeEvent | Pub/Sub |
apiserver_requested_deprecated_apis | 削除予定バージョンのラベル(removed_release)を含む | コントロールプレーンメトリクスを有効化していれば通常のアラート |
3行目だけが、これまでの仕組みに乗ります。そしてそれは、有効化した瞬間からサンプル課金の対象になるパッケージです。第14回の互換性チェックリストと紐づけて、「更新の安全のために払う監視費用」として計上してください。
もう1つ、消える記録があります。Kubernetes の Event は、既定で1時間しか保持されません(kube-apiserver の --event-ttl の既定値は1時間)。OOMKilled、FailedScheduling、ImagePullBackOff といった「なぜそうなったか」は Event にしか出ないことが多く、翌朝には消えています。Event をログとして残すかどうかは、費用との交換になります。第18回で開発者に渡すトラブルシューティングの手引きは、この保持がなければ「昨日の朝の失敗の理由」を調べられません。
ダッシュボード:見る人と判断で3階層に割る
3つの階層と、それぞれの判断
ダッシュボードの設計は、画面の並べ方ではありません。誰が、いつ、何を判断するために見るかを先に書きます。
| 階層 | 見る人 | 判断すること | 載せる指標 |
|---|---|---|---|
| サービス | 全員 | いま異常か | SLIの現在値、エラーバジェットの残量と消費速度 |
| 依存 | 一次対応者 | 原因が自分側か相手側か | 依存先の応答時間とエラー率、外形監視の結果 |
| 掘り下げ | 担当者 | どこを直すか | ゴールデンシグナルの内訳、飽和の指標、第11回のPod単位の指標 |
この分け方が効くのは障害時です。アラートの1行目から、どのダッシュボードを開けばよいかが決まります。アラート台帳に「この通知で最初に見るダッシュボード」という列を持たせるのは、そのためです。
そして作ってはいけないものが1つあります。常時眺めるための画面です。Google SRE は「SRE teams carefully avoid any situation that requires someone to stare at a screen to watch for problems」と書いています。人が見張らないと検知できない状態は、監視設計の失敗です。
設計時の上限も押さえておきます。1つのダッシュボードに置けるチャートは100、1つのチャートが扱える時系列は50(性能上の制限)、ダッシュボードはメトリクススコープあたり1,000です。1枚に詰め込む設計は、この上限より先に人間の判断速度に当たります。

【実務テンプレート】ログ規約・アラート台帳・ダッシュボード定義
ログ/メトリクス標準規約
これは技術文書ではなく、開発チームとの合意文書です。記入する項目は次のとおりです。
- 出力先。標準出力と標準エラー。ファイル出力を認めない
- 形式。JSON。1行1エントリ
- 必須フィールド。
severity、message、トレース関連の3つ。入口のサービスはhttpRequestも - 1エントリの最大サイズ。256 KiB。超過分をアプリ側でどう切り詰めるか
- 禁止事項。重複キー、予約語(
stream)、機微データ - トレースの伝播。採用するヘッダ、変換が必要な境界、ログに出す際の16進数への変換
- メトリクスの宣言。
PodMonitoringを書ける範囲、intervalの下限、ラベルの上限(カーディナリティ)
そして各行に「誰が実装するか」の列を持たせてください(第2回のRACI)。上の7項目は、すべてアプリケーション側の実装です。運用チームは強制も代行もできません。第5回のポリシーエンジンで検査できるのはラベルの有無までで、「ログがJSONかどうか」はAdmissionの段階では検査できません。
したがって規約の実効性は、3つの経路に委ねられます。第10回の共通Helm Chartの既定値(ログ形式とレベルを環境変数で持たせ、既定を安全な値にする)、第9回のCI(サンプルログがJSONとして解釈できるかを検査する。第14回で非推奨APIの静的検査を1本足したのと同じ形)、そして第18回のオンボーディング(新規サービスの開始チェック項目にする)。規約を書いただけで守られることはありません。
アラート台帳(1行が1つの通知)
列は次のとおりです。行を1つ足すたびに、前述の5つの問いを通します。
- 何を検知するか/症状か原因か
- SLIとSLO(症状の行のみ)/窓とバーンレート、または閾値とその測定根拠(原因の行)
- ページかチケットか
- 欠測データの扱い
- 検知までの想定時間(最低6分30秒+再テスト期間)
- 通知先
- この通知で最初に見るダッシュボード
- 参照している指標(棚卸しで削除判断に使う)
この台帳に対応手順は書きません。誰がどう動くか、どうエスカレーションするかは第16回の管轄です。本記事の成果物は「何が鳴るか」で終わります。
ダッシュボード定義と、有効化したパッケージの記録
3階層それぞれについて、見る人・判断すること・載せる指標を書きます。そこに次の6項目を添えます。
- 有効化した観測パッケージと、その理由。有効化しなかったものとその理由も残す
- 保持期間と、その期間になった根拠。系統ごとに既定値が違うため、要件と並べて書く
- 月次の取り込み量の実績値。ログのGiB、メトリクスのサンプル数、トレースのスパン数。第4回のK8s税に、この変動費を毎月加算する
- 管理場所。アラートポリシーとログシンクは第8回のTerraform、
PodMonitoringは第9回のGitOps。手で作った設定は、後から消してよいか判断できなくなる - 棚卸しの手順。Google SRE の削除基準は2つ——「Signals that are collected, but not exposed in any prebaked dashboard nor used by any alert, are candidates for removal.」と、四半期に一度も使われない収集・集計・アラート設定は削除候補、というもの。台帳と定義が揃っていれば、削除候補は機械的に列挙でき、そのまま取り込み費用の削減になります
- 監視が止まったことに気づく仕組み。収集が止まると画面は異常なしに見えます。停止の経路は4つ(ロギングエージェントのスループット上限、256 KiB超のJSONログの破棄、欠測データの扱いが既定のまま、除外フィルタの誤設定)。対処は取り込み量の急減を検知することで、公式が費用監視用に挙げている「Monthly log bytes ingested」と「Monthly trace spans ingested」がそのまま使えます。第12回のシナリオマトリクスにも「監視の停止」を1行追加してください
この3つの成果物を、四半期に一度見直します。2回目以降に新しく書く欄は、増えた指標と、消した指標だけになります。
次回予告
本記事で決めたのは、何が鳴るかまでです。バーンレートが14.4を超えたときに人を呼ぶと決め、CPUの飽和はチケットに回すと決めました。
しかし、鳴った後に誰がどう動くかは、まだ設計していません。誰が最初に受けるのか、何分以内に応答するのか、どの時点で人を増やすのか、終わった後に何を記録するのか。
次回の第16回「インシデント対応・ポストモーテムプロセス設計書」では、オンコールローテーション、Severity定義と各レベルの対応、エスカレーションフロー、そしてポストモーテムの実施ルールを扱います。ページとチケットの振り分けは本記事で決めたので、第16回はSeverityの定義と体制から始められます。
