交互式 K3s & Vault 网络故障深度分析报告

一份由表及里、层层深入的真实网络故障排查实录

案例档案

本报告将一个真实的 K3s 网络故障排查过程转化为交互式学习体验。一个看似无害的网络策略,在 Calico 的 eBPF 模式下引发了 Vault 集群的连锁性启动失败。您可以按时间线探索每个诊断阶段,揭示从误导性日志到架构性根源的完整过程。

  • 环境: K3s, Vault (HA), Calico CNI (eBPF 模式)
  • 初始症状: Pod 反复重启, 日志报 TLS handshake timeout
  • 核心挑战: 初始日志具有高度迷惑性,真实原因与平台架构紧密相关。

交互式排查路径

阶段一

误入歧途:追查 TLS

阶段二

转折点:定位网络策略

阶段三

揭示“隐形”流量

最终解决方案

制定精确策略

阶段一:误入歧途 - 追查 TLS 配置

故障初期的所有迹象都指向了 TLS 问题。Pod 日志中充斥着以下错误,这是典型的加密握手失败信号:

> http: TLS handshake error from 127.0.0.1:55066: EOF
> net/http: TLS handshake timeout
症状:TLS 握手失败 客户端 (Probe / Pod) Vault Pod SYN -> X 连接被切断,客户端超时或收到 EOF

基于此,我们进行了大量针对性的 TLS 排查,包括:

  • 1. 验证证书与私钥是否匹配:

    # 检查证书公钥的哈希
    openssl x509 -noout -modulus -in tls.crt | openssl md5
    # 检查私钥中公钥的哈希
    openssl rsa -noout -modulus -in tls.key | openssl md5

    排查结果: 两个哈希值完全一致,证明密钥对是匹配的。

  • 2. 验证证书链的有效性:

    # 使用根证书(ca.crt)验证包含中间证书的服务器证书(tls.crt)
    openssl verify -CAfile ca.crt tls.crt

    排查结果: 命令失败,返回 error 20 ... unable to get local issuer certificate。这曾让我们以为是 CA 证书链不完整的问题。

阶段结论:方向错误

尽管所有排查都围绕 TLS 进行,但问题依旧。事后证明,这些日志是底层网络连接被切断后,应用层表现出的“症状”,而非“病因”。

阶段二:转折点 - 定位网络策略

在排查陷入僵局时,一个决定性的操作彻底改变了我们的方向:

# 删除应用于 vault 命名空间的所有网络策略
kubectl delete networkpolicy --all -n vault
决定性测试:移除网络策略 Vault 启动失败 Network Policy 执行 `kubectl delete networkpolicy`

决定性发现:集群立即恢复正常!

这一现象无可辩驳地证明了,问题的根源 100% 在于网络策略。我们的排查方向因此从应用层配置,彻底转向了网络层隔离策略的分析。

阶段三:揭示被阻塞的“隐形”流量

为什么一个看似允许内部通信的策略会导致失败?深入分析后,我们发现它阻塞了两个应用启动和维持健康所必需的、但容易被忽略的“隐形”流量路径。

缺陷一:阻塞了来自节点的健康检查

Kubernetes 的健康检查探针是由运行在 节点(Node) 上的 kubelet 进程发起的,其源 IP 是节点的 IP。而网络策略的入口(Ingress)规则仅配置了 podSelector,它只允许来自其他 Pod 的流量,从而拒绝了来自节点的探针请求。

K8s Node (Kubelet) IP: 10.10.50.8 Vault Pod Port: 8200 Ingress 策略阻塞 原因:策略仅允许 Pod IP,未允许节点 IP

缺陷二:阻塞了与 K3s API Server 的通信

K3s 为了轻量化,其 API Server 是一个直接运行在 Master 节点 上的进程,而非 Pod。Vault Pod 在启动时,需要连接 API Server 进行服务发现。策略的出口(Egress)规则没有允许流量到 Master 节点的 IP,导致此关键步骤失败,Raft 集群无法建立。

Vault Pod K3s Master (API Server 进程) IP: 192.168.1.100:6443 Egress 策略阻塞 原因:策略未允许到 Master 节点 IP 的出站流量

最终解决方案:制定精确策略

基于对 K3s 架构和“隐形”流量的正确理解,我们制定了最终的、精确的网络策略,为所有必要的通信路径“开窗”。

最终生效的网络策略:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: vault-final-policy
  namespace: vault
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/name: vault
  policyTypes:
    - Ingress
    - Egress
  ingress:
    # 规则 A: 允许来自其他 Vault Pod 的内部通信
    - from:
      - podSelector:
          matchLabels:
            app.kubernetes.io/name: vault
    # 规则 B: 允许来自节点 (Kubelet) 的健康检查
    - from:
      - ipBlock:
          cidr: 10.10.0.0/16 # !! 重要:替换为您节点的 IP 地址范围
  egress:
    # 规则 A: 允许到其他 Vault Pod 的内部通信
    - to:
      - podSelector:
          matchLabels:
            app.kubernetes.io/name: vault
    # 规则 B: 允许 DNS 查询
    - to:
      - podSelector:
          matchLabels:
            k8s-app: kube-dns
    # 规则 C: 允许与 K3s API Server (Master 节点) 通信
    - to:
      - ipBlock:
          cidr: 192.168.1.100/32 # !! 关键:替换为您的 K3s Master 节点的 IP
      ports:
        - protocol: TCP
          port: 6443

结果:问题彻底解决

应用此精确策略后,Vault Pod 能够顺利完成服务发现和 Raft 集群建立,健康检查正常通过,集群稳定运行。