原文:https://sysdig.com/blog/kubernetes-limits-requests/
作者:JAVIER MARTÍNEZ
当在 Kubernetes 中使用容器时,重要的是要知道所涉及的资源是什么以及如何需要它们。有些进程比其他进程需要更多的 CPU 或内存。有些是关键的,不应该被饿死。
知道了这一点,我们应该正确配置我们的容器和 Pod,以获得两者的最佳效果。
在这篇文章中,我们将看到。
- Kubernetes 的 Limits 和 Requests 介绍
- 实践案例
- Kubernetes Requests
- Kubernetes Limits
- CPU 的特殊性
- 内存的特殊性
- Namespace ResourceQuta
- Namespace LimitRange
- 总结
Kubernetes 的 Limits 和 Requests 介绍
在使用 Kubernetes 时,Limits 和 Requests 是重要的配置,主要包含 CPU 和内存的配置。
Kubernetes 将 Limits 定义为一个容器使用的最大资源量,这意味着容器的消耗量永远不能超过所显示的内存量或 CPU 量。
另一方面,Requests 是指为容器保留的资源的最小保证量。
实践案例
让我们来看看下面这个 deployment,我们需要为两个不同的容器在 CPU 和内存上设置 Limits 和 Requests。
kind: Deployment
apiVersion: extensions/v1beta1
…
template:
spec:
containers:
- name: redis
image: redis:5.0.3-alpine
resources:
limits:
memory: 600Mi
cpu: 1
requests:
memory: 300Mi
cpu: 500m
- name: busybox
image: busybox:1.28
resources:
limits:
memory: 200Mi
cpu: 300m
requests:
memory: 100Mi
cpu: 100m
假如,我们要把该 deployment 部署到 4C16G 配置的节点上,可以得到如下信息。
- Pod 的有效请求是 400 MiB 的内存和 600 millicores 的 CPU,你需要一个有足够自由可分配空间的节点来安排 pod。
- Redis 容器的 CPU 份额将是 512,而 busybox 容器是 102,Kubernetes 总是为每个核心分配 1024 个份额,因此 redis:1024 * 0.5 cores ≅ 512 和 busybox:1024 * 0.1 核 ≅ 102
- 如果 Redis 容器试图分配超过 600MB 的 RAM,它将被 OOM 杀死,很可能使 pod 失败。
- 如果 Redis 试图在每 100ms 内使用超过 100ms 的 CPU,(因为我们有 4 个核心,可用时间为每 100ms 400ms),它将遭受 CPU 节流,导致性能下降。
- 如果 Busybox 容器试图分配超过 200MB 的 RAM,它将被 OOM 杀死,导致一个失败的 Pod。
- 如果 Busybox 试图每 100ms 使用超过 30ms 的 CPU,它将遭受 CPU 节流,导致性能下降。
Kubernetes Requests
Kubernetes 将请求定义为容器使用的资源的最低保证量。
基本上,它将设定容器所要消耗的资源的最小数量。
当一个 Pod 被调度时,kube-scheduler 将检查 Kubernetes 请求,以便将其分配给一个特定的节点:该节点至少可以满足 Pod 中所有容器的这个数量。如果请求的数量高于可用的资源,Pod 将不会被安排,并保持在 Pending 状态。
关于 Pending 状态的更多信息,请查看 Understanding Kubernetes Pod pending problems【1】。
在这个例子中,在容器定义中,我们设置了一个请求,要求 100m 核心的 CPU 和 4Mi 的内存。
resources:
requests:
cpu: 0.1
memory: 4Mi
Requests 通常被使用在以下场景:
- 当把 Pod 分配给一个节点时,所以 Pod 中的容器的指定请求被满足。
- 在运行时,指定的请求量将被保证为该 Pod 中的容器的最小值。
Kubernetes Limits
Kubernetes 将 Limits 定义为一个容器使用的最大资源量。
这意味着容器的消耗量永远不能超过指定的内存量或 CPU 量。
resources:
limits:
cpu: 0.5
memory: 100Mi
Limits 通常用于以下场景:
- 当把 Pod 分配给一个节点时,如果没有设置请求,默认情况下,Kubernetes 将分配请求 = 限制。
- 在运行时,Kubernetes 将检查 Pod 中的容器所消耗的资源量是否高于限制所显示的数量。
CPU 的特性
CPU 是一种可压缩的资源,这意味着它可以被拉伸,以满足所有的需求。如果进程要求太多的 CPU,其中一些将被节制。
CPU 代表计算处理时间,以核为单位。
- 你可以用毫微米(m)来表示比一个核心更小的数量(例如,500 米是半个核心)。
- 最小的数量是 1m
- 一个节点可能有一个以上的核心可用,所以请求 CPU>1 是可能的
内存的特性
内存是一种不可压缩的资源,意味着它不能像 CPU 那样被拉伸。如果一个进程没有得到足够的内存来工作,这个进程就会被杀死。
在 Kubernetes 中,内存的单位是字节。
- 你可以用,E,P,T,G,M,k 来代表 Exabyte,Petabyte,Terabyte,Gigabyte,Megabyte 和 kilobyte,尽管只有最后四个是常用的。(例如,500M, 4G)
- 警告:不要用小写的 m 表示内存(这代表 Millibytes,低得离谱)
- 你可以用 Mi 来定义 Mebibytes,其余的也可以用 Ei、Pi、Ti 来定义(例如,500Mi)
一个 Mebibyte(以及它们的类似物 Kibibyte、Gibibyte…)是 20 字节的 2 次方。它的出现是为了避免与公制中的 Kilo、Mega 定义相混淆。你应该使用这个符号,因为它是字节的典型定义,而 Kilo 和 Mega 是 1000 的倍数。
最佳实践
在 Kubernetes 中,你应该很少使用限制来控制你的资源使用。这是因为如果你想避免饥饿(确保每个重要的进程都能得到它的份额),你应该首先使用请求。
通过设置限制,你只是防止进程在特殊情况下检索额外的资源,在内存方面造成 OOM 杀戮,在 CPU 方面造成 Throttling(进程将需要等待 CPU 可以再次使用)。
欲了解更多信息,请查看 article about OOM and Throttling【2】。
如果你在一个 Pod 的所有容器中设置一个等于限制的请求值,该 Pod 将获得保证的服务质量。
还需要注意的是,资源使用量高于请求的 Pod 更有可能被驱逐,所以设置非常低的请求会造成弊大于利。可以在 Pod eviction and Quality of Service【3】查看。
Namespace ResourceQuata
由于命名空间的存在,我们可以将 Kubernetes 资源隔离到不同的组,也称为租户。
通过 ResourceQuota,你可以为整个命名空间设置一个内存或 CPU 限制,确保其中的实体不能消耗超过这个数量。
apiVersion: v1
kind: ResourceQuota
metadata:
name: mem-cpu-demo
spec:
hard:
requests.cpu: 2
requests.memory: 1Gi
limits.cpu: 3
limits.memory: 2Gi
- requests.cpu:这个命名空间中所有请求的最大 CPU 数量。
- requests.memory:这个命名空间中所有请求的最大内存量。
- limits.cpu:这个命名空间中所有限制的最大 CPU 数量。
- limits.memory:这个命名空间中所有限制的总和的最大内存量。
然后,将其应用于你的命名空间。
kubectl apply -f resourcequota.yaml --namespace=mynamespace
你可以用以下方法列出一个命名空间的当前 ResourceQuota。
kubectl get resourcequota -n mynamespace
注意,如果你为命名空间中的特定资源设置了 ResourceQuota,那么你就需要为该命名空间中的每个 Pod 指定相应的限制或请求。否则,Kubernetes 将返回一个 "failed quota" 的错误。
Error from server (Forbidden): error when creating "mypod.yaml": pods "mypod" is forbidden: failed quota: mem-cpu-demo: must specify limits.cpu,limits.memory,requests.cpu,requests.memory
如果你试图添加一个新的 Pod,其容器限制或请求超过了当前的 ResourceQuota,Kubernetes 将返回一个 "exceeded quota" 的错误。
Error from server (Forbidden): error when creating "mypod.yaml": pods "mypod" is forbidden: exceeded quota: mem-cpu-demo, requested: limits.memory=2Gi,requests.memory=2Gi, used: limits.memory=1Gi,requests.memory=1Gi, limited: limits.memory=2Gi,requests.memory=1Gi
Namespace LimitRange
如果我们想限制一个命名空间可分配的资源总量,ResourceQuotas 很有用。但如果我们想给里面的元素提供默认值,会发生什么?
LimitRanges 是一种 Kubernetes 策略,它限制了命名空间中每个实体的资源设置。
apiVersion: v1
kind: LimitRange
metadata:
name: cpu-resource-constraint
spec:
limits:
- default:
cpu: 500m
defaultRequest:
cpu: 500m
min:
cpu: 100m
max:
cpu: "1"
type: Container
- default。如果没有指定,创建的容器将有这个值。
- min: 创建的容器不能有比这更小的限制或请求。
- max: 创建的容器不能有大于此值的限制或请求。
以后,如果你创建一个没有设置请求或限制的新 Pod,LimitRange 会自动为其所有的容器设置这些值。
Limits:
cpu: 500m
Requests:
cpu: 100m
现在,想象一下,你添加一个新的 Pod,以 1200M 为限。你会收到以下错误。
Error from server (Forbidden): error when creating "pods/mypod.yaml": pods "mypod" is forbidden: maximum cpu usage per Container is 1, but limit is 1200m
请注意,默认情况下,Pod 中的所有容器将有效地拥有 100m CPU 的请求,即使没有设置 LimitRanges。
总结
为我们的 Kubernetes 集群选择最佳限制是关键,以便获得最佳的能源消耗和成本。
为我们的 Pod 分配过多的资源可能会导致成本激增。
规模过小或专用于极少的 CPU 或内存将导致应用程序不能正常运行,甚至 Pod 被驱逐。
如前所述,除非在非常特殊的情况下,否则不应该使用 Kubernetes 限制,因为它们可能会造成更大的伤害。在内存不足的情况下,容器有可能被杀死,在 CPU 不足的情况下,容器有可能被节流。
对于请求,当你需要确保一个进程获得一个有保障的资源份额时,可以使用它们。
文档
【1】https://sysdig.com/blog/kubernetes-pod-pending-problems/
【2】https://sysdig.com/blog/troubleshoot-kubernetes-oom/
【3】https://sysdig.com/blog/kubernetes-pod-evicted/