GKE ネットワークのオブザーバビリティのトラブルシューティング

このドキュメントでは、GKE Dataplane V2 のオブザーバビリティ、Hubble、Cloud Monitoring を使用して Google Kubernetes Engine(GKE)クラスタのネットワーク問題を診断するための手順と診断手順について説明します。

アーキテクチャの概要と概念的なベスト プラクティスについては、ネットワーク オブザーバビリティのベスト プラクティスをご覧ください。

トラブルシューティングのディシジョン ツリーと基本コンセプト

手順を確認する前に、次の決定マトリックスを使用して、GKE ネットワーキング スタックのどの階層で問題が発生している可能性が高いかを特定し、対応するセクションに移動します。

症状または診断用の問題 推定される階層 推奨される手順
Pod が外部ドメインまたは内部 Kubernetes Service を解決できない(DNS タイムアウト、NXDOMAIN、SERVFAIL)。 Tier 1: Pod と Service(DNS) DNS の解決の失敗を診断する
サービスが通信できない。Pod 間の接続タイムアウトまたはパケットのドロップ。 Tier 1: Pod と Service(ポリシーまたはパケットのドロップ) パケット ドロップと NetworkPolicy ブロックを診断する
ワークロード レプリカのトラフィック負荷が不均一であるか、Pod が高い割合の TCP リセット(RST)をログに記録している。 Tier 1: Pod と Service(ロード バランシングまたは転送) トラフィックの不均衡と TCP リセットを診断する
一般的なレイテンシ、断続的なタイムアウト、ノード間の CNI 再起動。 Tier 2: ノードと CNI(カーネル) ノードレベルのレイテンシと CNI のボトルネックのトリアージ
Pod は GKE 外のリソース(Cloud SQL、外部 API、他の VPC など)にアクセスできません。 Tier 3: VPC とルーティング GKE と VPC または外部接続の問題を切り分ける
ファイアウォール ルール、ルート、NetworkPolicy がトラフィックをブロックしているかどうかを確認するために、自動パス シミュレーションが必要です。 Tier 3: VPC とルーティング(シミュレーション) 接続テストを使用して接続を診断する
インターネットにトラフィックを送信して NAT を実行するワークロードを特定する必要があります。 Tier 4: 外部ゲートウェイと費用 NAT トラフィック(インターネットへの下り(外向き))を特定する
ゾーン間のデータ転送コストが高い場合や、SQL クエリを記述せずに上位の通信者を可視化する必要がある場合。 Tier 4: 外部ゲートウェイと費用 Flow Analyzer を使用してクラスタのトラフィック費用とパフォーマンスを分析する

ネットワーキングの基本コンセプト

Kubernetes または Google Cloud ネットワーキングを初めて使用する場合は、次の基本コンセプトに留意してください。

  • eBPF(Extended Berkeley Packet Filter): 安全なモニタリング プログラムとルーティング プログラムを Linux カーネル内で直接実行できるオペレーティング システム技術。GKE Dataplane V2 は、eBPF を使用してパケットをルーティングし、パフォーマンスのオーバーヘッドを最小限に抑えて NetworkPolicy を適用します。
  • IP マスカレード(SNAT): パケットの送信元 IP アドレスを書き換えるプロセス。(プライベート IP アドレスを持つ)GKE Pod がインターネットまたは外部 VPC リソースと通信する場合、GKE は Pod IP アドレスをノード IP アドレスにマスカレード(書き換え)して、外部システムが応答をルーティングする方法を認識できるようにします。
  • 接続トラッキング(Conntrack): すべてのアクティブなネットワーク接続をトラッキングするカーネル機能。GKE Dataplane V2 クラスタでは、このトラッキングは 2 つのテーブルに分割されます。標準の Linux カーネル conntrack(ip-masq-agent で使用)と、eBPF マップに保存される Cilium と GKE Dataplane V2 で管理される conntrack テーブルです。ノードが同時に処理する接続数が多すぎると、これらのトラッキング テーブルのいずれかがいっぱいになり(conntrack の枯渇)、ノードが新しいパケットを通知なしにドロップする可能性があります。
  • Hubble: GKE Dataplane V2 のオブザーバビリティ エンジン。eBPF 上で実行され、トラフィック フロー、パケット ドロップ、NetworkPolicy 評価をリアルタイムで可視化します。

Tier 1: Pod と Service(アプリケーション)のオブザーバビリティ

Pod と Service の階層は、Pod、Service、クラスタ DNS 間のネットワーク通信を対象としています。この階層の問題は通常、アプリケーション接続のタイムアウト、名前解決の失敗、負荷分散の不均一として現れます。

DNS の解決の失敗を診断する

DNS の問題のトラブルシューティングを行う前に、診断の範囲と前提条件を確認します。

  • フォーカス領域: Pod と CoreDNS または NodeLocal DNSCache 間の通信、DNS レイテンシ、アップストリーム DNS の解決のタイムアウト、および FQDN NetworkPolicy の検証。
  • 前提条件: GKE Dataplane V2 指標が有効になっていること。kubectl へのアクセス権があること。
  • CNI の互換性: GKE Dataplane V2(高度なデータパス)と標準の GKE CNI。
  • 現象: Pod が、アウトバウンド API 呼び出しで dial tcp: lookup <domain>: i/o timeout、NXDOMAIN、または断続的なレイテンシをログに記録します。
  • 目標: DNS エラーがクラスタ内(kube-dns または NodeLocal DNSCache の飽和状態)、NetworkPolicy による UDP ポート 53 または TCP ポート 53 のブロック、アップストリーム ネットワークのパフォーマンス低下のいずれに起因するかを特定します。

