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 を受けるワークロードを特定する必要がある。 ティア 4: 外部ゲートウェイと費用 NAT トラフィック(インターネットへの下り(外向き))を特定する
ゾーン間のデータ転送料金が高い、または SQL クエリを記述せずにトップトーカーを可視化する必要がある。 ティア 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 とサービス(アプリケーション)のオブザーバビリティ

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

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

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

  • 重点分野: Pod と CoreDNS または NodeLocal DNSCache 間の通信、DNS レイテンシ、アップストリーム DNS 解決のタイムアウト、FQDN NetworkPolicy の検証。
  • 前提条件: GKE Dataplane V2 指標が有効になっている。kubectl アクセス権。
  • CNI の互換性: GKE Dataplane V2(Advanced Datapath)と標準の GKE CNI。
  • 症状: Pod が dial tcp: lookup <domain>: i/o timeout、NXDOMAIN、または送信 API 呼び出しの断続的なレイテンシをログに記録します。
  • 目標: DNS 障害の原因がクラスタ内(kube-dns または NodeLocal DNSCache の飽和)、UDP または TCP ポート 53 をブロックする NetworkPolicy、またはアップストリーム ネットワークの劣化にあるかどうかを判断します。

ステップ 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 ベースの NetworkPolicies(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 は、Cloud Monitoring の組み込み DNS 指標を kubernetes.io/networking/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 Flow Observability が有効になっている。NetworkPolicy ロギングが構成されている。
  • CNI の互換性: GKE Dataplane V2 のみ。
  • 症状: アプリケーションの接続試行が Connection timed out または Connection reset by peer で失敗する。
  • 目標: セキュリティ ポリシーを試行錯誤しながら変更することなく、パケットをドロップしている正確な NetworkPolicy または eBPF の理由を特定します。

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

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

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

    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 handshake の失敗、接続の突然の終了。
  • 前提条件: GKE Dataplane V2 の指標が有効になっている。
  • CNI の互換性: GKE Dataplane V2。
  • 症状: 一部の Pod レプリカが過剰なトラフィックを受信し、他の 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 間のトラフィック分散を評価します。1 つの Pod がトラフィックの大部分を受信している場合は、接続の再利用またはスティッキー セッションを調査します。

    • gRPC または HTTP/2: 長期間存続する TCP 接続により、すべてのリクエストが 1 つの 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)のリクエストレベルのロード バランシングを有効にします。
    • クライアントサイドの接続制限またはキープアライブ タイムアウト(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: Fair Queuing または CoDel キューの飽和(VM の下り(外向き)帯域幅の上限を超えた)によりパケットがドロップされました。
  • FIREWALL_RULE_DROP: VPC ファイアウォール ルールによってパケットがドロップされました。
  • RATE_LIMIT_DROP: VM がネットワーク インターフェースの 1 秒あたりの最大パケット数(PPS)の割り当てを超えたため、パケットがドロップされました。

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

VM レベルの指標でドロップがゼロでも、GKE Pod でパケットがドロップされる場合は、GKE Dataplane V2 エージェント(anetd)で eBPF マップのドロップを調べます。

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

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


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

VPC と Cloud Router ティアは、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 \
    --round-trip

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 \
    --round-trip

Google Cloud コンソールでテストトレースを検査します。テストが DROP で GKE Network Policy evaluation のステップで終了する場合は、コレクタ 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 \
    --round-trip

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

  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 \
        --round-trip
    
  2. ドロップポイントを確認します。

    • 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 Flow Observability が有効になっている。
  • 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 フローログが有効になっている。ノード内の可視性が有効になっている。ログバケットでオブザーバビリティ分析が有効になっている。
  • CNI の互換性: すべてのクラスタ。
  • 症状: 毎月のGoogle Cloud 請求でゾーン間のデータ転送料金が増加している。
  • 目標: クロスゾーン トラフィックを生成している GKE ワークロードを視覚的に特定し、未加工のログをクエリすることなく配置を最適化する。

前提条件

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

  1. VPC Flow Logs: metadata="INCLUDE_ALL_METADATA" を使用してクラスタ サブネットで有効にする必要があります。
  2. ノード内の可視化: Pod 間のトラフィックが VPC フローロギング パイプラインに公開されるように、クラスタで有効にする必要があります。
  3. ログ分析: 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 出力をフィルタします。

次のステップ