kubernetes -- 删除namespace的过程以及遇到的bug解决

这篇具有很好参考价值的文章主要介绍了kubernetes -- 删除namespace的过程以及遇到的bug解决。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

通过阅读本篇文章你可以收获如下知识:

  • 解决一个bug。
  • 理解k8s的controller中,删除namespace的源码,理解其中的删除过程。

问题

执行kubectl delete ns {ns-name}命令来删除ns-name的时候,发现状态一直停留在Terminating

[root@k8smaster k8slearn]# kubectl get ns
NAME              STATUS        AGE
default           Active        99m
hello             Terminating   36m
kube-node-lease   Active        99m
kube-public       Active        99m
kube-system       Active        99m

我想到的是可能是namespace底下有资源,等资源被删除之后系统才能安心删除掉namespace,然后我们来看一下资源:

[root@k8smaster k8slearn]# kubectl get all -n hello
No resources found in hello namespace.

发现是没有资源的,那么到底是什么原因让删除失败了呢?

我们看一下namespace的具体内容:

[root@k8smaster k8slearn]# kubectl get ns hello -o yaml
apiVersion: v1
kind: Namespace
metadata:
  creationTimestamp: "2023-02-01T06:42:00Z"
  managedFields:
  - apiVersion: v1
    fieldsType: FieldsV1
    fieldsV1:
      f:status:
        f:phase: {}
    manager: kubectl-create
    operation: Update
    time: "2023-02-01T06:42:00Z"
  name: hello
  resourceVersion: "5676"
  uid: bc48ddf5-7456-44f0-8f7f-597c6a141a0f
spec:
  finalizers:
  - kubernetes
status:
  phase: Active

我猜测问题出在这里:

spec:
  finalizers:
  - kubernetes

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

源码分析

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

这个源码的位置我很快就发现了,因为删除一个命名空间肯定是controller做的事情,所以我们打开controller的这样一个文件,然后很快就发现了namespace,里面有一个deletion,打开之后很容易就发现了。

kubernetes -- 删除namespace的过程以及遇到的bug解决

我们来看一下删除命名空间的这个源码:

// Delete deletes all resources in the given namespace.
// Before deleting resources:
//   - It ensures that deletion timestamp is set on the
//     namespace (does nothing if deletion timestamp is missing).
//   - Verifies that the namespace is in the "terminating" phase
//     (updates the namespace phase if it is not yet marked terminating)
//
// After deleting the resources:
// * It removes finalizer token from the given namespace.
//
// Returns an error if any of those steps fail.
// Returns ResourcesRemainingError if it deleted some resources but needs
// to wait for them to go away.
// Caller is expected to keep calling this until it succeeds.
func (d *namespacedResourcesDeleter) Delete(nsName string) error {
	// Multiple controllers may edit a namespace during termination
	// first get the latest state of the namespace before proceeding
	// if the namespace was deleted already, don't do anything
	namespace, err := d.nsClient.Get(context.TODO(), nsName, metav1.GetOptions{})
	if err != nil {
		if errors.IsNotFound(err) {
			return nil
		}
		return err
	}
	if namespace.DeletionTimestamp == nil {
		return nil
	}

	klog.V(5).Infof("namespace controller - syncNamespace - namespace: %s, finalizerToken: %s", namespace.Name, d.finalizerToken)

	// ensure that the status is up to date on the namespace
	// if we get a not found error, we assume the namespace is truly gone
	namespace, err = d.retryOnConflictError(namespace, d.updateNamespaceStatusFunc)
	if err != nil {
		if errors.IsNotFound(err) {
			return nil
		}
		return err
	}

	// the latest view of the namespace asserts that namespace is no longer deleting..
	if namespace.DeletionTimestamp.IsZero() {
		return nil
	}

	// return if it is already finalized.
	if finalized(namespace) {
		return nil
	}

	// there may still be content for us to remove
	estimate, err := d.deleteAllContent(namespace)
	if err != nil {
		return err
	}
	if estimate > 0 {
		return &ResourcesRemainingError{estimate}
	}

	// we have removed content, so mark it finalized by us
	_, err = d.retryOnConflictError(namespace, d.finalizeNamespace)
	if err != nil {
		// in normal practice, this should not be possible, but if a deployment is running
		// two controllers to do namespace deletion that share a common finalizer token it's
		// possible that a not found could occur since the other controller would have finished the delete.
		if errors.IsNotFound(err) {
			return nil
		}
		return err
	}
	return nil
}