ステップ 1: 基本的な到達可能性の確認

DNS レイヤのトラブルシューティングを行う前に、影響を受ける Pod の内部から IP アドレスを使用して宛先に直接到達できるかどうかを確認します。

# 1. Test raw IP reachability (bypasses DNS entirely)
kubectl exec -it my-pod -n default -- curl -v --connect-timeout 5 http://10.240.0.10:8080

# 2. Test domain resolution
kubectl exec -it my-pod -n default -- curl -v --connect-timeout 5 http://my-service.default.svc.cluster.local:8080
  • IP 接続は成功したがドメインが失敗した場合: 問題は DNS の解決レイヤに限定されます。ステップ 2 に進みます。
  • 両方とも失敗した場合: ネットワーク レベルのルーティングまたはポリシーの適用に問題があります。パケットのドロップと NetworkPolicy のブロックを診断するに進みます。
FQDN NetworkPolicy DNS の事前入力の有無を確認する

クラスタが FQDN ベースの NetworkPolicy(FQDNNetworkPolicy)を使用している場合は、ドメイン名が明示的に許可されていることを確認します。Pod が GKE Dataplane V2 DNS プロキシ キャッシュに事前入力されていないか、ポリシーで許可されていない外部ドメインをクエリすると、GKE Dataplane V2 は解決された IP アドレスへの下り(外向き)トラフィックをブロックします。

# Verify whether UDP or TCP port 53 egress is permitted in the Pod's namespace
kubectl get networkpolicy -n default -o yaml | grep -A 5 -B 2 "port: 53"

ステップ 2: Cloud Monitoring で DNS 指標を確認する

GKE は、kubernetes.io/networking/dns/ 接頭辞で Cloud Monitoring の組み込み DNS 指標を公開します。

Cloud Monitoring で DNS 指標を確認する手順は次のとおりです。

  1. Google Cloud コンソールで、[Cloud Monitoring] > [ダッシュボード] に移動します。
  2. 事前定義された [GKE DNS Observability - Cluster View] ダッシュボードを選択します(または、Metrics Explorer に移動して kubernetes.io/networking/dns/ でフィルタします)。
  3. 次の主なシグナルを評価します(NodeLocal DNSCache の場合、指標パスの kubedns を node_local_dns に置き換えます)。
指標名 警告しきい値 根本的な原因
kubernetes.io/networking/dns/kubedns/max_concurrent_rejected_request_count > 0 同時クエリの上限に達しました。kube-dns または NodeLocal DNSCache がクエリをドロップしている。
kubernetes.io/networking/dns/kubedns/dns_request_latencies p99 > 100 ミリ秒 kube-dns または NodeLocal DNSCache 全体で DNS の解決のレイテンシが高い。
kubernetes.io/networking/dns/kubedns/forwarding_request_latencies p99 > 100 ミリ秒 アップストリーム DNS サーバーのレイテンシまたは飽和。
DNS のパフォーマンスとタイムアウトのトリアージ シーケンス

次のトリアージ シーケンスに沿って、DNS レイテンシ、キャッシュミス、アップストリーム タイムアウトを診断します。

  1. キャッシュ ヒット率を確認する: cache_status ラベルでグループ化されたクエリ kubernetes.io/networking/dns/kubedns/dns_cache_request_count(または node_local_dns/dns_cache_request_count)。cache_status="hit" が低く、cache_status="miss" が高い場合、アプリケーションが非 FQDN クエリ(my-service.default.svc.cluster.local ではなく my-service など)を発行している可能性があり、/etc/resolv.conf 内のすべてのエントリで検索パスのパス トラバーサルが発生します。
  2. アップストリーム レイテンシを評価する: forwarding_request_latencies が高い場合は、アップストリーム DNS サーバーに問題があることを示します(Cloud Interconnect または Cloud VPN 経由でアクセスされる企業オンプレミス DNS、Cloud DNS の上限など)。
  3. カスタム DNS オーバーライドを監査する: カスタム kube-dns ConfigMap を検査して、構成が誤っているスタブまたはアップストリーム転送がないか確認します。

    kubectl get configmap kube-dns -n kube-system -o yaml
    

ステップ 3: Hubble CLI でライブ DNS トラフィックをストリーミングする

Hubble CLI ヘルパー エイリアスを使用して、ノード カーネルからストリーミングされたライブ DNS リクエストとレスポンスを検査します。

# 1. Ensure the helper alias is set in your terminal session
alias gke-hubble="kubectl exec -it -n gke-managed-dpv2-observability deployment/hubble-relay -c hubble-cli -- hubble"

# 2. Observe live DNS (Port 53) traffic for a specific Pod
gke-hubble observe --pod default/my-pod --port 53

ステップ 4: 修正を検証する

同時クエリの拒否が発生した場合は、NodeLocal DNSCache を適用して、クラスタ全体の kube-dns 制限に達することなく、ノードで高頻度の DNS ルックアップを直接吸収します。

