kube-apiserver 的参数调优:并发、超时、缓存与 etcd 交互
kube-apiserver-tuning 关注的是 apiserver 这层的参数:能开多大并发、请求能挂多久、缓存怎么用、和 etcd 之间怎么配合。apiserver 本身无状态,理论上加副本就能扩;但真正决定它扛不扛得住的,是下面这几组参数与它们背后的资源(CPU、内存、etcd)。
调优前先建立一个判断标准:apiserver 变慢只有三种原因 —— 它自己 CPU 不够、它在等 etcd、或者它在等外部依赖(webhook、聚合 API、kubelet)。前两种靠参数与扩容解决,第三种只能去修那个外部依赖。
--max-requests-inflight=400 # 非变更请求的并发上限(开了 APF 后是"总座位数"的一半)
--max-mutating-requests-inflight=200 # 变更请求的并发上限(开了 APF 后是另一半)
--enable-priority-and-fairness=true # 默认开启,改为 false 会退回单一全局限额
--request-timeout=60s # 普通请求的默认超时
--min-request-timeout=1800 # watch 请求的最小超时下限(秒),实际取该值以上的随机数
--goaway-chance=0 # 随机发送 GOAWAY 让客户端换副本,取值区间 [0, 0.02]
--shutdown-delay-duration=0 # 优雅退出时的延迟,配合 LB 摘流量
--shutdown-send-retry-after=false # 退出期间对新请求返回 429 + Retry-After
--shutdown-watch-termination-grace-period=0 # 等待活跃 watch 排空的最长时间
默认 400 + 200 = 600 是整个服务器能同时处理的请求数(座位数),APF 会把这 600 个座位按优先级分配(见 api-priority-fairness)。
--etcd-servers=https://127.0.0.1:2379
--etcd-compaction-interval=5m # 由 apiserver 发起的 etcd compaction 间隔,0 表示关闭
--etcd-count-metric-poll-period=1m # 轮询各资源对象数量的间隔,0 表示关闭
--etcd-db-metric-poll-interval=30s # 轮询 etcd DB 指标的间隔,0 表示关闭
--etcd-healthcheck-timeout=2s # /healthz/etcd 的检查超时
--etcd-readycheck-timeout=2s # /readyz 的 etcd 就绪检查超时
--watch-cache=true # 开启 watch 缓存(默认开启)
--watch-cache-sizes= # 只支持把某个资源设为 0 来关缓存,非 0 值已被忽略
--storage-initialization-timeout=1m # 存储初始化(首次 list 填充缓存)的最长等待
--max-request-bytes=3145728 # 单个写入请求上限 3MiB(与 etcd 的 1.5MiB 配套)
--delete-collection-workers=1 # 处理 DeleteCollection 的 worker 数,影响命名空间清理速度
# 把 Event 分流到独立的 etcd 集群(大规模集群必做)
--etcd-servers-overrides=/events#https://etcd-events-1:2379;https://etcd-events-2:2379
--etcd-servers-overrides 的格式是 group/resource#servers,多个资源用逗号分隔、同一资源的多个地址用分号分隔,例如 /pods#http://etcd4:2379;http://etcd5:2379,/events#http://etcd6:2379。它只对编译进这个 apiserver 二进制的资源生效,CRD 不在其列。
--profiling=true # 默认开启 pprof(生产环境建议按需关闭)
--contention-profiling=false # 开启互斥锁竞争分析,开销较大
--debug-socket-path= # 不鉴权的 pprof unix socket,排障用,用完即关
--audit-policy-file=/etc/kubernetes/audit/policy.yaml
--audit-log-path=/var/log/kubernetes/audit.log
--v=2 # 日志级别
# 当前在途请求数(按请求类型分)
kubectl get --raw /metrics | grep apiserver_current_inflight_requests
# 请求延迟分布:定位是哪些 verb/resource 慢
kubectl get --raw /metrics | grep apiserver_request_duration_seconds_bucket
# 长期运行请求(watch、exec)数量
kubectl get --raw /metrics | grep apiserver_longrunning_requests
# 只有 apiserver 能访问的 pprof(默认只监听 localhost,可临时用 port-forward)
kubectl -n kube-system port-forward <apiserver-pod> 8001:6443
# 实际生产中更常用的是直接在控制面节点上抓
curl -sk https://127.0.0.1:6443/debug/pprof/goroutine?debug=2 | head
# 读写流量画像:哪些资源在被高频 list、哪些在被高频写
kubectl get --raw /metrics | grep -E 'apiserver_request_total' | head -20
# etcd 侧的请求延迟(apiserver 视角)
kubectl get --raw /metrics | grep etcd_request_duration_seconds
watch-cache)。--control-plane-endpoint 都指向 LB,而不是某个具体实例;--goaway-chance 只在有 LB 的前提下才有意义。| 症状 | 先查什么 |
|---|---|
kubectl 大面积超时 |
--request-timeout、apiserver_request_duration_seconds、etcd 延迟 |
| 大量 429 / 请求被拒 | APF 的 apiserver_flowcontrol_rejected_requests_total 与队列长度 |
| watch 频繁断开重连 | --min-request-timeout、LB 的空闲超时、apiserver_longrunning_requests |
| 大对象写不进去 | --max-request-bytes 与 etcd 的 --max-request-bytes 是否配套 |
| apiserver OOM | watch cache 容量与大对象(apiserver_watch_cache_capacity) |
| 命名空间删除极慢 | --delete-collection-workers |
| 启动后长时间不 Ready | --storage-initialization-timeout、etcd 是否健康 |
# apiserver 之间不直接通信,靠 etcd 保证一致性,因此前端必须有 LB
# kubeconfig 与 kubelet 的 --control-plane-endpoint 都要指向 LB 地址
kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}'
# kubelet 侧确认它连的是不是 LB
sudo grep server /etc/kubernetes/kubelet.conf
# 让客户端不要长期粘在一个副本上(仅在有多副本 + LB 时启用)
--goaway-chance=0.001
# 优雅退出:先让 LB 把自己摘掉,再真正停止服务
--shutdown-delay-duration=30s
--shutdown-send-retry-after=true
--shutdown-watch-termination-grace-period=30s
| 参数 | 默认值 | 说明 |
|---|---|---|
--max-requests-inflight |
400 | 读并发,和下一项一起决定总座位数 |
--max-mutating-requests-inflight |
200 | 写并发 |
--enable-priority-and-fairness |
true | 关掉会退回单一全局限额 |
--request-timeout |
60s | 普通请求超时 |
--min-request-timeout |
1800 | watch 超时下限(秒) |
--max-request-bytes |
3145728(3MiB) | 与 etcd 的 1.5MiB 配套 |
--watch-cache |
true | watch 缓存开关 |
--watch-cache-sizes |
空 | 只有 resource#0 有意义 |
--default-watch-cache-size |
100(已废弃) | no-op |
--storage-initialization-timeout |
1m | 存储初始化最长等待 |
--etcd-compaction-interval |
5m | 0 表示不由 apiserver 触发压缩 |
--etcd-count-metric-poll-period |
1m | 对象数量轮询间隔 |
--etcd-db-metric-poll-interval |
30s | etcd DB 指标轮询间隔 |
--etcd-healthcheck-timeout |
2s | etcd 健康检查超时 |
--etcd-readycheck-timeout |
2s | etcd 就绪检查超时 |
--delete-collection-workers |
1 | 命名空间清理速度 |
--goaway-chance |
0 | 上限 0.02,推荐 0.001 |
--profiling |
true | pprof 开关 |
--contention-profiling |
false | 锁竞争分析,开销大 |
--shutdown-delay-duration |
0 | 优雅退出延迟 |
--request-timeout 调大不是万能的。它只决定服务端等多久,调大意味着慢请求会占用更久的并发名额(座位),堆积起来反而更快打满并发。慢 list 应该用分页(--limit + continue)解决,而不是靠超时兜底。--min-request-timeout 是 watch 的超时下限,单位是秒。watch 会在这个值之上取一个随机值作为连接超时,目的是把大量 watch 的重连时间打散。调得过大,客户端切换时会留下大量闲置连接;调得过小,watch 会频繁重建。--max-request-bytes 必须与 etcd 的 --max-request-bytes 协调。apiserver 默认 3MiB、etcd 默认 1.5MiB,这个 2 倍关系是留给 JSON 转 protobuf 的余量。只调一方会出现"部分对象偶尔写失败"的诡异现象,排查成本极高。--goaway-chance 在单实例或没有 LB 的集群上不要开。它会让客户端连接被随机断开重连,没有 LB 时客户端只会回到同一个实例,白掉一次连接。推荐起始值 0.001,上限 0.02。--profiling 默认为 true。pprof 端点挂了鉴权(走 6443,需要认证),但生产环境仍建议按需关闭或限制访问;--debug-socket-path 打开的 socket 不做认证,绝对不要长期保留。--watch-cache=false 会让每个 list/watch 都打到 etcd,显著放大 etcd 的负载与 compaction 压力,只适合排障时临时验证。--watch-cache-sizes 现在几乎没有调优空间。非零值会被忽略并打日志 Dropping watch-cache-size for ..., watchCache size is now dynamic;唯一有意义的写法是把某个资源设为 0 来关闭它的缓存。同理 --default-watch-cache-size 已废弃且是 no-op,别再照着老文章抄。RequestResponse 级别的审计会记录完整请求与响应体,磁盘 IO 与空间占用都很大;必须配轮转(--audit-log-maxage/maxbackup/maxsize),并把日志目录从宿主机挂进容器,否则写不出来还以为没生效。kubectl 报 connection refused 属于正常。改静态 Pod 清单前务必备份,起不来时只能用 crictl 和 journalctl -u kubelet 救场。etcd_disk_backend_commit_duration_seconds,而不是先加 apiserver 的 CPU。--etcd-compaction-interval 设成 0 要非常小心。它表示"apiserver 不再发起 compaction",此时必须由 etcd 自己的 --auto-compaction-mode / --auto-compaction-retention 承担起压缩职责;两边都没配,历史版本会不断堆积,数据库会一路涨到配额触发 NOSPACE。--etcd-count-metric-poll-period 与 --etcd-db-metric-poll-interval 关闭后,你只是失去了可观测性。这两个轮询本身开销很小,关掉它们换来的是"看不到对象数量增长"和"看不到 etcd DB 大小",排障时会非常被动。--request-timeout 到点时,客户端看到的是 apiserver 超时,但根因可能在 webhook 或聚合服务;用 apiserver_request_duration_seconds 按 resource 分组能快速区分是自己的资源还是扩展资源。kube-apiserver — 集群 API 服务器etcd — 集群数据存储kubelet — 节点代理kubeadm — Kubernetes集群安装工具api-priority-fairness — apiserver 的过载保护机制watch-cache — watch 缓存机制与内存权衡audit-log — 审计日志配置