跳转到内容

ops

starlightBlog.tags.count

完全卸载 ArgoCD:级联删除与集群环境重置

核心概念:级联删除 (Cascading Deletion)

Section titled “核心概念:级联删除 (Cascading Deletion)”

在 GitOps 架构中,如果需要彻底重置测试环境或完全弃用当前的部署方案,我们需要将 ArgoCD 及其部署的所有业务应用一并清理。

ArgoCD 在创建 Application 资源时,默认会注入 resources-finalizer.argocd.argoproj.io 终结器 (Finalizer)。当执行删除操作时,ArgoCD 控制器会根据该终结器,主动向集群发送指令,依次删除该应用所关联的全部底层资源(如 Deployment、Service、ConfigMap 等)。这就是级联删除

为了实现彻底卸载,我们必须保留这些终结器,并让 ArgoCD 控制器先完成全部业务资源的回收,然后再卸载 ArgoCD 自身组件。


步骤 1:触发业务资源的级联删除

Section titled “步骤 1:触发业务资源的级联删除”

首先,直接删除 argocd 命名空间下的所有 Application 资源。 警告:此操作不可逆。执行后,由 ArgoCD 管理的所有实际业务(如 Portainer、数据库等集群应用)将立即开始停止并被销毁。

Terminal window
kubectl delete applications --all -n argocd

由于涉及大量底层资源的终止与销毁,该过程可能需要数分钟。通过以下命令实时监控 Application 的状态:

Terminal window
kubectl get applications -n argocd -w

当命令输出为空,或提示 No resources found in argocd namespace. 时,表明所有被托管的业务资源已彻底清理完毕。

业务资源清理完成后,使用当初部署时相同的官方清单反向卸载 ArgoCD 控制器及相关组件:

Terminal window
kubectl delete -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

步骤 4:清理底层全局资源 (CRD 与命名空间)

Section titled “步骤 4:清理底层全局资源 (CRD 与命名空间)”

最后,删除遗留的自定义资源定义 (CRD) 和专属命名空间。

1. 删除专属 CRD: (注意:如果集群内还有其他命名空间在使用 ArgoCD,请勿执行此步骤)

Terminal window
kubectl delete crd applications.argoproj.io applicationsets.argoproj.io appprojects.argoproj.io 2>/dev/null

2. 删除命名空间:

Terminal window
kubectl delete namespace argocd

如果在上述流程中遇到命令卡死或资源无法删除的情况,请参考以下处理方案:

如果 kubectl delete applications 长时间无响应,通常是因为某些底层资源(如持久化存储卷)无法正常释放,导致 ArgoCD 控制器无法完成终结器逻辑。 可以通过强制移除 Finalizer 来跳过等待:

Terminal window
kubectl get apps -n argocd -o name | xargs -I {} kubectl patch {} -n argocd -p '{"metadata": {"finalizers": null}}' --type merge

如果最后一步删除 argocd 命名空间时一直卡在 Terminating 状态,说明 Kubernetes API 垃圾回收遭遇死锁。可使用 API 强制注销指令直接清除(需确保已安装 jq 工具):

Terminal window
kubectl get namespace argocd -o json | \
jq '.spec.finalizers = []' | \
kubectl replace --raw "/api/v1/namespaces/argocd/finalize" -f -

完成以上步骤后,执行 kubectl get all -A,确认集群内已无相关组件残留,集群状态重置完成。

跑路但不拆楼:如何安全卸载 ArgoCD 且保留所有业务应用

🏗️ 核心概念:非级联删除 (Non-Cascading Deletion)

Section titled “🏗️ 核心概念:非级联删除 (Non-Cascading Deletion)”

在赛博堡垒中,有时我们只想重装或卸载 ArgoCD 本身(比如迁移 GitOps 工具,或者清理测试环境),但绝不希望它把正在运行的业务应用(比如我们的存储大坝、可视化面板)一起删掉。

ArgoCD 在部署应用时,默认会给资源打上一个叫 resources-finalizer.argocd.argoproj.io 的终结器(相当于一根引爆线)。如果你直接执行 kubectl delete namespace argocd,它会顺着这根线把底层真实的 Pod 和 Service 全部“爆破”掉,甚至会导致整个命名空间卡死在 Terminating 状态。

要做到**“监工跑路,大楼照常营业”**,我们必须在拆除指挥中心前,偷偷剪断所有的引爆线。


✂️ 无损拆除流程 (Safe Teardown Steps)

Section titled “✂️ 无损拆除流程 (Safe Teardown Steps)”

为了绝对的安全,我们将采用**“暗杀级”**施工法:先切断监工的大脑,再从容拆除。