# Verify NodeLocal DNSCache DaemonSet is running
kubectl get daemonset node-local-dns -n kube-system

パケットのドロップと NetworkPolicy のブロックを診断する

パケットのドロップとポリシーの拒否を調査する前に、診断の範囲と前提条件を確認します。

  • 焦点領域: Pod オブジェクト間または Pod オブジェクトと Service オブジェクト間のトラフィック ドロップ、NetworkPolicy の適用、カーネル eBPF ドロップの理由。
  • 前提条件: GKE Dataplane V2 フロー オブザーバビリティが有効になっている。NetworkPolicy ロギングが構成されている。
  • CNI の互換性: GKE Dataplane V2 のみ。
  • 症状: アプリケーションの接続試行が Connection timed out または Connection reset by peer で失敗します。
  • 目標: セキュリティ ポリシーを試行錯誤で変更することなく、パケットをドロップする正確な NetworkPolicy または eBPF の理由を特定します。

ステップ 1: Hubble ドロップ指標をモニタリングする

GKE Dataplane V2 がパケットをドロップすると、ドロップ理由と送信元 / 宛先のメタデータでタグ付けされた指標 hubble_drop_total が出力されます。Hubble のドロップ指標をモニタリングするには、次の操作を行います。

  1. まだ構成されていない場合は、Hubble 指標をスクレイピングする Google Cloud Managed Service for Prometheus PodMonitoring リソースをデプロイします。

    apiVersion: monitoring.googleapis.com/v1
    kind: PodMonitoring
    metadata:
      name: hubble-metrics
      namespace: gke-managed-dpv2-observability
    spec:
      selector:
        matchLabels:
          k8s-app: cilium
      endpoints:
      - port: hubble-metrics
        interval: 30s
    
  2. [Cloud Monitoring] > [Metrics Explorer] で次のクエリを実行して、理由別のドロップを表示します。

    sum by (reason) (rate(hubble_drop_total[5m])) > 0
    
  3. 一般的な reason コードの意味は次のとおりです。

    • Policy denied: Kubernetes NetworkPolicy が明示的または暗黙的に接続をブロックしている。
    • CT: Map insertion failed: 接続トラッキング(conntrack)テーブルが使い果たされています。
    • Unsupported L3 protocol: IPv4 パケットまたは IPv6 パケット以外のパケット、または破損したヘッダー。

ステップ 2: Cloud Logging で NetworkPolicy ログを確認する

NetworkPolicy ロギングは、すべてのポリシー決定について構造化された JSON ログをエクスポートします。Cloud Logging で NetworkPolicy ログをクエリするには、次の操作を行います。

  1. Google Cloud コンソールで、[Cloud Logging] > [ログ エクスプローラ] に移動します。
  2. 次のクエリを実行します。

    resource.type="k8s_node"
    log_name:"projects/PROJECT_ID/logs/events"
    jsonPayload.connection.verdict="DENY"
    jsonPayload.src.pod_name="my-source-pod"
    
  3. JSON ペイロードを調べます。

    • jsonPayload.drop_reason: パケットがドロップされた理由が表示されます。
    • jsonPayload.policies: 評価された NetworkPolicy を一覧表示します。DENY 判定で空のリストが返された場合、Namespace はデフォルト拒否モードで動作しており、トラフィックを許可するポリシーはありません。

NetworkPolicy ログが表示されない場合は、クラスタの NetworkLogging カスタム リソースでロギングが有効になっていることを確認します。たとえば、spec.cluster.deny.log フィールドが true に設定されていることを確認します。

kubectl get networklogging default -o yaml

ステップ 3: Hubble CLI でライブドロップをトレースする

Hubble CLI を使用してリアルタイム パケットを検査し、ライブ ドロップを直接ストリーミングします。

gke-hubble observe --verdict DROPPED --namespace default --follow

出力例:

TIMESTAMP             SOURCE               DESTINATION          TYPE     VERDICT
10:14:22.102          default/frontend     default/backend:80   to-stack DROPPED (Policy denied by NetworkPolicy: backend-deny-all)

出力には、トラフィックをブロックしている正確な NetworkPolicy(backend-deny-all)が示されます。

ステップ 4: conntrack の枯渇を確認する

ドロップの理由が CT: Map insertion failed の場合:

  1. GKE Dataplane V2 エージェントのログで、conntrack テーブルの飽和状態を確認します。

    kubectl logs -n kube-system daemonset/anetd -c cilium-agent --tail=100 | grep "Conntrack table full"
    
  2. 影響を受けるノードの conntrack テーブルの最大サイズを確認します。

    kubectl exec -it -n kube-system daemonset/anetd -c cilium-agent -- cilium status --all-controllers | grep -i conntrack
    

    conntrack テーブルがいっぱいになった場合は、ワークロードをより多くのノードにスケールアウトするか、クライアント Pod からの接続率を減らします。


トラフィックの不均衡と TCP リセットを診断する

