kubernetes namespace删除失败分析指北

本文涉及的产品
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
简介: ### 0x00. 现象说明 在kubernetes集群中执行`kubectl delete ns {ns-name}`命令来删除`ns-name`后,发现`ns-name`一直停留在Terminating状态,如下所示: ``` aliyun$ kubectl get ns NAME STATUS AGE ack-syst

0x00. 现象说明

在kubernetes集群中执行kubectl delete ns {ns-name}命令来删除ns-name后,发现ns-name一直停留在Terminating状态,如下所示:

aliyun$ kubectl get ns
NAME                    STATUS        AGE
ack-system-e2e-test-0   Terminating   14m     <-- 停留在Terminating状态
default                 Active        5d16h
kube-public             Active        5d16h
kube-system             Active        5d16h

我们可能会有一个模糊印象,namespace下所有的资源全部删除完成后,系统才能安心删除掉namespace。然后执行kubectl get all -n {ns-name},输出如下所示:

aliyun$ kubectl get all -n ack-system-e2e-test-0
No resources found.

namespace下明明没有资源,但是namespace却一直停留在terminating状态,嗯~跟自己的认知出现偏差~

0x01. 初步分析

既然namespace处在Terminating状态,我们尝试再次执行删除命令kubectl delete ns {ns-name},输出如下:

aliyun$ kubectl delete ns ack-system-e2e-test-0
Error from server (Conflict): Operation cannot be fulfilled on namespaces "ack-system-e2e-test-0": The system is ensuring all content is removed from this namespace.  Upon completion, this namespace will automatically be purged by the system.

根据错误信息可以看出,当系统确定namespace下所有资源已移除时,namespace会被自动删除。说明namespace下还有资源需要删除或者在确认是否已经全部删除时出现问题。

接下来我们看下namespace的内容(具体如下),可以猜测应该是spec.finalizers.kubernetes让系统一直没有删除namespace,因此一直停留在Terminating状态。

aliyun$ kubectl get ns ack-system-e2e-test-0 -o yaml
apiVersion: v1
kind: Namespace
metadata:
  creationTimestamp: "2019-11-26T23:33:28Z"
  deletionTimestamp: "2019-11-26T23:33:43Z"
  name: ack-system-e2e-test-0
  resourceVersion: "187978124"
  selfLink: /api/v1/namespaces/ack-system-e2e-test-0
  uid: 260db8c3-10a5-11ea-933e-8656ce263494
spec:
  finalizers:
  - kubernetes
status:
  phase: Terminating

那系统在什么情况下才能最终删除掉上面的spec.finalizers.kubernetes,从而删除namespace呢,有必要分析一下namespace controller的源码实现。

0x02. 源码探秘

从kubernetes架构可以推测出,删除namespace时系统删除namespace关联资源的处理应该是在contorller里面实现的。因此顺其自然去分析namespace controller的源码。可以理解如下两点:

  • namespace下所有关联资源全部删除后,才会删除spec.finalizers。在Delete方法中实现。
  • 关联资源删除处理由deleteAllContent方法实现

具体代码分析如下:

// k8s.io/kubernetes/pkg/controller/namespace/deletion/namespaced_resources_deleter.go
func (d *namespacedResourcesDeleter) Delete(nsName string) error {
    ...
    // 删除namespace所有关联资源
    estimate, err := d.deleteAllContent(namespace.Name, *namespace.DeletionTimestamp)
    ...

    // 然后删除namespace中的spec.finalizers
    namespace, err = d.retryOnConflictError(namespace, d.finalizeNamespace)
    ...

    // 最后删除namespace
    if d.deleteNamespaceWhenDone && finalized(namespace) {
        return d.deleteNamespace(namespace)
    }
    return nil
}

func (d *namespacedResourcesDeleter) deleteAllContent(namespace string, namespaceDeletedAt metav1.Time) (int64, error) {
    // 获取集群中所有注册namespace scope资源
    // discoverResourcesFn = DiscoveryClient.ServerPreferredNamespacedResources
    resources, err := d.discoverResourcesFn()
      if err != nil {
            return estimate, err  // 错误退出处理1: 获取失败错误
      }
      ...
      
      // 从namespace scope资源结果中,过滤出所有支持delete的资源
      deletableResources := discovery.FilteredBy(discovery.SupportsAllVerbs{Verbs: []string{"delete"}}, resources)
      groupVersionResources, err := discovery.GroupVersionResources(deletableResources)
      if err != nil {
            return estimate, err  // 错误退出2: gvr解析错误
      }
      ...
      
      // 在循环中删除namespace下的各个资源
      // 支持deletecollection的资源直接调用deletecollection删除,否则list所有该gvr资源然后逐个删除
      for gvr := range groupVersionResources {
            gvrEstimate, err := d.deleteAllContentForGroupVersionResource(gvr, namespace, namespaceDeletedAt)
                ...
        }
        if len(errs) > 0 {
            return estimate, utilerrors.NewAggregate(errs) // 错误退出3: namespace下gvr资源删除失败
      }
        ...
}

