Skip to content

필사 모드: Ingress オブザーバビリティ — メトリクス、アクセスログ、トレーシング

日本語
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.
원문 렌더가 준비되기 전까지 텍스트 가이드로 표시합니다.

はじめに

Ingress コントローラはクラスタのエッジに位置し、すべての外部トラフィックが通過する単一のポイントです。ここで何が起きているか見えないと、ユーザーが体感する遅延や 5xx エラーの原因を、誤ってバックエンドアプリケーションのせいにしてしまいがちです。実際の障害対応の現場で最初に問われるのは、ほぼ必ず「これは Ingress で詰まっているのか、それともバックエンドが遅いのか」です。

この問いに数秒で答えるには、Ingress 層のオブザーバビリティが整っている必要があります。オブザーバビリティとは単に「Prometheus をつなげた」ことではなく、メトリクス・ログ・トレースの三本柱が一貫したラベルで結ばれ、ダッシュボードで異常を見つけたらそのまま該当リクエストのログとトレースに降りていける状態を指します。

本記事では ingress-nginx を基準に、ゴールデンシグナルの定義から、Prometheus メトリクス、Grafana ダッシュボードの主要 PromQL、構造化された JSON アクセスログと Loki への収集、OpenTelemetry 分散トレーシングの連携、アラートルール、per-ingress / per-path 分析、キャパシティプランニング、そして実践的なトラブルシューティングのワークフローまでを段階的に見ていきます。2026年現在、Ingress API 自体は frozen(機能追加なし)であり Gateway API が後継標準となっていますが、オブザーバビリティの原理は両者に等しく当てはまるため、最後に Gateway API での違いにも触れます。

ゴールデンシグナル: 何を測定するか

Google SRE 本が提示する4つのゴールデンシグナルは、Ingress 層にほぼそのままマッピングできます。

| ゴールデンシグナル | Ingress 層での意味 | 代表的な指標 |

| --- | --- | --- |

| Traffic(トラフィック) | 秒間の受信 HTTP リクエスト数 | requests per second (RPS) |

| Errors(エラー) | 5xx/4xx の比率 | 5xx ratio, 4xx ratio |

| Latency(レイテンシ) | リクエスト処理時間の分布 | p50/p90/p99 request duration |

| Saturation(飽和度) | コントローラのリソース/コネクション限界 | active connections, CPU, reload 頻度 |

ここに Ingress 特有のシグナルを2つ加えます。1つ目は **帯域(bandwidth)** — リクエスト/レスポンスのバイト数で、大容量ダウンロードや異常トラフィックを捉えます。2つ目は **upstream(バックエンド)レイテンシと総レイテンシの差** — 総処理時間からバックエンド応答時間を引くと、コントローラ自身が消費した時間が分かり、これが大きくなるとコントローラがボトルネックです。

重要なのは「エラー率が高い」ではなく、**「どの ingress の、どの path で、どのバックエンドへ向かうリクエストのエラー率が高いのか」** まで分解できることです。そのため、すべてのメトリクスに ingress/service/path ラベルが必要です。

ingress-nginx の Prometheus メトリクス

ingress-nginx コントローラは、デフォルトで metrics エンドポイントを公開するよう設計されています。Helm values でメトリクスと ServiceMonitor を有効化するのが出発点です。

controller:

metrics:

enabled: true

service:

annotations:

prometheus.io/scrape: "true"

prometheus.io/port: "10254"

serviceMonitor:

enabled: true

namespace: monitoring

additionalLabels:

release: kube-prometheus-stack

scrapeInterval: 30s

メトリクスポート(デフォルト 10254)で公開される主要な時系列は次のとおりです。

| メトリクス名 | タイプ | 意味 |

| --- | --- | --- |

| nginx_ingress_controller_requests | counter | 処理したリクエスト数(status, method, host, ingress, service, path ラベル) |

| nginx_ingress_controller_request_duration_seconds | histogram | 総リクエスト処理時間 |

| nginx_ingress_controller_response_duration_seconds | histogram | レスポンス時間 |

| nginx_ingress_controller_request_size | histogram | リクエストバイト |

| nginx_ingress_controller_response_size | histogram | レスポンスバイト |

| nginx_ingress_controller_nginx_process_connections | gauge | active/reading/writing/waiting コネクション |

| nginx_ingress_controller_config_last_reload_successful | gauge | 直近の reload 成否 |

| nginx_ingress_controller_config_last_reload_success_timestamp_seconds | gauge | 直近の成功した reload 時刻 |

| nginx_ingress_controller_ssl_expire_time_seconds | gauge | 証明書の有効期限 |

このうち requests カウンタと request_duration ヒストグラムが、ゴールデンシグナルの大半をカバーします。ヒストグラムはバケット境界が重要です。デフォルトのバケットがアプリケーションのレイテンシ分布と合わないと p99 が不正確になるため、レイテンシの短い API ではより細かいバケットが必要になることがあります。

