三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

Kubernetes 节点卡顿:CPU Throttle、I/O 与调度怎么查

Kubernetes 节点卡顿:CPU Throttle、I/O 与调度怎么查

Kubernetes 节点卡顿:CPU Throttle、I/O 与调度怎么查

节点卡顿时,CPU 使用率只是入口。容器 Throttling、磁盘等待和调度失败可能同时出现,需要按节点、Pod 与请求三层对齐时间。本文给出命令顺序,不预设某次线上结论。

1. K8s 性能卡顿定位的四维决策树


2. 四大核心卡顿陷阱与底层机理拆解

陷阱一:CPU Throttling(CFS 调度器压制)

Kubernetes 在底层使用 Linux Kernel 的 CFS (Completely Fair Scheduler) 来限制容器的 CPU 使用。当你在 YAML 中配置resources.limits.cpu: "2"时,Docker/containerd 会写入 cgroup:

  • cpu.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.25

2. 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?debug=2 > goroutine_stack.txt # 2. 检索处于 select / semacquire 状态的高频堆栈 grep -A 10 "goroutine " goroutine_stack.txt | grep -E "(select|semacquire|netpoll)" | sort | uniq -c | sort -nr

5. Kubernetes 网络与调度的链路调优

针对上述四大卡顿原因,可以在拓扑和配置层面的收口方案如下:

生产优化落地方案总结

  1. 移除无意义的 CPU Limits,引入 CPU Manager:对延迟极度敏感的核心 API 服务,将其 QoS 级别设为Guaranteed(即 Request 等于 Limit 且为整数),由 K8s CPU Manager 分配独占物理核,避免 CFS 周期性剥夺 CPU 时间片。

Kubernetes 卡顿时,先区分 CPU Throttling、Conntrack 压力和运行时调度。证据指向资源不足后再扩容,否则应修正配置或请求并发。

← 返回列表