排查 GKE 网络可观测性问题

本文档提供了相关说明和诊断程序,用于使用 GKE Dataplane V2 可观测性、Hubble 和 Cloud Monitoring 诊断 Google Kubernetes Engine (GKE) 集群中的网络问题。

如需了解架构概览和概念性最佳实践,请参阅网络可观测性最佳实践。

问题排查决策树和核心概念

在查看相关程序之前,请使用以下决策矩阵确定 GKE 网络堆栈的哪个层级可能导致了问题,然后前往相应部分。

症状或诊断问题 疑似层级 建议的程序
Pod 无法解析外部网域或内部 Kubernetes 服务(DNS 超时、NXDOMAIN、SERVFAIL)。 第 1 层级:Pod 和服务 (DNS) 诊断 DNS 解析失败问题
服务无法通信;Pod 之间出现连接超时或丢包。 第 1 层级:Pod 和服务(政策或丢包) 诊断丢包和 NetworkPolicy 阻止问题
工作负载副本的流量负载不均匀,或者 Pod 记录了较高的 TCP 重置 (RST) 率。 第 1 层级:Pod 和服务(负载均衡或传输) 诊断流量不平衡和 TCP 重置问题
一般延迟时间、间歇性超时或跨节点的 CNI 重启。 第 2 层级:节点和 CNI(内核) 针对节点级延迟时间和 CNI 瓶颈进行初步诊断
Pod 无法访问 GKE 外部的资源(Cloud SQL、外部 API 或其他 VPC)。 第 3 级:VPC 和路由 隔离 GKE 与 VPC 或外部连接问题
需要自动路径模拟来验证防火墙规则、路由或 NetworkPolicy 是否会阻止流量。 第 3 级:VPC 和路由(模拟) 使用 Connectivity Tests 诊断连接问题
需要确定哪些工作负载会向互联网发送流量并接受 NAT。 第 4 级:外部网关和费用 识别 NAT 流量(出站到互联网)
区域间数据传输费用高昂,或者需要直观呈现热门通话者,但又不想编写 SQL 查询。 第 4 级:外部网关和费用 使用 Flow Analyzer 分析集群流量费用和性能

核心网络概念

如果您刚开始接触 Kubernetes 或 Google Cloud 网络,请记住以下核心概念:

  • eBPF(扩展型柏克莱封包过滤器):一种操作系统技术,可直接在 Linux 内核中运行安全监控和路由程序。GKE Dataplane V2 使用 eBPF 路由数据包并强制执行 NetworkPolicy,同时最大限度地减少性能开销。
  • IP 伪装 (SNAT):重写数据包的来源 IP 地址的过程。当 GKE Pod(具有专用 IP 地址)与互联网或外部 VPC 资源通信时,GKE 会将 Pod IP 地址伪装(重写)为节点 IP 地址,以便外部系统知道如何路由回复。
  • 连接跟踪 (Conntrack):一种用于跟踪所有有效网络连接的内核功能。在 GKE Dataplane V2 集群中,此跟踪在两个表之间进行拆分:标准 Linux 内核 conntrack(由 ip-masq-agent 使用)和存储在 eBPF 映射中的 Cilium 和 GKE Dataplane V2 管理的 conntrack 表。如果节点处理的并发连接过多,这两个跟踪表中的任何一个都可能会填满(conntrack 耗尽),导致节点悄然丢弃新数据包。
  • Hubble:GKE Dataplane V2 的可观测性引擎。它基于 eBPF 运行,可实时了解流量、丢包和 NetworkPolicy 评估情况。

第 1 层级:Pod 和服务(应用)可观测性

Pod 和服务层涵盖 Pod、服务和集群 DNS 之间的网络通信。此层级的问题通常表现为应用连接超时、名称解析失败或负载分布不均。

诊断 DNS 解析失败问题

在排查 DNS 问题之前,请查看诊断范围和前提条件:

  • 重点领域:Pod 与 CoreDNS 或 NodeLocal DNSCache 之间的通信、DNS 延迟时间、上游 DNS 解析超时以及 FQDN NetworkPolicy 验证。
  • 前提条件:已启用 GKE Dataplane V2 指标;具有 kubectl 访问权限。
  • CNI 兼容性:GKE Dataplane V2(高级数据路径)和标准 GKE CNI。
  • 症状:Pod 日志记录 dial tcp: lookup <domain>: i/o timeout、NXDOMAIN 或出站 API 调用出现间歇性延迟。
  • 目标:确定 DNS 故障是源自集群内部(kube-dns 或 NodeLocal DNSCache 饱和)、NetworkPolicy 阻止 UDP 或 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 在 Cloud Monitoring 中公开了 kubernetes.io/networking/dns/ 前缀下的内置 DNS 指标。

