kubelet垃圾(退出的容器和未使用的镜像)回收源码分析

1 概述:

1.1 代码环境

版本信息如下:
a、kubernetes集群:v1.15.0

1.2 垃圾回收的简述

为减少节点的资源消耗,kubelet组件内部运行着两个协程,一个协程每隔1分钟(时间周期不可修改,写死在代码)清理当前节点上状态为退出的容器,一个协程每隔5分钟(时间周期不可修改,写死在代码)清理当前节点上没被使用的镜像。


2 影响垃圾回收的参数:

2.1 用户不能配置的影响垃圾回收的参数

ContainerGCPeriod,固定为1分钟,此值是常量,定时任务方法在调用时是直接使用此常量。
ImageGCPeriod,固定为5分钟,此值是常量,定时任务方法在调用时是直接使用此常量。

const (
	// ContainerGCPeriod is the period for performing container garbage collection.
	ContainerGCPeriod = time.Minute
	// ImageGCPeriod is the period for performing image garbage collection.
	ImageGCPeriod = 5 * time.Minute
)

2.2 用户能配置的影响垃圾回收的参数

2.2.1容器垃圾回收相关的三个参数

(只能写在kubelet启动命令参数中,不能写在其配置文件,因为kubeconfigration对象中没有相关的字段)

a、–maximum-dead-containers-per-container:
一个pod最多可以保存多少个已经停止的容器,默认为1;
b、–maximum-dead-containers:
当前节点上最多可以保留多少个已经停止的容器,默认为 -1,表示没有限制;
c、–minimum-container-ttl-duration:
已经退出的容器可以存活的最小时间,默认为 0s;

fs.DurationVar(&f.MinimumGCAge.Duration, "minimum-container-ttl-duration", f.MinimumGCAge.Duration, "Minimum age for a finished container before it is garbage collected.  Examples: '300ms', '10s' or '2h45m'")
fs.Int32Var(&f.MaxPerPodContainerCount, "maximum-dead-containers-per-container", f.MaxPerPodContainerCount, "Maximum number of old instances to retain per container.  Each container takes up some disk space.")
fs.Int32Var(&f.MaxContainerCount, "maximum-dead-containers", f.MaxContainerCount, "Maximum number of old instances of containers to retain globally.  Each container takes up some disk space. To disable, set to a negative number.")

maximum-dead-containers-per-container、maximum-dead-containers、–minimum-container-ttl-duration参数的值最终会保存到此结构体对象中,此结构体对象会被当成垃圾回收的方法GarbageCollect()的入参。


type ContainerGCPolicy struct {
	// Minimum age at which a container can be garbage collected, zero for no limit.
	MinAge time.Duration

	// Max number of dead containers any single pod (UID, container name) pair is
	// allowed to have, less than zero for no limit.
	MaxPerPodContainer int

	// Max number of total dead containers, less than zero for no limit.
	MaxContainers int
}

2.2.2 镜像回收相关的主要有以下三个参数:

–image-gc-high-threshold:磁盘使用率到达此阈值,kubelet开始回收镜像,默认为 85%;
–image-gc-low-threshold:磁盘使用率到达此阈值,kubelet停止回收镜像,默认为 80%;
–minimum-image-ttl-duration:未使用的镜像在被回收前的最小存留时间,默认为 2m0s;

fs.DurationVar(&c.ImageMinimumGCAge.Duration, "minimum-image-ttl-duration", c.ImageMinimumGCAge.Duration, "Minimum age for an unused image before it is garbage collected.  Examples: '300ms', '10s' or '2h45m'.")
fs.Int32Var(&c.ImageGCHighThresholdPercent, "image-gc-high-threshold", c.ImageGCHighThresholdPercent, "The percent of disk usage after which image garbage collection is always run. Values must be within the range [0, 100], To disable image garbage collection, set to 100. ")
fs.Int32Var(&c.ImageGCLowThresholdPercent, "image-gc-low-threshold", c.ImageGCLowThresholdPercent, "The percent of disk usage before which image garbage collection is never run. Lowest disk usage to garbage collect to. Values must be within the range [0, 100] and should not be larger than that of --image-gc-high-threshold.")

这三个参数也可写在var/lib/kubelet/config.yaml,分别是
imageGCHighThresholdPercent
imageGCLowThresholdPercent
ImageMinimumGCAge


3 kubelet回收容器的过程:

1)kubelet先回收所有pod中的可回收的业务container(列出可回收的容器,回收动作是调用kubeGenericRuntimeManager对象的killContainer方法和removeContainer方法,ContainerGCPolicy对象的值影响着容器筛选和代码分叉)

2)kubelet接着回收所有pod中的可回收的sandboxes(列出可回收的容器,回收动作是调用kubeGenericRuntimeManager对象的killContainer方法和removeContainer方法,ContainerGCPolicy对象的值影响着容器筛选和代码分叉)

3)kubelet最后回收pod和容器的日志目录(/var/log/pods/目录和/var/log/containers/目录)。

在这里插入图片描述
在这里插入图片描述


4 kubelet 回收镜像的过程:

获取镜像所在磁盘分区的磁盘信息(容量、使用量等),判断磁盘使用率是否大于用户配置的阈值(–image-gc-high-threshold的值),如果是尝试进行删除未使用的镜像,以让镜像磁盘的使用率低于阈值。更直白地说,kubelet执行df命令查询磁盘使用率,使用率大于用户在命令参数或配置文件中设置的阈值,则进行docker rmi命令删除未使用的镜像。

5 重要的结构体

type Kubelet struct {
	
	/*
		其他属性
	*/
	
	// Policy for handling garbage collection of dead containers.
	//用于回收退出状态的容器
	//实现类是结构体realContainerGC,包含了kubeGenericRuntimeManager对象,位于k8s.io/kubernetes/pkg/kubelet/container/container_gc.go
	containerGC kubecontainer.ContainerGC

	// Manager for image garbage collection.
	//用于回收未使用的镜像
	//实现类是结构体realImageGCManager,位于k8s.io/kubernetes/pkg/kubelet/images/image_gc_manager.go
	imageManager images.ImageGCManager
}

type kubeGenericRuntimeManager struct {

	/*
		其他属性
	*/

	// Container GC manager
	containerGC *containerGC
	
	// gRPC service clients
	// 以下两个属性包含了grpc客户端,最终是调用容器运行时(一般情况为docker)的容器接口和镜像接口
	runtimeService internalapi.RuntimeService
	imageService   internalapi.ImageManagerService
}

6 主干代码



//在创建kubelet对象时(即kubelet还没运行主循环),就启动了垃圾回收的协程StartGarbageCollection()
func createAndInitKubelet(...) {
    k, err = kubelet.NewMainKubelet(
       //代码省略
    )
    if err != nil {
        return nil, err
    }
    //代码省略
    
    k.StartGarbageCollection()
    return k, nil
}


func (kl *Kubelet) StartGarbageCollection() {
    loggedContainerGCFailure := false

    // 1)启动容器垃圾回收服务
	//启动协程定时执行kl.containerGC.GarbageCollect()方法,此方法是被封装在wait.Until()方法中,从而起到定时执行的效果。
	go wait.Until(func() {
		if err := kl.containerGC.GarbageCollect(); err != nil {
			//此代码可忽略
		} else {
			//此代码可忽略
			klog.V(vLevel).Infof("Container garbage collection succeeded")
		}
	}, ContainerGCPeriod, wait.NeverStop)
	
    //2)当ImageGCHighThresholdPercent 参数的值为100时,直接返回,即不启动镜像回收的协程。
    if kl.kubeletConfiguration.ImageGCHighThresholdPercent == 100 {
        return
    }

    //3)启动镜像垃圾回收服务
    //启动协程定时执行kl.imageManager.GarbageCollect()方法,此方法是被封装在wait.Until()方法中,从而起到定时执行的效果。
	go wait.Until(func() {
		if err := kl.imageManager.GarbageCollect(); err != nil {
			//此代码可忽略
		} else {
			//此代码可忽略
			klog.V(vLevel).Infof("Image garbage collection succeeded")
		}
	}, ImageGCPeriod, wait.NeverStop)
}



func (cgc *containerGC) GarbageCollect(gcPolicy kubecontainer.ContainerGCPolicy, allSourcesReady bool, evictTerminatedPods bool) error {
    errors := []error{}
    // 回收pod 中的container
    //kubeGenericRuntimeManager对象的killContainer方法和removeContainer方法
    if err := cgc.evictContainers(gcPolicy, allSourcesReady, evictTerminatedPods); err != nil {
        errors = append(errors, err)
    }

    // 回收pod中的sandboxes
     //kubeGenericRuntimeManager对象的killContainer方法和removeContainer方法
    if err := cgc.evictSandboxes(evictTerminatedPods); err != nil {
        errors = append(errors, err)
    }

    //回收pod和容器的日志目录(/var/log/pods/目录和/var/log/containers/目录)
    if err := cgc.evictPodLogsDirectories(allSourcesReady); err != nil {
        errors = append(errors, err)
    }
    return utilerrors.NewAggregate(errors)
}


func (im *realImageGCManager) GarbageCollect() error {
    // 获取容器镜像存储目录所在分区的磁盘信息
    fsStats, err := im.statsProvider.ImageFsStats()
    
	/*
	一些异常情况判断和处理
	*/
	
    //若分区使用率使用率大于HighThresholdPercent,此时进入if语句进行回收未使用的镜像
    usagePercent := 100 - int(available*100/capacity)
    if usagePercent >= im.policy.HighThresholdPercent {
        //计算需要释放的磁盘量,相比用户设置的阈值
        amountToFree := capacity*int64(100-im.policy.LowThresholdPercent)/100 - available

        //调用im.freeSpace()方法真正地回收未使用的镜像,底层是调用kubeGenericRuntimeManager对象的RemoveImage()方法
        freed, err := im.freeSpace(amountToFree, time.Now())
        if err != nil {
            return err
        }
		
		/*
			一些日志相关的代码
			此处省略
		*/       
    }

    return nil
}

7 总结

kubelet开启了定时任务来删除pod中退出的容器和未使用的镜像,命令和配置文件的一些参数可影响着这两个定时任务的执行,和用户在linux crontab中执行docker rmi、docker rm命令以到达减少节点资源消耗的目的并无本质区别。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值