合理设置K8S中的Requests和Limits

发布时间 2023-06-01 19:10:59作者: jiayou111

所有容器都应该设置Requests

requests 的值并不是指给容器实际分配的资源大小,它仅仅是给调度器看的,调度器会 "观察" 每个节点可以用于分配的资源有多少,也知道每个节点已经被分配了多少资源。被分配资源的大小就是节点上所有 Pod 中定义的容器 requests 之和,它可以计算出节点剩余多少资源可以被分配(可分配资源减去已分配的 requests 之和)。如果发现节点剩余可分配资源大小比当前要被调度的 Pod 的 reuqests 还小,那么就不会考虑调度到这个节点,反之,才可能调度。所以,如果不配置requests,那么调度器就不能知道节点大概被分配了多少资源出去,调度器得不到准确信息,也就无法做出合理的调度决策,很容易造成调度不合理,有些节点可能很闲,而有些节点可能很忙,甚至 NotReady。

所以,建议是给所有容器都设置 requests,让调度器感知节点有多少资源被分配了,以便做出合理的调度决策,让集群节点的资源能够被合理的分配使用,避免陷入资源分配不均导致一些意外发生。

CPU requests与limits的一般性建议

• 如果不确定应用最佳的 CPU 限制,可以不设置 CPU limit,参考: Understanding resource limits in kubernetes: cpu time。
• 如果要设置 CPU requests,大多可以设置到不大于 1 核,除非是 CPU 密集型应用。

为什么 CPU 利用率远不到 limit 还会被 throttle ?

CPU 限流是因为内核使用 CFS 调度算法,对于微突发场景,在一个 CPU 调度周期内 (100ms) 所占用的时间超过了 limit 还没执行完,就会强制 "抢走" CPU 使用权(throttle),等待下一个周期再执行,但是时间拉长一点,进程使用 CPU 所占用的时间比例却很低,监控上就看不出来 CPU 有突增,但实际上又被 throttle 了。

怎样设置才能提高资源利用率?

如果给你的应用设置较高的 requests 值,而实际占用资源长期远小于它的 requests 值,导致节点整体的资源利用率较低。当然这对时延非常敏感的业务除外,因为敏感的业务本身不期望节点利用率过高,影响网络包收发速度。所以对一些非核心,并且资源不长期占用的应用,可以适当减少 requests 以提高资源利用率。
如果你的服务支持水平扩容,单副本的 requests 值一般可以设置到不大于 1 核,CPU 密集型应用除外。比如 coredns,设置到 0.1 核就可以,即 100m。

尽量避免使用过大的 requests 与 limit

如果你的服务使用单副本或者少量副本,给很大的 requests 与 limit,让它分配到足够多的资源来支撑业务,那么某个副本故障对业务带来的影响可能就比较大,并且由于 requests 较大,当集群内资源分配比较碎片化,如果这个 Pod 所在节点挂了,其它节点又没有一个有足够的剩余可分配资源能够满足这个 Pod 的 requests 时,这个 Pod 就无法实现漂移,也就不能自愈,加重对业务的影响。
相反,建议尽量减小 requests 与 limit,通过增加副本的方式来对你的服务支撑能力进行水平扩容,让你的系统更加灵活可靠。

老是忘记设置怎么办?

可以对一个namespace下的所有pod设置限制的默认值————LimitRange
LimitRange不会影响已经创建的资源

# 配置默认的requests和limits
apiVersion: v1
kind: LimitRange
metadata:
  name: cpu-mem-limit-range 
spec:
  limits:
  - default:
      cpu: 1
      memory: 512Mi
    defaultRequest:
      cpu: 0.5
      memory: 256Mi
    type: Container

# 创建pod
apiVersion: v1
kind: Pod
metadata:
  name: default-cpu-demo
spec:
  containers:
  - name: default-cpu-demo-ctr
    image: nginx

# 查看此pod的配置
k get pod default-cpu-demo -o yaml
apiVersion: v1
kind: Pod
metadata:
  name: default-cpu-demo
  namespace: default
spec:
  containers:
  - image: nginx
    imagePullPolicy: Always
    name: default-cpu-demo-ctr
    resources:
      limits:
        cpu: "1"
        memory: 512Mi
      requests:
        cpu: 500m
        memory: 256Mi

如果配置了limits而没有配置requests,那么requests默认值就会被设置为limits配置的参数。

apiVersion: v1
kind: LimitRange
metadata:
  name: cpu-mem-limit-range 
spec:
  limits:
  - default:
      cpu: 1
      memory: 512Mi
    type: Container

# 查看此pod的配置
k get pod default-cpu-demo -o yaml
apiVersion: v1
kind: Pod
metadata:
  name: default-cpu-demo
  namespace: default
spec:
  containers:
  - image: nginx
    imagePullPolicy: Always
    name: default-cpu-demo-ctr
    resources:
      limits:
        cpu: "1"
        memory: 512Mi
      requests:
        cpu: "1"
        memory: 512Mi# 配置requests和limits的范围
apiVersion: v1
kind: LimitRange
metadata:
  name: cpu-min-max-demo-lr
spec:
  limits:
  - max:
      cpu: "800m"
      memory: 1Gi
    min:
      cpu: "200m"
      memory: 500Mi
    type: Container

# 会自动添加一个默认配置
k get limitrange cpu-min-max-demo-lr -o yaml
apiVersion: v1
kind: LimitRange
metadata:
  name: cpu-min-max-demo-lr
  namespace: default
spec:
  limits:
  - default:
      cpu: 800m
      memory: 1Gi
    defaultRequest:
      cpu: 800m
      memory: 1Gi
    max:
      cpu: 800m
      memory: 1Gi
    min:
      cpu: 200m
      memory: 500Mi
    type: Container# 限制申请存储空间的大小