如需在 Cloud Monitoring 中查看 DNS 指标,请执行以下操作:

  1. 在 Google Cloud 控制台中,前往 Cloud Monitoring > 信息中心。
  2. 选择预定义的 GKE DNS 可观测性 - 集群视图信息中心(或前往 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 而不是 my-service.default.svc.cluster.local),从而导致在 /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 以直接在节点上吸收高频 DNS 查找,而不会达到集群范围内的 kube-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. 如果尚未配置,请部署 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,则表示相应命名空间以默认拒绝模式运行,并且没有任何政策允许该流量。

如果未显示任何 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 流传输到单个后端 Pod。传输层(OSI 第 4 层,TCP)Kubernetes 服务路由无法平衡已建立的 HTTP/2 连接内的请求。
    • ClientIP 会话亲和性:验证服务是否配置了 sessionAffinity: ClientIP。
    • 无头服务:客户端可能会解析 DNS 一次,然后永久缓存单个 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),以强制定期重新建立连接。
  • 对于服务 IP 亲和性:移除 service.spec.sessionAffinity,除非应用状态严格要求。
  • 对于无头服务 DNS 缓存:确保应用运行时(例如 JVM networkaddress.cache.ttl)不会无限期缓存 DNS 结果。
  • 对于应用积压队列溢出:当应用监听队列已满时,Linux 内核会丢弃传入的 SYN 数据包或发送 TCP RST。扩容 Pod 副本或增加应用监听积压 (somaxconn)。

第 2 级:节点和 CNI(内核)可观测性

节点和 CNI 层包含主机 Linux 内核、eBPF 程序和节点网络接口。此层级的瓶颈会影响在受影响节点上运行的所有工作负载。

系统完整性检查:检测 anetd 的未经授权的补丁

GKE Dataplane V2 在 kube-system 命名空间中作为受管理的 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 瓶颈进行初步诊断

在诊断节点级延迟时间和内核瓶颈之前,请查看诊断范围和前提条件:

  • 重点关注领域:主机内核延迟时间、虚拟机接口处的数据包丢弃、GKE Dataplane V2 eBPF 代理饱和度、连接跟踪耗尽。
  • 前提条件:已启用 Compute Engine 虚拟机指标;具有 kubectl 访问权限。
  • CNI 兼容性:GKE Dataplane V2 和标准 GKE CNI。
  • 症状:跨节点流量出现延迟高峰或随机下降,而节点内流量保持正常。
  • 目标:区分宿主机虚拟机网络节流、Linux 内核丢弃和 CNI 级瓶颈。

第 1 步:区分 GKE 外部的问题与 GKE 内部的问题

运行 Compute Engine 虚拟机基准测试:在与 GKE 集群节点相同的 VPC 子网中部署独立的 Compute Engine 虚拟机。测试从虚拟机到目标目的地的连接。

  • 如果独立虚拟机遇到相同的丢包或延迟问题:问题位于 GKE 外部(VPC 防火墙、Cloud NAT、Cloud Interconnect 或外部服务器)。继续执行隔离 GKE 与 VPC 或外部连接问题。
  • 如果独立虚拟机的通信正常,但 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 遥测数据验证虚拟机级丢包

Google Cloud Compute Engine 会将虚拟机级网络接口指标导出到 Cloud Monitoring。

在 Cloud Monitoring > Metrics Explorer 中,查询:

sum by (drop_reason) (rate(compute_googleapis_com:instance_network_dropped_packets_count[5m]))
  • FQ_CODEL_DROP:由于公平排队或 CoDel 队列饱和(虚拟机出站带宽限制超出)而丢弃的数据包。
  • FIREWALL_RULE_DROP:被 VPC 防火墙规则丢弃的数据包。
  • RATE_LIMIT_DROP:数据包被丢弃,因为虚拟机超出了其网络接口每秒最大数据包数 (PPS) 配额。

第 4 步:使用 eBPF 调查内核级丢弃

如果虚拟机级指标显示丢包数为零,但 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。


第 3 级:VPC 和路由(云网络)可观测性

VPC 和云路由层将 GKE 节点连接到其他 Google Cloud 服务、本地网络和互联网。此处的失败通常源于 VPC 防火墙规则、自定义路由或网关配置。

隔离 GKE 与 VPC 或外部连接问题