トラフィックの不均衡と接続のリセットのトラブルシューティングを行う前に、診断の範囲と前提条件を確認します。

  • 重点分野: ロード バランシングの不均一性、TCP ハンドシェイクの失敗、突然の接続終了。
  • 前提条件: GKE Dataplane V2 指標が有効になっていること。
  • CNI の互換性: GKE Dataplane V2。
  • 症状: 特定の Pod レプリカに過剰なトラフィックが流れ込み、他のレプリカはアイドル状態のままになる。クライアント アプリケーションが connection reset by peer または broken pipe をログに記録する。
  • 目標: トラフィックの不均衡がトランスポート レイヤ(OSI レイヤ 4、TCP)とアプリケーション レイヤ(OSI レイヤ 7、HTTP/2 または gRPC)の接続スティッキー セッションによって発生しているかどうかを判断し、TCP RST パケットの送信元を特定します。

ステップ 1: Pod レベルのトラフィック フローを比較する

トラフィックがレプリカ間で均等に分散されているかどうかを確認するには、次の操作を行います。

  1. Cloud Monitoring で、Deployment 内のすべての Pod の上り(内向き)フロー数をクエリします。

    sum by (pod) (rate(pod_flow_ingress_flows_count{destination_workload="my-service"}[5m]))
    
  2. Pod 間のトラフィック分配を評価します。単一の Pod がトラフィックの大部分を受信している場合は、接続の再利用またはスティッキー セッションを調査します。

    • gRPC または HTTP/2: 存続期間の長い TCP 接続により、すべてのリクエストが単一の TCP ストリームを介して 1 つのバックエンド Pod に転送されます。トランスポート レイヤ(OSI レイヤ 4、TCP)の Kubernetes Service ルーティングでは、確立された HTTP/2 接続内のリクエストを分散できません。
    • ClientIP セッション アフィニティ: Service が sessionAffinity: ClientIP で構成されているかどうかを確認します。
    • ヘッドレス Service: クライアントは DNS を 1 回解決し、単一の IP アドレスを永続的にキャッシュに保存する可能性があります。

ステップ 2: TCP リセット指標を分析する

TCP リセット(RST)は、接続を直ちに終了します。これらは、エンドポイントが不明なポートのパケットを受信したとき、またはアプリケーションがバッファ内の未読データを含む接続を閉じたときに、オペレーティング システム カーネルによって出力されます。

Cloud Monitoring で次の MQL クエリを実行します。

fetch prometheus_target
| metric 'prometheus.googleapis.com/hubble_tcp_flags_total/counter'
| filter (metric.flag == 'RST')
| align rate(1m)
| every 1m
| group_by [metric.source, metric.destination, metric.traffic_direction], sum(val())

クエリの結果を確認して、リセットの原因を特定します。

  • 送信 RST(traffic_direction=egress): ローカル Pod がリセットを生成しています。Pod アプリケーションがクラッシュしているか、接続上限に達しているか、接続を積極的に拒否しているかを確認します。
  • 受信 RST(traffic_direction=ingress): リモート ピア(外部データベース、API、リモート Pod)がリセットを送信しました。移行先サーバーのヘルス状態とファイアウォール状態を確認します。

ステップ 3: TCP リセットのライブ ストリーム

Hubble CLI を使用して、ライブ リセット ハンドシェイクをキャプチャします。

gke-hubble observe --type trace --verdict FORWARDED --tcp-flags RST --namespace default

ステップ 4: 修正と実行可能な修正

トラフィックの不均衡または TCP リセットの原因に応じて、次の修復手順を適用します。

  • アプリケーション レイヤ(OSI レイヤ 7)または gRPC 接続のスティッキー性の場合:
    • Cloud Service Mesh をデプロイして、アプリケーション レイヤ(OSI レイヤ 7)のリクエスト レベルのロード バランシングを有効にします。
    • クライアントサイドの接続制限または keep-alive タイムアウト(gRPC MAX_CONNECTION_AGE や MAX_CONNECTION_AGE_GRACE など)を構成して、定期的な接続の再確立を強制します。
  • Service IP アフィニティの場合: アプリケーションの状態で厳密に必要とされない限り、service.spec.sessionAffinity を削除します。
  • ヘッドレス Service の DNS キャッシュ保存の場合: アプリケーション ランタイム(JVM networkaddress.cache.ttl など)が DNS の結果を無期限にキャッシュに保存しないようにします。
  • アプリケーション バックログ キューのオーバーフローの場合: アプリケーション リッスンキューが満杯になると、Linux カーネルは受信した SYN パケットをドロップするか、TCP RST を送信します。Pod レプリカをスケールアウトするか、アプリケーションのリスン バックログ(somaxconn)を増やします。

Tier 2: ノードと CNI(カーネル)のオブザーバビリティ

ノードと CNI の階層には、ホスト Linux カーネル、eBPF プログラム、ノード ネットワーク インターフェースが含まれます。この階層のボトルネックは、影響を受けるノードで実行されているすべてのワークロードに影響します。

システムの完全性チェック: anetd の不正なパッチ適用を検出

GKE Dataplane V2 は、kube-system Namespace でマネージド DaemonSet(anetd)として実行されます。Cloud Logging では、Kubernetes 監査ログをクエリして、不正なユーザーまたは自動化スクリプトが anetd をパッチ適用または再起動したかどうかを検出できます。

protoPayload.methodName="io.k8s.core.v1.daemonsets.patch" OR
protoPayload.methodName="io.k8s.core.v1.daemonsets.update"
protoPayload.resourceName="namespaces/kube-system/daemonsets/anetd"

