WEBKT

大规模 K8s 集群中 RunPodSandbox 频繁超时的深层诱因与落地调优指南

16 0 0 0

在 Kubernetes 集群规模迈向数百甚至数千个节点时,平台工程师或 SRE 经常会遭遇一个经典而顽固的“幽灵故障”:新调度的 Pod 长期卡在 ContainerCreating 状态,查看 Kubelet 日志或 K8s Event,会充斥着大量的 Failed to create pod sandbox 以及 RunPodSandbox from runtime service failed: rpc error: code = DeadlineExceeded

RunPodSandbox 是 Pod 启动生命周期中的第一步。在这个阶段,Kubelet 会通过 CRI(如 containerd、CRI-O)向容器运行时发起请求,创建 Pod 的网络命名空间、基础设施容器(Pause 容器),并通过 CNI 插件为该命名空间配置网络、分配 IP。

一旦这个过程超过了 Kubelet 默认的超时限制(通常为 2 分钟,部分底层调用甚至在几秒内就会触发上层重试),就会发生 RunPodSandbox 超时。在高并发调度、大规模节点网络拓扑复杂的场景下,CNI 往往会成为这个链条中最脆弱的一环。

CNI 导致 RunPodSandbox 超时的核心诱因

要彻底解决这个问题,必须先解构在大规模场景下,CNI 与系统底层及控制面交互时产生的几个性能瓶颈。

1. CNI 客户端 QPS 限流与 API Server 压力传导

大部分非 overlay 模式的 CNI 插件(如 Calico 处于 non-overlay 模式,或者各类云厂商的 VPC-CNI,如 AWS VPC CNI、阿里云 Terway),在给 Pod 分配网络时需要频繁向 Kubernetes API Server 查询或更新资源(如 Pod 状态、Node 信息、Custom Resource Definitions 如 IPPool/ENIConfg 等)。

当大规模 HPA 触发或者大批量服务滚动更新时,成百上千的 Pod 同时调度到不同节点。每个节点上的 CNI 插件(或 CNI DaemonSet 代理)都会向 API Server 发起高并发的读写请求。

  • 如果 CNI 客户端没有合理配置 QPS 和 Burst 阈值,CNI 自身就会因客户端限流(Client-side Throttling)而导致请求积压。
  • 反之,如果客户端未作限制,API Server 就会因为 APF(API Priority and Fairness)或流量控制而拒绝连接,返回 429 Too Many Requests,导致 CNI 内部的状态机陷入无休止的指数退避重试,直接耗尽 RunPodSandbox 的 2 分钟时间窗口。

2. IPAM 锁争抢与 IP 分配延迟

IPAM(IP 地址管理)是 CNI 的核心子模块。在大规模集群中,IPAM 的设计缺陷很容易被放大:

  • 集中式 IPAM 锁争抢:如果 IPAM 强依赖于全局唯一的 etcd 数据存储或全局 CRD,在高并发申请 IP 时,为了避免 IP 冲突,CNI 会使用分布式锁。高并发下的锁竞争(Lock Contention)会导致单个 IP 分配请求的延迟从几毫秒飙升至数秒。
  • 云厂商 VPC 限流:使用云厂商原生 CNI 时,分配 IP 通常等同于调用云平台的底层 OpenAPI(如创建弹性网卡 ENI、绑定辅助私网 IP)。云厂商的 API 网关通常有严格的账户级限流。当大规模弹性伸缩发生时,OpenAPI 频繁报错 LimitExceeded,直接拉长了 Sandbox 的初始化链路。

3. Linux 内核邻居表(ARP/ND)溢出

在非 Overlay(如 BGP 直连或 Direct Routing)的大规模集群中,节点需要直接维护庞大的 Pod 到 Pod、Node 到 Pod 的路由与 MAC 地址映射关系。
Linux 内核通过邻居表(Neighbor Table,即 ARP 缓存表)来管理这些映射。Linux 内核对邻居表的大小有默认的安全限制:

/proc/sys/net/ipv4/neigh/default/gc_thresh1 = 128
/proc/sys/net/ipv4/neigh/default/gc_thresh2 = 512
/proc/sys/net/ipv4/neigh/default/gc_thresh3 = 1024
  • gc_thresh1:超过此条目数,垃圾回收(GC)随时可能启动。
  • gc_thresh2:超过此条目数,如果新条目创建,系统将在 5 秒后强制启动 GC。
  • gc_thresh3绝对最大值。一旦表中条目数达到此阈值,内核将不再写入新的 ARP 记录,并在 dmesg 中抛出经典的 Neighbor table overflow 错误。

在大规模集群中,尤其是服务间存在大量东西向跨节点通信时,节点的邻居表条目数极易突破 1024。一旦溢出,新创建的 Pod 与网关、其他 Pod 之间的 ARP 请求将无法得到响应,CNI 在尝试通过 Ping/ARP 探测网络连通性或配置本地 Veth Pair 时就会挂起,进而引发超时。

4. 系统调用阻塞与 containerd 任务积压

CNI 插件(尤其是以 Binary 可执行文件形式运行的旧版 CNI 插件)在每次 Pod 创建时,都会被 Kubelet/containerd 通过 fork-exec 的方式拉起。
在极高并发下,频繁的 fork 进程会消耗大量的系统 CPU,并可能触发 Linux 内核的 pid_max 限制。此外,如果 CNI 插件在执行过程中需要调用 iptablesip routeip link 等系统命令行工具,由于 iptables 在更新规则时使用文件锁(/run/xtables.lock),高并发的进程竞争会导致大量的锁等待(Lock Wait),严重拖慢 CNI 执行进度。


针对性的生产级调优方案

针对上述瓶颈,可以从内核参数、CRI 运行时、CNI 配置以及集群控制面四个维度进行深度调优。

一、 优化 Linux 内核邻居表与网络栈参数

在大规模 K8s 节点上,必须大幅提升内核邻居表的阈值,以容纳庞大的 IP 数量。

在所有 K8s 节点的 /etc/sysctl.d/99-k8s-net.conf 中追加以下配置:

# 大幅提升 ARP/邻居表阈值(适用于大于 1000 节点的集群)
net.ipv4.neigh.default.gc_thresh1 = 2048
net.ipv4.neigh.default.gc_thresh2 = 4096
net.ipv4.neigh.default.gc_thresh3 = 8192

net.ipv6.neigh.default.gc_thresh1 = 2048
net.ipv6.neigh.default.gc_thresh2 = 4096
net.ipv6.neigh.default.gc_thresh3 = 8192

# 增大连接队列长度,防止 CRI 与 CNI 守护进程(如 Cilium/Calico Node)间通信积压
net.core.somaxconn = 32768
net.ipv4.tcp_max_syn_backlog = 16384

# 提升文件描述符及异步 I/O 限制
fs.file-max = 2097152
fs.aio-max-nr = 1048576

执行 sysctl --system 使其立即生效。

二、 优化 CNI 客户端与 API Server 交互性能

如果使用 Calico 或 Cilium,应优化其内置客户端与 K8s API Server 通信的 QPS 与 Burst 限制,避免因内部限流导致 RunPodSandbox 超时。

Calico 调优示例

修改 calico-node 容器的环境变量,提升其与 API Server 通信的限制:

kind: DaemonSet
apiVersion: apps/v1
metadata:
  name: calico-node
  namespace: kube-system
spec:
  template:
    spec:
      containers:
        - name: calico-node
          env:
            - name: K8S_API_QPS
              value: "100"
            - name: K8S_API_BURST
              value: "150"

Cilium 调优示例

在 Cilium 的 ConfigMap 或 Helm Value 中,调整其 API 客户端配置,并启用局部缓存,减少直接对 API Server 的 LIST 请求:

# Helm values.yaml 示例
k8s:
  requireIPv4PodCIDR: true
  apiLimits:
    qps: 100
    burst: 150
# 开启 Cilium 的局部 K8s 缓存以减少 API 请求压力
k8sClientRateLimit:
  qps: 100
  burst: 150

三、 优化 IPAM 的预分配策略(以云原生 CNI 为例)

