K3s 中 kube-apiserver 内存持续上涨主因是旧版 Kubernetes 缺失流式 List 响应、pprof 未启用致诊断困难、Watch 缓存与 informer 泄漏,以及 SQLite 后端高写入时内存暂留。

K3s 中的 kube-apiserver 内存持续上涨且不释放,不是典型的配置错误,而是与轻量级设计、组件集成方式及上游 Kubernetes 已知问题交织导致的典型现象。尤其在长期运行、资源受限的边缘或嵌入式场景中,该问题更易暴露。核心原因集中在内存无法及时回收、流式响应未启用、pprof 未开启导致诊断困难,以及部分版本存在已知泄漏点。
流式 List 响应未启用(v1.33+ 才默认支持)
Kubernetes v1.33 之前,kube-apiserver 对 List 请求(如 kubectl get pods -A)采用全量序列化:把所有对象一次性加载进内存、拼成大 JSON 块再发送。即使客户端只拉取几百个 Pod,若其中含大量 annotation 或 event,单次响应可能超数 MB。这部分内存不会随响应结束立即释放,而是滞留在 Go runtime 的堆中,等待 GC 触发——但 GC 在高负载下可能延迟或效率下降,造成“内存只涨不跌”的假象。
- K3s 默认使用较旧的 Kubernetes 版本(如 v1.28–v1.31),尚未集成流式编码器
- 缓解方法:避免高频执行宽泛 List 操作;用 label selector 限定范围,例如
kubectl get pods -n default -l app=nginx - 升级到 K3s v1.33+(需确认对应 Kubernetes 版本 ≥ v1.33)可原生启用流式响应,逐 item 编码并释放内存
pprof 未启用 + heap 分析缺失
K3s 默认关闭了 kube-apiserver 的 profiling 接口(/debug/pprof/heap),导致无法导出内存快照定位具体泄漏对象(如堆积的 watch cache、未清理的 admission audit buffer、重复注册的 informer)。没有 heap 文件,就只能靠猜测——比如误以为是 etcd 或 client-go 导致,实则可能是某个自定义 webhook 的缓存未限容。
- 手动开启方法:编辑
/var/lib/rancher/k3s/server/manifests/kube-apiserver.yaml(或通过k3s server --kube-apiserver-arg=profiling=true启动) - 验证是否生效:
curl -s http://localhost:6443/debug/pprof/heap | head -n 5应返回 profile 数据 - 配合
go tool pprof分析 top allocs/inuse_space,常能发现runtime.mallocgc下挂载的异常结构体(如cache.ListerWatcher或admission.Plugin实例)
Watch 缓存与 informer 泄漏(常见于多租户或 CRD 频繁变更)
K3s 将 controller-manager、scheduler 等集成进单进程,但 kube-apiserver 仍需维护 watch cache 和 internal informer。当集群中存在大量 CRD、频繁 apply 自定义资源、或第三方 operator 频繁重建 informer(未复用 shared informer factory),会导致 watch 缓存条目堆积,且部分版本中 cache key 未正确失效,引发内存缓慢爬升。
- 典型表现:内存增长曲线平缓但持续数天,
watch_cache_size参数未生效(K3s 不暴露该 flag) - 缓解方法:减少非必要 CRD;禁用不用的内置控制器(如
--disable servicelb,traefik)以降低 informer 负载 - 检查 watch 连接数:
ss -tan | grep :6443 | wc -l,若稳定在 50+ 且伴随内存增长,需排查 client 是否未 close watch 或重连过于激进
SQLite 后端在高写入下的内存暂留(K3s 单节点特有)
K3s 默认用 SQLite 替代 etcd,虽然节省资源,但其 WAL 日志和 page cache 在批量写入(如日志采集器上报大量 Event、Prometheus scrape 大量 metrics)时,会暂时将数据保留在进程内存中,直到 checkpoint 触发。SQLite 的内存管理不如 etcd 显式可控,容易被误判为“泄漏”。
- 观察指标:
sqlite_db_page_cache_bytes(若启用 metrics)或cat /proc/$(pidof k3s)/status | grep VmRSS结合写入节奏比对 - 缓解方法:限制 Event 保留时间(
--event-ttl=1h),关闭非必要 metrics 收集(如禁用--metrics-server-service) - 生产环境建议:3 节点以上集群改用 etcd 后端,规避 SQLite 的隐式内存行为