在将外部网络问题与集群内故障隔离开来之前,请查看诊断范围和前提条件:

  • 重点领域:Kubernetes 内部路由与 VPC 云路由之间的边界隔离。
  • 前提条件:gcloud CLI;创建 Connectivity Tests 的权限。
  • CNI 兼容性:所有集群。
  • 症状:Pod 无法连接到外部资源(例如 Cloud SQL、本地 API 或第三方端点)。
  • 目标:快速确定丢弃发生在 GKE 节点内还是 Google Cloud VPC 或外部网络内。

第 1 步:Compute Engine 虚拟机基准测试

如需部署基准虚拟机并评估问题是否在 GKE 之外仍然存在,请执行以下操作:

  1. 在与 GKE 节点池相同的 VPC 子网和可用区中部署临时 Compute Engine 虚拟机实例:

    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 虚拟机无法连接:问题出在 VPC 或外部网络(防火墙规则、路由表、Cloud NAT IP 耗尽或外部 IP 地址列入许可名单)。
    • 如果 Compute Engine 虚拟机成功连接:问题出在 GKE 内部(NetworkPolicy 阻止出站流量、非伪装的 Pod CIDR 或容器级 DNS)。

第 2 步:运行按需 Connectivity Tests 测试

从节点虚拟机运行 Google Cloud Connectivity Tests 测试,以测试到目标的连接:

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 防火墙规则还是路由丢弃了流量。


使用 Connectivity Tests 诊断连接问题

Connectivity Tests 可模拟 GKE 和 VPC 资源之间的数据包路径,而无需发送实时流量。此模拟评估了以下方面:Service ClusterIP-to-Pod DNAT 解析、GKE Dataplane V2 NetworkPolicy 入站和出站规则,以及节点 IP 伪装 (SNAT)。在运行自动化路径模拟之前,请查看诊断范围和前提条件:

  • 重点领域:针对 GKE Pod、服务、NetworkPolicy 和 VPC 路由的自动化静态路径模拟。
  • 前提条件:已启用 Network Intelligence Center;已启用 Network Management API。
  • CNI 兼容性:GKE Dataplane V2(增强型分析)。
  • 症状:连接无故中断,手动检查后发现所有配置均有效。
  • 目标:静态模拟并跟踪从 Pod 源到目标位置的整个数据包路径,确定导致丢弃的确切政策行。

场景 A:验证 GKE NetworkPolicy 是否会阻止指标收集

从安全命名空间中的 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 服务的可访问性

模拟从客户端虚拟机到内部 Kubernetes 服务的可访问性:

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. 服务 DNAT 到后端 Pod IP 地址。
  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. 检查投放点:

    • 在 NetworkPolicy 中丢弃:Pod 缺少允许流量流向 0.0.0.0/0 的出站 NetworkPolicy。
    • 在 VPC 防火墙处丢弃:VPC 防火墙规则拒绝来自节点子网的出站流量。
    • 在 Cloud NAT 或路由处丢弃:子网缺少通往互联网网关的默认路由,或者未为子网配置 Cloud NAT。

第 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

如需识别出站流量最多的 Pod,您还可以将 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 查询即可实现的节点间流量可视化。
  • 前提条件:已启用 VPC 流日志 (INCLUDE_ALL_METADATA);已启用节点内可见性;已在日志存储桶中启用可观测性分析。
  • CNI 兼容性:所有集群。
  • 症状:每月Google Cloud 账单上的可用区间数据传输费用偏高。
  • 目标:直观地识别哪些 GKE 工作负载正在生成跨可用区流量,并在不查询原始日志的情况下优化放置位置。

前提条件

如需将 Flow Analyzer 用于 GKE,请执行以下操作:

  1. VPC 流日志:必须在具有 metadata="INCLUDE_ALL_METADATA" 的集群子网上启用。
  2. 节点内可见性:必须在集群上启用,以便将 pod 到 pod 流量公开给 VPC 流日志记录流水线。
  3. Log Analytics:Cloud Logging 中的 _Default 存储桶必须升级才能使用 Observability Analytics。

第 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.kubernetes.io/topology-mode: Auto),以将流量保持在源可用区内。

第 3 步:深入了解 Observability Analytics(适用于高级 SQL 查询)

在 Flow Analyzer 中,点击在 Log Analytics 中查看,即可针对流量数据运行 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 在集群控制平面内运行,因此请配置 shell 别名以执行 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
在任何命名空间中,针对特定 Pod 流式传输所有流量 gke-hubble observe --pod default/my-pod --follow
过滤两个特定命名空间之间的流量 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 流日志。
  • 没有内置的逻辑 OR:Hubble CLI 标志使用逻辑 AND 来评估多个实参。如需搜索多个条件(例如端口 80 或端口 443),请运行单独的命令或使用 jq 过滤 JSON 输出。

后续步骤