公司动态
Kubernetes 节点卡顿:CPU Throttle、I/O 与调度怎么查
Kubernetes 节点卡顿CPU Throttle、I/O 与调度怎么查节点卡顿时CPU 使用率只是入口。容器 Throttling、磁盘等待和调度失败可能同时出现需要按节点、Pod 与请求三层对齐时间。本文给出命令顺序不预设某次线上结论。1. K8s 性能卡顿定位的四维决策树2. 四大核心卡顿陷阱与底层机理拆解陷阱一CPU ThrottlingCFS 调度器压制Kubernetes 在底层使用 Linux Kernel 的 CFS (Completely Fair Scheduler) 来限制容器的 CPU 使用。当你在 YAML 中配置resources.limits.cpu: 2时Docker/containerd 会写入 cgroupcpu.cfs_quota_us: 200,000 微秒cpu.cfs_period_us: 100,000 微秒问题二CoreDNS 超时与 Conntrack 竞争陷阱三Go Runtime 的GOMAXPROCS错配Go 语言运行时默认通过sysconf(_SC_NPROCESSORS_ONLN)读取宿主机的 CPU 核心数。如果宿主机有 64 个 CPU 逻辑核而 Pod 的 limits 仅设置为 2 核Go runtime 依然会拉起 64 个 P (Processor) 和大量的 M (OS Thread)。这会导致稳定的线程上下文切换Context Switch开销大量 CPU 时间白白浪费在调度锁争用上。3. 可部署的优化方案与 Go 运行时自适应代码针对 Go 应用在 K8s 环境下的卡顿问题必须在应用初始化阶段通过uber-go/automaxprocs动态读取容器的 cgroup CFS 配额纠正GOMAXPROCS。以下是带有调优参数的 Go 应用可部署的初始化代码package main import ( context fmt log net/http runtime time // 自动依据 cgroup CPU Limit 矫正 GOMAXPROCS避免多线程调度踩坑 _ go.uber.org/automaxprocs ) func init() { // 打印矫正后的 GOMAXPROCS确认不再使用宿主机默认物理核心数 log.Printf([System Config] 优化后的 GOMAXPROCS: %d (宿主机逻辑核: %d), runtime.GOMAXPROCS(0), runtime.NumCPU()) } // OptimizedTransport 构建防 DNS 5s 延时与长连接积压的 HTTP Client func OptimizedTransport() *http.Client { return http.Client{ Transport: http.Transport{ MaxIdleConns: 1000, MaxIdleConnsPerHost: 100, IdleConnTimeout: 90 * time.Second, // 开启 KeepAlive 与 TCP QuickAck ResponseHeaderTimeout: 5 * time.Second, ExpectContinueTimeout: 1 * time.Second, }, Timeout: 10 * time.Second, } } func main() { client : OptimizedTransport() http.HandleFunc(/healthz, func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write([]byte(OK)) }) log.Println(服务成功启动端口 :8080...) _ client }4. 关键诊断工具与排障命令现场排查卡顿问题时需要掌握一套精准的命令行组合拳快速从 Prometheus、K8s 节点与 Linux 内核中提取证据。1. 查询 Prometheus 中 Pod 的 CPU Throttling 比例在 Prometheus 中执行以下 PromQL找出被严重压制的容器列表# 计算容器在 5 分钟内 CPU 被 Throttle 的时间周期比例 sum(increase(container_cpu_cfs_throttled_periods_total[5m])) by (pod, container, namespace) / sum(increase(container_cpu_cfs_periods_total[5m])) by (pod, container, namespace) 0.252. Linux 节点内核 Conntrack 状态与丢包诊断登上宿主机 Node 查看 conntrack 表与内核丢包统计# 1. 检查当前节点 conntrack 表使用率 sysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_max # 2. 检索内核日志中是否有 conntrack: table full 报警 dmesg -T | grep -i conntrack: table full # 3. 使用 bpftrace 动态追踪 UDP DNS 丢包事件 bpftrace -e kprobe:udp_recvmsg { [ustack] count(); }3. 使用 pprof 诊断 Go 应用卡顿时的 Goroutine 积压# 1. 抓取正在等待锁或网络 I/O 的 Goroutine 栈信息 curl -s http://localhost:6060/debug/pprof/goroutine?debug2 goroutine_stack.txt # 2. 检索处于 select / semacquire 状态的高频堆栈 grep -A 10 goroutine goroutine_stack.txt | grep -E (select|semacquire|netpoll) | sort | uniq -c | sort -nr5. Kubernetes 网络与调度的链路调优针对上述四大卡顿原因可以在拓扑和配置层面的收口方案如下生产优化落地方案总结移除无意义的 CPU Limits引入 CPU Manager对延迟极度敏感的核心 API 服务将其 QoS 级别设为Guaranteed即 Request 等于 Limit 且为整数由 K8s CPU Manager 分配独占物理核避免 CFS 周期性剥夺 CPU 时间片。Kubernetes 卡顿时先区分 CPU Throttling、Conntrack 压力和运行时调度。证据指向资源不足后再扩容否则应修正配置或请求并发。