承認されていないパッチ適用が検出された場合は、DaemonSet をデフォルト構成に戻すか、ノードプールの再作成をトリガーしてマネージド状態を復元します。


ノードレベルのレイテンシと CNI ボトルネックのトリアージ

ノードレベルのレイテンシとカーネルのボトルネックを診断する前に、診断の範囲と前提条件を確認します。

  • 重点分野: ホストカーネルのレイテンシ、VM インターフェースでのパケット ドロップ、GKE Dataplane V2 eBPF エージェントの飽和、conntrack の枯渇。
  • 前提条件: Compute Engine VM 指標が有効になっていること。kubectl アクセス権があること。
  • CNI の互換性: GKE Dataplane V2 と標準の GKE CNI。
  • 症状: ノード間トラフィックでレイテンシの急増やランダムなドロップが発生するが、ノード内トラフィックは正常な状態が維持される。
  • 目標: ホスト VM ネットワーク スロットリング、Linux カーネル ドロップ、CNI レベルのボトルネックを区別します。

ステップ 1: GKE の外部の問題と GKE の内部の問題を区別する

Compute Engine VM ベースライン テストを実行する: GKE クラスタノードと同じ VPC サブネットにスタンドアロンの Compute Engine VM をデプロイします。VM からターゲット宛先への接続をテストします。

  • スタンドアロン VM で同じパケット損失またはレイテンシが発生する場合: 問題は GKE の外部(VPC ファイアウォール、Cloud NAT、Cloud Interconnect、外部サーバー)にあります。GKE と VPC または外部接続の問題を切り分けるに進みます。
  • スタンドアロン VM は正常に通信するが、GKE Pod が失敗する場合: 問題は GKE 内(ノードレベルの eBPF、conntrack、CNI)にあります。ステップ 2 に進みます。

ステップ 2: アプリケーションの問題とノードおよび CNI 階層の問題を区別する

レイテンシがアプリケーションで発生しているか、ノードと CNI 階層で発生しているかを判断するには、次の操作を行います。

  1. アプリケーション リソースの飽和状態を確認する: ユーザー空間でのパケット処理を遅延させる CPU スロットリングやメモリプレッシャーがノードで発生していないことを確認します。

    kubectl top nodes
    kubectl top pods -n default
    
  2. Linux カーネルの conntrack 数を検査する: ホストでアクティブな conntrack 数を確認します。

    # On a node where you have debugging access
    cat /proc/sys/net/netfilter/nf_conntrack_count
    cat /proc/sys/net/netfilter/nf_conntrack_max
    

    nf_conntrack_count が nf_conntrack_max に近づくと、ホストカーネルは新しい TCP SYN パケットをドロップします。

ステップ 3: Compute Engine テレメトリーを使用して VM レベルのパケット ドロップを確認する

Google Cloud Compute Engine は、VM レベルのネットワーク インターフェース指標を Cloud Monitoring にエクスポートします。

[Cloud Monitoring] > [Metrics Explorer] で、次のクエリを実行します。

sum by (drop_reason) (rate(compute_googleapis_com:instance_network_dropped_packets_count[5m]))
  • FQ_CODEL_DROP: フェア キューイングまたは CoDel キューの飽和(VM 下り(外向き)帯域幅の上限超過)によりドロップされたパケット。
  • FIREWALL_RULE_DROP: VPC ファイアウォール ルールによってドロップされたパケット。
  • RATE_LIMIT_DROP: VM がネットワーク インターフェースの最大パケット数 / 秒(PPS)割り当てを超えたため、パケットがドロップされました。

ステップ 4: eBPF を使用してカーネルレベルのドロップを調査する

VM レベルの指標でドロップがゼロと表示されているにもかかわらず、GKE Pod がパケットをドロップしている場合は、eBPF マップのドロップについて GKE Dataplane V2 エージェント(anetd)を調べます。

kubectl logs -n kube-system daemonset/anetd -c cilium-agent --tail=200 | grep -i "drop"

ct-map-insertion-failed または fib-lookup-failed を探します。


階層 3: VPC とルーティング(クラウド ネットワーク)のオブザーバビリティ

VPC とクラウド ルーティング階層は、GKE ノードを他の Google Cloud サービス、オンプレミス ネットワーク、インターネットに接続します。通常、このエラーは VPC ファイアウォール ルール、カスタムルート、またはゲートウェイ構成が原因で発生します。

GKE と VPC または外部接続の問題を切り分ける

クラスタ内の障害から外部ネットワークの問題を切り離す前に、診断の範囲と前提条件を確認します。

  • 重点分野: Kubernetes 内部ルーティングと VPC クラウドルーティング間の境界分離。
  • 前提条件: gcloud CLI、接続テストを作成する権限。
  • CNI の互換性: すべてのクラスタ。
  • 現象: Pod が外部リソース(Cloud SQL、オンプレミス API、サードパーティ エンドポイントなど)に接続できない。
  • 目標: GKE ノード内、 Google Cloud VPC 内、外部ネットワークのいずれでドロップが発生したかを迅速に特定します。

ステップ 1: Compute Engine VM のベースライン テスト