Grafana ダッシュボードと主要 PromQL

ダッシュボードの最初の画面は、常に4つのゴールデンシグナルであるべきです。以下はそのままパネルに入れられる PromQL です。

リクエストレート(RPS)を ingress 単位に分解。

sum(rate(nginx_ingress_controller_requests[5m])) by (ingress)

5xx エラー率(全体に対する比率)。分母が0のときに備えるパターン。

sum(rate(nginx_ingress_controller_requests{status=~"5.."}[5m])) by (ingress)

/

sum(rate(nginx_ingress_controller_requests[5m])) by (ingress)

p99 レイテンシ(ヒストグラム分位数)。分位数計算のため le ラベルを保持する必要があります。

histogram_quantile(

0.99,

sum(rate(nginx_ingress_controller_request_duration_seconds_bucket[5m])) by (le, ingress)

)

p50/p90/p99 を1つのパネルに重ねて描くと、テールレイテンシの増加が一目で分かります。p50 は安定しているのに p99 だけが跳ね上がる場合は、一部の遅いバックエンドや GC、コネクションプールの枯渇を疑います。

upstream レイテンシに対するコントローラのオーバーヘッド。

histogram_quantile(0.99, sum(rate(nginx_ingress_controller_request_duration_seconds_bucket[5m])) by (le))

-

histogram_quantile(0.99, sum(rate(nginx_ingress_controller_response_duration_seconds_bucket[5m])) by (le))

アクティブコネクション(飽和度)。

sum(nginx_ingress_controller_nginx_process_connections) by (state)

reload 頻度 — 頻繁な reload はコネクション切断とレイテンシスパイクの原因です。

changes(nginx_ingress_controller_config_last_reload_success_timestamp_seconds[15m])

帯域(秒間レスポンスバイト)。

sum(rate(nginx_ingress_controller_response_size_sum[5m])) by (ingress)

ダッシュボード構成のコツ: 上段にクラスタ全体のゴールデンシグナル、中段に ingress 別テーブル(RPS・エラー率・p99 を1行に)、下段に reload/コネクション/証明書期限といったコントローラの健全性指標を配置します。ingress をテンプレート変数にしてドリルダウンすれば、per-ingress 分析が自然に行えます。

構造化アクセスログと Loki への収集

メトリクスは「何が問題か」を教えますが、「どのリクエストが問題か」はログが答えます。デフォルトの nginx ログフォーマットは空白区切りのテキストでパースが面倒です。JSON 構造化ログに変えると、Loki / Elasticsearch でフィールドベースのクエリが可能になります。

ingress-nginx のログフォーマットは ConfigMap で変更します。

apiVersion: v1

kind: ConfigMap

metadata:

name: ingress-nginx-controller

namespace: ingress-nginx

data:

log-format-escape-json: "true"

log-format-upstream: >-

{"time": "$time_iso8601",

"remote_addr": "$remote_addr",

"x_forwarded_for": "$proxy_add_x_forwarded_for",

"request_method": "$request_method",

"host": "$host",

"uri": "$uri",

"status": $status,

"request_time": $request_time,

"upstream_addr": "$upstream_addr",

"upstream_response_time": "$upstream_response_time",

"upstream_status": "$upstream_status",

"request_length": $request_length,

"bytes_sent": $bytes_sent,

"namespace": "$namespace",

"ingress_name": "$ingress_name",

"service_name": "$service_name",

"trace_id": "$opentelemetry_trace_id"}

ここでの要点は、request_time(総時間)と upstream_response_time(バックエンド時間)の両方を記録することです。2つの差がそのままコントローラのオーバーヘッドになります。また trace_id をログに埋め込んでおくと、ログからトレースへジャンプ(log-to-trace)できます。

Promtail / Grafana Alloy で Loki に送る際は、JSON をパースし、status・namespace・ingress_name をラベルに昇格させます。ただしラベルのカーディナリティ爆発を避けるため、trace_id や remote_addr のような高カーディナリティのフィールドは、ラベルではなくログ行の中にのみ置きます。

scrape_configs:

- job_name: ingress-nginx

static_configs:

- targets: [localhost]

labels:

job: ingress-nginx

pipeline_stages:

- json:

expressions:

status: status

namespace: namespace

ingress_name: ingress_name

request_time: request_time

- labels:

status:

namespace:

ingress_name:

LogQL で 5xx のみを抽出し、最も遅いリクエストを探すクエリは次のとおりです。

{job="ingress-nginx"} | json | status >= 500

| request_time > 1.0

| line_format "{{.host}}{{.uri}} {{.status}} {{.request_time}}s"

OpenTelemetry 分散トレーシングの連携

