资讯专栏INFORMATION COLUMN

Kubelet源码分析(四) diskSpaceManager

hlcfan / 706人阅读

摘要:顾名思义就是管理磁盘空间的,实际它的实现较为简单,就是给所在的节点预留磁盘空间的,当该节点磁盘空间低于该值时,将拒绝的创建。其实就是中的接口该接口很简单,就是分别调用实现的两个接口,然后判断磁盘空间是否够用。

源码版本

kubernetes version: v1.3.0

简介

前一节介绍了Garbage Collection,涉及到的策略基本与磁盘资源有关。对于k8s集群如何高效的利用各种资源,也非常值得我们涉猎学习。管理好资源才能更好的创建服务,所以这节继续学习kubelet的diskSpaceManager。
diskSpaceManager顾名思义就是管理磁盘空间的,实际它的实现较为简单,就是给kubelet所在的节点预留磁盘空间的,当该节点磁盘空间低于该值时,将拒绝Pod的创建。

策略初始化

跟GC介绍的套路一样,先从策略入手。
相关的结构如下:

type DiskSpacePolicy struct {
    // free disk space threshold for filesystem holding docker images.
    DockerFreeDiskMB int
    // free disk space threshold for root filesystem. Host volumes are created on root fs.
    RootFreeDiskMB int
}

该结构的出厂设置在cmd/kubelet/app/server.go中的UnsecuredKubeletConfig()接口进行。

func UnsecuredKubeletConfig(s *options.KubeletServer) (*KubeletConfig, error) {
...
    diskSpacePolicy := kubelet.DiskSpacePolicy{
        DockerFreeDiskMB: int(s.LowDiskSpaceThresholdMB),
        RootFreeDiskMB:   int(s.LowDiskSpaceThresholdMB),
    }
...
}

赋值的KubeletServer的LowDiskSpaceThresholdMB参数的初始化在cmd/kubelet/app/options/options.go中的NewKubeletServer()接口中进行:

func NewKubeletServer() *KubeletServer {
    return &KubeletServer{
        ...
        LowDiskSpaceThresholdMB:      256,
        ...
    }
}

diskSpaceManager保留磁盘空间的默认值是256MB,当然这个值我们也可以手动修改,这些kubelet的手动配置都是在cmd/kubelet/app/options/options.go中的AddFlags()接口:

