k8s资源限制

文章目录

      • 容器资源需求和限制
      • Pod服务质量类别
      • LimitRange
      • ResourceQuota

在Kubernetes中可以从容器、Pod和名称空间级别来实现资源限制

容器资源需求和限制

在Pod内的容器上可以对cpu和内存资源定义需求和限制,主要通过pod.spec.containers.resources.requests和pod.spec.containers.resources.limits两个字段实现:

  • pod.spec.containers.resources.requests:用来定义容器资源需求,表明系统需要预留给该容器使用的资源的最小可用值
  • pod.spec.containers.resources.limits:用来定义资源限制,指定了该容器可以申请使用的资源的最大值,资源限制需要大于等于资源需求

资源需求会影响Pod调度结果,如果一个节点的可用资源小于一个Pod内所有容器的资源需求的和,那么这个Pod不会被调度到这个节点上。资源限制并不影响Pod的调度结果,即一个节点上所有Pod对象资源限制数量之和可以大于节点拥有的资源量,即支持资源过载使用。不过,一旦内存资源耗尽,几乎必然的会有容器因OOMKilled而终止

配置容器的cpu和内存限制可以参考官方文档:https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/assign-memory-resource/和https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/assign-cpu-resource/

下面是一个示例:

kind: Deployment
metadata:
  name: pod-with-requests-limits
spec:
  replicas: 1
  selector:
    matchLabels:
      app: stress-ng
  template:
    metadata:
      labels:
        app: stress-ng
    spec:
      containers:
      - name: stress-ng
        image: lorel/docker-stress-ng
        imagePullPolicy: IfNotPresent
        args: ["--vm", "3", "--vm-bytes", "256M"]
        resources:
          requests:		#设置容器的资源需求
            cpu: 500m	#最少需要0.5个cpu,1cpu=1000m
            memory: 256Mi	#最少需要256MB内存
          limits:	#设置容器的资源限制
            cpu: 1	#最多可以使用1个cpu
            memory: 512Mi	#最多可以有使用512MB内存

查看Pod的资源利用率,cpu和内存的使用量是不会超过limits限制的
在这里插入图片描述
如果Pod内容器的资源需求之和超过节点的可用资源,Pod是无法被调度成功的,例如将容器的内存需求修改为100G,再进行创建,Pod会调度失败:
k8s资源限制_第1张图片
kubectl describe可以看到Pod调度失败
k8s资源限制_第2张图片

对于压缩性资源CPU来说,若未定义容器的资源需求量,在资源紧缺的情况下,该Pod占用的CPU资源可能会被压缩至极低的水平,甚至导致Pod无法运行。对于非压缩性资源内存来说,在资源紧缺的情况下可能会导致相关业务容器被杀死。所以,对于运行关键业务的Pod,建议一定要使用requests指定资源需求,或者为Pod定义高优先级避免这种情况。

另外,k8s仅确保Pod可以获得它请求的cpu时间额度,它能否获得额外的cpu时间额度,则取决与其它正在运行作业的cpu资源占用情况。例如对于总数1000m的cpu资源来说,容器A请求使用200m,容器b请求使用500m,在不超出它们最大限额的情况下,剩余的的300m会在双方都需要时以2:5(200m:500m)的比例进行分配。

Pod服务质量类别

容器的limits可以大于requests,这表节点资源可以被Pod过载使用,那么在内存资源紧张时,应该以什么顺序终结哪些Pod就成为一个问题。k8s借助Pod的服务质量类别和Pod优先级完成判定。k8s根据Pod内容器的requests和limits属性将Pod分为BestEffort、Burstable和Guaranteed三个服务质量类别:

  • Guaranteed:为Pod中的所有容器设置了cpu和内存的requests和limits属性,且requests和limits属性的值相等。这类Pod具有最高级别服务质量
  • Burstable:至少为一个容器设置了cpu和内存的requests或limits属性,但不满足Guaranteed类别的要求,这类Pod具有中等级别服务质量
  • BestEffort:没有为任何一个容器设置requests和limits属性,这类Pod具有最低的服务质量