メトリクスとログで「遅いリクエスト」まで絞り込めたら、トレーシングは「そのリクエストがどのサービスのどの区間で時間を使ったか」を示します。ingress-nginx は OpenTelemetry モジュールを組み込みでサポートし、コントローラをトレースの最初のスパン(root span または edge span)にできます。

ConfigMap で OpenTelemetry を有効化します。

apiVersion: v1

kind: ConfigMap

metadata:

name: ingress-nginx-controller

namespace: ingress-nginx

data:

enable-opentelemetry: "true"

opentelemetry-trace-sampler-ratio: "0.1"

otlp-collector-host: "otel-collector.observability.svc"

otlp-collector-port: "4317"

otel-service-name: "ingress-nginx"

サンプリング比率(sampler-ratio)はコストに直結します。全リクエストのトレーシングは負荷が大きいため、通常は1〜10パーセントから始め、エラーや遅いリクエストは tail-based sampling で collector 側に100パーセント保持する戦略が一般的です。

核心は **コンテキスト伝播(context propagation)** です。コントローラが W3C traceparent ヘッダをバックエンドへ転送して初めて、バックエンドのスパンが同じトレースに紐づきます。ingress-nginx は OpenTelemetry が有効になると traceparent を自動で注入・伝播するため、バックエンドアプリケーションが同じ標準を使えば end-to-end トレースが完成します。

トレース・メトリクス・ログをつなぐラベル規約を統一することが、オブザーバビリティの最後のピースです。ログの trace_id、トレースの service.name、メトリクスの ingress ラベルが Grafana で相互にリンクするようデータソース間の correlation を設定すれば、ダッシュボードのエラー率スパイク → その時間帯のログ → 問題リクエストのトレース、へ3クリック以内で移動できます。

アラートルールの例

ダッシュボードは人が見る必要がありますが、アラートは人が見ていないときに起こしてくれます。Prometheus alerting rule の例です。

groups:

- name: ingress-nginx.rules

rules:

- alert: IngressHigh5xxRate

expr: |

sum(rate(nginx_ingress_controller_requests{status=~"5.."}[5m])) by (ingress)

/

sum(rate(nginx_ingress_controller_requests[5m])) by (ingress)

> 0.05

for: 5m

labels:

severity: critical

annotations:

summary: "Ingress の 5xx 比率が5パーセントを超過"

description: "ingress 単位の 5xx 比率が5分以上にわたり5パーセントを超えました。"

- alert: IngressHighLatencyP99

expr: |

histogram_quantile(0.99,

sum(rate(nginx_ingress_controller_request_duration_seconds_bucket[5m])) by (le, ingress)

) > 1

for: 10m

labels:

severity: warning

annotations:

summary: "Ingress の p99 レイテンシが1秒を超過"

- alert: IngressConfigReloadFailed

expr: nginx_ingress_controller_config_last_reload_successful == 0

for: 5m

labels:

severity: critical

annotations:

summary: "Ingress 設定の reload に失敗"

- alert: IngressCertExpiringSoon

expr: |

(nginx_ingress_controller_ssl_expire_time_seconds - time()) / 86400 < 14

for: 1h

labels:

severity: warning

annotations:

summary: "TLS 証明書が14日以内に期限切れ"

アラート設計の原則は、症状ベース(symptom-based)で発報することです。「CPU が高い」よりも「ユーザーが 5xx を受け取っている」のほうがページングに値するシグナルです。reload 失敗と証明書期限は、ユーザーがまだ影響を受けていなくても、まもなく受ける兆候なので別途捉えます。

per-ingress / per-path 分析

運用規模が大きくなると、「全体のエラー率」は意味が薄れます。1つのコントローラが数十の ingress を提供する場合、特定の ingress や path だけが壊れても全体平均に埋もれてしまうからです。

ingress-nginx は metrics-per-host と path ラベルをサポートします。path ラベルはカーディナリティが高くなりうるため、静的なパス中心にのみ有効化し、動的な ID を含むパスは正規化するのが安全です。

特定の path のエラーを分解する PromQL。

topk(10,

sum(rate(nginx_ingress_controller_requests{status=~"5.."}[5m])) by (ingress, path)

)

これにより「決済 ingress の /checkout path だけ 5xx が跳ねている」といった診断が即座にできます。さらに LogQL で同じ path をフィルタして実際のエラーメッセージを見れば、メトリクスの「何が」とログの「なぜ」がつながります。

キャパシティプランニング

観測データは事後対応だけでなく、事前計画にも使えます。コントローラのキャパシティプランニングの入力値は次のとおりです。

- ピーク RPS とその増加トレンド(週/月単位の回帰)

- リクエストあたりの CPU コスト — コントローラの CPU 使用率を RPS で割った値

- 同時アクティブコネクション数と worker connection の限界

