list-watch引发api server内存暴涨的主因是resourceversion=0全量重watch导致对象重复序列化进内存缓冲区,且默认--max-open-requests=0无上限;可通过apf限流(prioritylevelconfiguration)、关闭非关键watch或排查etcd僵尸watch(>5000连接)紧急止血。

当Kubernetes集群中出现大量客户端持续发起List-Watch请求时,API Server内存使用会急剧上升,严重时触发OOMKilled或导致etcd响应延迟升高,整个控制平面变慢甚至不可用。
确认是否为List-Watch引发的内存暴涨
执行以下命令查看API Server进程内存占用趋势:
kubectl top pods -n kube-system | grep apiserver
若发现某个apiserver Pod的内存持续高于其他副本(如超3Gi且缓慢爬升),进入下一步验证。
连接到该apiserver所在节点,运行:ps aux --sort=-%mem | head -10,确认kube-apiserver进程RSS值是否异常(例如>4GB);同时检查其启动参数中--max-open-requests和--min-request-timeout是否被设为过宽松值——【若未显式设置--max-open-requests,默认值为0(无上限),这是高内存风险的关键前提】。
定位高频Watch客户端
启用并查询kube-audit日志(需提前在AKS或自建集群中开启):
az aks show --resource-group <rg> --name <cluster> --query "addonProfiles.azurePolicy.enabled"</cluster></rg> → 确保诊断日志已发送至Log Analytics。
在Log Analytics中运行KQL查询:
KubeAuditLogs | where Operation == "WATCH" | summarize count() by userAgent_s, requestURI_s | top 10 by count_ desc
重点关注userAgent含client-go、prometheus、flux、argocd或自定义operator名称的条目;若某userAgent每秒发出数百次/api/v1/pods?watch=true&resourceVersion=0类请求,即为元凶。
注意:resourceVersion=0表示从当前状态全量重Watch,极易造成API Server重复序列化全部对象进内存缓冲区。
紧急止血与限流配置
方法一:对特定客户端IP实施API Server层限流
编辑kube-apiserver静态Pod清单(通常位于/etc/kubernetes/manifests/kube-apiserver.yaml),在command字段末尾添加:
- --enable-admission-plugins=RequestHeader,PriorityLevelConfiguration,FlowControl
- --feature-gates=APIPriorityAndFairness=true
保存后,kubelet将自动重启apiserver。此操作启用APF(API Priority and Fairness)机制。
方法二:立即创建PriorityLevelConfiguration资源隔离恶意流量
执行以下YAML创建低优先级队列:
apiVersion: flowcontrol.apiserver.k8s.io/v1beta3kind: PriorityLevelConfigurationmetadata: name: watch-burstyspec: type: Limited limited: assuredConcurrencyShares: 5 limitResponse: type: Queue requestWaitLimitSeconds: 30 subjects: - kind: User user: "system:unauthenticated" - kind: Group group: "system:serviceaccounts:<attacker-ns>"</attacker-ns>
替换<attacker-ns></attacker-ns>为真实命名空间名,然后kubectl apply -f pl.yaml。APF将强制该客户端请求排队或拒绝,内存增长立刻放缓。
方法三:临时关闭非关键Watch(仅调试用)
找到问题客户端Deployment,将其env中WATCH_NAMESPACE置为空或删掉,再执行kubectl rollout restart deploy/<name> -n <ns></ns></name>。这一步操作起来很简单,直接切断Watch源头。
检查etcd侧资源泄漏
步骤一:登录任一etcd节点,执行:
ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key endpoint status -w table
观察DB Size列是否远超正常值(如>2GB),以及Is Leader是否为false但Raft Term持续跳变。
步骤二:检查watcher数量是否异常:
ETCDCTL_API=3 etcdctl --endpoints=... --cert=... --key=... --cacert=... watch --prefix "" --create-key --rev=0 2>/dev/null | wc -l
若返回值长期>5000,说明存在大量未关闭的watch连接滞留,需结合客户端日志排查连接未释放原因。
步骤三:导出当前watcher堆栈(需etcd v3.5+):
ETCDCTL_API=3 etcdctl --endpoints=... --cert=... --key=... --cacert=... debug watch progress --hex
输出中若大量显示revision=0且progress notify间隔超5分钟,即为僵尸watch,必须终止对应客户端进程。