一旦内存资源紧张,BestEffort类别的Pod首先被终止,因为系统不为其提供任何级别的资源保证,但相应的这类Pod可以尽量占用更多的资源。如果此时已经没有BestEffort类别的Pod,接下来会接着终止Burstable类别的Pod。Guaranteed类别的Pod拥有最高优先级,它们不会被杀死,除非其内存资源超过限制,或者没有更低服务质量类别的Pod。

每个运行状态的Pod都有其OOM评分,评分越高越优先被杀死。OOM评分主要根据两个维度进行计算:从服务质量类别继承的默认分值和容器的可用内存资源比例。同等类别的Pod对象的默认分值相同,Guaranteed类别Pod的默认分值为-998,BestEffort类别Pod的默认分值为1000,Burstable类别未定义默认分值,其OOM分值会通过相应的算法计算得出。

因此,同等级别的Pod资源,在内存资源紧缺别终止时,与自身的requests属性相比,内存占用比例最大的Pod会优先被杀死。例如:同属于Burstable类别,PodA内存使用比例为%95,PodB内存占用比例为%80,此时PodA会被终止

LimitRange

LimitRange官方文档:https://kubernetes.io/zh-cn/docs/concepts/policy/limit-range/

虽然可以在Pod内的容器上定义cpu和内存的需求和限制,但这不是强制性要求,那些未定义明确需求的Pod有可能会占用节点上的所有资源。这个时候可以使用LimitRange资源在名称空间中限制每个容器的最小和最大资源用量,以及为那些没有设置资源需求和资源限制属性的容器设置默认的资源需求和限制。

LimitRange支持在Pod级别和容器级别分别设置cpu和内存的可用范围。一旦在名称空间上启用了LimitRange,该名称空间中Pod或容器的requests和limits属性值必须在LimitRange定义的可用的范围之内,否则请求会被拒绝。而未显式指定requests和limits属性的容器,将会从LimitRange资源上自动继承相应的默认设置。这都是LimitRange准入控制器的功能。

另外,LimitRange也支持对PVC的存储空间进行限制,它用于限制创建PVC时请求的存储空间不能超过指定范围。同样的,未指定requests和limits属性值的PVC也会从LimitRange继承默认值

需要注意,LimitRange是名称空间级别资源,需要在每个名称空间都定义;定义的限制仅对LimitRange创建后的Pod和PVC创建请求生效,对之前已存在的资源无效。

下面是一个示例:

apiVersion: v1
kind: LimitRange
metadata:
  name: limitrange-demo
  namespace: default	#针对default名称空间生效
spec:
  limits:	#定义限制
  - type: Container		#针对容器的限制
    min:	#单个容器最小可用的资源,容器的requests属性值要>=这里的设置
      cpu: 100m		#cpu最少申请100m
      memory: 128Mi	#内存最少申请128Mi
    max:	#单个容器最大可用的资源,容器的limits属性值要<=这里的设置
      cpu: 2	#cpu最多使用2核
      memory: 2Gi	#内存最多使用2G
    default:	#容器limits属性的默认值设置,在容器未设置limits属性时生效
      cpu: 500m		#默认单容器cpu限制使用500m
      memory: 512Mi		#默认单容器内存限制使用512Mi
    defaultRequest:		#容器requests属性的默认值设置,在容器未设置requests属性时生效
      cpu: 200m		#默认单容器cpu申请200m
      memory: 256Mi		#默认单容器内存申请256Mi
    maxLimitRequestRatio:	#容器limits和requests属性的最大比值
      cpu: 4	#limits.cpu/requests.cpu不能超过4倍
      memory: 4		#limits.memory/requests.memory不能超过4倍
  - type: Pod		#针对Pod的限制,
    max:		#单Pod内所有容器最大可用资源限制
      cpu: 4		#单Pod内所有容器最多可用4个cpu
      memory: 4Gi	#Pod内所有容器最多可用4G
    min:		#单Pod内所有容器最小可用资源限制
      cpu: 200m		#单Pod内所有容器最少需要申请200m cpu
      memory: 256Mi		#单Pod内所有容器最少需要申请256Mi内存
  - type: PersistentVolumeClaim		#针对PVC资源存储空间的限制
    min:	
      storage: 1Gi		#单个PVC最少申请1Gi空间
    max:
      storage: 10Gi		#单个PVC最多申请10Gi空间
    default:	#PVC的默认limits属性值,PVC未设置limits属性时生效
      storage: 5Gi
    defaultRequest:		#PVC的默认requests属性值,PVC未设置requests属性时生效
      storage: 2Gi
    maxLimitRequestRatio:	#PVC资源的limits属性和requests属性的最大比值
      storage: 5		#PVC子资源limits.storage/requests.storage最多为5倍