ベースライン VM をデプロイし、GKE の外部で問題が解決するかどうかを評価するには、次の操作を行います。

  1. GKE ノードプールと同じ VPC サブネットとゾーンに一時的な Compute Engine VM インスタンスをデプロイします。

    gcloud compute instances create gke-baseline-tester \
        --zone=us-central1-a \
        --subnet=gke-subnet \
        --machine-type=e2-micro
    
  2. SSH を使用してインスタンスに接続し、宛先への接続をテストします。

    curl -v --connect-timeout 5 https://api.example.com
    
  3. 結果を評価する:

    • Compute Engine VM が接続できない場合: 問題は VPC または外部ネットワーク(ファイアウォール ルール、ルーティング テーブル、Cloud NAT IP の枯渇、外部 IP のホワイトリスト登録)にあります。
    • Compute Engine VM が正常に接続された場合: 問題は GKE 内にあります(NetworkPolicy による下り(外向き)のブロック、マスカレードされていない Pod CIDR、コンテナレベルの DNS)。

ステップ 2: オンデマンドの接続テストを実行する

ノード VM から宛先への Google Cloud 接続テストを実行します。

gcloud network-management connectivity-tests create test-node-to-dest \
    --source-instance=projects/PROJECT_ID/zones/us-central1-a/instances/gke-baseline-tester \
    --destination-ip-address=203.0.113.10 \
    --destination-port=443 \
    --protocol=TCP

Google Cloud コンソールで結果を確認して、VPC ファイアウォール ルールまたはルートがトラフィックをドロップしているかどうかを特定します。


接続テストを使用して接続を診断する

接続テストでは、ライブ トラフィックを送信せずに、GKE リソースと VPC リソース間のパケットパスをシミュレートします。このシミュレーションでは、Service ClusterIP から Pod への DNAT 解決、GKE Dataplane V2 NetworkPolicy の上り(内向き)ルールと下り(外向き)ルール、ノード IP マスカレード(SNAT)を評価します。自動パス シミュレーションを実行する前に、診断スコープと前提条件を確認します。

  • 焦点領域: GKE Pod、Service、NetworkPolicy、VPC ルートの自動静的パス シミュレーション。
  • 前提条件: Network Intelligence Center が有効になっていること、Network Management API が有効になっていること。
  • CNI の互換性: GKE Dataplane V2(拡張分析)。
  • 現象: 手動検査ではすべての構成が有効に見えるにもかかわらず、接続が原因不明で切断される。
  • 目標: Pod の送信元から宛先までのパケットパス全体を静的にシミュレートしてトレースし、ドロップの原因となったポリシーの正確な行を特定します。

シナリオ A: GKE NetworkPolicy が指標の収集をブロックしているかどうかを確認する

安全な Namespace の Pod から指標をスクレイピングするときに、Google Cloud Managed Service for Prometheus コレクタがデフォルト拒否の NetworkPolicy によってブロックされることがあります。

gcloud network-management connectivity-tests create test-gmp-to-pod \
    --source-ip-address=10.0.0.15 \
    --destination-gke-pod=projects/PROJECT_ID/locations/LOCATION/clusters/CLUSTER_NAME/k8s/namespaces/prod/pods/backend-pod \
    --destination-port=8080 \
    --protocol=TCP

Google Cloud コンソールでテスト トレースを調べます。テストがステップ GKE Network Policy evaluation で DROP で終了する場合は、コレクタ Pod からのトラフィックを許可する上り(内向き)ルールを追加する必要があります。

シナリオ B: GKE Service への到達可能性を確認する

クライアント VM から内部 Kubernetes Service への到達可能性をシミュレートします。

gcloud network-management connectivity-tests create test-vm-to-service \
    --source-instance=projects/PROJECT_ID/zones/us-central1-a/instances/client-vm \
    --destination-ip-address=10.96.0.100 \
    --destination-port=80 \
    --protocol=TCP

シミュレートされたトレースには次の情報が表示されます。

  1. VPC ルートが一致しました。
  2. VPC ファイアウォールの下り(外向き)と上り(内向き)を許可します。
  3. GKE ノードに到着。
  4. バックエンド Pod IP アドレスへの Service DNAT。
  5. バックエンド Pod での Ingress NetworkPolicy の評価。

シナリオ C: Pod からインターネットへの下り(外向き)の問題を診断する

Pod がインターネット上の外部 API にアクセスできない場合:

  1. Pod から外部パブリック IP アドレス(8.8.8.8 など)へのテストを作成します。

    gcloud network-management connectivity-tests create test-pod-to-internet \
        --source-gke-pod=projects/PROJECT_ID/locations/LOCATION/clusters/CLUSTER_NAME/k8s/namespaces/prod/pods/app-pod \
        --destination-ip-address=8.8.8.8 \
        --destination-port=53 \
        --protocol=UDP
    
  2. ドロップ ポイントを確認します。

    • Dropped at NetworkPolicy: Pod に、0.0.0.0/0 へのトラフィックを許可する下り(外向き)NetworkPolicy がありません。
    • VPC ファイアウォールでドロップ: VPC ファイアウォール ルールがノード サブネットからの下り(外向き)トラフィックを拒否しています。
    • Cloud NAT またはルートでドロップ: サブネットにインターネット ゲートウェイへのデフォルト ルートがないか、Cloud NAT がサブネット用に構成されていません。