apiVersion: v1
kind: LimitRange
metadata:
  name: storagelimits 
spec:
  limits:
  - max:
      storage: 2Gi
    min:
      storage: 1Gi
    type: PersistentVolumeClaim 

避免测试 namespace 消耗过多资源影响生产业务

若生产集群有用于测试的 namespace,如果不加以限制,可能导致集群负载过高,从而影响生产业务。可以使用 ResourceQuota 来限制测试 namespace 的 requests 与 limit 的总大小。

对一个namespace设置限制——ResourceQuota
namespace在创建了ResourceQuota后才会启用资源使用的配额,没有创建ResourceQuota的Namespace不限制资源使用。

# 定义
apiVersion: v1
kind: ResourceQuota
metadata:
  name: resource-test
  labels:
    app: resourcequota
spec:
  hard:
    pods: 50
    requests.cpu: 0.5
    requests.memory: 512Mi
    limits.cpu: 5
    limits.memory: 16Gi
    configmaps: 20
    requests.storage: 40Gi
    persistentvolumeclaims: 20
    replicationcontrollers: 20
    secrets: 20
    services: 50
    services.loadbalancers: "2"
    services.nodeports: "10"

# 查看详情
k get quota
NAME            AGE   REQUEST    LIMIT
resource-test   12m   configmaps: 1/20, persistentvolumeclaims: 2/20, pods: 1/50, replicationcontrollers: 0/20, requests.cpu: 0/500m, requests.memory: 0/512Mi, requests.storage: 200Mi/40Gi, secrets: 0/20, services: 5/50, services.loadbalancers: 0/2, services.nodeports: 1/10   limits.cpu: 0/5, limits.memory: 0/16Gi

# 创建测试ns
k create ns quota-example

# 使用
apiVersion: v1
kind: ResourceQuota
metadata:
  name: object-quota-demo
  namespace: quota-example
spec:
  hard:
    persistentvolumeclaims: 1

# 查看详情
k get quota object-quota-demo -n quota-example -o yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: object-quota-demo
  namespace: quota-example
spec:
  hard:
    persistentvolumeclaims: "1"
status:
  hard:
    persistentvolumeclaims: "1"
  used:
    persistentvolumeclaims: "0"  # 可用1

# 创建一个pvc:
apiVersion: v1
kind: PersistentVolumeClaim 
metadata:
  name: pvc-quota-demo
  namespace: quota-example
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi

# 当前资源使用情况
apiVersion: v1
kind: ResourceQuota
metadata:
  name: object-quota-demo
  namespace: quota-example
spec:
  hard:
    persistentvolumeclaims: "1"
status:
  hard:
    persistentvolumeclaims: "1"
  used:
    persistentvolumeclaims: "1"  # 当前使用量为1,配额为1,再次创建会报错

QoS(Quality of Service服务质量)

内存和磁盘属于不可压缩的资源,当内存达到最高峰时会引起OOMKilled故障。

3种级别的服务质量,按优先级排序:

  • Guaranteed最高服务质量,limits的cpu和memory等于requests的cpu和memory
  • Burstable limits的cpu和memory大于requests的cpu和memory
  • BestEffort 尽力而为,不设置limits和requests

重要的线上应用应该怎么配置

节点资源不足时,会触发自动驱逐,将一些低优先级的 Pod 删除掉以释放资源让节点自愈。没有设置 requests,limit 的 Pod 优先级最低,容易被驱逐;requests 不等于 limit 的其次; requests 等于 limit 的 Pod 优先级较高,不容易被驱逐。所以如果是重要的线上应用,不希望在节点故障时被驱逐导致线上业务受影响,就建议将 requests 和 limit 设成一致。即QoS中的Guaranteed。

# Guaranteed
pod中的每个容器都必须指定limits.memory=requests.memory,limits.cpu=limits.memory。
如果配置了limits而没有配置requests,那么requests默认值就会被设置为limits配置的参数。

apiVersion: v1
kind: Pod
metadata:
  name: qos-demo
  namespace: qos-example
spec:
  containers:
  - image: nginx
    name: qos-demo
    resources:
      limits:
        memory: "200Mi"
        cpu: "700m"
      requests:
        memory: "200Mi"
        cpu: "700m"

k get pod qos-demo -n qos-example -o yaml
status:
  qosClass: Guaranteed

# Burstable
1.Pod不符合Guaranteed的配置要求
2.Pod中至少有1个容器配置了requests.cpu或requests.memory

apiVersion: v1
kind: Pod
metadata:
  name: qos-demo-2
  namespace: qos-example
spec:
  containers:
  - image: nginx
    name: qos-demo
    resources:
      limits:
        memory: "200Mi"
      requests:
        memory: "100Mi"

k get pod qos-demo-2 -n qos-example -o yaml
status:
  qosClass: Burstable

# besteffort
容器没有设置limits和requests即可
apiVersion: v1
kind: Pod
metadata:
  name: qos-demo-3
  namespace: qos-example
spec:
  containers:
  - image: nginx
    name: qos-demo

k get pod qos-demo-3 -n qos-example -o yaml 
status:
  qosClass: BestEffort

总结

通常情况,底层依赖的中间件的QoS会配置为Guaranteed,其他服务可能不需要太那么高的Qos。实际使用中,最有可能超额分配的是内存,而CPU通常使用率不高,只有在高频工作时,CPU才会处于忙碌状态。所以在设置resources参数时,内存的requests需要按需分配,对于CPU,Limits参数很重要,防止因为Pod的CPU过高,引起宿主机的问题。

应用健壮性要从哪些点去考虑?

  • 应用的健康检查
  • 平滑退出
  • 亲和力
  • Qos
  • ResourceQuota