注意:Pod级别不支持设置默认值,一般情况下只设置最大限制,不设置最小限制,这里只是一个示例。

查看LimitRange资源:
k8s资源限制_第3张图片

创建不符合limitrange-demo限制的Pod,看是否可以创建成功,验证LimitRange资源的限制是否生效。部署文件如下,其中容器的limits.cpu/requests.cpu超过4倍,不符合LimitRange的要求。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: limitrange-test-deploy
spec:
  replicas: 2
  selector:
    matchLabels:
      app: limitrange-test-deploy
  template:
    metadata:
      labels:
        app: limitrange-test-deploy
    spec:
      containers:
      - name: nginx
        image: nginx
        imagePullPolicy: IfNotPresent
        resources:		
          requests:		
            cpu: 200m
            memory: 256Mi
          limits:
            cpu: 1
            memory: 512Mi

创建之后,查询没有Pod被创建出来,如下图:
k8s资源限制_第4张图片
然后通过kubectl get deploy/limitrange-test-deploy -o yaml的输出可以看到Pod违反了资源限制,所以创建失败
k8s资源限制_第5张图片

再创建一些未设置资源需求和限制的容器,验证LimitRange资源是否会为容器设置默认的资源需求和限制。部署文件如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: limitrange-test-deploy1
spec:
  replicas: 2
  selector:
    matchLabels:
      app: limitrange-test-deploy1
  template:
    metadata:
      labels:
        app: limitrange-test-deploy1
    spec:
      containers:
      - name: nginx
        image: nginx
        imagePullPolicy: IfNotPresent

创建之后,通过kubectl describe的输出可以看到容器被添加了limits和requests属性值,和LimitRange资源设定的默认值一致。
k8s资源限制_第6张图片

ResourceQuota

ResourceQuota官方文档:https://kubernetes.io/zh-cn/docs/concepts/policy/resource-quotas/
ResourceQuota资源能够在名称空间级别定义资源的配额,它支持以资源类型来限制可在名称空间中创建的相关资源对象的数量,以及这些资源对象消耗的计算资源总量。ResourceQuota的功能由同名的ResourceQuota准入控制器实现。

ResourceQuota支持针对计算资源配额、存储资源配额和资源对象数量配额。

计算资源配额,ResourceQuota可以限制名称空间中所有非终止状态的所有Pod对象的计算资源需求和计算资源限制的总量。可以使用下面这些字段进行定义:

  • limits.cpu:CPU 资源的限制总量不能超过该值。
  • limits.memory:内存资源的限制总量不能超过该值。
  • requests.cpu或cpu:CPU资源请求总量不能超过该值。
  • requests.memory或memory: 内存请求总量不能超过该值。
  • hugepages- 指定页面大小的大页内存请求总数不能超过此值

另外也可以对扩展资源进行限制,比如GPU,对于扩展资源而言,目前仅允许使用前缀为 requests. 的配额项:

  • requests.nvidia.com/gpu:设置可以请求使用的GPU数量