Tier 4: 外部ゲートウェイと費用(インターネットと NAT)のオブザーバビリティ

外部ゲートウェイ ティアは、Cloud NAT または外部ゲートウェイを介して、公共インターネットの宛先へのアウトバウンド トラフィックを管理します。この階層のオブザーバビリティは、下り(外向き)トラフィックの多いワークロードを特定し、データ転送コストを制御するのに役立ちます。

NAT トラフィック(インターネットへの下り)を特定する

アウトバウンド インターネット トラフィックと NAT トラフィックを分析する前に、診断の範囲と前提条件を確認します。

  • 重点分野: アウトバウンド インターネット トラフィック、Cloud NAT ポートの使用率、外部データ転送費用。
  • 前提条件: GKE Dataplane V2 フロー オブザーバビリティが有効になっていること。
  • CNI の互換性: GKE Dataplane V2。
  • 症状: Cloud NAT ポートの枯渇エラーまたは予期しないアウトバウンド インターネット下り(外向き)費用の増加。
  • 目標: どの特定の GKE Pod オブジェクトと Service オブジェクトが外部インターネット エンドポイントにトラフィックを送信しているかを特定します。

ステップ 1: GKE Dataplane V2 の「to-stack」と「world」のコンセプトを理解する

GKE Dataplane V2 の場合:

  • world: GKE クラスタの外部と VPC の外部(パブリック インターネット)の宛先を表します。
  • to-stack: eBPF コンテナ veth インターフェースからホスト Linux ネットワーキング スタックに移行し、Cloud NAT に到達する前に IP マスカレード(SNAT)を受けるパケットを表します。

ステップ 2: Hubble CLI を使用して NAT トラフィックをストリーミングしてフィルタする

クラスタから発信されるアウトバウンドのインターネット フローをライブ ストリーミングします。

# Stream egress flows heading to external internet ("world")
gke-hubble observe \
    --traffic-direction egress \
    --verdict FORWARDED \
    --to-identity world \
    --follow

出力例:

TIMESTAMP             SOURCE               DESTINATION          TYPE     VERDICT
10:25:01.120          default/worker-pod   142.250.190.46:443   to-stack FORWARDED

上位のアウトバウンド トーカーを特定するには、Hubble JSON 出力を jq にパイプして、Pod ごとに外部下り(外向き)フローをカウントすることもできます。

timeout 60s gke-hubble observe \
    --traffic-direction egress \
    --verdict FORWARDED \
    --to-identity world \
    -o json | jq -r '.flow.source.namespace + "/" + .flow.source.pod_name' | sort | uniq -c | sort -nr | head -n 10

ステップ 3: 指標を使用して外部トラフィックをモニタリングする

Cloud Monitoring で、外部フローの量を追跡します。

fetch prometheus_target
| metric 'prometheus.googleapis.com/pod_flow_egress_flows_count/counter'
| filter (metric.destination_identity == 'world')
| align rate(1m)
| every 1m
| group_by [metric.source_workload, metric.source_namespace], sum(val())

Flow Analyzer を使用してクラスタ トラフィックの費用とパフォーマンスを分析する

トラフィック フローとクロスゾーン費用を可視化する前に、診断の範囲と前提条件を確認します。

  • 重点分野: ゾーン間のデータ転送料金、上位の通信元、SQL クエリなしのノード間トラフィックの可視性。
  • 前提条件: INCLUDE_ALL_METADATA で VPC Flow Logs が有効になっている。ノード内の可視性が有効になっている。ログバケットでオブザーバビリティ分析が有効になっている。
  • CNI の互換性: すべてのクラスタ。
  • 現象: 月次Google Cloud 請求書でゾーン間のデータ転送料金が高額になる。
  • 目標: 生ログをクエリせずに、どの GKE ワークロードがクロスゾーン トラフィックを生成しているかを視覚的に特定し、配置を最適化します。

前提条件

GKE で Flow Analyzer を使用するには:

  1. VPC Flow Logs: metadata="INCLUDE_ALL_METADATA" を使用して、クラスタ サブネットで有効にする必要があります。
  2. ノード内の可視化: ポッド間トラフィックが VPC Flow Logs パイプラインに公開されるように、クラスタで有効にする必要があります。
  3. ログ分析: Observability Analytics を使用するには、Cloud Logging の _Default バケットをアップグレードする必要があります。

ステップ 1: Flow Analyzer で GKE のトラフィック使用量が特に多いプロセスを特定する

Flow Analyzer で大容量のワークロードを表示する手順は次のとおりです。

  1. Google Cloud コンソールで、[Flow Analyzer] ページに移動します。
  2. [ソースバケット] をクリックし、フローログを保持するログバケットを選択します。別の場所にルーティングしていない限り、これは _Default バケットです。
  3. [トラフィックの集計] で、[送信元 - 宛先] を選択します。
  4. 分析ウィンドウの期間を設定します。
  5. [フローの整理基準] で、GKE Pod またはワークロードのフィールドを選択します。
  6. [新しいクエリを実行] をクリックします。[上位データフロー] グラフには、最も多くのデータを移動するワークロードが表示されます。

ステップ 2: ゾーン間トラフィックの費用を分析する