针对云厂商 VPC-CNI 因调用 OpenAPI 限流导致的超时,必须开启 IP 预分配(Warm Pool)

以阿里云 Terway 或 AWS VPC-CNI 为例,调整 DaemonSet 环境变量,在节点初始化时预先申请并缓存一定数量的弹性网卡(ENI)和次要 IP,避免在 Pod 调度时“现用现建”:

# 以 AWS VPC-CNI (aws-node) 为例
env:
  - name: WARM_IP_TARGET
    value: "5"      # 节点上始终保持 5 个空闲 IP 供快速分配
  - name: MINIMUM_IP_TARGET
    value: "10"     # 节点初始阶段最少持有的 IP 数
  - name: WARM_ENI_TARGET
    value: "1"      # 预热 1 个弹性网卡,避免动态挂载 ENI 耗时数十秒

注意:预分配策略会占用 VPC 内的部分 IP 资源,需确保 VPC 子网的 CIDR 空间足够大。

四、 CRI(containerd)运行时的并发与限制调优

容器运行时如果面临高并发请求且没有足够的系统资源额度,同样会导致 RunPodSandbox 的 gRPC 响应延迟。

1. 调整 containerd 配置

修改 /etc/containerd/config.toml,确保其任务并发上限和超时设置处于合理区间:

[plugins."io.containerd.grpc.v1.cri"]
  # 允许并发执行的容器和 Sandbox 操作数
  max_concurrent_downloads = 20
  # 针对 sandbox 执行的超时时间,必要时可将其放宽,给 CNI 留出更多缓冲时间
  sandbox_image = "registry.k8s.io/pause:3.9"

2. 解除 Systemd 的 TasksMax 限制

高并发下,containerd 及其衍生进程可能因超出 Systemd 默认的系统任务(线程/进程)上限而无法创建新线程,表现为 CNI 脚本执行卡死。

修改 containerd 的 systemd service 文件(通常位于 /lib/systemd/system/containerd.service):

[Service]
# 将 TasksMax 设置为 infinity 或足够大的数值
TasksMax=infinity
LimitNPROC=infinity
LimitNOFILE=1048576

执行以下命令重载配置并重启服务:

systemctl daemon-reload
systemctl restart containerd

五、 CNI 升级:从 Binary 转向 gRPC Daemon 模式

如果你的集群还在使用传统的 CNI 插件(每次创建 Pod 都通过 shell 启动 CNI 二进制文件并加载 JSON 配置文件),在大规模场景下建议尽快升级到基于 Daemon 架构的 CNI(例如 Cilium、Calico eBPF 模式、或者采用了持久守护进程的专属 CNI)。

在 Daemon 模式下,Pod 创建时,Kubelet 调用的 CNI 插件二进制文件仅作为一个极简的 gRPC 客户端,直接向运行在本地的 CNI Daemon(如 cilium-agentcalico-node)发送 gRPC 请求。所有的网络逻辑、路由计算、IP 分配都是在本地的持久化 Daemon 中异步、常驻内存完成,这彻底消除了高并发下 fork-exec 带来的 CPU 开销与 iptables 锁竞争。


总结:故障排查与预防路径

在日常运维中,可以通过以下排查链条快速定位并预防 RunPodSandbox 超时:

  1. 看日志层级:先看 Kubelet 日志,确定是否是 DeadlineExceeded;再看 containerd 日志,寻找 CNI 调用失败的底层输出;接着看 CNI Daemon(如 cilium-agent)日志。
  2. 查内核邻居表:执行 dmesg -T | grep -i "neighbor"arp -an | wc -l。如果条目数逼近 1024 且伴随内核报警,立即调大 gc_thresh
  3. 查控制面流控:在 API Server 审计日志中检索 status: 429 响应,重点看 User-Agent 是否包含特定的 CNI 标识。如果包含,说明急需调优 CNI 的 API QPS 参数。
  4. 监控 CNI 执行时延:通过 Prometheus 监控 CNI 插件的 cni_request_duration_seconds 等指标,一旦 P99 耗时超过 5 秒,就要开始审视 IPAM 锁和云平台网关限流问题。
云原生践行者 KubernetesCNI

评论点评