存储资源配额,ResourceQuota资源支持为名称空间中的所有PVC资源设置存储空间需求和存储空间限制的总量,它支持从名称空间中的全部PVC、隶属与特定存储类的PVC和基于临时存储的PVC进行限制。可以使用下面这些字段进行定义:

  • requests.storage:所有PVC请求存储空间的总量
  • persistentvolumeclaims:可以创建的PVC数量限制
  • .storageclass.storage.k8s.io/requests.storage:隶属于指定存储类的所有PVC请求存储空间的总量
  • .storageclass.storage.k8s.io/persistentvolumeclaims:可以创建的隶属于指定存储类的PVC的数量限制

对象数量配额,ResourceQuota支持对名称空间中可创建的资源对象的数量进行限制,通过以下字段实现

  • count/.:用于非核心API群组的资源,例如deployments、jobs、cronjobs等
  • count/:用于核心组的资源,例如pods、services、persistentvolumes等

一些资源可以省略前面的count前缀,如下所示:
k8s资源限制_第7张图片

下面是一个示例:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: resource-quota-demo
  namespace: default
spec:
  hard:
    requests.cpu: 2
    requests.memory: 4Gi
    limits.cpu: 4
    limits.memory: 8Gi
    persistentvolumeclaims: 10
    requests.storage: 100Gi
    managed-nfs-storage.storageclass.storage.k8s.io/persistentvolumeclaims: 5	#限制在managed-nfs-storage这个存储类上最多创建5个pvc
    managed-nfs-storage.storageclass.storage.k8s.io/requests.storage: 30Gi
    pods: 5
    services: 5
    count/deployments.apps: 3
    count/statefulsets.apps: 3
    count/roles.rbac.authorization.k8s.io: 10

创建后,查看ResourceQuota资源详情:
k8s资源限制_第8张图片

创建一个具有6副本的deployment,验证ResourceQuotai资源对象对于pod数量配额是否生效:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: resourcequota-test-deploy
spec:
  replicas: 6
  selector:
    matchLabels:
      app:  resourcequota-test-deploy
  template:
    metadata:
      labels:
        app: resourcequota-test-deploy
    spec:
      containers:
      - name: nginx
        image: nginx
        imagePullPolicy: IfNotPresent

创建之后查看只创建了5个Pod,deployment的当前副本数为5
k8s资源限制_第9张图片
然后通过kubecetl get deploy/resourcequota-test-deploy -o yaml命令查看status字段,提示Pod数量超出限制,请求被拒绝
k8s资源限制_第10张图片

再创建两个Pod,它们的requests.cpu之和超过2,验证ResourceQuota资源对象对于cpu和内存的配额是否生效

apiVersion: apps/v1
kind: Deployment
metadata:
  name: resourcequota-test-deploy1
spec:
  replicas: 2
  selector:
    matchLabels:
      app:  resourcequota-test-deploy1
  template:
    metadata:
      labels:
        app: resourcequota-test-deploy1
    spec:
      containers:
      - name: nginx
        image: nginx
        imagePullPolicy: IfNotPresent
        resources:
          requests:
            cpu: 1.5
            memory: 1Gi
          limits:
            cpu: 1.5
            memory: 1Gi

创建之后查看只创建了一个Pod,另一个Pod未创建
k8s资源限制_第11张图片
同样查看deployment的status字段,可以看到requests.cpu超出配额,所以另一个Pod创建失败
k8s资源限制_第12张图片

最后需要注意,和LimitRange不同,ResourceQuota资源对象创建之前已存在的资源对象也会在被ResourceQuota资源对象计入配额统计范围,即之前已存在的资源对象也会占用配额。

关于ResourceQutoa的更多使用方式,比如配额作用域、基于优先级设置配额等可以参考官方文档:https://kubernetes.io/zh-cn/docs/concepts/policy/resource-quotas/

你可能感兴趣的:(kubernetes)