47

如何限制Kubernetes本地临时存储的容量1559248280668090

 4 years ago
source link: https://www.tuicool.com/articles/BRrEbij
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

介绍

作为Kubernetes平台的提供方,必须要对某些“流氓”应用做出一些限制,防止它们滥用平台的CPU、内存、磁盘、网络等资源。

例如,Kubernetes提供了对CPU,内存的限制,可以防止应用无限制的使用系统的资源;Kubernetes提供的PVC,如CephFS、RBD,也支持容量的限制。

但是,早期Kubernetes版本并没有限制container的rootfs的容量,由于默认容器使用的log存储空间是在 /var/lib/kubelet/ 下,rootfs在/var/lib/docker下,而这两个目录默认就在宿主机Node的根分区,如果应用恶意攻击,可以通过在容器内大量dd从而迅速造成宿主机Node根分区文件系统满。我们知道,当Linux根分区使用达到100%的时候,通常会很危险。

Kubernetes在1.8版本引入了一种新的resource:local ephemeral storage(临时存储),用来管理本地临时存储,对应特性 LocalStorageCapacityIsolation。从1.10开始该特性转为beta状态,默认开启。 如果你想和更多 Kubernetes 技术专家交流,可以加我微信liyingjiese,备注『加群』。群里每周都有全球各大公司的最佳实践以及行业最新动态

临时存储,如emptyDir volumes, container logs,image layers and container writable layers,默认它们使用的是 /var/lib/kubelet,通过限制临时存储容量,也就可以保护Node的root分区了。

本地临时存储管理只对root分区有效,如果你定制了相关的参数,例如 --root-dir,则不会生效。

配置

我的集群版本是1.14,默认开启了 local ephemeral storage 的特性,只需要配置Pod即可。

Pod的每个container都可以配置:

  • spec.containers[].resources.limits.ephemeral-storage
  • spec.containers[].resources.requests.ephemeral-storage

单位是byte,可以直接配置,也可以按E/P/T/G/M/K或者Ei, Pi, Ti, Gi, Mi, Ki.为单位来配置,例如 128974848, 129e6, 129M, 123Mi 表示的是同一个容量。

下面创建一个Deployment,设置其使用的临时存储最大为2Gi。

apiVersion: extensions/v1beta1

kind: Deployment

metadata:

name: nginx

namespace: default

spec:

selector:

matchLabels:

  run: nginx

template:

metadata:

  labels:

    run: nginx

spec:

  containers:

  - image: nginx

    name: nginx

    resources:

      limits:

        ephemeral-storage: 2Gi

      requests:

        ephemeral-storage: 2Gi

Pod启动后,进入容器,执行dd if=/dev/zero of=/test bs=4096 count=1024000 ,尝试创建一个4Gi的文件,可以发现在执行一段时间后,Pod被 Evict,controller重新创建了新的Pod。

nginx-75bf8666b8-89xqm                    1/1     Running             0          1h

nginx-75bf8666b8-pm687                    0/1     Evicted             0          2h

实现

Evict Pod动作是由kubelet完成的。每个节点上的kubelet会启动一个evict manager,每10秒种(evictionMonitoringPeriod)进行一次检查,ephemeral storage的检查也是在这个阶段完成的。

evict manager可以对pod和container来检查超额应用。

func (m *managerImpl) localStorageEviction(summary *statsapi.Summary, pods []*v1.Pod) []*v1.Pod {

statsFunc := cachedStatsFunc(summary.Pods)

evicted := []*v1.Pod{}

for _, pod := range pods {

    podStats, ok := statsFunc(pod)

    if !ok {

        continue

    }



    if m.emptyDirLimitEviction(podStats, pod) {

        evicted = append(evicted, pod)

        continue

    }



    if m.podEphemeralStorageLimitEviction(podStats, pod) {

        evicted = append(evicted, pod)

        continue

    }



    if m.containerEphemeralStorageLimitEviction(podStats, pod) {

        evicted = append(evicted, pod)

    }

}



return evicted

} 

其中Pod为GetActivePods获取的本节点所有非Terminated状态的Pod。

kubelet会依此检查Pod的emptyDir、Pod级临时存储、container级临时存储,若Pod需要被evict,则加到evicted数组,之后会将evicted的Pod挤出。

contaier级检查比较简单,因为ephemeral storage设置的就是在container上,依次检查container的使用情况和设置的limits,如果超过了limits,则要加入到evicted pods列表中。

相关代码在 containerEphemeralStorageLimitEviction 中。

而Pod级别的检查会复杂一点。

首先是限制值的计算。

kubelet会统计Pod所有container(但不包括init container)的ephemeral storage limits之和。init container指定的是Pod的配额最低需求(有点像最低工资标准,用于生活保障),当所有container指定的配额,超过init container指定的配额时,将忽略init container指定的配额。数学描述如下。

max(sum(containers), initContainer1, initContainer2, ...)

而实际临时存储用量的计算,除了会计算指定过ephemeral storage的container的使用量,还会统计未指定过ephemeral storage的container,以及emptyDir的使用量。

当实际临时存储用量,超过了限制值时,kubelet会将该Pod Evict,然后等待controller重新创建新的Pod并重新调度。

相关代码在 podEphemeralStorageLimitEviction 中。

requests

注意,设置的local ephemeralstorage requests在evict manager处理过程中没有用到。但是它不是没用的。

创建Pod后,scheduler会将该Pod调度到集群中某个node上。由于每个node所能承载的local ephemeral storage是有上限的,所以scheduler会保证该node上所有Pod的 local ephemeralstorage requests 总和不会超过node的根分区容量。

inode 保护

有的时候,我们会发现磁盘写入时会报磁盘满,但是df查看容量并没有100%使用,此时可能只是因为inode耗尽造成的。因此,对平台来说,inode的保护也是需要的。

其中,podLocalEphemeralStorageUsage 也统计了container或者pods使用的inode的数量。

但是当前Kubernetes并不支持对Pod的临时存储设置inode的limits/requests。

当然了,如果node进入了inode紧缺的状态,kubelet会将node设置为 under pressure,不再接收新的Pod请求。

emptyDir

emptyDir也是一种临时存储,因此也需要限制使用。

在Pod级别检查临时存储使用量时,也会将emptyDir的使用量计算在内,因此如果对emptyDir使用过量后,也会导致该Pod被kubelet Evict。

另外,emptyDir本身也可以设置容量上限。如下所摘录编排文件片段,我指定了emptyDir使用内存作为存储介质,这样用户可以获得极好的读写性能,但是由于内存比较珍贵,我只提供了64Mi的空间,当用户在 /cache 目录下使用超过64Mi后,该Pod会被kubelet evict。

volumeMounts:

    - mountPath: /cache

      name: cache-volume

  volumes:

  - emptyDir:

      medium: Memory

      sizeLimit: 64Mi

    name: cache-volume

相关代码在 emptyDirLimitEviction 中。

原文链接: https://ieevee.com/tech/2019/0 ... .html


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK