kubelet 中垃圾回收機(jī)制的設(shè)計與實現(xiàn)

kubernetes 中的垃圾回收機(jī)制主要有兩部分組成:

  • 一是由 kube-controller-manager 中的 gc controller 自動回收 kubernetes 中被刪除的對象以及其依賴的對象只怎;
  • 二是在每個節(jié)點上需要回收已退出的容器以及當(dāng) node 上磁盤資源不足時回收已不再使用的容器鏡像侈咕;

本文主要分析 kubelet 中的垃圾回收機(jī)制疙教,垃圾回收的主要目的是為了節(jié)約宿主上的資源,gc controller 的回收機(jī)制可以參考以前的文章 garbage collector controller 源碼分析韧拒。

kubelet 中與容器垃圾回收有關(guān)的主要有以下三個參數(shù):

  • --maximum-dead-containers-per-container: 表示一個 pod 最多可以保存多少個已經(jīng)停止的容器淹接,默認(rèn)為1;(maxPerPodContainerCount)
  • --maximum-dead-containers:一個 node 上最多可以保留多少個已經(jīng)停止的容器叭莫,默認(rèn)為 -1蹈集,表示沒有限制;
  • --minimum-container-ttl-duration:已經(jīng)退出的容器可以存活的最小時間雇初,默認(rèn)為 0s拢肆;

與鏡像回收有關(guān)的主要有以下三個參數(shù):

  • --image-gc-high-threshold:當(dāng) kubelet 磁盤達(dá)到多少時,kubelet 開始回收鏡像靖诗,默認(rèn)為 85% 開始回收郭怪,根目錄以及數(shù)據(jù)盤;
  • --image-gc-low-threshold:回收鏡像時當(dāng)磁盤使用率減少至多少時停止回收刊橘,默認(rèn)為 80%鄙才;
  • --minimum-image-ttl-duration:未使用的鏡像在被回收前的最小存留時間,默認(rèn)為 2m0s促绵;

kubelet 中容器回收過程如下:
pod 中的容器退出時間超過--minimum-container-ttl-duration后會被標(biāo)記為可回收攒庵,一個 pod 中最多可以保留--maximum-dead-containers-per-container個已經(jīng)停止的容器,一個 node 上最多可以保留--maximum-dead-containers個已停止的容器败晴。在回收容器時浓冒,kubelet 會按照容器的退出時間排序,最先回收退出時間最久的容器尖坤。需要注意的是稳懒,kubelet 在回收時會將 pod 中的 container 與 sandboxes 分別進(jìn)行回收,且在回收容器后會將其對應(yīng)的 log dir 也進(jìn)行回收慢味;

kubelet 中鏡像回收過程如下:
當(dāng)容器鏡像掛載點文件系統(tǒng)的磁盤使用率大于--image-gc-high-threshold時(containerRuntime 為 docker 時场梆,鏡像存放目錄默認(rèn)為 /var/lib/docker)墅冷,kubelet 開始刪除節(jié)點中未使用的容器鏡像,直到磁盤使用率降低至--image-gc-low-threshold 時停止鏡像的垃圾回收或油。

kubelet GarbageCollect 源碼分析

kubernetes 版本:v1.16

GarbageCollect 是在 kubelet 對象初始化完成后啟動的寞忿,在 createAndInitKubelet 方法中首先調(diào)用 kubelet.NewMainKubelet 初始化了 kubelet 對象,隨后調(diào)用 k.StartGarbageCollection 啟動了 GarbageCollect装哆。

k8s.io/kubernetes/cmd/kubelet/app/server.go:1089

func createAndInitKubelet(......) {
    k, err = kubelet.NewMainKubelet(
        ......
    )
    if err != nil {
        return nil, err
    }

    k.BirthCry()

    k.StartGarbageCollection()

    return k, nil
}

k.StartGarbageCollection

在 kubelet 中鏡像的生命周期和容器的生命周期是通過 imageManager 和 containerGC 管理的罐脊。在 StartGarbageCollection 方法中會啟動容器和鏡像垃圾回收兩個任務(wù)定嗓,其主要邏輯為:

  • 1蜕琴、啟動 containerGC goroutine,ContainerGC 間隔時間默認(rèn)為 1 分鐘宵溅;
  • 2凌简、檢查 --image-gc-high-threshold 參數(shù)的值,若為 100 則禁用 imageGC恃逻;
  • 3雏搂、啟動 imageGC goroutine,imageGC 間隔時間默認(rèn)為 5 分鐘寇损;

k8s.io/kubernetes/pkg/kubelet/kubelet.go:1270

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

    // 1凸郑、啟動容器垃圾回收服務(wù)
    go wait.Until(func() {
        if err := kl.containerGC.GarbageCollect(); err != nil {
            loggedContainerGCFailure = true
        } else {
            var vLevel klog.Level = 4
            if loggedContainerGCFailure {
                vLevel = 1
                loggedContainerGCFailure = false
            }

            klog.V(vLevel).Infof("Container garbage collection succeeded")
        }
    }, ContainerGCPeriod, wait.NeverStop)

    // 2、檢查 ImageGCHighThresholdPercent 參數(shù)的值
    if kl.kubeletConfiguration.ImageGCHighThresholdPercent == 100 {
        return
    }

    // 3矛市、啟動鏡像垃圾回收服務(wù)
    prevImageGCFailed := false
    go wait.Until(func() {
        if err := kl.imageManager.GarbageCollect(); err != nil {
            ......
            prevImageGCFailed = true
        } else {
            var vLevel klog.Level = 4
            if prevImageGCFailed {
                vLevel = 1
                prevImageGCFailed = false
            }
        }
    }, ImageGCPeriod, wait.NeverStop)
}

kl.containerGC.GarbageCollect

kl.containerGC.GarbageCollect 調(diào)用的是 ContainerGC manager 中的方法芙沥,ContainerGC 是在 NewMainKubelet 中初始化的,ContainerGC 在初始化時需要指定一個 runtime浊吏,該 runtime 即 ContainerRuntime而昨,在 kubelet 中即 kubeGenericRuntimeManager,也是在 NewMainKubelet 中初始化的找田。

k8s.io/kubernetes/pkg/kubelet/kubelet.go

func NewMainKubelet(){
    ......
    // MinAge歌憨、MaxPerPodContainer、MaxContainers 分別上文章開頭提到的與容器垃圾回收有關(guān)的
    // 三個參數(shù)
    containerGCPolicy := kubecontainer.ContainerGCPolicy{
        MinAge:             minimumGCAge.Duration,
        MaxPerPodContainer: int(maxPerPodContainerCount),
        MaxContainers:      int(maxContainerCount),
    }

    // 初始化 containerGC 模塊
    containerGC, err := kubecontainer.NewContainerGC(klet.containerRuntime, containerGCPolicy, klet.sourcesReady)
    if err != nil {
        return nil, err
    }
    ......
}

以下是 ContainerGC 的初始化以及 GarbageCollect 的啟動:

k8s.io/kubernetes/pkg/kubelet/container/container_gc.go:68

func NewContainerGC(runtime Runtime, policy ContainerGCPolicy, sourcesReadyProvider SourcesReadyProvider) (ContainerGC, error) {
    if policy.MinAge < 0 {
        return nil, fmt.Errorf("invalid minimum garbage collection age: %v", policy.MinAge)
    }

    return &realContainerGC{
        runtime:              runtime,
        policy:               policy,
        sourcesReadyProvider: sourcesReadyProvider,
    }, nil
}

func (cgc *realContainerGC) GarbageCollect() error {
    return cgc.runtime.GarbageCollect(cgc.policy, cgc.sourcesReadyProvider.AllReady(), false)
}

可以看到墩衙,ContainerGC 中的 GarbageCollect 最終是調(diào)用 runtime 中的 GarbageCollect 方法务嫡,runtime 即 kubeGenericRuntimeManager。

cgc.runtime.GarbageCollect

cgc.runtime.GarbageCollect 的實現(xiàn)是在 kubeGenericRuntimeManager 中漆改,其主要邏輯為:

  • 1心铃、回收 pod 中的 container;
  • 2籽懦、回收 pod 中的 sandboxes于个;
  • 3、回收 pod 以及 container 的 log dir暮顺;

k8s.io/kubernetes/pkg/kubelet/kuberuntime/kuberuntime_gc.go:378

func (cgc *containerGC) GarbageCollect(gcPolicy kubecontainer.ContainerGCPolicy, allSourcesReady bool, evictTerminatedPods bool) error {
    errors := []error{}
    // 1厅篓、回收 pod 中的 container
    if err := cgc.evictContainers(gcPolicy, allSourcesReady, evictTerminatedPods); err != nil {
        errors = append(errors, err)
    }

    // 2秀存、回收 pod 中的 sandboxes
    if err := cgc.evictSandboxes(evictTerminatedPods); err != nil {
        errors = append(errors, err)
    }

    // 3、回收 pod 以及 container 的 log dir
    if err := cgc.evictPodLogsDirectories(allSourcesReady); err != nil {
        errors = append(errors, err)
    }
    return utilerrors.NewAggregate(errors)
}
cgc.evictContainers

cgc.evictContainers 方法中會回收所有可被回收的容器羽氮,其主要邏輯為:

  • 1或链、首先調(diào)用 cgc.evictableContainers 獲取可被回收的容器作為 evictUnits,可被回收的容器指非 running 狀態(tài)且創(chuàng)建時間超過 MinAge档押,evictUnits 數(shù)組中包含 pod 與 container 的對應(yīng)關(guān)系澳盐;
  • 2、回收 deleted 狀態(tài)以及 terminated 狀態(tài)的 pod令宿,遍歷 evictUnits叼耙,若 pod 是否處于 deleted 或者 terminated 狀態(tài),則調(diào)用 cgc.removeOldestN 回收 pod 中的所有容器粒没。deleted 狀態(tài)指 pod 已經(jīng)被刪除或者其 status.phase 為 failed 且其 status.reason 為 evicted 或者 pod.deletionTimestamp != nil 且 pod 中所有容器的 status 為 terminated 或者 waiting 狀態(tài)筛婉,terminated 狀態(tài)指 pod 處于 Failed 或者 succeeded 狀態(tài);
  • 3癞松、對于非 deleted 或者 terminated 狀態(tài)的 pod爽撒,調(diào)用 cgc.enforceMaxContainersPerEvictUnit 為其保留 MaxPerPodContainer 個已經(jīng)退出的容器,按照容器退出的時間進(jìn)行排序優(yōu)先刪除退出時間最久的响蓉,MaxPerPodContainer 在上文已經(jīng)提過硕勿,表示一個 pod 最多可以保存多少個已經(jīng)停止的容器,默認(rèn)為1枫甲,可以使用 --maximum-dead-containers-per-container 在啟動時指定源武;
  • 4、若 kubelet 啟動時指定了--maximum-dead-containers(默認(rèn)為 -1 即不限制)言秸,即需要為 node 保留退出的容器數(shù)软能,若 node 上保留已經(jīng)停止的容器數(shù)超過 --maximum-dead-containers,首先計算需要為每個 pod 保留多少個已退出的容器保證其總數(shù)不超過 --maximum-dead-containers 的值举畸,若計算結(jié)果小于 1 則取 1查排,即至少保留一個,然后刪除每個 pod 中不需要保留的容器抄沮,此時若 node 上保留已經(jīng)停止的容器數(shù)依然超過需要保留的最大值跋核,則將 evictUnits 中的容器按照退出時間進(jìn)行排序刪除退出時間最久的容器,使 node 上保留已經(jīng)停止的容器數(shù)滿足 --maximum-dead-containers 值叛买;

k8s.io/kubernetes/pkg/kubelet/kuberuntime/kuberuntime_gc.go:222

func (cgc *containerGC) evictContainers(gcPolicy kubecontainer.ContainerGCPolicy, allSourcesReady bool, evictTerminatedPods bool) error {
    // 1砂代、獲取可被回收的容器列表
    evictUnits, err := cgc.evictableContainers(gcPolicy.MinAge)
    if err != nil {
        return err
    }

    // 2、回收 Deleted 狀態(tài)以及 Terminated 狀態(tài)的 pod率挣,此處 allSourcesReady 指 kubelet
    //    支持的三種 podSource 是否都可用
    if allSourcesReady {
        for key, unit := range evictUnits {
            if cgc.podStateProvider.IsPodDeleted(key.uid) || (cgc.podStateProvider.IsPodTerminated(key.uid) && evictTerminatedPods) {
                cgc.removeOldestN(unit, len(unit))
                delete(evictUnits, key)
            }
        }
    }

    // 3刻伊、為非 Deleted 狀態(tài)以及 Terminated 狀態(tài)的 pod 保留 MaxPerPodContainer 個已經(jīng)退出的容器
    if gcPolicy.MaxPerPodContainer >= 0 {
        cgc.enforceMaxContainersPerEvictUnit(evictUnits, gcPolicy.MaxPerPodContainer)
    }

    // 4、若 kubelet 啟動時指定了 --maximum-dead-containers(默認(rèn)為 -1 即不限制)參數(shù),
    //   此時需要為 node 保留退出的容器數(shù)不能超過 --maximum-dead-containers 個
    if gcPolicy.MaxContainers >= 0 && evictUnits.NumContainers() > gcPolicy.MaxContainers {
        numContainersPerEvictUnit := gcPolicy.MaxContainers / evictUnits.NumEvictUnits()
        if numContainersPerEvictUnit < 1 {
            numContainersPerEvictUnit = 1
        }
        cgc.enforceMaxContainersPerEvictUnit(evictUnits, numContainersPerEvictUnit)

        numContainers := evictUnits.NumContainers()
        if numContainers > gcPolicy.MaxContainers {
            flattened := make([]containerGCInfo, 0, numContainers)
            for key := range evictUnits {
                flattened = append(flattened, evictUnits[key]...)
            }
            sort.Sort(byCreated(flattened))

            cgc.removeOldestN(flattened, numContainers-gcPolicy.MaxContainers)
        }
    }
    return nil
}
cgc.evictSandboxes

cgc.evictSandboxes 方法會回收所有可回收的 sandboxes捶箱,其主要邏輯為:

  • 1智什、首先獲取 node 上所有的 containers 和 sandboxes;
  • 2丁屎、構(gòu)建 sandboxes 與 pod 的對應(yīng)關(guān)系并將其保存在 sandboxesByPodUID 中荠锭;
  • 3、對 sandboxesByPodUID 列表按創(chuàng)建時間進(jìn)行排序晨川;
  • 4证九、若 sandboxes 所在的 pod 處于 deleted 狀態(tài),則刪除該 pod 中所有的 sandboxes 否則只保留退出時間最短的一個 sandboxes共虑,deleted 狀態(tài)在上文 cgc.evictContainers 方法中已經(jīng)解釋過愧怜;

k8s.io/kubernetes/pkg/kubelet/kuberuntime/kuberuntime_gc.go:274

func (cgc *containerGC) evictSandboxes(evictTerminatedPods bool) error {
    // 1、獲取 node 上所有的 container
    containers, err := cgc.manager.getKubeletContainers(true)
    if err != nil {
        return err
    }
    // 2看蚜、獲取 node 上所有的 sandboxes
    sandboxes, err := cgc.manager.getKubeletSandboxes(true)
    if err != nil {
        return err
    }

    // 3叫搁、收集所有 container 的 PodSandboxId
    sandboxIDs := sets.NewString()
    for _, container := range containers {
        sandboxIDs.Insert(container.PodSandboxId)
    }

    // 4、構(gòu)建 sandboxes 與 pod 的對應(yīng)關(guān)系并將其保存在 sandboxesByPodUID 中
    sandboxesByPod := make(sandboxesByPodUID)
    for _, sandbox := range sandboxes {
        podUID := types.UID(sandbox.Metadata.Uid)
        sandboxInfo := sandboxGCInfo{
            id:         sandbox.Id,
            createTime: time.Unix(0, sandbox.CreatedAt),
        }

        if sandbox.State == runtimeapi.PodSandboxState_SANDBOX_READY {
            sandboxInfo.active = true
        }

        if sandboxIDs.Has(sandbox.Id) {
            sandboxInfo.active = true
        }

        sandboxesByPod[podUID] = append(sandboxesByPod[podUID], sandboxInfo)
    }

    // 5供炎、對 sandboxesByPod 進(jìn)行排序
    for uid := range sandboxesByPod {
        sort.Sort(sandboxByCreated(sandboxesByPod[uid]))
    }

    // 6、遍歷 sandboxesByPod疾党,若 sandboxes 所在的 pod 處于 deleted 狀態(tài)音诫,
    // 則刪除該 pod 中所有的 sandboxes 否則只保留退出時間最短的一個 sandboxes
    for podUID, sandboxes := range sandboxesByPod {
        if cgc.podStateProvider.IsPodDeleted(podUID) || (cgc.podStateProvider.IsPodTerminated(podUID) && evictTerminatedPods) {
            cgc.removeOldestNSandboxes(sandboxes, len(sandboxes))
        } else {
            cgc.removeOldestNSandboxes(sandboxes, len(sandboxes)-1)
        }
    }
    return nil
}
cgc.evictPodLogsDirectories

cgc.evictPodLogsDirectories 方法會回收所有可回收 pod 以及 container 的 log dir,其主要邏輯為:

  • 1雪位、首先回收 deleted 狀態(tài) pod logs dir竭钝,遍歷 pod logs dir /var/log/pods/var/log/pods 為 pod logs 的默認(rèn)目錄雹洗,pod logs dir 的格式為 /var/log/pods/NAMESPACE_NAME_UID香罐,解析 pod logs dir 獲取 pod uid,判斷 pod 是否處于 deleted 狀態(tài)时肿,若處于 deleted 狀態(tài)則刪除其 logs dir庇茫;

  • 2、回收 deleted 狀態(tài) container logs 鏈接目錄螃成,/var/log/containers 為 container log 的默認(rèn)目錄旦签,其會軟鏈接到 pod 的 log dir 下,例如:

    /var/log/containers/storage-provisioner_kube-system_storage-provisioner-acc8386e409dfb3cc01618cbd14c373d8ac6d7f0aaad9ced018746f31d0081e2.log -> /var/log/pods/kube-system_storage-provisioner_b448e496-eb5d-4d71-b93f-ff7ff77d2348/storage-provisioner/0.log
    

k8s.io/kubernetes/pkg/kubelet/kuberuntime/kuberuntime_gc.go:333

func (cgc *containerGC) evictPodLogsDirectories(allSourcesReady bool) error {
    osInterface := cgc.manager.osInterface
    // 1寸宏、回收 deleted 狀態(tài) pod logs dir
    if allSourcesReady {
        dirs, err := osInterface.ReadDir(podLogsRootDirectory)
        if err != nil {
            return fmt.Errorf("failed to read podLogsRootDirectory %q: %v", podLogsRootDirectory, err)
        }
        for _, dir := range dirs {
            name := dir.Name()
            podUID := parsePodUIDFromLogsDirectory(name)
            if !cgc.podStateProvider.IsPodDeleted(podUID) {
                continue
            }
            err := osInterface.RemoveAll(filepath.Join(podLogsRootDirectory, name))
            if err != nil {
                klog.Errorf("Failed to remove pod logs directory %q: %v", name, err)
            }
        }
    }
    // 2宁炫、回收 deleted 狀態(tài) container logs 鏈接目錄
    logSymlinks, _ := osInterface.Glob(filepath.Join(legacyContainerLogsDir, fmt.Sprintf("*.%s", legacyLogSuffix)))
    for _, logSymlink := range logSymlinks {
        if _, err := osInterface.Stat(logSymlink); os.IsNotExist(err) {
            err := osInterface.Remove(logSymlink)
            if err != nil {
                klog.Errorf("Failed to remove container log dead symlink %q: %v", logSymlink, err)
            }
        }
    }
    return nil
}

kl.imageManager.GarbageCollect

上面已經(jīng)分析了容器回收的主要流程,下面會繼續(xù)分析鏡像回收的流程氮凝,kl.imageManager.GarbageCollect 是鏡像回收任務(wù)啟動的方法羔巢,鏡像回收流程是在 imageManager 中進(jìn)行的,首先了解下 imageManager 的初始化,imageManager 也是在 NewMainKubelet 方法中進(jìn)行初始化的竿秆。

k8s.io/kubernetes/pkg/kubelet/kubelet.go

func NewMainKubelet(){
    ......
    // 初始化時需要指定三個參數(shù)炭臭,三個參數(shù)已經(jīng)在上文中提到過
    imageGCPolicy := images.ImageGCPolicy{
        MinAge:               kubeCfg.ImageMinimumGCAge.Duration,
        HighThresholdPercent: int(kubeCfg.ImageGCHighThresholdPercent),
        LowThresholdPercent:  int(kubeCfg.ImageGCLowThresholdPercent),
    }
    ......
    imageManager, err := images.NewImageGCManager(klet.containerRuntime, klet.StatsProvider, kubeDeps.Recorder, nodeRef, imageGCPolicy, crOptions.PodSandboxImage)
    if err != nil {
        return nil, fmt.Errorf("failed to initialize image manager: %v", err)
    }
    klet.imageManager = imageManager
    ......
}

kl.imageManager.GarbageCollect 方法的主要邏輯為:

  • 1、首先調(diào)用 im.statsProvider.ImageFsStats 獲取容器鏡像存儲目錄掛載點文件系統(tǒng)的磁盤信息袍辞;
  • 2鞋仍、獲取掛載點的 available 和 capacity 信息并計算其使用率;
  • 3搅吁、若使用率大于 HighThresholdPercent威创,首先根據(jù) LowThresholdPercent 值計算需要釋放的磁盤量,然后調(diào)用 im.freeSpace 釋放未使用的 image 直到滿足磁盤空閑率谎懦;

k8s.io/kubernetes/pkg/kubelet/images/image_gc_manager.go:269

func (im *realImageGCManager) GarbageCollect() error {
    // 1肚豺、獲取容器鏡像存儲目錄掛載點文件系統(tǒng)的磁盤信息
    fsStats, err := im.statsProvider.ImageFsStats()
    if err != nil {
        return err
    }

    var capacity, available int64
    if fsStats.CapacityBytes != nil {
        capacity = int64(*fsStats.CapacityBytes)
    }
    if fsStats.AvailableBytes != nil {
        available = int64(*fsStats.AvailableBytes)
    }

    if available > capacity {
        available = capacity
    }

    if capacity == 0 {
        err := goerrors.New("invalid capacity 0 on image filesystem")
        im.recorder.Eventf(im.nodeRef, v1.EventTypeWarning, events.InvalidDiskCapacity, err.Error())
        return err
    }
    // 2、若使用率大于 HighThresholdPercent界拦,此時需要回收鏡像
    usagePercent := 100 - int(available*100/capacity)
    if usagePercent >= im.policy.HighThresholdPercent {
        // 3吸申、計算需要釋放的磁盤量
        amountToFree := capacity*int64(100-im.policy.LowThresholdPercent)/100 - available

        // 4、調(diào)用 im.freeSpace 回收未使用的鏡像信息
        freed, err := im.freeSpace(amountToFree, time.Now())
        if err != nil {
            return err
        }

        if freed < amountToFree {
            err := fmt.Errorf("failed to garbage collect required amount of images. Wanted to free %d bytes, but freed %d bytes", amountToFree, freed)
            im.recorder.Eventf(im.nodeRef, v1.EventTypeWarning, events.FreeDiskSpaceFailed, err.Error())
            return err
        }
    }

    return nil
}
im.freeSpace

im.freeSpace 是回收未使用鏡像的方法享甸,其主要邏輯為:

  • 1截碴、首先調(diào)用 im.detectImages 獲取已經(jīng)使用的 images 列表作為 imagesInUse;
  • 2蛉威、遍歷 im.imageRecords 根據(jù) imagesInUse 獲取所有未使用的 images 信息日丹,im.imageRecords 記錄 node 上所有 images 的信息;
  • 3蚯嫌、根據(jù)使用時間對未使用的 images 列表進(jìn)行排序哲虾;
  • 4、遍歷未使用的 images 列表然后調(diào)用 im.runtime.RemoveImage 刪除鏡像择示,直到回收完所有未使用 images 或者滿足空閑率束凑;

k8s.io/kubernetes/pkg/kubelet/images/image_gc_manager.go:328

func (im *realImageGCManager) freeSpace(bytesToFree int64, freeTime time.Time) (int64, error) {
    // 1、獲取已經(jīng)使用的 images 列表
    imagesInUse, err := im.detectImages(freeTime)
    if err != nil {
        return 0, err
    }

    im.imageRecordsLock.Lock()
    defer im.imageRecordsLock.Unlock()

    // 2栅盲、獲取所有未使用的 images 信息
    images := make([]evictionInfo, 0, len(im.imageRecords))
    for image, record := range im.imageRecords {
        if isImageUsed(image, imagesInUse) {
            klog.V(5).Infof("Image ID %s is being used", image)
            continue
        }
        images = append(images, evictionInfo{
            id:          image,
            imageRecord: *record,
        })
    }
    // 3汪诉、按鏡像使用時間進(jìn)行排序
    sort.Sort(byLastUsedAndDetected(images))
    // 4、回收未使用的鏡像
    var deletionErrors []error
    spaceFreed := int64(0)
    for _, image := range images {
        if image.lastUsed.Equal(freeTime) || image.lastUsed.After(freeTime) {
            continue
        }

        if freeTime.Sub(image.firstDetected) < im.policy.MinAge {
            continue
        }

        // 5剪菱、調(diào)用 im.runtime.RemoveImage 刪除鏡像
        err := im.runtime.RemoveImage(container.ImageSpec{Image: image.id})
        if err != nil {
            deletionErrors = append(deletionErrors, err)
            continue
        }
        delete(im.imageRecords, image.id)
        spaceFreed += image.size
        if spaceFreed >= bytesToFree {
            break
        }
    }

    if len(deletionErrors) > 0 {
        return spaceFreed, fmt.Errorf("wanted to free %d bytes, but freed %d bytes space with errors in image deletion: %v", bytesToFree, spaceFreed,   errors.NewAggregate(deletionErrors))
    }
    return spaceFreed, nil
}

