ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

重启虚拟机后ArgoCD部分Pod状态检测失效:根因与修复

重启虚拟机后ArgoCD部分Pod状态检测失效:根因与修复 值班时的真实经历重启一台虚拟机后ArgoCD 管理的部分应用 Pod 直接失联了——说它们是 Pending 吧节点上根本没有 Pod 对象说它们被删了吧ArgoCD 页面里状态却停留在 Degraded / Unknown。更奇怪的是kubectl 查 K8s 集群一切正常资源也都在 Running但 ArgoCD 就是报检测不到状态。这种坑遇到过的人都知道不是 K8s 挂了而是 ArgoCD 自己的状态同步链路出了问题。这篇就拆解一次完整的重启虚拟机 - ArgoCD 部分 Pod 状态检测失败故障排查过程包括根因定位、止血手段、永久修复以及我总结的一套 SRE 智能运维排查模板适合正在维护 GitOps 生产环境、或者被 ArgoCD 状态刷新折磨过的朋友参考。1. 故障现场重启虚拟机后 ArgoCD 出现部分 Pod 状态盲区1.1 故障现象复盘先说背景。环境是 K8s 集群 ArgoCD 做 GitOps 应用发布ArgoCD 部署在独立命名空间业务应用分布在多个工作节点上。某次因底层宿主机 BIOS 参数变更需要重启一批虚拟机重启分批进行单台虚拟机对应一个 K8s 节点。重启完成后我照例打开 ArgoCD UI 检查应用同步状态发现一个严重情况ArgoCD 管理的 3 个应用显示异常。两个应用处于Degraded状态点进去看 Pod 列表部分 Pod 状态是Unknown。一个应用处于Missing状态ArgoCD 认为该应用对应的 Deployment 或 Pod从集群中消失了。但用kubectl get pods -n 业务命名空间查Pod 都还在状态是Running副本数正常。用kubectl get deployment查Deployment 也在和 Git 仓库里的期望状态一致。再查节点状态刚才重启的节点已经显示Ready。这里出现了一个核心矛盾K8s 集群视角一切正常ArgoCD 视角却认为资源丢失或状态未知。这就是典型的ArgoCD 状态检测链路失效故障。为什么 ArgoCD 会看不见其实还活着的 Pod因为 ArgoCD 工作方式不是每次打开 UI 都去实时 List API而是依赖 Application Controller 内部的缓存和事件监听机制。节点重启会引发大量 Pod 状态事件、节点状态变更、API Server 连接抖动一旦 Controller 的事件流处理出了问题缓存里的状态就凝固在故障时刻直到下次强制刷新。1.2 第一反应别急着删重建先确认检测不到的范围遇到这种事新手最容易犯的错是直接在 ArgoCD 里点Sync或者Hard Refresh甚至Delete再重建。如果 ArgoCD 误判Missing此时直接同步ArgoCD 会真的把集群中存在的资源干掉重新创建造成服务中断。实际第一反应应该是# 1. 确认节点状态 kubectl get nodes -o wide # 2. 确认 ArgoCD 管理的应用当前状态 argocd app list # 3. 对比 ArgoCD 看到的资源状态和集群真实状态 kubectl get deployment,statefulset,daemonset -n 业务命名空间这一步的目的是区分真丢失和假丢失。如果 kubectl 能看到资源正常ArgoCD 却显示 Missing那就是 ArgoCD 的感知链路出问题如果 kubectl 也看不到那就是 K8s 侧真的出问题了。本次故障属于前者。2. 先搞清 ArgoCD 状态检测的工作原理再排查2.1 ArgoCD 三大组件的职责分工要快速定位ArgoCD 检测不到 Pod 状态这类问题必须先理解 ArgoCD 的组件架构。ArgoCD 部署后主要跑几个核心组件组件职责argocd-server提供 UI/API是用户和 CI/CD 系统入口argocd-application-controller核心控制器负责对比 Git 期望状态和集群实际状态驱动 Syncargocd-repo-server拉取 Git 仓库内容生成 Helm/Kustomize 渲染后的 manifestargocd-redis缓存 ArgoCD 自身的状态数据应用列表、仓库状态、资源树等其中负责实时感知集群资源状态的是argocd-application-controller。这个控制器通过 K8s 的 Informer/Watch 机制监听集群资源变化并维护本地缓存。每次 Git 仓库有变更、集群资源有变更、或者用户手动刷新时Controller 才重新计算应用的健康状态和同步状态。ArgoCD检测不到 Pod 状态的直接原因几乎都和 application-controller 的缓存、监听器、计算循环有关。2.2 状态检测的完整链路从 kubelet 到 ArgoCD UI一条完整的 ArgoCD 状态显示链路是这样K8s 节点上的 kubelet 定期向 API Server 上报节点状态和 Pod 状态。ArgoCD application-controller 通过 K8s API 监听 Pod、Deployment 等资源变动事件。Controller 将变动写入本地缓存内存 Redis。Controller 对比 Git 期望清单和缓存的实际清单计算同步状态Synced/OutOfSync/Degraded。argocd-server 从 Redis/Controller 状态里读取结果展示到 UI。重启虚拟机后kubelet 会经历一个停止上报 - 节点 NotReady - kubelet 恢复 - 节点 Ready - Pod 重启/事件风暴的过程。在这个过程里argo-application-controller 的 Watch 连接可能出现短暂中断丢失部分事件。kubelet 重启时节点上的 Pod 会重新被 kubelet 识别产生大量的 Add/Update 事件。如果事件量过大Controller 的 Informer 队列可能积压或丢弃事件。缓存更新的时序一旦错乱ArgoCD 眼中的 Pod 列表和真实 K8s 里的 Pod 列表就出现偏差。由于大部分 Pod 的最终状态是Running和期望一致ArgoCD 不会频繁告警。只有涉及重启时被重新调度的 PodIP 变更、UID 变化或短暂进入 ContainerCreating/Unknown 的 PodArgoCD 的缓存没能正确更新才会卡在检测不到的状态上。2.3 排除一个常见误区不是所有 Pod 都走 ArgoCD 管理还要注意ArgoCD 管理的资源范围不是整个集群而是通过Application中的spec.source和spec.destination定义的。如果某几个 Pod 是 StatefulSet 有状态应用或者使用了automated sync策略ArgoCD 对其状态检测的敏感度和普通 Deployment 不一样。本次故障中部分 ArgoCD pod 状态检测不到恰好集中在两个 StatefulSet 和一个多副本 Deployment 上。StatefulSet 的 Pod 在节点重启后往往需要重建、重新挂载 PVC整个生命周期比普通 Deployment 长更容易在 ArgoCD 缓存里留下中间状态。3. 核心排查链路一步步定位根因3.1 第一步检查 ArgoCD 各组件自身健康状态先看 ArgoCD 自己有没有问题。kubectl get pods -n argocd正常应该看到 argocd-application-controller、argocd-server、argocd-repo-server、argocd-redis 都是 Running/Ready。如果 controller 处于 CrashLoopBackOff 或者反复重启那大概率是它自身的缓存出了问题。本次检查结果ArgoCD 所有 Pod 都正常但 argocd-application-controller 的 CPU 和内存占用比平时高出一倍日志里大量出现Failed to watch *v1.Pod和watch channel closed类记录。这说明 Watch 连接确实断过Controller 正在重建监听。常用排查命令# 查看 controller 日志 kubectl logs -n argocd deployment/argocd-application-controller --tail200 # 查看 Redis 连接和缓存状态 kubectl exec -n argocd deploy/argocd-redis -- redis-cli info memory这里要注意ArgoCD 的Failed to watch日志很多情况下不代表故障因为 K8s Watch 本来就可能因网络抖动、版本协商等原因重连。但如果伴随Unable to list和reflector错误再加上 UI 显示 Unknown/Missing就基本能确定是事件同步问题。3.2 第二步检查 Application 的 Sync 策略和 Refresh 机制接着看 ArgoCD Application 的配置。kubectl get application -n argocd kubectl get application 应用名 -n argocd -o yaml关注几个关键字段spec.syncPolicy.automated.selfHeal是否开启自动自愈。如果开启ArgoCD 检测到某个资源Missing时会自动重建。spec.syncPolicy.retry同步失败重试次数。status.operationState最近一次同步是否成功、卡在哪个阶段。metadata.annotations[argocd.argoproj.io/refresh]是否被强制刷新过。如果status.operationState.phase是Error或Running说明 Controller 一直在尝试同步但没成功。此次故障中3 个异常应用的operationState.phase都是Error错误信息都指向同一个问题failed to get job status: resource not found但实际 Job 资源还在集群里。这就是 Controller 缓存里的 Job 列表和真实状态不一致的铁证。3.3 第三步对比 ArgoCD 资源树和集群真实状态ArgoCD 提供了一个很实用的调试工具——argocd app resources能看到 ArgoCD 视角下每个资源的状态argocd app get 应用名 --refresh argocd app resources 应用名同时用 kubectl 拉取同一命名空间的真实资源kubectl get pods -n 业务命名空间 -o wide kubectl get statefulset -n 业务命名空间 -o wide两条命令一对比答案就浮出水面了ArgoCD 显示某个 PodMissing但 kubectl 显示该 PodRunning并且kubectl get pod 名称 -o yaml能看到 Pod 的uid和 ArgoCD 缓存的 uid 不一致。ArgoCD 显示某个 PodUnknown但 kubectl 显示 PodRunning节点Readykubectl get events显示 Pod 最近有一次容器重建。结论是重启节点后部分 Pod 经历了Evicted - 重新调度 - 新节点启动或原地重启Pod UID 变了。ArgoCD 的缓存里还记录着旧 UID 的 Pod新 UID 的 Pod 没有及时进入它的缓存列表导致 ArgoCD 认为旧的 Pod 没了新的 Pod 不在期望列表里。其实 K8s API 的 Watch 事件是支持 Pod 替换的但 ArgoCD Controller 在节点重启期间自身也在经历 Leader Election、缓存重建事件处理顺序错位后某个 Pod 的 ADDED 事件在 DELETED 事件之前到达就会触发Failed to reconcile该资源被标记为 Unknown。3.4 第四步翻 Controller 日志确认事件流断裂的时间点为了验证上面的判断我去了日志里找具体时间线。kubectl logs -n argocd deployment/argocd-application-controller --since-time2025-01-20T10:00:00Z | grep -E 重建|Pod|Application日志显示在节点重启后约 3~5 分钟内Controller 大量打印time... levelwarning msgFailed to watch *v1.Pod: too old resource version time... levelerror msgFailed to list *v1.Pod: resource version too oldtoo old resource version意味着 Controller 的 Informer 缓存落后于 API Server 当前版本太多API Server 认为 Controller 请求的 resourceVersion 已过期要求它重新 List 全部数据。正常 List 没问题但如果 List 过程中部分 Pod 被删除或重建List 返回的列表和实时状态之间会出现窗口期。也就是说不是 ArgoCD 坏了而是 Controller 在节点重启后重新同步集群全量资源时错过了部分 Pod 的状态更新事件。所以显示 Unknown/Missing纯粹是缓存还没追上现实。这个案例给 SRE 的一个启示是resource version too old不只是 ArgoCD 独有任何基于 Informer 的组件比如自定义 Operator、Ingress Controller都可能遇到。排查时不要总怀疑组件代码 bug先看日志有没有这类重同步提示。4. 根因确认后的止血与修复方案4.1 止血方案一Hard Refresh 强制刷新状态确认是缓存滞后问题后操作就简单了。第一步做 Hard Refreshargocd app get 应用名 --hard-refreshHard Refresh 会绕过 ArgoCD 的缓存直接从 K8s API 拉取真实资源清单重置资源树。执行后观察状态如果是纯缓存问题状态会立即恢复为Synced和Healthy。如果部分资源还是Degraded说明该资源本身真的有问题再继续排查资源细节。本次执行后标为Unknown的 Pod 恢复正常Missing的 Deployment 也恢复了。但有一个 StatefulSet 仍然报Degraded原因不是缓存了而是其 Pod 确实发生了 IP 变化ArgoCD 里旧 IP 记录没清掉。此时在应用详情里点击一下单个资源节点选择Refresh把资源状态树重置即可。Hard Refresh是 ArgoCD 提供的最直接的状态修复手段等价于告诉 Controller别信缓存重新去集群里数一遍资源。它不会触发 Sync不会修改任何资源非常安全。注意Hard Refresh 不等于 Sync。Hard Refresh 只刷新状态展示Sync 才会真正改动集群资源。遇到状态异常先 Refresh 再考虑 Sync不要反向操作。4.2 止血方案二手动删除异常的 ArgoCD Application 缓存如果 Hard Refresh 没解决或者 Controller 反复 Crash还有一招删掉 ArgoCD Application 的缓存条目让 Controller 重新全量计算。# 方式一直接删除 Application然后重新创建风险较高务必先备份 yaml kubectl get app 应用名 -n argocd -o yaml 应用名-backup.yaml kubectl delete app 应用名 -n argocd --waitfalse kubectl apply -f 应用名-backup.yaml但更推荐的方式是只清理 Redis 缓存。ArgoCD 将应用的资源树、健康状态缓存在 argocd-redis 中清掉后 Controller 会自动重新计算kubectl exec -n argocd deploy/argocd-redis -- redis-cli KEYS proj* | wc -l # 精确删除某个 app 的缓存键名通常是 proj:项目名:应用名 或 app:应用名 kubectl exec -n argocd deploy/argocd-redis -- redis-cli --scan --pattern *应用名* | xargs kubectl exec -n argocd deploy/argocd-redis -- redis-cli DEL执行后 Application Controller 会短时间高负载重新计算所有应用状态属正常现象无需干预。这个方法适合 Hard Refresh 无效的硬核场景比如 Redis 内部数据错乱、多个 Application 同时出现状态盲区。但要注意清理 Redis 属于重启类操作会在短时间内影响 ArgoCD UI 的访问速度建议在低峰期执行。4.3 永久修复让 ArgoCD Controller 更抗节点重启这类事件风暴止血之后得做永久修复否则下次重启虚拟机还会复发。核心目标是增强 ArgoCD Controller 对节点重启、Pod 重建事件的容忍度。第一项调整是提高 Controller 的--kubectl-parallelism-limit和--status-processors。ArgoCD Application Controller 默认只用几个 Processor 并发处理Application 状态更新。节点重启期间大量 Pod 状态变更会塞满 Processor导致状态计算排队、滞后。可以调高# argocd-application-controller 启动参数 --status-processors25 --operation-processors25 --kubectl-parallelism-limit20具体数值根据集群规模来。小集群100个应用用默认值也能过但大集群500个应用建议至少翻倍。可以在 argocd 的 ConfigMapargocd-cmd-params-cm中配置apiVersion: v1 kind: ConfigMap metadata: name: argocd-cmd-params-cm namespace: argocd data: controller.status.processors: 25 controller.operation.processors: 25改完后重启 argocd-application-controller 让其生效。第二项调整是给 Controller 配置足够的资源 limits。Node 重启引发的事件风暴会消耗大量内存Controller 如果 OOM Kill重启期间又会丢失事件。建议给 controller 的 requests 和 limits 至少配到resources: requests: cpu: 500m memory: 512Mi limits: cpu: 2 memory: 2Gi第三项开启 Redis 持久化。ArgoCD 的 Redis 默认不持久化重启后缓存全丢Controller 不得不全量重新 List 集群资源。这个其实对状态检测不到影响不大但能加快恢复。第四项配合 K8s 节点优雅重启。如果是主动运维场景不是意外宕机在重启节点前先对要重启的节点上的 Pod 做kubectl drain这样 K8s 会先驱逐 Pod再把 Pod 调度到其他节点状态变化是平滑迁移而非突然消失 重建ArgoCD 缓存也就不会出现盲区。drain操作在 K8s 里是最能减少 GitOps 工具状态误判的手段。很多 SRE 图省事直接重启虚拟机结果 kubelet 异常下线Pod 被强制 kIllArgoCD、Prometheus、各种 Operator 都会产生一堆误报警。5. 常见问题排查表与避坑经验5.1 高频问题速查表现象可能原因优先操作ArgoCD 显示 Pod Unknownkubectl 显示 RunningController 缓存滞后 / Watch 事件丢失argocd app get 应用名 --hard-refreshArgoCD 显示 Deployment Missingkubectl 存在UID 变化 / 缓存未更新先 Hard Refresh再检查是否 Pod 重建导致Controller 日志大量too old resource versionInformer 重建事件流窗口调高 status-processors实在不行重启 ControllerArgoCD 显示 Sync 成功但 Pod 一直 CrashLoop资源真的异常与缓存无关直接看kubectl logs 对应PodApplication 状态 ErroroperationState.phaseRunningSync 卡死 / 资源冲突查看 operationState 错误信息必要时取消 SyncHard Refresh 卡住不动Controller 过载查看 Controller CPU/内存扩容后重试5.2 几个必须记住的避坑经验第一个坑别在生产环境直接点 Sync 去修复状态异常。如果 ArgoCD 因为缓存问题显示 Missing你点 SyncArgoCD 会把集群中真实存在的资源当作多余删掉再按 Git 重建。整个过程会引发服务中断。状态异常时先 Hard Refresh观察资源树恢复情况再决定要不要 Sync。第二个坑Hard Refresh 和普通 Refresh 的区别要分清。普通 Refresh 只是触发一次 Git 仓库重新拉取和状态重新计算缓存还在Hard Refresh 是跳过缓存直接从集群 List 全量资源。遇到节点重启后状态不对的情况直接上 Hard Refresh省时间。第三个坑看日志时不要只看error级别。这次故障里最有价值的日志其实是warning级的Failed to watch *v1.Pod: too old resource version。很多 SRE 喜欢grep ERROR结果把真正线索漏掉了。日志排查时建议同时看 warning 和 error 两个级别尤其涉及 K8s Watch 机制时。第四个坑不要小看 ArgoCD Redis 的持久化配置。ArgoCD 的 Redis 默认无密码、无持久化。如果 ArgoCD 部署在节点重启环境中Redis 重建会导致大量缓存缺失Controller 全量重算此时 UI 卡顿、状态瞬时异常都很正常。建议至少在 ConfigMap 里开启--redis-property相关参数或者用外部 Redis。6. 用 SRE 智能运维手段让这类故障不再反复6.1 建立 GitOps 集群健康巡检自动化这次故障之后我在巡检系统里加了两个专项检查项用来提前发现 ArgoCD 状态缓存异常第一个是ArgoCD Application 状态与 K8s 集群真实资源的一致性检查。写一个巡检脚本每天定时执行kubectl get application -n argocd -o json | jq -r .items[] | [.metadata.name, .status.sync.status, .status.health.status] | tsv kubectl get pods -A | grep -v Running如果发现 ArgoCD 报 Degraded/Missing但 kubectl 侧没有对应异常立刻告警让人介入处理。这个检查能提前揪出缓存盲区。第二个是Controller Watch 断裂的监控指标。ArgoCD Controller 暴露 Prometheus metrics其中controller_app_info、argocd_app_k8s_resource_status等指标可以配合 Grafana 做状态趋势监控。更直接的是在日志里增加一个告警规则匹配too old resource version和failed to watch一旦出现持续多次自动触发 Webhook 通知。数字化运维不只体现在故障后自动重启更体现在自动识别状态不一致。脚本化的巡检 日志告警能把这次经历的手动排查固化成自动化能力。6.2 节点重启前增加预检查的 SRE 操作规范给团队定了个硬性规定重启虚拟机之前必须执行以下预检查kubectl drain 节点名 --ignore-daemonsets --delete-emptydir-data把 Pod 平滑迁移走。确认 ArgoCD 所有 Application 同步状态正常。记录当前 ArgoCD 所有应用的状态快照方便重启后对比。维护 ArgoCD Controller 的资源余量至少 30% 的 CPU 空闲用于应对事件风暴。这是完全免费的改进只是把运维动作规范化。很多重启后出问题的情况都源于不 drain 直接重启虚拟机。kubelet 强制下线K8s 对节点上的 Pod 执行 Pod 删除操作但 ArgoCD Controller 此时可能正在处理其他事件根本来不及记录状态变化最终表现为状态盲区。如果线上环境真的没法 drain比如节点上存在非容忍的 local-storage 数据至少要在重启前手动标记节点为不可调度kubectl cordon降低 Pod 调度扰动。6.3 告警分级把 ArgoCD 状态异常按严重程度分流把 ArgoCD 状态告警做成三级分流能避免夜间误报警把人折腾死严重级别判断条件响应方式P1ArgoCD 显示 Missing kubectl 确认资源确实消失立即响应执行 Sync 重建P2ArgoCD 显示 Degraded/Unknown kubectl 资源正常进入审批池触发 Hard Refresh 并观察30分钟未恢复则人工介入P3ArgoCD 显示 OutOfSync 集群资源正常记录事件工作时间处理这套分级核心逻辑是ArgoCD 是控制面K8s 是数据面两边状态不一致时以 K8s 数据面为准。只要资源还活着就不要急着动手删改。ArgoCD 状态异常只是控制面近视的表现先验光Hard Refresh再决定要不要手术Sync。7. 智能运维视角的实战心得ArgoCD 状态检测问题排查到现在我个人最深的体会是GitOps 工具的状态展示本质上是基于缓存的快照而不是实时真相。任何基于 Informer 的组件的状态都是最终一致的存在时间窗口很正常。SRE 在做智能运维时不要被控制面的状态牵着走要建立数据面验证控制面的思维。具体到这次案例Hard Refresh是对付缓存盲区的万能钥匙kubectl drain是预防节点重启故障的护身符argocd-application-controller的资源裕量是抵御事件风暴的缓冲垫。最后再分享一个小技巧ArgoCD 有个不太引人注意但很好用的参数argocd.argoproj.io/refresh注解。你可以直接给 Application 打这个注解来触发刷新比命令行工具更精准还能在 CI 流水线里用kubectl annotate app 应用名 argocd.argoproj.io/refreshhard --overwrite远程强制刷新。把它写进故障自愈的自动化流程里比任何人工操作都稳。
返回列表