步骤 1:瘫痪指挥中心大脑 (停止控制器)

Section titled “步骤 1:瘫痪指挥中心大脑 (停止控制器)”

这是应对复杂 GitOps 架构最关键的一步。我们必须先把 ArgoCD 的应用控制器副本数缩容为 0,让其失去对集群状态的监听和自愈能力。

Terminal window
kubectl scale statefulset argocd-application-controller -n argocd --replicas=0

(💡 监理提示:执行后,ArgoCD 变成了“瞎子和聋子”,绝对不会再产生任何反扑动作。)

步骤 2:从容剪断所有应用的“引爆线”

Section titled “步骤 2:从容剪断所有应用的“引爆线””

大脑死机后,我们就可以毫无顾忌地强制抹除所有 Application 对象的 Finalizer。

打开终端,执行这行“批量剪线”脚本:

Terminal window
kubectl get apps -n argocd -o name | xargs -I {} kubectl patch {} -n argocd -p '{"metadata": {"finalizers": null}}' --type merge

(💡 监理提示:执行完后,这些应用就已经彻底脱离 ArgoCD 的“同归于尽”机制了。)

步骤 3:销毁 GitOps 档案 (删除 Application 对象)

Section titled “步骤 3:销毁 GitOps 档案 (删除 Application 对象)”

既然引爆线已经没了,且没有控制器来阻挠,我们现在就可以安全地删掉 ArgoCD 里的那些“项目档案”了。

Terminal window
kubectl delete apps --all -n argocd

这条命令会瞬间执行完毕,而你集群里的真实业务应用完全不会受到任何影响,它们已经变成了 K8s 的原生脱管应用。

步骤 4:拆除主体建筑 (反向执行安装脚本)

Section titled “步骤 4:拆除主体建筑 (反向执行安装脚本)”

清空了档案后,我们就可以按图索骥,用当初浇筑时的官方图纸进行反向拆除,把 ArgoCD 的 Redis 缓存、API 服务等组件清理掉:

Terminal window
kubectl delete -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

步骤 5:深度清理残骸 (删除 CRD 与命名空间)

Section titled “步骤 5:深度清理残骸 (删除 CRD 与命名空间)”

最后一步,把划拨给 ArgoCD 的专属园区,以及它留在 K8s 底层的那些自定义资源定义(CRD)连根拔起。

1. 彻底粉碎专属 CRD: (⚠️ 警告:这会删除全集群所有命名空间下的 ArgoCD 相关资源,确保你没有其他的 ArgoCD 实例在使用它们!)

Terminal window
kubectl delete crd applications.argoproj.io appprojects.argoproj.io applicationsets.argoproj.io

2. 抹平命名空间:

Terminal window
kubectl delete namespace argocd

🧰 进阶排障:应对“强拆”中的钉子户 (Troubleshooting)

Section titled “🧰 进阶排障:应对“强拆”中的钉子户 (Troubleshooting)”

在实际的集群拆迁中,K8s 的垃圾回收机制偶尔会发生死锁。如果你在执行上述步骤时卡住了,请使用以下“重火力”工程手段。

1. 漏网之鱼:AppProject 的终结器卡死

Section titled “1. 漏网之鱼:AppProject 的终结器卡死”

除了 Application,ArgoCD 的“项目对象” (AppProject) 也带有防删机制。如果你发现删除一直卡住,请再补上这一刀:

Terminal window
# 剪断所有项目的引爆线
kubectl get appprojects -n argocd -o name | xargs -I {} kubectl patch {} -n argocd --type json -p='[{"op": "remove", "path": "/metadata/finalizers"}]' 2>/dev/null

2. 清扫游荡的幽灵:全局权限残留

Section titled “2. 清扫游荡的幽灵:全局权限残留”

ArgoCD 拥有控制整个集群的极高权限,这意味着它创建的某些 ClusterRole 是不受命名空间限制的。拆除后,我们可以用标签(Label)进行一次全局扫荡:

Terminal window
kubectl delete clusterrole -l app.kubernetes.io/part-of=argocd 2>/dev/null
kubectl delete clusterrolebinding -l app.kubernetes.io/part-of=argocd 2>/dev/null

3. 终极黑魔法:强杀卡死的 Namespace

Section titled “3. 终极黑魔法:强杀卡死的 Namespace”

如果所有的图纸都已经清理完毕,但最后执行 kubectl delete namespace argocd 时依然无限卡在 Terminating 状态。这意味着 K8s 底层的 API Server 陷入了死循环,通常是因为残留的无法解析的资源阻塞了垃圾回收。