func (s *KubeletServer) AddFlags(fs *pflag.FlagSet) {
...
    fs.Int32Var(&s.LowDiskSpaceThresholdMB, "low-diskspace-threshold-mb", s.LowDiskSpaceThresholdMB, "The absolute free disk space, in MB, to maintain. When disk space falls below this threshold, new pods would be rejected. Default: 256")
...

所以手动的话,需要修改kubelet命令flags中的low-diskspace-threshold-mb。

diskSpaceManager初始化

先介绍diskSpaceManager:

type diskSpaceManager interface {
    // Checks the available disk space
    IsRootDiskSpaceAvailable() (bool, error)
    IsRuntimeDiskSpaceAvailable() (bool, error)
}

跟前一章的套路一样,该diskSpaceManager是个interface。
具体的初始化需要查看pkg/kubelet/kubelet.go中的NewMainKubelet()接口。
调用流程: main -- app.Run -- UnsecuredKubeletConfig --> RunKubelet -- CreateAndInitKubelet -- NewMainKubelet
NewMainKubelet接口如下:

func NewMainKubelet(
    hostname string,
    nodeName string,
...
) (*Kubelet, error) {
...
    diskSpaceManager, err := newDiskSpaceManager(cadvisorInterface, diskSpacePolicy)
    if err != nil {
        return nil, fmt.Errorf("failed to initialize disk manager: %v", err)
    }
...
}

继续查看newDiskSpaceManager()接口,该接口传入了cadvisorInterface和diskSpacePolicy参数,比较好理解这些参数,因为磁盘管理需要使用cAdvisor来获取磁盘信息,而diskSpacePolicy就是上面介绍策略初始化的结构。
该接口在pkg/kubelet/disk_manager.go中,继续查看源码:

func newDiskSpaceManager(cadvisorInterface cadvisor.Interface, policy DiskSpacePolicy) (diskSpaceManager, error) {
    // 检查策略参数是否有效
    // 实际就是判断下是否 < 0
    err := validatePolicy(policy)
    if err != nil {
        return nil, err
    }

    dm := &realDiskSpaceManager{
        cadvisor:   cadvisorInterface,
        policy:     policy,
        cachedInfo: map[string]fsInfo{},
    }

    return dm, nil
}

该接口的返回值是diskSpaceManager,而实际返回是realDiskSpaceManager结构体。
所以所有diskSpaceManager的接口实现需要根据realDiskSpaceManager进行查看。
realDiskSpaceManager结构如下:

type realDiskSpaceManager struct {
    // 用于获取磁盘相关信息
    cadvisor   cadvisor.Interface
    // 用于缓存文件系统信息
    cachedInfo map[string]fsInfo // cache of filesystem info.
    // 操作锁
    lock       sync.Mutex        // protecting cachedInfo.
    // 磁盘管理策略
    policy     DiskSpacePolicy   // thresholds. Set at creation time.
}

查看下realDiskSpaceManager结构实现的diskSpaceManager接口:

// 查看Runtime占用的磁盘空间是否够用
func (dm *realDiskSpaceManager) IsRuntimeDiskSpaceAvailable() (bool, error) {
    return dm.isSpaceAvailable("runtime", dm.policy.DockerFreeDiskMB, dm.cadvisor.ImagesFsInfo)
}
// 查看根目录的磁盘空间是否够用
func (dm *realDiskSpaceManager) IsRootDiskSpaceAvailable() (bool, error) {
    return dm.isSpaceAvailable("root", dm.policy.RootFreeDiskMB, dm.cadvisor.RootFsInfo)
}

上面两个方法最终都调用了dm.isSpaceAvailable(),该接口使用了3个参数:

文件系统类型

磁盘保留空间大小,用于判断是否有效

cAdvisor的接口,用于获取RootFS和ImagesFs使用的磁盘情况

而该接口返回的是一个Bool值,true or false。
接口如下:

func (dm *realDiskSpaceManager) isSpaceAvailable(fsType string, threshold int, f func() (cadvisorapi.FsInfo, error)) (bool, error) {
    fsInfo, err := dm.getFsInfo(fsType, f)
    if err != nil {
        return true, fmt.Errorf("failed to get fs info for %q: %v", fsType, err)
    }
    // 有效值判断
    if fsInfo.Capacity == 0 {
        return true, fmt.Errorf("could not determine capacity for %q fs. Info: %+v", fsType, fsInfo)
    }
    if fsInfo.Available < 0 {
        return true, fmt.Errorf("wrong available space for %q: %+v", fsType, fsInfo)
    }
    // 判断该文件系统可用的磁盘空间是否小于最小预留空间
    if fsInfo.Available < int64(threshold)*mb {
        glog.Infof("Running out of space on disk for %q: available %d MB, threshold %d MB", fsType, fsInfo.Available/mb, threshold)
        return false, nil
    }
    return true, nil
}

继续看dm.getFsInfo()接口:

func (dm *realDiskSpaceManager) getFsInfo(fsType string, f func() (cadvisorapi.FsInfo, error)) (fsInfo, error) {
    dm.lock.Lock()
    defer dm.lock.Unlock()
    // 先查看缓存中的文件系统信息
    // 需要比较该信息的时间有效性,为2s内
    fsi := fsInfo{}
    if info, ok := dm.cachedInfo[fsType]; ok {
        timeLimit := time.Now().Add(-2 * time.Second)
        if info.Timestamp.After(timeLimit) {
            fsi = info
        }
    }
    // 2s之外的话,需要调用cAdvisor接口重新获取磁盘信息
    if fsi.Timestamp.IsZero() {
        // 该f()接口作为参数传入,不同的文件系统对应不同的接口
        // runtime: dm.cadvisor.ImagesFsInfo
        // rootfs: dm.cadvisor.RootFsInfo
        fs, err := f()
        if err != nil {
            return fsInfo{}, err
        }
        fsi.Timestamp = time.Now()
        fsi.Usage = int64(fs.Usage)
        fsi.Capacity = int64(fs.Capacity)
        fsi.Available = int64(fs.Available)
        // 更新cache
        dm.cachedInfo[fsType] = fsi
    }
    return fsi, nil
}
diskSpaceManager实现

上面的初始化可以看到diskSpaceManager的两个关键性接口IsRuntimeDiskSpaceAvailable()和IsRootDiskSpaceAvailable(),说简单点就是用于判断对应的磁盘空间是否还够用。
该功能具体作用的地方,一下子找不到的话,我们可以通过搜索上面两个接口来查看调用者。
其实就是pkg/kubelet/kubelet.go中的isOutOfDisk()接口:

// handleOutOfDisk detects if pods can"t fit due to lack of disk space.
func (kl *Kubelet) isOutOfDisk() bool {
    // Check disk space once globally and reject or accept all new pods.
    withinBounds, err := kl.diskSpaceManager.IsRuntimeDiskSpaceAvailable()
    // Assume enough space in case of errors.
    if err != nil {
        glog.Errorf("Failed to check if disk space is available for the runtime: %v", err)
    } else if !withinBounds {
        return true
    }

    withinBounds, err = kl.diskSpaceManager.IsRootDiskSpaceAvailable()
    // Assume enough space in case of errors.
    if err != nil {
        glog.Errorf("Failed to check if disk space is available on the root partition: %v", err)
    } else if !withinBounds {
        return true
    }
    return false
}

该接口很简单,就是分别调用diskSpaceManager实现的两个接口,然后判断磁盘空间是否够用。
到这里我们可以猜想一下,最开始介绍该功能的时候已经说了是用于预留磁盘空间,以此为条件来判断Pods是否能成功创建。所以可以想到这个isOutOfDisk()肯定是作用于Pods创建的流程中,在创建之前判断是否有条件可以创建。
具体的Pod管理流程内容较多,后面新启一篇文章进行多带带介绍。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/32535.html

相关文章

  • Kubelet源码分析(一):启动流程分析

    摘要:源码版本简介在急群众,在每个节点上都会启动一个服务进程。该进程用于处理节点下发到本节点的任务,管理及中的容器。每个进程会在上注册节点自身信息,定期向节点汇报节点资源的使用情况,并通过监控容器和节点资源。最后运行健康检测服务。 源码版本 kubernetes version: v1.3.0 简介 在Kubernetes急群众,在每个Node节点上都会启动一个kubelet服务进程。该进程...

    mindwind 评论0 收藏0
  • Kubelet源码分析(三):Garbage Collection

    摘要:源码版本介绍在分析启动流程时,老是会碰到各类,这里单独提出来做下较详细的分析。主要由两部分组成使用指定的回收策略,删除那些已经结束的所有的生命周期管理就是通过来实现的,其实该也是依赖了。相关配置该值表示磁盘占用率达到该值后会触发。 源码版本 kubernetes version: v1.3.0 kubelet GC介绍 在分析kubelet启动流程时,老是会碰到各类GC,这里单独提出来...

    siberiawolf 评论0 收藏0
  • Kubelet无法访问rancher-metadata问题分析

    摘要:引言能够支持,可以快速几乎无障碍的拉起一套环境,这对刚入门的小白来说简直是一大利器。本文就分析一下关于无法访问问题。检查一下有问题的节点的系统,果然会发现安装了服务服务名为。 引言 Rancher能够支持Kubernetes,可以快速几乎无障碍的拉起一套K8s环境,这对刚入门K8s的小白来说简直是一大利器。当然由于系统特性五花八门,系统内置软件也相互影响,所以有时候伙伴们会碰到比较难缠...

    hiYoHoo 评论0 收藏0
  • Kubelet无法访问rancher-metadata问题分析

    摘要:引言能够支持,可以快速几乎无障碍的拉起一套环境,这对刚入门的小白来说简直是一大利器。本文就分析一下关于无法访问问题。检查一下有问题的节点的系统,果然会发现安装了服务服务名为。 引言 Rancher能够支持Kubernetes,可以快速几乎无障碍的拉起一套K8s环境,这对刚入门K8s的小白来说简直是一大利器。当然由于系统特性五花八门,系统内置软件也相互影响,所以有时候伙伴们会碰到比较难缠...

    bigdevil_s 评论0 收藏0
  • Kubelet源码分析(二): DockerClient

    摘要:接口分析源码目录实现的接口如下可以看到结构体实现了所有的接口。这些接口其实是对的操作接口进行了封装,下面取一个接口进行分析该接口的关键就是调用了所以关键对象还是,继续回到上面讲初始化时介绍到的结构体。 源码版本 kubernetes version: v1.3.0 DockerClient初始化 DockerClient是KubeletConfig的成员之一。KubeletConfig...

    李世赞 评论0 收藏0

发表评论

0条评论

hlcfan

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<