總結(jié)

本文主要分析了 kubelet 中垃圾回收機(jī)制的實現(xiàn)摩瞎,kubelet 中會定期回收 node 上已經(jīng)退出的容器已經(jīng)當(dāng) node 磁盤資源不足時回收不再使用的鏡像來釋放磁盤資源,容器以及鏡像回收策略主要是通過 kubelet 中幾個參數(shù)的閾值進(jìn)行控制的孝常。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末旗们,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子构灸,更是在濱河造成了極大的恐慌上渴,老刑警劉巖岸梨,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異稠氮,居然都是意外死亡曹阔,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進(jìn)店門隔披,熙熙樓的掌柜王于貴愁眉苦臉地迎上來赃份,“玉大人,你說我怎么就攤上這事奢米∽ズ” “怎么了?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵鬓长,是天一觀的道長谒拴。 經(jīng)常有香客問我,道長涉波,這世上最難降的妖魔是什么英上? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮啤覆,結(jié)果婚禮上苍日,老公的妹妹穿的比我還像新娘。我一直安慰自己城侧,他們只是感情好易遣,可當(dāng)我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著嫌佑,像睡著了一般。 火紅的嫁衣襯著肌膚如雪侨歉。 梳的紋絲不亂的頭發(fā)上屋摇,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天,我揣著相機(jī)與錄音幽邓,去河邊找鬼炮温。 笑死,一個胖子當(dāng)著我的面吹牛牵舵,可吹牛的內(nèi)容都是我干的柒啤。 我是一名探鬼主播,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼畸颅,長吁一口氣:“原來是場噩夢啊……” “哼担巩!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起没炒,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤涛癌,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拳话,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡先匪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了弃衍。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片呀非。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖镜盯,靈堂內(nèi)的尸體忽然破棺而出岸裙,到底是詐尸還是另有隱情,我是刑警寧澤形耗,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布哥桥,位于F島的核電站,受9級特大地震影響激涤,放射性物質(zhì)發(fā)生泄漏拟糕。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一倦踢、第九天 我趴在偏房一處隱蔽的房頂上張望送滞。 院中可真熱鬧,春花似錦辱挥、人聲如沸犁嗅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽褂微。三九已至,卻和暖如春园爷,著一層夾襖步出監(jiān)牢的瞬間宠蚂,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工童社, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留求厕,地道東北人。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓扰楼,卻偏偏與公主長得像呀癣,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子弦赖,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,047評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 在運行一段時候之后项栏,節(jié)點上會下載很多鏡像,也會有很多因為各種原因退出的容器腾节。為了保證節(jié)點能夠正常運行忘嫉,kubele...
    shinwing閱讀 3,644評論 0 0
  • Kubernetes架構(gòu) Kubernetes是當(dāng)今最流行的開源容器管理平臺荤牍,來自Google Borg的開源版本...
    華陽_3bcf閱讀 1,416評論 0 0
  • 本來這篇文章會繼續(xù)講述 kubelet 中的主要模塊,但由于網(wǎng)友反饋能不能先從 kubelet 的啟動流程開始庆冕,k...
    田飛雨閱讀 536評論 0 3
  • 一康吵、概要 kubelet 是運行在每個節(jié)點上的主要的“節(jié)點代理”,每個節(jié)點都會啟動 kubelet進(jìn)程访递,用來處理 ...
    田飛雨閱讀 19,779評論 0 8
  • 一 今晚拷姿,DISC社群線上商學(xué)院邀請我進(jìn)行一場微課分享惭载,主題是涉及DISC學(xué)習(xí)心得和應(yīng)用方面的內(nèi)容 。經(jīng)過與群主D...
    靜待花開的聲音閱讀 1,724評論 21 23