请掏出这段极其危险但也极其有效的 API 强制注销指令(需要宿主机已安装 jq 工具):

Terminal window
kubectl get namespace argocd -o json | \
jq '.spec.finalizers = []' | \
kubectl replace --raw "/api/v1/namespaces/argocd/finalize" -f -

这条指令会直接绕过正常的垃圾回收流程,粗暴地把该园区的终结器全部清空,Namespace 瞬间就会灰飞烟灭!


至此,ArgoCD 已经被你彻底从集群中抹除,你可以使用 kubectl get pods -A 巡查一圈。

你会发现,argocd 命名空间已经彻底消失,且没有任何幽灵权限残留。但你之前通过它部署的真实业务,依然在各自的命名空间里稳健运行。现在,你可以随时重新安装一个崭新的 ArgoCD,再把它们重新“收编”回流水线中!

赛博赶羊战术:不改 YAML 强制定向调度 Pod

在赛博堡垒的日常巡检中,我们偶尔会萌生一些“非分之想”:我想把正在运行的 Headlamp 面板,强行从 01 节点挪到 03 节点上去。

按照正规军的做法,我们应该去修改 Deployment 的 YAML 文件,加上 nodeSelector 或节点亲和性。但如果这仅仅是一次临时测试,为了这么点小事去改核心图纸,未免太兴师动众。如果给 0102 节点打 NoSchedule 污点,又会像“核弹打蚊子”一样误伤集群里的其他无辜业务。

今天,包工头教你一招极客圈里经典的“障眼法”——利用节点封锁 (Cordon) 机制的“赛博赶羊战术”


🐑 战术核心:关上多余的门,只留一条路

Section titled “🐑 战术核心:关上多余的门,只留一条路”

Kubernetes 提供了一个专门用于节点临时维护的命令:kubectl cordon(封锁)。 当节点被 Cordon 后,它会被打上 SchedulingDisabled 的标记。这就像给节点挂上了“暂停营业”的牌子:已经在里面吃饭的顾客(运行中的 Pod)不受影响,但绝对不接待新客(新创建的 Pod 无法调度进来)。

我们的战术逻辑极其简单:把不想去的节点全封锁,然后把 Pod 杀掉让它重生。K8s 大脑环顾四周发现只有一台节点“开着门”,就只能乖乖把 Pod 丢进去。


假设我们要把 kube-system 命名空间下的 Headlamp 强行赶到 k3s-master-03 节点。

第一步:封锁非目标节点 (关门)

Section titled “第一步:封锁非目标节点 (关门)”

我们要逼迫目标去 03,所以先把 0102 的大门焊死:

Terminal window
kubectl cordon k3s-master-01 k3s-master-02

验证:此时敲击 kubectl get nodes,你会看到 0102 的 STATUS 变成了 Ready,SchedulingDisabled

第二步:击杀当前 Pod (放狗咬羊)

Section titled “第二步:击杀当前 Pod (放狗咬羊)”

直接删掉当前正在运行的 Headlamp Pod。由于它是由 Deployment 管理的,K8s 会瞬间拉起一个新的替代品。

Terminal window
kubectl delete pod -l app.kubernetes.io/name=headlamp -n kube-system

去看看新重生的 Headlamp 落在哪了:

Terminal window
kubectl get pods -n kube-system -o wide | grep headlamp

因为大脑别无选择,你会发现新的 Pod 已经精准无误地降落在了 k3s-master-03 上!微操成功!

第四步:光速解除封锁 (极其重要!)

Section titled “第四步:光速解除封锁 (极其重要!)”

羊已经进圈了,赶紧把 0102 的大门重新打开,否则你后续部署的任何新业务都会因为找不到节点而卡在 Pending 状态。

Terminal window
kubectl uncordon k3s-master-01 k3s-master-02

验证:再次 kubectl get nodesSchedulingDisabled 标记消失,集群恢复常态。


虽然这套连招玩起来行云流水,极其酷炫,但我必须提醒你:它终究是一张“体验卡”。

Kubernetes 的核心信仰是声明式架构(Declarative)。Cordon 大法只是一次性欺骗了调度器。如果明天 03 节点重启了,或者 Headlamp 意外崩溃了,K8s 大脑在重新调度时,面对三扇全开的大门,依然会随机把 Headlamp 扔回 0102。系统并没有记住你的真实意图。

总结:

  • 临时测试、排查干扰:用 Cordon 大法,干净利落。
  • 永久固化、生产要求:请务必遵守纪律,老老实实去写 nodeSelectornodeAffinity。这才是让 Tech Fortress 坚不可摧的终极规范。