来源: DevOpSec公众号 作者: DevOpSec
背景
工作需要经常执行部署升级k8s资源,偶尔会遇到kubectl delete
k8s资源卡主,资源状态处于Terminating
状态。
这是为什么呢?下面我们来看看执行kubectl delete
操作命令的执行过程,了解其原理后我们具体实践一下。
delete原理
kubectl delete
是 Kubernetes 命令行工具(kubectl)提供的一个命令,用于删除 Kubernetes 集群中的资源对象。当您运行 kubectl delete
命令时,它会与 Kubernetes API 服务器通信,并按照指定的删除选项和参数来执行删除操作。
kubectl delete
命令执行原理:
建立与 Kubernetes API 服务器的连接:kubectl 命令与 Kubernetes API 服务器建立连接,使用配置文件中指定的 API 服务器地址和认证凭据进行身份验证。
解析命令参数和选项:kubectl 解析命令行中指定的资源类型、名称、命名空间和其他选项。例如,
kubectl delete pod my-pod -n my-namespace
命令将解析为删除名为 my-pod 的 Pod 对象,位于my-namespace
命名空间下。发送删除请求:kubectl 构建一个删除请求,其中包含要删除的资源的相关信息。它将这个请求发送到 Kubernetes API 服务器的相应端点(例如
/api/v1/namespaces/my-namespace/pods/my-pod
)。鉴权和认证:API 服务器接收到删除请求后,会进行鉴权和认证验证,以确保执行该操作的用户具有足够的权限。
处理删除请求:API 服务器根据请求中的信息,找到要删除的资源对象,并根据指定的删除选项执行相应的操作。删除选项可以是立即删除、先进行软删除(即将对象标记为 "Terminating")并等待终结器处理完成后再删除,或者是执行一些预定义的清理逻辑。
更新资源状态:如果资源对象成功删除,API 服务器将更新集群中的状态,以反映该资源的删除操作。
返回结果:API 服务器将删除操作的结果返回给 kubectl 命令行工具。
输出结果:kubectl 命令行工具将接收到的结果输出到终端,以供用户查看。这可能包括成功删除的资源信息、删除错误的错误消息等。
从上面执行步骤上来看,阻止删除的步骤出现在第五步,为了保护资源不被删除或者优雅的删除,会设计Finalizers
终结器,终结器为执行介绍资源不会被删掉。
通过kubectl delete
删不掉的资源通过kubectl delete --grace-period=0 --force
进行尝试
--grace-period=0
:这个选项指定了删除资源的优雅期(grace period)。优雅期是一个时间段,用于等待资源对象自行终止和清理,以便在删除之前完成相关的关闭和清理操作。通过将 --grace-period 设置为 0,表示不等待优雅期,而是立即删除资源。
--force
:这个选项用于强制删除资源对象,即忽略任何可能存在的依赖关系和终止过程。使用 --force 选项会立即删除资源对象,而不会等待资源进行清理或终止操作。
注意:
--force
选项只会强制删除资源对象本身,但不会跳过 finalizers 的执行。当执行删除命令时,Kubernetes API Server 会检查资源对象是否具有 finalizers,并在删除之前确保执行相应的操作。因此,即使使用 --force 选项,也无法绕过 finalizers 的逻辑。
后面实践会介绍跳过finalizers
的办法和万能删除资源的方法。
下面先介绍一下Owner References
和Finalizers
终结器,让我们进一步理解他们怎么影响delete操作的。
Owner References 和 Finalizers 介绍
在 Kubernetes 中,Owner References 和 Finalizers 是两个关键的机制,用于管理资源之间的所有权和确保资源的安全删除。
Owner References(所有者引用):Owner References 是一种机制,用于建立资源之间的所有权关系。通过在一个资源对象中设置 Owner References,您可以指定该资源拥有另一个资源,并建立它们之间的父子关系。当一个资源是另一个资源的所有者时,它将负责管理其子资源的生命周期。例如,当一个 Deployment 对象是一个 Pod 对象的所有者时,删除 Deployment 对象将自动删除其关联的 Pod 对象。
管理关联资源的生命周期:通过设置 Owner References,您可以管理资源之间的父子关系,并确保当父资源删除时,与之相关的子资源也会被删除。
Finalizers(终结器):Finalizers 是一种机制,用于控制资源删除的过程。当资源对象被删除时,Kubernetes 控制器将检查该资源的 Finalizers 列表。如果资源具有 Finalizers,则它们表示在删除资源之前必须执行的特定操作或条件。只有当资源的 Finalizers 列表为空时,Kubernetes 才会真正删除该资源。这个机制可以确保在资源删除之前完成一些清理工作,比如释放相关的资源或发送通知。
执行资源删除前的清理工作:通过添加 Finalizers,您可以确保在资源删除之前执行特定的清理工作。例如,您可以释放与资源关联的存储卷或发送通知,以确保资源的安全删除。
介绍完枯燥原理和两种机制,下面我们来看一看愉快的实践部分。
实践
1. 删除pod,删除不了
Owner References
的例子:执行kubectl delete pod podname -n my-namespace
删除pod后又创建了一个pod,这pod死活删不掉,删除又从新创建
查看一下pod的describe
, kubectl -n my-namespace describe pod podname
Name: podname
Namespace: release
Priority: 0
Service Account: default
Node: 192.168.1.18
Start Time: Wed, 24 May 2023 15:21:13 +0800
Labels: app.kubernetes.io/name=xxxx
rollouts-pod-template-hash=7897c56fd9
Annotations: cni.projectcalico.org/containerID: 876ba395a9631524d0c465bcfef0de4420e06e190927431c55cda271fe1cb128
cni.projectcalico.org/podIP: 10.233.97.25/32
cni.projectcalico.org/podIPs: 10.233.97.25/32
Status: Running
IP: 10.234.97.25
IPs:
IP: 10.234.97.25
Controlled By: ReplicaSet/xxxx-7897c56fd9
...
可以看到Controlled By
ReplicaSet/xxxx-7897c56fd9
,这个pod是被RS xxxx-7897c56fd9
创建出来的,他的Owner References
是RS
那我们再看看这个RS资源是谁创建的kubectl describe -n my-namespace rs xxxx-7897c56fd9
Name: xxxx-7897c56fd9
Namespace: release
Selector: app.kubernetes.io/name=xxxx,rollouts-pod-template-hash=7897c56fd9
Labels: app.kubernetes.io/name=xxxx
rollouts-pod-template-hash=7897c56fd9
Annotations: rollout.argoproj.io/desired-replicas: 3
rollout.argoproj.io/revision: 9
Controlled By: Rollout/xxxx
...
到这里就水落石出了,这个RS xxxx-7897c56fd9
是由Rollout xxxx
定义的。argo Rollout 是定义pod的workload,kubectl describe -n my-namespace rollout xxxx
它没有被其他控制器控制。
所以删除rollout资源既可以把pod删除kubectl -n my-namespace delete rollout xxxx
。
看到这里相信你对rollout控制器又加深了一点印象,在rollout资源文件里定义了replicas: 3
期望的pod副本是3,实际运行中如果副本低于3,控制器就会拉起新的pod使其满足yaml文件里定期的期望数量。
2. 删除svc,删除不了
正常删除
kubectl delete svc -n my-namespace mysvc
回车后,命令卡主
强制删除试试
kubectl delete svc n my-namespace mysvc --grace-period=0 --force
Warning: Immediate deletion does not wait for confirmation that the running resource has been terminated. The resource may continue to run on the cluster indefinitely.
service "mysvc" force deleted 悲剧的是,命令也是卡主,删除不成功。
执行describe查看一下svc如下
kubectl describe svc -n my-namespace mysvc
apiVersion: v1
kind: Service
metadata:
annotations:
eip.openelb.kubesphere.io/v1alpha2: eip-pool
....
lb.kubesphere.io/v1alpha1: openelb
protocol.openelb.kubesphere.io/v1alpha1: layer2
creationTimestamp: "2023-05-31T03:23:08Z"
finalizers:
- finalizer.lb.kubesphere.io/v1alpha1
labels:
app.kubernetes.io/name: mysvc
name: mysvc
namespace: release
resourceVersion: "14704212"
uid: 1644e3bc-f6bd-4e6e-a911-512ce30321ae
我们发现svc有finalizers,是finalizers阻止了删除svc
finalizers:
- finalizer.lb.kubesphere.io/v1alpha1
导致finalizers执行不成功的原因是已经删除了finalizers执行依赖的资源,执行删除svc时finalizers的操作不满足条件,阻塞了删除。
解决办法:
kubectl edit svc -n my-namespace mysvc
删除下面的两行
finalizers:
- finalizer.lb.kubesphere.io/v1alpha1
再次执行删除svc,这时候你发现mysvc
已经成功删除。
注意:这样可以绕过 finalizers,并强制删除资源对象。在跳过 finalizers 的情况下,可能会导致一些未完成的操作或资源泄漏。确保在执行这样的操作之前,对其可能产生的后果有充分的了解,并谨慎操作。
作者这里出现是因为卸载openelb
后,finalizers执行依赖的资源又依赖我卸载的openelb
,所有绕过 finalizers,并强制删除资源对象,是没问题的。
3. 删除namespace,删除不了
执行kubectl delete ns my-namespace
卡半天,查看ns状态一直是Terminating。
kubectl get all -n my-namespace
显示没有被删除的资源。
执行kubectl delete namespace my-namespace --grace-period=0 --force
也无济于事。
kubectl get ns my-namespace -o yaml
查看也没有Finalizers
阻止。
是不是我们还有资源在my-namespace
里没有删掉呢?我们通过下面命令查看一下该namespace里所有资源
kubectl api-resources --verbs=list --namespaced -o name|xargs -n1 kubectl get --show-kind --ignore-not-found -n y-namespace
通过上面命令执行我们发现该namespace还有很多资源,逐一删除上面显示资源后,my-namespace
被删除成功。
4. 万能删除法
这个是粗暴的解决办法,轻易不要使用,直接操作etcd,不会考虑k8s资源是否释放。
在etcd节点上执行:
删除ns my-namespace
etcdctl del /registry/namespaces/my-namespace
删除pod test-test-0
etcdctl del /registry/pods/my-namespace/test-test-0
注:etcd集群数最好要备份。
执行 etcdctl del /registry/namespaces/my-namespace
命令删除命名空间(namespace)时,只会删除 etcd 存储中与该命名空间相关的键值对,而不会直接删除 Kubernetes API Server 中的资源对象(如 CRD、CustomResourceDefinition)。
总结,资源删除不掉原因和解决办法:
因为资源本身被父资源管理,删除父资源即可删除该资源。
资源有
Finalizers
没有执行结束,可以强制跳过Finalizers
对资源进行删除。删除namespace里包含了其他隐式资源,需要把该namespace里所有资源找出删除,然后再删除namespace。
直接操作etcd,删除k8s资源。