我们把几行关键的源码分析一下:

	// return if it is already finalized.
	if finalized(namespace) {
		return nil
	}

我们来看一下这个里面发生了什么?

// finalized returns true if the namespace.Spec.Finalizers is an empty list
func finalized(namespace *v1.Namespace) bool {
	return len(namespace.Spec.Finalizers) == 0
}

这个代表如果我的finalized数组为空的话,那么就算作清理完成了,直接退出函数。

所以说到底,这个Finalizers到底是什么呢?我们区kubernetes的官方文档里面查看一下。

以下内容来自于kubernetes的官方文档:

https://kubernetes.io/zh-cn/docs/concepts/overview/working-with-objects/finalizers/

Finalizer 是带有命名空间的键,告诉 Kubernetes 等到特定的条件被满足后, 再完全删除被标记为删除的资源。 Finalizer 提醒控制器清理被删除的对象拥有的资源。

当你使用清单文件创建资源时,你可以在 metadata.finalizers 字段指定 Finalizers。 当你试图删除该资源时,处理删除请求的 API 服务器会注意到 finalizers 字段中的值, 并进行以下操作:

  • 修改对象,将你开始执行删除的时间添加到metadata.deletionTimestamp字段。
  • 禁止对象被删除,直到其 metadata.finalizers 字段为空。
  • 返回 202 状态码(HTTP “Accepted”)。

管理 finalizer 的控制器注意到对象上发生的更新操作,对象的 metadata.deletionTimestamp 被设置,意味着已经请求删除该对象。然后,控制器会试图满足资源的 Finalizers 的条件。 每当一个 Finalizer 的条件被满足时,控制器就会从资源的 finalizers 字段中删除该键。 当 finalizers 字段为空时,deletionTimestamp 字段被设置的对象会被自动删除。 你也可以使用 Finalizers 来阻止删除未被管理的资源。

一个常见的 Finalizer 的例子是 kubernetes.io/pv-protection, 它用来防止意外删除 PersistentVolume 对象。 当一个 PersistentVolume 对象被 Pod 使用时, Kubernetes 会添加 pv-protection Finalizer。 如果你试图删除 PersistentVolume,它将进入 Terminating 状态, 但是控制器因为该 Finalizer 存在而无法删除该资源。 当 Pod 停止使用 PersistentVolume 时, Kubernetes 清除 pv-protection Finalizer,控制器就会删除该卷。

那么接下来我们就在源码中看一看这个过程:

	estimate, err := d.deleteAllContent(namespace)

首先是删除namespace所有关联资源。

	// we have removed content, so mark it finalized by us
	_, err = d.retryOnConflictError(namespace, d.finalizeNamespace)

然后删除namespace中的spec.finalizers。

而由官方文档得知删除finalizers之后,ns才能被删除。

因此问题就出现在了deleteAllContent这个函数里面,这个函数里面某些东西出现了问题,导致finalizers无法被删除,从而出现上面的情况。

所以我们来读一下deleteAllContent的源码。