クロスゾーン トラフィックでは、データ転送料金が発生します。ゾーン間で転送するワークロードを見つけるには:

  1. [フロー整理の基準] で、ソースゾーンと宛先ゾーンのフィールドを選択します。
  2. [新しいクエリを実行] をクリックして、[すべてのデータフロー] テーブルを読み取ります。送信元ゾーンと宛先ゾーンが異なる行は、クロスゾーン トラフィックです。Flow Analyzer フィルタは値に一致するため、「等しくない」でフィルタすることはできません。代わりに、結果でゾーンペアを比較してください。
  3. 大容量のゾーンペアを展開して、基盤となる送信元と宛先の GKE ワークロードを表示します。

再発防止対策:

  • Kubernetes topologySpreadConstraints または podAffinity を実装して、同じアベイラビリティ ゾーン内で通信するサービスをコロケーションします。
  • Service でトポロジ対応ルーティング(service.kubernetes.io/topology-mode: Auto)を有効にして、トラフィックを元のゾーン内に維持します。

ステップ 3: オブザーバビリティ分析にドリルダウンする(高度な SQL クエリの場合)

Flow Analyzer で、[ログ分析で表示] をクリックして、フローデータに対して SQL クエリを実行します。

次の SQL クエリは、上位のクロスゾーン Pod トーカーを計算します。

SELECT
  JSON_VALUE(json_payload.src_gke_details.pod.workload.workload_name) AS src_workload,
  JSON_VALUE(json_payload.dest_gke_details.pod.workload.workload_name) AS dest_workload,
  JSON_VALUE(json_payload.src_instance.zone) AS src_zone,
  JSON_VALUE(json_payload.dest_instance.zone) AS dest_zone,
  SUM(CAST(JSON_VALUE(json_payload.bytes_sent) AS INT64)) / 1024 / 1024 / 1024 AS total_gb_sent
FROM
  `PROJECT_ID.global._Default._AllLogs`
WHERE
  log_name LIKE '%vpc_flows%'
  AND timestamp >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY)
  AND JSON_VALUE(json_payload.src_instance.zone) != JSON_VALUE(json_payload.dest_instance.zone)
GROUP BY
  1, 2, 3, 4
ORDER BY
  total_gb_sent DESC
LIMIT 20;

Hubble CLI リファレンスとクエリのクイック リファレンス

Hubble CLI は、GKE Dataplane V2 カーネル リングバッファからライブ ネットワーク フローデータを直接ストリーミングしてフィルタします。

設定: ヘルパー エイリアスを作成する

Hubble はクラスタ コントロール プレーン内で実行されるため、ローカル バイナリをデプロイせずに Hubble コマンドを実行するようにシェル エイリアスを構成します。

alias gke-hubble="kubectl exec -it -n gke-managed-dpv2-observability deployment/hubble-relay -c hubble-cli -- hubble"

一般的なフィルタリング レシピ

トラブルシューティングの目標 Hubble CLI コマンド
クラスタ全体でドロップされたすべてのパケットをリアルタイムでモニタリングする gke-hubble observe --verdict DROPPED --follow
任意の Namespace 内の特定の Pod のすべてのトラフィックをストリーミングする gke-hubble observe --pod default/my-pod --follow
2 つの特定の Namespace 間のトラフィックをフィルタする gke-hubble observe --from-namespace frontend --to-namespace backend
特定のポート(ポート 80 など)のトラフィックを分離する gke-hubble observe --port 80
ライブ DNS クエリと回答を検査する gke-hubble observe --port 53
ライブ TCP リセット(RST パケット)を表示する gke-hubble observe --type trace --verdict FORWARDED --tcp-flags RST
公共のインターネットに送信されるすべてのアウトバウンド トラフィックをストリーミングする gke-hubble observe --traffic-direction egress --to-identity world
HTTP アプリケーション レイヤ(OSI レイヤ 7)トラフィックを検査する gke-hubble observe --protocol http

否定を使用した高度なフィルタリング(--not)

既知の大容量トラフィックまたは正常なトラフィックを除外して、異常に焦点を当てることができます。

# Observe drops, but exclude internal kube-system health probes and DNS
gke-hubble observe \
    --verdict DROPPED \
    --not --namespace kube-system \
    --not --port 53

出力形式と jq の統合

フローレコードをプログラムで処理するには、JSON 形式で出力します。

# Extract only source, destination, and drop reason from the last 100 flows
gke-hubble observe --verdict DROPPED -o json --last 100 | \
    jq -r '[.time, .flow.source.pod_name, .flow.destination.pod_name, .flow.drop_reason_desc] | @tsv'

制限事項

Hubble CLI を使用してライブ トラブルシューティングを行う場合は、次の技術的な制限事項に注意してください。

  • エフェメラル ノードローカル リングバッファ: Hubble フローは、各ノードのインメモリ リングバッファに保存されます。トラフィックの多いイベント中は、古いフローログが数秒以内に上書きされます。履歴分析には、Cloud Logging の NetworkPolicy ロギングと VPC Flow Logs を使用します。
  • 組み込みの論理 OR がない: Hubble CLI フラグは、論理 AND を使用して複数の引数を評価します。複数の条件(ポート 80 またはポート 443 など)を検索するには、個別のコマンドを実行するか、jq を使用して JSON 出力をフィルタします。

次のステップ