- TLS handshake コスト(特に keep-alive が短いとき)

- reload 頻度と reload あたりの瞬間負荷

例えばピーク RPS が四半期ごとに30パーセントずつ増えており、現在のリクエストあたり CPU コストが一定なら、2四半期後に必要な replica 数を線形外挿で推定できます。ただし reload 負荷と TLS コストは非線形なので、負荷テスト(例: k6, vegeta)で実際の限界を定期的に確認すべきです。飽和度メトリクス(active connections, CPU)が70パーセントを超え始めたら、スケールアウトのトリガーとするのが保守的な基準です。

トラブルシューティングのワークフロー

オブザーバビリティが整うと、障害対応は次のような一貫した流れになります。

1. アラート受信 (例: IngressHigh5xxRate, ingress=payment)

2. ダッシュボード確認 — ゴールデンシグナルのどれが壊れたか?

├─ エラー率のみ ↑、レイテンシ正常 → バックエンド 5xx を疑う

├─ レイテンシ ↑、エラー率正常 → バックエンド遅延 or コントローラ飽和

└─ reload 回数が急増 → 頻繁なデプロイ/設定変更を疑う

3. per-ingress/path 分解 — どの path が原因か?

4. LogQL で該当 path の 5xx ログを抽出 — upstream_status, request_time を確認

├─ upstream_status 5xx → バックエンドアプリの問題

├─ upstream_addr が空 → エンドポイントなし(503)、service/selector を点検

└─ request_time が大きい → トレースへ移動

5. trace_id で分散トレースを照会 — どの区間で時間を消費?

6. 根本原因を確定 → 修正 → ダッシュボードで回復を確認

このワークフローの価値は、推測をデータに置き換えることにあります。「バックエンドのせいだろう」ではなく、upstream_response_time が request_time の95パーセントを占めるという事実でバックエンドを指し示すのです。

Gateway API 時代のオブザーバビリティ

2026年現在、Ingress API は frozen であり Gateway API が後継標準です。オブザーバビリティの観点での朗報は、原理がそのまま引き継がれることです。Gateway 実装(Envoy ベースの Contour、Istio、Cilium、NGINX Gateway Fabric など)も同じゴールデンシグナルを公開し、多くは Envoy の豊富な統計とネイティブの OpenTelemetry サポートを活用します。

違いはラベルの次元がより豊かになる点です。Gateway API は GatewayClass → Gateway → HTTPRoute の3層なので、メトリクスに gateway、route、backend といったラベルが自然に付き、per-route 分析が ingress-nginx より精緻になります。また Envoy ベースのデータプレーンは、サーキットブレーキングやアウトライア検出といった追加シグナルを提供します。いま ingress-nginx でゴールデンシグナル・構造化ログ・トレーシングをきちんと整えておけば、Gateway API へ移行する際にダッシュボードとアラートルールの概念をほぼそのまま再利用できます。

おわりに

Ingress オブザーバビリティの核心はツールではなくつながりです。メトリクスで異常を検知し、per-ingress/path で範囲を絞り、構造化ログで原因を見て、トレースで区間を特定する流れが途切れなくつながって初めて、「Ingress かバックエンドか」という問いに数秒で答えられます。

始めは小さくて構いません。まずメトリクスとゴールデンシグナルのダッシュボードを立て、次に JSON アクセスログを Loki へ送り、最後に trace_id をログに埋め込んでトレーシングをつなぎます。三本柱が同じラベルで結ばれた瞬間、Ingress はもはやブラックボックスではなく、最も信頼できる最初の診断ポイントになります。

参考資料

- Kubernetes Ingress コンセプト: https://kubernetes.io/docs/concepts/services-networking/ingress/

- ingress-nginx メトリクス/モニタリング: https://kubernetes.github.io/ingress-nginx/user-guide/monitoring/

- ingress-nginx ログフォーマット設定: https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/log-format/

- ingress-nginx ConfigMap オプション: https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/

- Prometheus クエリ(PromQL): https://prometheus.io/docs/prometheus/latest/querying/basics/

- Grafana Loki LogQL: https://grafana.com/docs/loki/latest/query/

- OpenTelemetry ドキュメント: https://opentelemetry.io/docs/

- Gateway API: https://gateway-api.sigs.k8s.io/

- Contour(Envoy ベースの Ingress/Gateway): https://projectcontour.io/docs/

- cert-manager(TLS 証明書の自動化): https://cert-manager.io/docs/

현재 단락 (1/222)

Ingress コントローラはクラスタのエッジに位置し、すべての外部トラフィックが通過する単一のポイントです。ここで何が起きているか見えないと、ユーザーが体感する遅延や 5xx エラーの原因を、誤って...

작성 글자: 0원문 글자: 10,912작성 단락: 0/222