// deleteAllContent will use the dynamic client to delete each resource identified in groupVersionResources.
// It returns an estimate of the time remaining before the remaining resources are deleted.
// If estimate > 0, not all resources are guaranteed to be gone.
func (d *namespacedResourcesDeleter) deleteAllContent(ns *v1.Namespace) (int64, error) {
	namespace := ns.Name
	namespaceDeletedAt := *ns.DeletionTimestamp
	var errs []error
	conditionUpdater := namespaceConditionUpdater{}
	estimate := int64(0)
	klog.V(4).Infof("namespace controller - deleteAllContent - namespace: %s", namespace)

	resources, err := d.discoverResourcesFn()
	if err != nil {
		// discovery errors are not fatal.  We often have some set of resources we can operate against even if we don't have a complete list
		errs = append(errs, err)
		conditionUpdater.ProcessDiscoverResourcesErr(err)
	}
	// TODO(sttts): get rid of opCache and pass the verbs (especially "deletecollection") down into the deleter
	deletableResources := discovery.FilteredBy(discovery.SupportsAllVerbs{Verbs: []string{"delete"}}, resources)
	groupVersionResources, err := discovery.GroupVersionResources(deletableResources)
	if err != nil {
		// discovery errors are not fatal.  We often have some set of resources we can operate against even if we don't have a complete list
		errs = append(errs, err)
		conditionUpdater.ProcessGroupVersionErr(err)
	}

	numRemainingTotals := allGVRDeletionMetadata{
		gvrToNumRemaining:        map[schema.GroupVersionResource]int{},
		finalizersToNumRemaining: map[string]int{},
	}
	for gvr := range groupVersionResources {
		gvrDeletionMetadata, err := d.deleteAllContentForGroupVersionResource(gvr, namespace, namespaceDeletedAt)
		if err != nil {
			// If there is an error, hold on to it but proceed with all the remaining
			// groupVersionResources.
			errs = append(errs, err)
			conditionUpdater.ProcessDeleteContentErr(err)
		}
		if gvrDeletionMetadata.finalizerEstimateSeconds > estimate {
			estimate = gvrDeletionMetadata.finalizerEstimateSeconds
		}
		if gvrDeletionMetadata.numRemaining > 0 {
			numRemainingTotals.gvrToNumRemaining[gvr] = gvrDeletionMetadata.numRemaining
			for finalizer, numRemaining := range gvrDeletionMetadata.finalizersToNumRemaining {
				if numRemaining == 0 {
					continue
				}
				numRemainingTotals.finalizersToNumRemaining[finalizer] = numRemainingTotals.finalizersToNumRemaining[finalizer] + numRemaining
			}
		}
	}
	conditionUpdater.ProcessContentTotals(numRemainingTotals)

	// we always want to update the conditions because if we have set a condition to "it worked" after it was previously, "it didn't work",
	// we need to reflect that information.  Recall that additional finalizers can be set on namespaces, so this finalizer may clear itself and
	// NOT remove the resource instance.
	if hasChanged := conditionUpdater.Update(ns); hasChanged {
		if _, err = d.nsClient.UpdateStatus(context.TODO(), ns, metav1.UpdateOptions{}); err != nil {
			utilruntime.HandleError(fmt.Errorf("couldn't update status condition for namespace %q: %v", namespace, err))
		}
	}

	// if len(errs)==0, NewAggregate returns nil.
	klog.V(4).Infof("namespace controller - deleteAllContent - namespace: %s, estimate: %v, errors: %v", namespace, estimate, utilerrors.NewAggregate(errs))
	return estimate, utilerrors.NewAggregate(errs)
}

我们看有哪些if err != nil。就可以直到哪里会发生错误。

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

那么具体为什么,我们也不知道该怎么解决了,因此有一个治标不治本的方式,就是强行删除finalizers。

解决方法

强制性删除spec.finalizer[]。

  • 查看hello的namespace描述

    kubectl get ns hello -o json > hello.json
    
  • 编辑json文件,删除spec字段的内存,因为k8s集群需要认证

     vim hello.json
    
    "spec": {       
    	"finalizers": [           
        		"kubernetes"
            ]
        },
    更改为:
    "spec": {
        
      },
    
  • 新开一个窗口运行kubectl proxy跑一个API代理在本地的8081端口

    kubectl proxy --port=8081
    
  • 运行curl命令,直接调用kube api进行删除

    curl -k -H "Content-Type:application/json" -X PUT --data-binary @hello.json http://127.0.0.1:8081/api/v1/namespaces/hello/finalize
    

总结

我们来梳理一下删除namespace的过程。

kubernetes -- 删除namespace的过程以及遇到的bug解决文章来源地址https://www.toymoban.com/news/detail-489256.html