从源码分析可以看出,namespace一直处在Terminating状态,肯定是因为下面某种Error给hang住了。

  • 错误1: 获取所有注册namesapce scope资源失败
  • 错误2: 获取资源的gvr信息解析失败
  • 错误3: namespace下某些gvr资源删除失败

0x03. 困惑解除

针对上面的错误,如果有条件分析到kube-controller-manager组件的日志,直接去看日志应该很快可以定位到错误信息。我这边因为网络原因无法查看日志,采用逐步确认错误的方案。

  • 利用如下命令,获取所有注册namespace scope资源(顺便提取能delete的资源),输出如下:
aliyun$ kubectl api-resources --namespaced=true --verbs=delete
helinbodeMacBook-Pro:.kube helinbo$ kubectl api-resources --namespaced=true --verbs=delete
NAME                       SHORTNAMES   APIGROUP                    NAMESPACED   KIND
...
rolebindings                            rbac.authorization.k8s.io   true         RoleBinding
roles                                   rbac.authorization.k8s.io   true         Role
error: unable to retrieve the complete list of server APIs: metrics.k8s.io/v1beta1: the server is currently unable to handle the request

根据输出信息,发现获取metrics.k8s.io/v1beta1资源的注册信息时出错了,接着可以执行kubectl get apiservice查询系统中注册服务,输出如下(省略了时间信息):

aliyun$ kubectl get apiservice
helinbodeMacBook-Pro:.kube helinbo$ kubectl get apiservice
NAME                                    SERVICE                      AVAILABLE
...
v1beta1.events.k8s.io                   Local                        True
v1beta1.extensions                      Local                        True
v1beta1.metrics.k8s.io                  kube-system/metrics-server   False (MissingEndpoints)
...

根据输出信息,发现原来是metrics-server出问题了。当metrics-server恢复正常后(metrics-server组件恢复属于另一个话题,后续再分享),apiservice等待片刻也恢复正常,同时Terminating状态的namespace也自动被删除了。至此namespace删除失败问题算真正解决了。

0x04. 最后总结

  1. 当看到因为spec.finalizers卡住namespace无法删除,有可能会直接手动删除namespace中的finalizers信息,从而让系统删除namespace。但是要相信spec.finalizers的设计是有原因的,如果偷懒跳过就错过了问题背后的真正原因。
  2. 当碰到kubernetes系统没有按预期工作时,当根据系统日志,事件等信息仍无法分析到原因并解决时,直接阅读kubernetes源码不失为一种解决问题的方法。同时在理解kubernetes系统架构基础上阅读源码会有更好的效果。
相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
相关文章
|
6月前
|
Prometheus Kubernetes 监控
Kubernetes 的 NameSpace 无法删除应该怎么办?
Kubernetes 的 NameSpace 无法删除应该怎么办?
|
11月前
|
Kubernetes Nacos 数据中心
k8s(9)Namespace(命名空间)
Namespace(命名空间)
247 0
|
3月前
|
Kubernetes 容器 Perl
【Azure K8S】AKS升级 Kubernetes version 失败问题的分析与解决
【Azure K8S】AKS升级 Kubernetes version 失败问题的分析与解决
|
3月前
|
Kubernetes 前端开发 API
在K8S中,在容器内如何获取pod和namespace名字?
在K8S中,在容器内如何获取pod和namespace名字?
|
3月前
|
Kubernetes API 容器
在K8s中,容器内如何获取pod和namespace名?
在K8s中,容器内如何获取pod和namespace名?
|
5月前
|
Kubernetes 监控 调度
K8S中Scheduler原理分析
【6月更文挑战第20天】K8S Scheduler是集群的关键组件,它监听API Server,为新Pod选择合适的Node。
|
6月前
|
Kubernetes 应用服务中间件 nginx
Kubernetes服务网络Ingress网络模型分析、安装和高级用法
Kubernetes服务网络Ingress网络模型分析、安装和高级用法
152 5
|
6月前
|
Kubernetes 监控 调度
|
6月前
|
Kubernetes 容器
k8s集群部署成功后某个节点突然出现notready状态的问题原因分析和解决办法
k8s集群部署成功后某个节点突然出现notready状态的问题原因分析和解决办法
346 0
|
6月前
|
Kubernetes 调度 微服务
K8s 生产最佳实践 - 限制 NameSpace 资源用量
K8s 生产最佳实践 - 限制 NameSpace 资源用量
下一篇
无影云桌面