Kubernetes 查询异常排查:从 kubectl 到 etcd 的全链路诊断

发布时间:2026/10/21 16:14:48
Kubernetes 查询异常排查:从 kubectl 到 etcd 的全链路诊断
聊一聊 Kubernetes 日常运维里最容易让人血压升高的一件事kubectl get pods突然不正常。这里说的“不正常”不只是命令行报红。命令卡住直到超时、返回的节点全是 NotReady、Pod 状态一片 Pending 但业务容器明明在跑、客户端显示严重版本警告这些都算。我在线上踩过几次坑后发现大多数查询状态异常问题不是出在 Kubernetes 集群本身而是出在“查询这条链路”的某一个环节。这条链路从你手上的kubectl开始经过 Kubeconfig、API Server、etcd最后才到 kubelet 上报的状态数据。任何一个环节迟滞最终都会表现为一句“状态不正常”。这篇文章我会结合自己实际排查过的案例把这条链路拆开讲清楚提供可以直接照抄的排查命令和修复动作。适合刚接触 K8s 的开发者也适合已经开始维护集群但还没系统整理过排查思路的运维同学。1. 先把问题定义清楚五种“查询状态不正常”不是一回事很多人一说“查询状态不正常”就直接去看 Pod 的 YAML Event结果绕了半天没找到问题。我建议先停下来把现象分类。不同现象对应的根因范围完全不同对号入座才能少走弯路。1.1 客户端报错、命令卡住、结果异常先对号入座我一般把“查询状态不正常”分成五类直接报连接错误比如Unable to connect to the server: dial tcp 10.0.0.10:6443: connect: connection refused。这类问题基本是与 API Server 的网络不通或者 API Server 进程已经不在监听状态。命令卡住直到超时kubectl get pods执行后一直转圈过了几秒甚至几十秒才输出context deadline exceeded。这种情况往往不是“连不上”而是“连接以后响应不过来”要重点查 API Server 过载、etcd 慢、负载均衡超时这类中间环节。能返回结果但状态字段异常比如节点全是NotReady、Pod 全是Pending或Unknown。这时候链路通常是通的问题大概率出在数据上报侧比如 kubelet、CNI、容器运行时。返回结果和实际情况对不上业务进程明明在跑但通过kubectl get pods看 Pod 的 Ready 条件一直为 False或者删掉的 Pod 迟迟不消失。这类问题往往和 kubelet 的 PLEG、状态上报周期有关不是 API Server 挂了。一堆 Warning 和版本警告比如certificate has expired or is not yet valid或者Kubernetes version is too old。这类问题更像是客户端配置、证书、版本兼容性出了岔子。你可以把上面五类当作一个“故障分类漏斗”。先判断自己属于哪一类再决定下一步查什么。1.2 一个最小动作帮你划清故障边界我在所有排查开头都会先跑两个命令kubectl cluster-info kubectl get --raw/healthzkubectl cluster-info会返回集群里几个核心服务的访问地址如果这一步能正常输出说明你本地到 API Server 的网络、TLS 握手、身份认证基本是通的。接着执行kubectl get --raw/healthz如果返回ok说明 API Server 的进程健康检查也是通过的。这两个命令都成功后问题就被大大缩小了不是连接层而是具体资源查询、权限或状态上报的问题。这时候再执行kubectl get pods -A kubectl get pods -n 目标命名空间对比全量和单命名空间的结果。如果-A正常但目标 namespace 异常优先怀疑 RBAC 或 namespace 本身如果-A也卡住那就继续向 API Server 和 etcd 方向排查。2. 拆解查询链路kubectl、API Server、etcd 分别扮演什么角色很多同学排查时总是盯着 Pod 本身看却忘了“查询”这件事是一个完整的链路。把链路拆开理解很多排查步骤其实是顺理成章的。2.1 kubectl 找集群靠的是 Kubeconfig不是“直觉”kubectl本身不存储任何集群信息它所有行为都依赖 Kubeconfig 文件。默认路径是~/.kube/config但也可以通过KUBECONFIG环境变量指定一个或多个配置文件。Kubeconfig 里有三个核心概念clusters定义了 API Server 地址和 CA 证书users定义了你的客户端证书或 Tokencontexts把某个 cluster 和某个 user 绑定在一起顺便指定默认 namespace。kubectl实际生效的是current-context其它配置哪怕写得再完整只要 context 没切对命令就会打到别的集群去。我遇到过一个典型情况本地同时放了开发和测试两套环境的 Kubeconfig某次切环境之后忘了切回来导致在测试环境怎么查都查不到资源。这种问题看起来也像“状态不正常”但本质是查错了地方。排查 Kubeconfig 最常用的命令是kubectl config get-contexts kubectl config current-context kubectl config view --minify --raw--minify可以只看当前 context 对应的配置--raw会输出明文方便检查证书和 Token。注意--raw会暴露敏感信息别随便发到群里。2.2 API Server 是唯一入口认证、授权、限流都在这里所有通过kubectl发出的请求最终都是向 API Server 发起的 HTTP 请求。比如kubectl get pods实际上相当于请求了GET /api/v1/pods?limit500如果指定 namespace则是GET /api/v1/namespaces/dev/podsAPI Server 收到请求后先做认证确认“你是谁”然后做授权确认“你能看什么”再经过准入控制最后从 etcd 或本地缓存里读取数据并返回。这个链路里任何一个环节出问题表现都不一样认证失败返回Unauthorized一般和证书过期、Token 失效有关。授权失败返回Forbidden一般和 RBAC 绑定缺失有关。限流策略生效返回429 Too Many Requests一般和 API Priority and FairnessAPF配置或集群负载有关。etcd 读取超时请求会一直挂着最后超时。所以遇到“状态不正常”不要只盯 Pod 字段先判断 API Server 返回的是“拒绝”还是“超时”两者排查方向完全不同。2.3 节点状态和 Pod 状态是“上报”出来的不是实时轮询还有一个容易误解的点kubectl get nodes和kubectl get pods看到的“当前状态”其实不是实时读取容器运行时得到的。节点状态由每个节点上的 kubelet 周期性上报Pod 状态也由 kubelet 把容器运行情况写入 API Server。举个例子kubelet 挂了之后节点不会立刻变成NotReady默认要经过node-monitor-grace-period一般是 40 秒之后节点控制器才会把节点标记为NotReady。如果你看到节点 1 分钟前还是 Ready现在突然 NotReady先不要惊讶这已经是系统在给你兜底了。同样Pod 的status.phase、conditions也是 kubelet 上报后写入 etcd 的。你看到的状态反映的是“上次上报的事实”不一定是“此刻的事实”。这条认知对排查非常重要尤其是当“实际业务正常但 K8s 状态异常”时优先怀疑上报链路。3. 四板斧实操从命令到日志逐步定位接下来是我自己总结的一套排查流程按照“本地配置 → API Server 健康 → 原始 API 请求 → etcd 存储”逐层递进。每一步都有明确动作正常情况下按顺序走一遍就能找到病根。3.1 第一板斧验证 kubectl 本身和本地配置先确认 kubectl 自身版本和配置没问题kubectl version --client kubectl config get-contexts kubectl config current-context kubectl config view --minify --raw重点看三样东西server地址是否正确。如果证书里签的地址和访问地址不一致TLS 握手会失败。current-context是否指向预期的集群和 namespace。注意 namespace 不是默认default的话kubectl get pods不带-n是查不到其它 namespace 的。证书是否在有效期内。Kubeconfig 里的client-certificate-data字段是一段 base64 编码的 PEM 证书可以直接解出来看kubectl config view --raw | grep client-certificate-data | awk {print $2} | base64 -d | openssl x509 -noout -dates如果这里直接显示notAfter已经过去了那后面的所有报错都不用再猜了先换证书再继续。3.2 第二板斧探测 API Server 健康接口Kubernetes 的 API Server 提供了几个健康检查端点可以直接用 kubectl 访问kubectl get --raw/healthz kubectl get --raw/livez kubectl get --raw/readyz?verbose/healthz是传统健康检查/livez表示进程是否存活/readyz才是真正是否准备好接收请求的检查并且后面的?verbose会列出每一项检查的具体状态比如 etcd 连接检查是否通过。如果使用负载均衡对外暴露 API Server还可以直接从外部用 curl 验证curl -sk https://server-address:6443/readyz -o /dev/null -w %{http_code}\n返回 200 不代表查询一定正常但至少说明 API Server 的接受连接检查是好的。如果这里超时问题可能出在服务暴露层而不是集群本身。当健康检查正常但查询仍然卡顿时下一步去看 API Server 日志。常见日志位置取决于部署方式静态 Pod 方式就执行kubectl -n kube-system logs -l componentkube-apiserver --tail200如果是 systemd 方式就登录控制面节点执行journalctl -u kube-apiserver --no-pager -n 200日志里如果频繁出现etcdserver: request timed out、apply request took too long那基本可以断定是存储侧问题继续往下走。3.3 第三板斧绕过 kubectl用 curl 请求原始 API有时 kubectl 本身有问题但又不想直接动集群配置可以用kubectl proxy在本地起一个代理然后像调第三方 API 一样访问kubectl proxy --port8080 curl http://127.0.0.1:8080/api/v1/pods如果kubectl proxy起不来说明 kubectl 和 API Server 的认证链路已经断了。如果 proxy 能起来但 curl 请求慢问题就更偏向 API Server 后端。另外还有一个特别实用的命令kubectl auth can-i。它专门用来检查当前用户对某类资源到底有没有权限kubectl auth can-i list pods -A kubectl auth can-i get nodes kubectl auth can-i list pods -n dev这一步能把“权限问题”和“性能问题”快速分开。如果can-i返回no说明是 RBAC 的锅而不是集群真的没返回数据。3.4 第四板斧确认 etcd 和后端存储健康状况当 API Server 日志明确指向 etcd 或者查询在接近超时阈值时就要看存储层了。etcd 是 Kubernetes 的最终数据源所有kubectl get的 List 请求最终都要读 etcd。如果 etcd 慢API Server 再快也没有用。如果 etcd 以静态 Pod 方式运行可以这样进到容器里执行 etcdctlkubectl -n kube-system get pods | grep etcd kubectl -n kube-system exec etcd-node -- sh -c \ etcdctl --cacert/etc/kubernetes/pki/etcd/ca.crt \ --cert/etc/kubernetes/pki/etcd/server.crt \ --key/etc/kubernetes/pki/etcd/server.key \ endpoint health --cluster再看一下每个 etcd 成员的状态kubectl -n kube-system exec etcd-node -- sh -c \ etcdctl --cacert/etc/kubernetes/pki/etcd/ca.crt \ --cert/etc/kubernetes/pki/etcd/server.crt \ --key/etc/kubernetes/pki/etcd/server.key \ endpoint status --write-outtable同时登录控制面节点看磁盘df -h /var/lib/etcdetcd 对磁盘延迟极其敏感即使容量没满如果磁盘 IO 延迟飙升也会导致 API Server 查询大量超时。后面第 4.4 小节我会单独展开。4. 高频根因和修复方案证书过期、RBAC、限流、存储下面这几个根因是我在排查“查询状态不正常”时遇到频率最高的。每一条都有明显特征和对应修复动作可以直接对照使用。4.1 证书过期和 Context 切错查询异常的第一大坑证书过期是“查询状态不正常”的头号杀手。常见报错包括error: You must be logged in to the server (Unauthorized) x509: certificate has expired or is not yet valid tls: failed to verify certificate: x509: certificate signed by unknown authority第一个错误要查认证第二个直接说明客户端证书过期第三个则可能是 CA 文件错了或者你在用一个不匹配当前集群的 Kubeconfig 访问。处理思路很简单确认服务端证书和客户端证书的有效期然后重新获取一份有效的 Kubeconfig。如果是自建集群通常可以找集群管理员用集群 CA 重新签一个用户证书如果是托管集群就在控制台重新下载 Kubeconfig。另外多集群开发者最常见的问题就是 Context 切错。我和朋友协作的时候经常看到一个人对着 A 集群的 Kubeconfig 查 B 集群的资源然后告诉我“状态不正常”。这种情况不用动集群执行kubectl config get-contexts kubectl config use-context 正确的context名就能解决。4.2 RBAC 权限不足报 forbidden 不等于集群挂了看到Forbidden很多人会紧张以为是集群安全出了问题。其实大部分情况下就是当前用户的 RBAC 权限不够。典型报错Error from server (Forbidden): pods is forbidden: User devops cannot list resource pods in API group in the namespace dev如果你只是想给这个用户临时查看权限可以用下面的命令先验证kubectl auth can-i list pods -n dev --as devops然后给用户绑定一个最小权限的角色。比如只允许查看default命名空间下的 Podkubectl create role pod-reader --verbget,list,watch --resourcepods -n default kubectl create rolebinding devops-pod-reader --rolepod-reader --userdevops -n default有一个容易忽视的点有些查询工具或 CI 使用的是 ServiceAccount而不是人。ServiceAccount 权限缺失时返回的错误信息里会带有system:serviceaccount:...前缀。记得用--as模拟对应 ServiceAccount 来排查。4.3 API Server 过载请求被 APF 限流挡住Kubernetes 从 1.20 左右开始全面使用 APFAPI Priority and Fairness对 API Server 的请求做优先级和公平性控制。当某类请求并发过高时API Server 会主动丢弃或延迟某些请求返回 429。判断方法有两个看 API Server 日志里有没有apf_...相关拒绝记录。查监控指标kubectl get --raw/metrics | grep apiserver_flowcontrol_rejected_requests_total | grep -v ^#如果相关指标在不断增长说明确实有请求被限流。这类问题常见诱因是大量客户端用短连接频繁List整个集群的 Pod或者是 Informer 全量重新同步。修复方向不是“重启 API Server”而是降低异常客户端的请求频率使用长连接和watch而不是疯狂轮询。调整 APF 配置比如提高某个优先级等级的nominalConcurrencyShares。如果 API Server 副本本身 CPU 和内存已经很高再考虑扩容或优化 etcd。切记不要一看到 API Server 负载高就手动重启这样会短暂清掉连接缓存但很快又会打满甚至让故障扩大。4.4 etcd 磁盘性能问题get 慢、超时的隐形元凶如果kubectl get命令变慢但 API Server 日志里没有明显报错我优先看 etcd。etcd 的写入需要持久化到磁盘才能给客户端返回成功所以它对磁盘 fsync 延迟非常敏感。一旦磁盘 IO 延迟超过 100msetcd 的提交速率就会掉下来然后整个 API Server 的 List、Watch、状态更新全部受影响。典型日志etcdserver: request timed out apply request took too long mvcc: database space exceeded排查动作分三步看 etcd 健康状态和 db 大小前面第 3.4 节已经写了命令。看 etcd 所在磁盘的使用率和 IO 延迟df -h /var/lib/etcd iostat -x 1如果 db size 增长过快考虑开启自动压缩或者手动压缩整理碎片etcdctl compact revision etcdctl defrag注意defrag是一个对 etcd 有全局影响的重操作执行前必须确认有快照备份并且尽量在业务低峰期做。很多同学在磁盘快满的时候急急忙忙做 defrag结果因为空间不够反而把 etcd 搞到只读模式。正确做法是先清理磁盘空间再考虑压缩。4.5 节点状态长期异常kubelet 和 CNI 的问题如果查询结果是节点NotReady同时对应的 Pod 一直ContainerCreating那问题很可能在 kubelet 或 CNI 网络插件上。排查顺序kubectl describe node 节点名 | tail -30 journalctl -u kubelet --no-pager -n 100常见的三种原因kubelet 自身异常比如证书过期、PLEG 卡死、容器运行时连接超时。日志里通常能直接看到。CNI 配置错误Pod 网络无法创建导致 Pod 一直创建失败。常见错误集中在failed to set up pod network。资源压力磁盘、内存、PID 占用过高节点控制器会把节点标记为DiskPressure或MemoryPressure从而拒绝调度新 Pod。这类问题虽然表现为“查询状态不正常”但根源在节点侧。只靠kubectl get是看不出来的必须到节点上看日志和资源数据。5. 完整定位复盘一次 “Pending 且 get 卡顿” 的处理过程这部分我拿前段时间处理过的一个模拟环境问题举个例子把整套排查流程串起来。5.1 现场现象和初始判断当时同事反馈某测试环境的kubectl get pods -n demo执行以后要卡十几秒才出结果而且不少 Pod 显示Pending业务容器实际上有些已经跑起来了。我的第一反应不是去 describe Pod而是先跑第 1.2 节里的两个命令kubectl cluster-info kubectl get --raw/healthz结果显示cluster-info正常healthz也返回ok。这说明从客户端到 API Server 的连接没有问题。于是问题被压缩到“API Server 能响应但查询类请求很慢”这个范围。接着执行kubectl get pods -A同样卡顿。我判断这不是某个 namespace 的问题而是整个 List 链路都慢。这种情况要么是 API Server 处理请求的性能问题要么是 etcd 读慢。5.2 逐步排查与关键证据先看 API Server 日志kubectl -n kube-system logs -l componentkube-apiserver --tail200 | grep -i etcd日志里出现大量etcdserver: request timed out。这说明问题不在 API Server 的 HTTP 处理层而在它读取 etcd 时超时。登录控制面节点执行df -h /var/lib/etcd iostat -x 1发现/var/lib/etcd所在分区使用率已经到了 98%iostat里的%util接近 100%。进一步查看发现是容器镜像 tar 包和日志文件把磁盘占满了导致 etcd 每次提交都要等磁盘 IO整个集群的状态更新和查询全部被拖慢。为了确认是不是 etcd 本身的问题我还在 etcd 容器里执行了endpoint health --cluster发现 etcd 成员之间网络是通的但健康检查的响应时间异常偏高基本坐实了存储磁盘是瓶颈。5.3 根因与恢复措施清理磁盘上无用的镜像包和旧日志同时给日志目录配置了轮转策略确保单个日志文件不会无限增长。清理后kubectl get pods -n demo恢复到了秒级返回Pod 的Pending状态也在分批恢复。这个案例给我的教训是不要因为健康检查是 ok 就认为集群没问题。healthz检查的是 API Server 进程本身而查询卡在它和 etcd 之间时健康检查可能依然是绿的。越是“健康检查绿、实际查询慢”的诡异现象越要往存储层查。6. 日常巡检和预防建议别等故障发生才看状态排查经验只能救火真正省事的是在问题发生前提前发现苗头。下面几个建议都是我踩坑之后加进日常巡检清单里的。6.1 建立几个关键监控指标至少盯住以下几个指标API Server 的请求延迟分布和错误码特别是超时和 429。etcd 的 leader 变化、db 大小、磁盘 fsync 延迟。这三项能提前暴露存储隐患。kubelet 的证书过期时间。证书过期不是一瞬间的事到期前 30 天就报警完全可以提前处理。节点状态和 Pod 状态的变化频率。如果某节点频繁 Ready/NotReady 抖动说明节点本身有问题。命令层面建议每天或每次变更后跑一遍kubectl get nodes kubectl get pods -A kubectl get --raw/healthz kubectl auth can-i list pods -A这一步相当于给集群做一次“体检”成本极低但能把大多数隐患暴露在用户报障之前。6.2 常见问题速查表下面这个表是我自己排查时常用的速查表症状、定位命令、常见原因和应对动作都放在一起遇到问题时直接对号入座。现象定位命令常见原因应对动作连接拒绝或超时kubectl cluster-info、curl -sk https://server:6443/healthzKubeconfig 地址错、防火墙不通、API Server 未监听修正 server 地址检查安全组重建 API Server证书相关报错kubectl config view --raw、openssl x509 -noout -dates客户端证书过期、CA 不匹配重新获取 Kubeconfig轮换证书返回 Forbiddenkubectl auth can-i list pods -n nsRBAC 角色缺失创建 Role / RoleBinding 并绑定用户命令卡住后超时API Server 日志、kubectl get --raw/metricsAPF 限流、etcd 慢降低客户端频率调整 APF优化 etcd节点 NotReadykubectl describe node、kubelet 日志kubelet 异常、CNI 故障、磁盘压力到节点查看日志恢复对应服务Pod 一直是 Pendingkubectl describe pod资源不足、调度失败、节点异常检查events、调度器日志、节点资源数据与业务不符kubectl get pod -o yamlkubelet 状态上报延迟、PLEG 卡住查看 kubelet 日志重启 kubelet版本警告kubectl versionkubectl 与 Server 版本差距过大将 kubectl 升级到受支持范围最后说一个我自己的习惯每次看到“查询状态不正常”不急着重启任何组件。先把kubectl cluster-info、kubectl get --raw/healthz、kubectl auth can-i --list跑一遍这三条命令分别告诉你连接、健康、权限三个层面。配置对了吗权限够吗数据源还活着吗顺序别搞反很多事故都是重启出来的。