到了这里,关于kubernetes -- 删除namespace的过程以及遇到的bug解决的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 开发过程中遇到的问题以及解决方法

    巩固基础,砥砺前行 。 只有不断重复,才能做到超越自己。 能坚持把简单的事情做到极致,也是不容易的。 简单易用的git命令 git命令: 查看有几个分支:git branch -a 切换分支:git checkout 分支名称 下载项目:git clone url 拉取项目:每次提交代码之前都需要,相当于更新代码

    2024年02月13日
    浏览(32)
  • 新建vite+vue3+ts项目,以及解决过程中遇到的问题

    目录 一、新建vite+vue3+ts项目 二、解决过程中遇到的问题 解决报错:Module ‘“xx.vue“‘ has no default export. 解决报错:Error [ERR_MODULE_NOT_FOUND]: Cannot find package ‘uuid’ imported from xxx的解决 解决报错:[plugin:vite:css] Preprocessor dependency \\\"less\\\" not found. Did you install it? 此处我使用npm做一下

    2024年02月06日
    浏览(112)
  • KubeSphere Namespace 数据删除事故分析与解决全记录

    作者:宇轩辞白,运维研发工程师,目前专注于云原生、Kubernetes、容器、Linux、运维自动化等领域。 2023 年 7 月 23 日在项目上线前夕,K8s 生产环境出现故障,经过紧急修复之后,K8s 环境恢复正常;另外我们环境引入了 KubeSphere 云原生平台技术,为了方便研发人员对于 K8s 权

    2024年02月09日
    浏览(39)
  • 设计师需要掌握的网站设计相关知识、技巧、工具以及在建站过程中遇到的常见问题和解决方案

    作者:禅与计算机程序设计艺术 “一个设计师的手册:关于网站设计的一切你需要知道”,本文全面系统地介绍了设计师需要掌握的网站设计相关知识、技巧、工具以及在建站过程中遇到的常见问题和解决方案,从基础知识到常用效果、细节调整、模板制作、SEO优化,都有详

    2024年02月06日
    浏览(38)
  • Kubernetes三探(安装calico,join,以及遇到的问题)

    昨晚加班到10点····搞这个破玩意儿 言归正传 上一篇在master成功 kubeadm init了,但是安装network add-on时总是出错。今天来再试一试。 首先我是按照这篇博文安装的, https://blog.csdn.net/weixin_43645454/article/details/124952184 因为国内安装真的太多坑了。官网根本没法看 首先我按照这篇

    2024年02月01日
    浏览(33)
  • 关于uniApp自定义配置的底部的tabBar配略解, 以及会遇到的bug若干

    首先, 想要自定义tabBar 有两种方法, 可以自己去编写一个tabBar导航栏, 但是性能不会比原生高, 第二种, 使用  uni.setTabBarItem 这是配置项, 有两个注意点,  pagePath 的路径配置和 pages.json 有所不同, 需要加上代表绝对路径的  /  如下 , index代表需要替换的tabBar数组位置, 其余配置一

    2024年02月04日
    浏览(31)
  • 前端开发中遇到的小bug--解决方案

    1.在 searchBox 搜索栏中,用到了多级下拉框的筛选条件,样式如下:  这样看起来是没什么问题的,但当我选择时,在框中显示的内容和筛选条件的内容就出错了: 这里其实是选择了 采矿业 -- 石油和天然气开采业 ,但显示框中是 林业,筛选条件的内容也有问题: 这里没有显

    2024年02月10日
    浏览(27)
  • 解决VIvado编程中遇到的bug I

    解决VIvado编程中遇到的bug I 1.[DRC MDRV-1] Multiple Driver Nets: Net has multiple drivers: GND/G, and VCC/P. 解决: 这个问题很常见,reg变量被在不同的always模块同时赋值 2.[Designutils 20-1307] Command ‘get_ports{led4_tri_io[0]}’ is not supported in the xdc constraint file. [“***/test_EMIO_LED.srcs/constrs_1/new/system.xdc”

    2023年04月26日
    浏览(24)
  • 软件工具安装遇到bug、报错不知道怎么解决?看这里!

    本文举例了几个常见的软件工具使用问题,文末会提供一些我自己整理和使用的工具资料 。 \\\"在追逐零 Bug 的路上,我们不断学习、改进,更加坚定自己的技术信念。让我们相信,每一个 Bug 都是我们成长的机会。\\\"  1、VMware虚拟机未开启虚拟化 VT-x AMD-V 英特尔 Intel CPU   AMD

    2024年02月21日
    浏览(40)
  • OpenMediaVault 6.x 配置网络的过程以及遇到的问题

    1.1总结 U盘安装OMV以后,尽管在安装页面中配置了网络,但是这个配置不会保留在系统当中,进入系统后需要重新配置。 第一次系统的后,在 /etc/network/interfaces 配置网络。 如果在Web管理页面中对网络进行了修改, /etc/network/interfaces 的网络配置会失效,需要重新配置网络,这

    2024年02月03日
    浏览(25)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包