在Kubernetes中使用容器Containers时,对于了解所涉及的资源及其所需非常重要,有些进程需要比其他进程更多的CPU或内存,因为有些进程的优先级更高,不应该因为资源的争抢而出现饥饿状态。
了解了这一点,我们应该适当配置我们的containers和Pods的属性,以便充分发挥它们的作用。
在这篇文章中,我们将看到:
- Kubernetes Limit和 Request的介绍
- 操作示例
- Kubernetes Request详细说明
- Kubernetes Limit详细说明
- CPU 特性
- Memory特性
- namespace的资源管控
- namespace LimitRange
- 结论
Kubernetes Limits 和 Requests的介绍
Limits 和 Requests 是在使用Kubernetes时非常重要的配置,本文将重点介绍两个最重要的设置:CPU和内存。
Kubernetes将Limits定义为containers可使用的资源的最大值。这意味着containers永远不能超过所指示的内存或CPU
与之相反,Requests,表示为container保留的最小资源用量。
操作示例
让我们看一下这个部署,我们在CPU和内存上为两个不同的container设置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
假设我们正在运行一个4核和16GB节点的集群。我们可以得到如下节点信息:
Pod 有效请求为 400 MiB 内存和 600 毫核 CPU。您需要一个具有足够可分配空间的节点来调度 Pod。 Redis 容器的 CPU 分配将为 512,繁忙容器的分配将为 102。
Kubernetes 总是为每个核心分配 1024 份额,因此 redis:1024 * 0.5 核心 ≅ 512,busybox:1024 * 0.1 核心 ≅ 102。 如果 Redis 容器尝试分配超过 600MB 的 RAM,它将被 OOM 杀死,很可能导致 Pod 失败。 如果 Redis 尝试在每 100ms 使用超过 100ms 的 CPU(由于我们有 4 个核心,可用时间为每 100ms 的 400ms),将导致性能下降。 如果 Busybox 容器尝试分配超过 200MB 的 RAM,它将被 OOM 杀死,导致 Pod 失败。 如果 Busybox 尝试在每 100ms 使用超过 30ms 的 CPU,将导致性能下降。
- Pod有效请求是400 MiB内存和600m CPU,需要一个具有足够可分配空间的节点来调度pod
- redis容器的CPU份额为512,busybox容器的CPU份额为102。Kubernetes总是为每个核心分配1024个共享,因此redis: 1024 * 0.5 cores = 512和busybox: 1024 * 0.1cores = 102
- 如果Redis container 试图分配超过600MB的RAM,它将被OOM杀死,很可能导致pod失败。
- 如果Redis试图在每100毫秒内使用超过100毫秒的CPU,它将遭受CPU限流(因为我们有4个核心,每100毫秒可用时间将是400毫秒),导致性能下降。
- 如果Busybox容器试图分配超过200MB的RAM,它将被OOM杀死,从而导致pod失败。
- 如果Busybox试图每100毫秒使用超过30毫秒的CPU,那么它将被CPU限流,从而导致性能下降。
Kubernetes Requests介绍
Kubernetes 将Requsts 定义为容器使用的资源的保证最小量,基本上,它被用来设置容器所使用的最小资源用量。
当一个Pod被调度时,kube-scheduler会检查Kubernetes的requests,以便将其分配给一个特定的节点,该节点至少可以满足Pod中所有容器的request。如果请求的量高于可用资源,Pod将无法被调度并保持在Pending状态。
比如,在容器定义中,我们设置了对100m核心CPU和4Mi内存的请求:
resources:
requests:
cpu: 0.1
memory: 4Mi
Request的使用:
将Pod分配给某一个节点时,为了满足Pod中容器指定的Request资源。 在运行时,将保证为该Pod中的容器提供所需的最小Request
Kubernetes Limits介绍
Kubernetes将Limits定义为容器可以使用的资源的最大数量,这意味着容器永远不会占用超过指定的内存量或CPU量
resources:
limits:
cpu: 0.5
memory: 100Mi
Limits 使用说明:
- 将pod分配给节点时。如果没有设置Request,默认情况下,Kubernetes将分配requests = limits
- 在运行时,Kubernetes将检查Pod中的容器消耗的资源是否超过了Limits的值
CPU 特性
CPU是一种可压缩资源,这意味着它可以被延伸以满足所有的需求,如果进程请求过多的CPU,那么另外一些进程将被限制。
- CPU表示计算处理时间,以核为单位
- CPU使用毫级(m)来表示比一个核心更小的数量(例如,500m将是半个核心)。
- 最低的核数为1m
- 一个节点可能有多个内核可用,因此请求CPU > 1 是可能的
Memory 特性
内存是一种不可压缩的资源,这意味着它不能像CPU那样被扩展。如果一个进程没有足够的内存来工作,那么这个进程就会被kill掉
内存在Kubernetes中以字节为单位进行测量。
- 您可以使用E、P、T、G、M、k来表示Exabyte、Petabyte、Terabyte、Gigabyte、兆字节和千字节,尽管通常只使用后四个字节。(如500M、4G)
- 警告:不要使用小写的m表示内存(表示"Millibytes" 是计算数据存储容量或传输速度时使用的单位,它是字节的一千分之一)
- 您可以使用Mi来定义Mebibytes,以及其他的Ei、Pi、Ti(例如500Mi)
最佳实践
在很少的情况下,您应该使用Limits来控制Kubernetes中的资源使用。这是因为,如果想要避免“饥饿”(确保每个重要进程都得到它的份额),我们应该首先使用Request。
通过设置Limits,我们只能防止进程在特殊情况下获取额外资源,在内存事件中导致OOM终止,在CPU事件中导致Throttling(进程将需要等待,直到CPU可以再次使用)。
如果设置的Request值等于一个Pod的所有容器中的限制,那么该Pod将获得有保证的服务质量。
还要注意,资源使用量高于请求的pod更有可能被驱逐,因此设置非常低的请求弊大于利
Namespace 资源限制
通过名称空间,我们可以将Kubernetes资源隔离到不同的组中,也称为租户。
使用ResourceQuotas,我们可以为整个名称空间设置内存或CPU限制,确保其中的实体不能消耗超过该数量的内存或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量
- reqquests.memory: 该命名空间中所有请求总和的最大内存量
- limits.cpu: 该命名空间中所有限制之和的最大cpu数量
- limits.memory: 该名称空间中所有限制之和的最大内存量
接下来,apply 我们的namespace:
kubectl apply -f resourcequota.yaml --namespace=mynamespace
你可以用下面的命令列出一个命名空间的当前资源配额:
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
Namesapce LimitRange
通常用于 Kubernetes 中,用于限制容器中资源的使用范围,例如 CPU、内存、存储等。它可以帮助管理员控制容器的资源使用,避免出现资源耗尽的情况。
如果我们想要限制一个namespace可分配的资源总量,那么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
现在,假设添加了一个限制为1200m的新Pod。将收到以下错误:
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 request,即使没有设置LimitRanges。
结论
- 为我们的Kubernetes集群选择最佳的Limits值是获得最佳能耗和成本的关键。
- 为我们的pod投入太多资源可能会导致成本飙升。
- 精简或使用很少的CPU或内存将导致应用程序不能正确执行,甚至pod被驱逐。
如前所述,Kubernetes Limits一般不建议使用,除非在非常特殊的情况下,因为它们可能会造成弊大于利。在内存不足的情况下,容器有可能被杀死,或者在CPU不足的情况下被限流
对于requests,在需要确保进程获得有保证的资源共享时使用它们。