K8s 资源管控:ResourceQuota 与 LimitRange 机制详解与实战
- [Quota and Limits](#Quota and Limits)
-
- 环境准备
- ResourceQuota
- [Request 和 Limits](#Request 和 Limits)
- LimitRange
-
- [未指定 resources](#未指定 resources)
- [只指定 limit 值](#只指定 limit 值)
-
- [示例 2-1:limit 值大于 max 值](#示例 2-1:limit 值大于 max 值)
- [示例 2-2:limit 值小于 min 值](#示例 2-2:limit 值小于 min 值)
- [示例 2-3:min 值< limit 值< max 值](#示例 2-3:min 值< limit 值< max 值)
- [只指定 requests](#只指定 requests)
-
- [示例 3-1:requests 大于 max 值](#示例 3-1:requests 大于 max 值)
- [示例 3-2:requests 小于 min 值](#示例 3-2:requests 小于 min 值)
- [示例 3-3:min 值< request 值< max 值](#示例 3-3:min 值< request 值< max 值)
- 限定资源类型
- 环境清理
Quota and Limits
摘要: 本文全面介绍了 Kubernetes 中的资源配额(ResourceQuota)和限制范围(LimitRange)机制。主要内容包括:1)ResourceQuota 用于限制命名空间级别的资源使用总量,包括对象数量和计算资源;2)Pod 必须指定 requests 和 limits 才能在有资源配额的命名空间中运行;3)通过实际测试展示了不指定资源、仅指定 requests、仅指定 limits 三种场景下的配额验证;4)LimitRange 为命名空间中的容器设置默认的资源请求和限制范围,确保资源使用的规范性;5)提供了完整的测试示例和环境清理步骤。这些机制共同保障了 Kubernetes 集群中多租户环境下的资源公平分配和稳定性。
学习参考:
环境准备
-
创建一个独立的名字空间quota,并切换到该ns
bashroot@master30:~# kubectl create ns quota root@master30:~# kubectl config set-context --current --namespace quota -
提前部署好 Metric Server
ResourceQuota
**问题:**当多个用户或团队共享Kubernetes集群时,有人会使用超过其基于公平原则所分配到的资源量。
**解决:**可以使用资源配额限制 Namespace 使用的资源。
资源配额 ,通过 ResourceQuota 对象来定义,对每个命名空间的资源消耗总量提供限制。
资源配额的工作方式如下:
-
不同的团队在不同的命名空间下工作。这可以通过 RBAC 强制执行。
-
集群管理员可以为每个命名空间创建一个或多个 ResourceQuota 对象。
-
当用户在命名空间下创建资源(如 Pod、Service 等)时,Kubernetes 的配额系统会跟踪集群的资源使用情况, 以确保使用的资源用量不超过 ResourceQuota 中定义的硬性资源限额。
-
如果资源创建或者更新请求违反了配额约束,那么该请求会报错(HTTP 403 FORBIDDEN), 并在消息中给出有可能违反的约束。
-
如果命名空间下的计算资源 (如
cpu和memory)的配额被启用, 则用户必须为这些资源设定请求值(request)和约束值(limit),否则配额系统将拒绝 Pod 的创建。提示: 可使用
LimitRanger准入控制器来为没有设置计算资源需求的 Pod 设置默认值。
启用资源配额
Kubernetes 默认启用了资源配额功能 。 当 API 服务器 的命令行标志 --enable-admission-plugins= 中包含 ResourceQuota 时, 资源配额会被启用。
当命名空间中存在一个 ResourceQuota 对象时,对于该命名空间而言,资源配额就是开启的。
配额类型
Kubernetes可以限制两种类型资源:
-
对象数量:Kubernetes 资源数量,例如pods,services等。
实施资源数量配额可以提高kubernetes稳定性,避免Etcd数据库无限增长,还可以避免占用node中其他功能资源(例如ip地址服务)。
-
计算资源:物理或者虚拟资源容量,例如 CPU,memory 和存储容量。
实施计算资源配额可以避免消耗 kubernetes 集群中单个node所有计算资源,避免单个namespace中应用消耗所有集群资源,导致其他namespace中应用无法正常运行。
**kubernetes 通过 ResourceQuota 类型资源实施配额。**一个namespace可以包含多个ResourceQuota对象,这些限制是累加的,一般情况,多个ResourceQuota对象不会限定同一个资源。
-
对象数量:
persistentvolumeclaimsservicessecretsconfigmapsreplicationcontrollersdeployments.appsreplicasets.appsstatefulsets.appsjobs.batchcronjobs.batch
-
资源名称 描述 limits.cpu在所有处于非终止状态的 Pod 中,CPU 限制的总和不能超过此值。 limits.memory在所有处于非终止状态的 Pod 中,内存限制的总和不能超过这个值。 requests.cpu在所有处于非终止状态的 Pod 中,CPU 请求的总和不能超过此值。 requests.memory在所有处于非终止状态的 Pod 中,内存请求的总和不能超过这个值。 requests.storage在所有持久卷声明中,存储请求的总和不能超过此值。 cpu与 requests.cpu一样memory与 requests.memory一样
单位说明:
- CPU :1 cpu 等于1000 m,默认单位是 cpu核心数量。
- memory :支持两种格式。
- Ki | Mi | Gi | Ti | Pi | Ei,进制是1024,例如1024 = 1Ki
- k | M | G | T | P | E,进制是1000,例如1000 = 1k
- 默认单位是 G,例如1.5,代表1500M。
配额管理
重要说明: 如果项目级别配额限定了 request 和 limit,那么创建pod的时候必须指定 request 和 limit。
创建 ResourceQuota 对象
bash
root@master30:~# kubectl create quota myquota --hard=pods=2,services=3,secrets=5,persistentvolumeclaims=10
root@master30:~# kubectl get resourcequotas
NAME AGE REQUEST LIMIT
my-quota 10s persistentvolumeclaims: 0/10, pods: 0/2, secrets: 1/5, services: 0/3
root@master30:~# kubectl describe quota myquota
Name: myquota
Namespace: quota
Resource Used Hard
-------- ---- ----
persistentvolumeclaims 0 10
pods 0 2
secrets 1 5
services 0 3
通过 yaml 文件创建
yaml
apiVersion: v1
kind: ResourceQuota
metadata:
name: myquota
spec:
hard:
persistentvolumeclaims: "10"
pods: "2"
secrets: "5"
services: "3"
测试配额
bash
root@master30:~# kubectl create deployment web --image=hub.laoma.cloud/library/nginx --replicas=3
root@master30:~# kubectl get all
NAME READY STATUS RESTARTS AGE
pod/web-96d5df5c8-dl7qk 1/1 Running 0 40s
pod/web-96d5df5c8-j7fhh 1/1 Running 0 40s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/web 2/3 2 2 40s
NAME DESIRED CURRENT READY AGE
replicaset.apps/web-96d5df5c8 3 2 2 40s
root@master30:~# kubectl describe rs web-96d5df5c8
LAST SEEN TYPE REASON OBJECT MESSAGE
......
33s Warning FailedCreate replicaset/web-96d5df5c8 Error creating: pods "web-96d5df5c8-xg6c9" is forbidden: exceeded quota: myquota, requested: pods=1, used: pods=2, limited: pods=2
2s Warning FailedCreate replicaset/web-96d5df5c8 (combined from similar events): Error creating: pods "web-96d5df5c8-89p7j" is forbidden: exceeded quota: myquota, requested: pods=1, used: pods=2, limited: pods=2
25s Normal SuccessfulCreate replicaset/web-96d5df5c8 Created pod: web-96d5df5c8-bkzmd
34s Normal ScalingReplicaSet deployment/web Scaled up replica set web-96d5df5c8 to 3
# 超过配额,创建失败
# 修改配额 pod数量为10
root@master30:~# kubectl patch resourcequotas myquota -p '{"spec":{"hard":{"pods":10}}}'
# 此时重新扩展rs
root@master30:~# kubectl scale rs web-96d5df5c8 --replicas 3
# 再次验证pod数量
root@master30:~# kubectl get pods
NAME READY STATUS RESTARTS AGE
pod/web-96d5df5c8-ajcz2 1/1 Running 0 2s
pod/web-96d5df5c8-dl7qk 1/1 Running 0 60s
pod/web-96d5df5c8-j7fhh 1/1 Running 0 60s
# 清理环境
root@master30:~# kubectl delete deployments.apps web
root@master30:~# kubectl delete resourcequotas myquota
思考: 如果一个用户可以管理多个 namespace,能否限定该用户配额呢?
Request 和 Limits
如果命名空间下的计算资源 (如 cpu 和 memory)的配额被启用, 则用户必须为这些资源设定请求值(request)和约束值(limit),否则配额系统将拒绝 Pod 的创建。
pod.containers.resources 定义包含两部分:
-
requests ,指明pod运行需要的最少计算资源,调度器查找具有充足计算资源的nodes。
-
limits ,指明pod运行可以获得节点最多计算资源,用于阻止pod占用node太多计算资源。node使用Linux内核功能cgroup,限制pod资源使用。
测试-不指定计算资源
配额示例
bash
root@master30:~# vim resourcequota.yaml
yaml
apiVersion: v1
kind: ResourceQuota
metadata:
name: myquota
spec:
hard:
persistentvolumeclaims: "10"
pods: "2"
secrets: "5"
services: "3"
requests.cpu: 1000m
requests.memory: "2048Mi"
limits.cpu: 1000m
limits.memory: "2048Mi"
bash
root@master30:~# kubectl apply -f resourcequota.yaml
pod 示例
bash
root@master30:~# vim pod-without-quota.yaml
yaml
apiVersion: v1
kind: Pod
metadata:
name: web
labels:
app: web
spec:
containers:
- name: web
image: hub.laoma.cloud/library/httpd
imagePullPolicy: IfNotPresent
ports:
- name: web
containerPort: 80
protocol: TCP
bash
root@master30:~# kubectl apply -f pod-without-quota.yaml
Error from server (Forbidden): error when creating "pod-without-quota.yaml": pods "web" is forbidden: failed quota: myquota: must specify limits.cpu for: web; limits.memory for: web; requests.cpu for: web; requests.memory for: web
# 清理环境
root@master30:~# kubectl delete resourcequotas myquota
测试-Request
配额示例
bash
root@master30:~# vim resourcequota.yaml
yaml
apiVersion: v1
kind: ResourceQuota
metadata:
name: myquota
spec:
hard:
requests.cpu: 1000m
requests.memory: "2048Mi"
bash
root@master30:~# kubectl apply -f resourcequota.yaml
pod 示例1:超上限
bash
root@master30:~# vim pod-request-1.yaml
yaml
apiVersion: v1
kind: Pod
metadata:
name: web
labels:
app: web
spec:
containers:
- name: web
image: hub.laoma.cloud/library/httpd
imagePullPolicy: IfNotPresent
resources:
requests:
cpu: 2000m
memory: 4096Mi
ports:
- name: web
containerPort: 80
protocol: TCP
bash
root@master30:~# kubectl apply -f pod-request-1.yaml
Error from server (Forbidden): error when creating "pod-request-1.yaml": pods "web" is forbidden: exceeded quota: myquota, requested: requests.cpu=2,requests.memory=4Gi, used: requests.cpu=0,requests.memory=0, limited: requests.cpu=1,requests.memory=2Gi
pod 示例2:未超上限
bash
root@master30:~# vim pod-request-2.yaml
yaml
apiVersion: v1
kind: Pod
metadata:
name: web
labels:
app: web
spec:
containers:
- name: web
image: hub.laoma.cloud/library/httpd
imagePullPolicy: IfNotPresent
resources:
requests:
cpu: 200m
memory: 1024Mi
ports:
- name: web
containerPort: 80
protocol: TCP
bash
root@master30 ~# kubectl apply -f pod-request-2.yaml
root@master30:~# kubectl get pod
NAME READY STATUS RESTARTS AGE
web 1/1 Running 0 43s
# 验证使用情况
root@master30:~# kubectl describe resourcequotas myquota
Name: myquota
Namespace: quota
Resource Used Hard
-------- ---- ----
requests.cpu 200m 1
requests.memory 1Gi 2Gi
# 删除pod和quota
root@master30:~# kubectl delete pod web
root@master30:~# kubectl delete resourcequotas myquota
测试-Limits
压力测试镜像
可以直接使用镜像 hub.laoma.cloud/progrium/stress 进行压力测试,该镜像中运行stress命令。
找讲师索取镜像。
bash
Usage: stress [OPTION [ARG]] ...
-?, --help show this help statement
--version show version statement
-v, --verbose be verbose
-q, --quiet be quiet
-n, --dry-run show what would have been done
-t, --timeout N timeout after N seconds
--backoff N wait factor of N microseconds before work starts
-c, --cpu N spawn N workers spinning on sqrt()
-i, --io N spawn N workers spinning on sync()
-m, --vm N spawn N workers spinning on malloc()/free()
--vm-bytes B malloc B bytes per vm worker (default is 256MB)
--vm-stride B touch a byte every B bytes (default is 4096)
--vm-hang N sleep N secs before free (default none, 0 is inf)
--vm-keep redirty memory instead of freeing and reallocating
-d, --hdd N spawn N workers spinning on write()/unlink()
--hdd-bytes B write B bytes per hdd worker (default is 1GB)
Example: stress --cpu 8 --io 4 --vm 2 --vm-bytes 128M --timeout 10s
Note: Numbers may be suffixed with s,m,h,d,y (time) or B,K,M,G (size).
常用选项:
-
-c, --cpu N spawn N workers spinning on sqrt()
-
-m, --vm N spawn N workers spinning on malloc()/free()
--vm-bytes B malloc B bytes per vm worker (default is 256MB)
-
-d, --hdd N spawn N workers spinning on write()/unlink()
--hdd-bytes B write B bytes per hdd worker (default is 1GB)
示例:
bash
# 压力测试内存
$ docker run --name stress hub.laoma.cloud/progrium/stress -m 1 --vm-bytes 512M
# 压力测试CPU
$ docker run --name stress hub.laoma.cloud/progrium/stress -c 1
# 压力测试IO
$ docker run --name stress hub.laoma.cloud/progrium/stress --d 1 --hdd-bytes 3G
对于 pod:
yaml
apiVersion: v1
kind: Pod
metadata:
name: stress
spec:
containers:
- name: stress
image: hub.laoma.cloud/progrium/stress
imagePullPolicy: IfNotPresent
command: ['sh','-c','sleep 3600']
# 或者不用command,而是使用args作为参数传递给镜像的Entrypoint。
#args: ['-m','1','--vm-bytes','512M']
#args: ['-c','1']
#args: ['-d','1','--hdd-bytes','3G']
配额示例
bash
root@master30:~# vim resourcequota.yaml
yaml
apiVersion: v1
kind: ResourceQuota
metadata:
name: myquota
spec:
hard:
limits.cpu: 1000m
limits.memory: "2048Mi"
bash
root@master30:~# kubectl apply -f resourcequota.yaml
测试 CPU 资源
pod示例:
bash
root@master30:~# vim pod-limit-cpu.yaml
yaml
apiVersion: v1
kind: Pod
metadata:
name: stress
spec:
containers:
- name: stress
image: hub.laoma.cloud/progrium/stress
imagePullPolicy: IfNotPresent
args: ['-c','1']
resources:
limits:
cpu: 200m
memory: "256Mi"
bash
root@master30:~# kubectl apply -f pod-limit-cpu.yaml
打开一个终端监控
kubectl top 命令需要提前部署Metrics-Server
bash
root@master30:~# kubectl top pods
NAME CPU(cores) MEMORY(bytes)
stress 201m 0Mi
**可以发现:**CPU 使用率维持在 200m 左右。
bash
# 删除 pod
root@master30:~# kubectl delete pod stress --force
测试 MEMORY 资源
pod示例:
bash
root@master30:~# vim pod-limit-memory.yaml
yaml
apiVersion: v1
kind: Pod
metadata:
name: stress
spec:
containers:
- name: stress
image: hub.laoma.cloud/progrium/stress
imagePullPolicy: IfNotPresent
args: ['-m','1','--vm-bytes','512M']
resources:
limits:
cpu: 200m
memory: "256Mi"
bash
root@master30:~# kubectl apply -f pod-limit-memory.yaml
打开一个终端监控
bash
root@master30:~# kubectl get pods -w
NAME READY STATUS RESTARTS AGE
stress 0/1 OOMKilled 1 (2s ago) 3s
stress 0/1 CrashLoopBackOff 1 (2s ago) 4s
可以发现: Pod 状态为 OOMKilled,并进行restart。
bash
# 删除 pod he
root@master30:~# kubectl delete pod stress --force
root@master30:~# kubectl delete resourcequotas myquota
总结
-
计算资源的 limits 总和是否会超过节点上资源总和?
答案:可能会。 假设 node可用MEMORY为1G。
每个pod内存 requests是256M,limit是512M。创建5个pod,pod实际占用内存也为256M(有可能小于256M)。
node上大概可以创建4个pod,而此时的limits总和是2G。
-
当计算资源的limits总和超过节点上资源总和时,kubernetes如何处理?
- 对于 cpu ,kubernetes 认为 cpu 是可被压缩的资源,在应用达到limits时,减少该容器的调度时间,并不会杀死应用。
- 对于 memory ,kubernetes 认为 memory 是无法被压缩的资源 ,此时k8s 会杀死占用资源超过其request的应用(1.9版本之后的版本)。首当其冲的是没有指定request的container,然后是使用资源超过其request更多的container。同等情况下优先级更低的container更容易被杀死。
LimitRange
kubernetes创建pod时,默认不指定资源请求和限制。如果namespace设置了配额,那么创建不指定资源请求和资源限制的pod是不允许的。为了在设定配额的namespace中使用pod,namespace还需要为pod资源请求设定默认范围。
LimitRange 资源,也称为limits,定义了单个pod的资源请求和资源限制default、minimum、maximum值。pod的资源请求是其中所有容器请求的总和。
LimitRange 资源用于限定特定 namespace。
namespace设定了LimitRange,创建资源规则:
- 如果项目中请求一个未提供计算资源的对象,那么此时namespace将使用limit范围default值创建该对象。
- 如果项目中请求一个计算资源的对象,请求的资源小于limit最小值 ,那么该资源**无法创建**。
- 如果项目中请求一个计算资源的对象,请求的资源大于limit最大值 ,那么该资源**无法创建**。
LimitRange 示例
bash
root@master30:~# vim limits.yaml
yaml
apiVersion: v1
kind: LimitRange
metadata:
name: mylimit
spec:
limits:
- type: Container
max:
memory: 1024Mi
cpu: 1
min:
memory: 128Mi
cpu: 100m
default:
memory: 512Mi
cpu: 500m
defaultRequest:
memory: 256Mi
cpu: 200m
说明:
name:只能使用小写字母,数字, '-' 和 '.',而且只能是数字或字母开头和结尾。default:即该namespace配置resourceQuota时,创建container的默认limit上限defaultRequest:即该namespace配置resourceQuota时,创建container的默认request上限max:即该namespace下创建container的资源最大值min:即该namespace下创建container的资源最小值
其中: min <= defaultRequest <= default <= max
bash
root@master30:~# kubectl apply -f limits.yaml
root@master30:~# kubectl get limitranges
NAME CREATED AT
mylimit 2021-09-09T04:09:15Z
root@master30:~# kubectl describe limitranges mylimit
Name: mylimit
Namespace: quota
Type Resource Min Max Default Request Default Limit Max Limit/Request Ratio
---- -------- --- --- --------------- ------------- -----------------------
Container cpu 100m 1 200m 500m -
Container memory 128Mi 1Gi 256Mi 512Mi -
未指定 resources
示例1:
bash
root@master30:~# vim pod-without-limits.yaml
yaml
apiVersion: v1
kind: Pod
metadata:
name: stress
spec:
containers:
- name: stress
image: hub.laoma.cloud/progrium/stress
imagePullPolicy: IfNotPresent
args: ['-c','1']
**结论:**创建出来的pod的resources 与 limitranage 指定的相关默认值一致。
bash
root@master30:~# kubectl apply -f pod-without-limits.yaml
root@master30:~# kubectl top pods
NAME CPU(cores) MEMORY(bytes)
stress 501m 0Mi
root@master30:~# kubectl get pod stress -o yaml
yaml
......
spec:
containers:
image: hub.laoma.cloud/progrium/stress
imagePullPolicy: IfNotPresent
name: stress
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 200m
memory: 256Mi
......
bash
# 清理资源
root@master30:~# kubectl delete limitranges mylimit
root@master30:~# kubectl delete pod web --force
只指定 limit 值
示例 2-1:limit 值大于 max 值
yaml
apiVersion: v1
kind: Pod
metadata:
name: web
spec:
containers:
- name: web
image: hub.laoma.cloud/library/nginx
resources:
limits:
cpu: 1.1
memory: 1100Mi
bash
root@master30:~# kubectl apply -f limit.yml
Error from server (Forbidden): error when creating "limit.yml": pods "web" is forbidden: [maximum cpu usage per Container is 1, but limit is 1100m, maximum memory usage per Container is 1Gi, but limit is 1181116006400m]
示例 2-2:limit 值小于 min 值
yaml
apiVersion: v1
kind: Pod
metadata:
name: web
spec:
containers:
- name: web
image: hub.laoma.cloud/library/nginx
resources:
limits:
cpu: 60m
memory: 60Mi
bash
root@master30:~# kubectl apply -f limit.yml
Error from server (Forbidden): error when creating "limit.yml": pods "web" is forbidden: [minimum cpu usage per Container is 100m, but request is 60m, minimum memory usage per Container is 128Mi, but request is 60Mi]
示例 2-3:min 值< limit 值< max 值
yaml
apiVersion: v1
kind: Pod
metadata:
name: web
spec:
containers:
- name: web
image: hub.laoma.cloud/library/nginx
resources:
limits:
cpu: 600m
memory: 600Mi
结论:
-
创建的容器limits值必须满足条件:min值<指定的limit值<max值
-
当只指定limits值时,requests值与limits值保持一致,而不是default request。
yamlroot@master30:~# kubectl get pod web -o yaml ...... spec: containers: - image: hub.laoma.cloud/library/nginx imagePullPolicy: Always name: web resources: limits: cpu: 600m memory: 600Mi requests: cpu: 600m memory: 600Mi ......
只指定 requests
示例 3-1:requests 大于 max 值
yaml
apiVersion: v1
kind: Pod
metadata:
name: web
spec:
containers:
- name: web
image: hub.laoma.cloud/library/nginx
resources:
requests:
cpu: 1600m
memory: 600Mi
bash
root@master30:~# kubectl apply -f limit4.yml
The Pod "web" is invalid:
* spec.containers[0].resources.requests: Invalid value: "1600m": must be less than or equal to cpu limit
* spec.containers[0].resources.requests: Invalid value: "1600Mi": must be less than or equal to memory limit
示例 3-2:requests 小于 min 值
yaml
apiVersion: v1
kind: Pod
metadata:
name: web
spec:
containers:
- name: web
image: hub.laoma.cloud/library/nginx
resources:
requests:
cpu: 60m
memory: 60Mi
bash
root@master30:~# kubectl apply -f limit.yml
Error from server (Forbidden): error when creating "limit.yml": pods "web" is forbidden: [minimum cpu usage per Container is 100m, but request is 60m, minimum memory usage per Container is 128Mi, but request is 60Mi]
示例 3-3:min 值< request 值< max 值
yaml
apiVersion: v1
kind: Pod
metadata:
name: web
spec:
containers:
- name: web
image: hub.laoma.cloud/library/nginx
resources:
requests:
cpu: 400m
memory: 400Mi
结论:
-
创建的容器requests值必须满足条件:min值<requests值<limits值
-
当只指定requests值时,limits值与default值保持一致。
yamlroot@master30:~# kubectl get pod web -o yaml ...... spec: containers: - image: hub.laoma.cloud/library/nginx imagePullPolicy: Always name: web resources: limits: cpu: 500m memory: 512Mi requests: cpu: 400m memory: 400Mi ......
限定资源类型
LimitRange 资源可以限定如下资源:
| Type | Resource Name | Description |
|---|---|---|
| container | cpu、memory | 限定容器 cpu、memroy |
| Pod | cpu、memory | 限定 Pod 中所有容器cpu、memroy的总和 |
| PVC | storage | 限定PVC申请的存储空间大小 |
LimitRange for PVC 示例:
yaml
apiVersion: v1
kind: LimitRange
metadata:
name: storagelimits
spec:
limits:
- type: PersistentVolumeClaim
max:
storage: 2Gi
min:
storage: 1Gi
环境清理
bash
root@master30:~# kubectl delete ns quota