13 K8S 资源限制
- CPU 以核心为单位
- memory 以字节为单位
- requests 为 kubernetes scheduler 执行 pod 调度时 node 节点至少需要拥有的资源,如果匹配到该节点的可用资源符合 requests 的预取,就会将 pod 调度到该节点
- limits 为 pod 运行成功后最多可以使用的资源上限。
kubernetes对单个容器的CPU及memory实现资源限制
https://kubernetes.io/zh/docs/tasks/configure-pod-container/assign-memory-resource/
在工作中各个服务之间的推荐限制:
- nginx:2C 2G
- 微服务:2G 2G
- 数据库:mysql/ES:4C 6G
13.1 实现对单个容器资源限制
主要是限制单个服务中的使用过多的内存
1.创建 ns
[10:58:03 root@k8s-master limit]#vim ns.yaml
apiVersion: v1
kind: Namespace
metadata:
name: limits
2.创建 ns
[10:58:02 root@k8s-master limit]#kubectl apply -f ns.yaml
3.编写控制器
该 yaml 文件中使用的容器会有两个工作线程,每个线程使用 256M 内存,当该容器起来之后会占用两个 cpu 并会占用 512M 内存,如果我们在 yaml 文件中不添加资源限制的话该容器使用的资源会特别多
[10:58:18 root@k8s-master limit]#vim case1-pod-memory-limit.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: limit-test-deployment
namespace: limits
spec:
replicas: 1
selector:
matchLabels: #rs or deployment
app: limit-test-pod
template:
metadata:
labels:
app: limit-test-pod
spec:
containers: # 这个容器使用的镜像是用于压力测试
- name: limit-test-container
image: lorel/docker-stress-ng
# 容器资源限制
resources:
limits: # 硬限制该容器最多只能使用这么多资源
memory: "200Mi"
cpu: 200m
requests: # 软限制,被调度的 node 必须要这么多资源
memory: "100Mi"
args: ["--vm", "2", "--vm-bytes", "256M"]
4.创建
[11:00:21 root@k8s-master limit]#kubectl apply -f case1-pod-memory-limit.yml
5.通过 top 查看
[11:07:06 root@k8s-master limit]#kubectl top pod -n limits
NAME CPU(cores) MEMORY(bytes)
limit-test-deployment-7545f64fcc-xzvtd 202m 196Mi
# 可以看到当前 pod 的 cpu 和 mem 已经用满
13.2 对 pod 资源限制
Limit Range是对具体某个Pod或容器的资源使用进行限制
https://kubernetes.io/zh/docs/concepts/policy/limit-range/
Limit Range限制类型:
- 容器
- pod
- pvc
默认情况下, Kubernetes 集群上的容器运行使用的计算资源没有限制。 使用资源配额,集群管理员可以以名字空间为单位,限制其资源的使用与创建。 在命名空间中,一个 Pod 或 Container 最多能够使用命名空间的资源配额所定义的 CPU 和内存用量。 有人担心,一个 Pod 或 Container 会垄断所有可用的资源。 LimitRange 是在命名空间内限制资源分配(给多个 Pod 或 Container)的策略对象。
一个 LimitRange(限制范围) 对象提供的限制能够做到:
- 在一个命名空间中实施对每个 Pod 或 Container 最小和最大的资源使用量的限制。
- 在一个命名空间中实施对每个 PersistentVolumeClaim 能申请的最小和最大的存储空间大小的限制。
- 在一个命名空间中实施对一种资源的申请值和限制值的比值的控制。
- 设置一个命名空间中对计算资源的默认申请/限制值,并且自动的在运行时注入到多个 Container 中。
也就是说担心研发人员在创建 pod 的时候资源使用过高而导致其他的 work pod 无法被正常启动
启用 LimitRange:
对 LimitRange 的支持自 Kubernetes 1.10 版本默认启用。
LimitRange 支持在很多 Kubernetes 发行版本中也是默认启用的。
LimitRange 的名称必须是合法的 DNS 子域名。
限制范围总览:
- 管理员在一个命名空间内创建一个
LimitRange
对象。 - 用户在命名空间内创建 Pod ,Container 和 PersistentVolumeClaim 等资源。
LimitRanger
准入控制器对所有没有设置计算资源需求的 Pod 和 Container 设置默认值与限制值, 并跟踪其使用量以保证没有超出命名空间中存在的任意 LimitRange 对象中的最小、最大资源使用量以及使用量比值。- 若创建或更新资源(Pod、 Container、PersistentVolumeClaim)违反了 LimitRange 的约束, 向 API 服务器的请求会失败,并返回 HTTP 状态码
403 FORBIDDEN
与描述哪一项约束被违反的消息。 - 若命名空间中的 LimitRange 启用了对
cpu
和memory
的限制, 用户必须指定这些值的需求使用量与限制使用量。否则,系统将会拒绝创建 Pod。 - LimitRange 的验证仅在 Pod 准入阶段进行,不对正在运行的 Pod 进行验证。
能够使用限制范围创建的策略示例有:
- 在一个 master 有两个 node 节点,8 GiB 内存与16个核的集群中,限制一个命名空间的 Pod 申请 100m 单位,最大 500m 单位的 CPU,以及申请 200Mi,最大 600Mi 的内存。
- 为 spec 中没有 cpu 和内存需求值的 Container 定义默认 CPU 限制值与需求值 150m,内存默认需求值 300Mi。
在命名空间的总限制值小于 Pod 或 Container 的限制值的总和的情况下,可能会产生资源竞争。 在这种情况下,将不会创建 Container 或 Pod。
竞争和对 LimitRange 的改变都不会影响任何已经创建了的资源。
13.2.1 创建 Limit
1.编写 LimitRange yaml 文件
[14:53:23 root@k8s-master limit]#vim case3-LimitRange.yaml
apiVersion: v1
kind: LimitRange
metadata:
name: limitrange
namespace: limits # 指定对该 namespace 下的资源进行限制
spec:
limits:
- type: Container # 限制的资源类型
max:
cpu: "2" # 限制单个容器的最大CPU
memory: "2Gi" # 限制单个容器的最大内存
min:
cpu: "500m" # 限制单个容器的最小CPU
memory: "512Mi" # 限制单个容器的最小内存
default: # 默认情况下,也就是没有对这个 namespace 中的 pod 做限制
cpu: "500m" # 默认单个容器的CPU限制
memory: "512Mi" # 默认单个容器的内存限制
defaultRequest:
cpu: "500m" # 默认单个容器的CPU创建请求
memory: "512Mi" # 默认单个容器的内存创建请求
maxLimitRequestRatio:
cpu: 2 # 限制 CPU limit/request 比值最大为2
memory: 2 # 限制内存 limit/request 比值最大为1.5
- type: Pod
max:
cpu: "4" # 限制单个 Pod 的最大CPU
memory: "4Gi" # 限制单个 Pod 最大内存
- type: PersistentVolumeClaim
max:
storage: 50Gi # 限制PVC最大的 requests.storage
min:
storage: 30Gi # 限制PVC最小的 requests.storage
2.创建
[14:58:13 root@k8s-master limit]#kubectl apply -f case3-LimitRange.yaml
3.查看已经创建
[14:58:58 root@k8s-master limit]#kubectl get limitranges -n limits
NAME CREATED AT
limitrange 2021-11-01T06:58:21Z
# 通过 describe 查看
[14:59:25 root@k8s-master limit]#kubectl describe limitranges -n limits limitrange
Name: limitrange
Namespace: limits
Type Resource Min Max Default Request Default Limit Max Limit/Request Ratio
---- -------- --- --- --------------- ------------- -----------------------
Container memory 512Mi 2Gi 512Mi 512Mi 2
Container cpu 500m 2 500m 500m 2
Pod cpu - 4 - - -
Pod memory - 4Gi - - -
PersistentVolumeClaim storage 30Gi 50Gi - - -
13.2.2 创建 pod 进行验证
1.编写 yaml 文件
[15:05:50 root@k8s-master limit]#cat case4-pod-RequestRatio-limit.yaml
kind: Deployment
apiVersion: apps/v1
metadata:
labels:
app: wordpress-deployment-label
name: wordpress-deployment
namespace: limits
spec:
replicas: 1
selector:
matchLabels:
app: wordpress-selector
template:
metadata:
labels:
app: wordpress-selector
spec:
containers:
- name: wordpress-nginx-container
image: nginx:1.16.1
imagePullPolicy: Always
ports:
- containerPort: 80
protocol: TCP
name: http
env:
- name: "password"
value: "123456"
- name: "age"
value: "18"
resources:
limits: # limits 的资源是 requests 的两倍
cpu: 2
memory: 2Gi
requests:
cpu: 2
memory: 1Gi
- name: wordpress-php-container
image: php:5.6-fpm-alpine
imagePullPolicy: Always
ports:
- containerPort: 80
protocol: TCP
name: http
env:
- name: "password"
value: "123456"
- name: "age"
value: "18"
resources:
limits: # limits 的资源是 requests 的两倍
cpu: 1
#cpu: 2
memory: 1Gi
requests:
cpu: 500m
memory: 512Mi
---
kind: Service
apiVersion: v1
metadata:
labels:
app: wordpress-service-label
name: wordpress-service
namespace: limits
spec:
type: NodePort
ports:
- name: http
port: 80
protocol: TCP
targetPort: 8080
nodePort: 30063
selector:
app: wordpress-selector
2.创建
[15:06:01 root@k8s-master limit]#kubectl apply -f case4-pod-RequestRatio-limit.yaml
3.可以看到当前的 pod 已经创建成功
因为我们在 yaml 文件中指定的 requests 和 limits 百分比都是两倍
[15:09:40 root@k8s-master limit]#kubectl get pod -n limits
NAME READY STATUS RESTARTS AGE
wordpress-deployment-8676469d57-72l9r 2/2 Running 0 59s
13.2.3 错误资源请求限制演示
如果我将刚才在 13.2.2 中的创建 pod yaml 文件中修改了需要的资源超出了 LimitRange 的配置我们看看会发生什么问题
1.先删除当前的 pod
[15:13:59 root@k8s-master limit]#kubectl delete -f case4-pod-RequestRatio-limit.yaml
2.修改 yaml 文件
如下图中我将内存改为了超出 LimitRange 的配置,并且百分比也不是 2 的倍数
3.创建
[15:18:31 root@k8s-master limit]#kubectl apply -f case4-pod-RequestRatio-limit.yaml
# 虽然创建成功
4.但是通过指定 namespace 的话并未能看到对应的 pod
[15:18:39 root@k8s-master limit]#kubectl get pod -n limits
No resources found in limits namespace.
5.通过查看 events 会发现警告信息
[15:20:34 root@k8s-master limit]#kubectl get events -n limits
11m Warning FailedCreate replicaset/wordpress-deployment-7fc9cd79b8 Error creating: pods "wordpress-deployment-7fc9cd79b8-qj7z2" is forbidden: maximum memory usage per Container is 2Gi, but limit is 3Gi
11m Warning FailedCreate replicaset/wordpress-deployment-7fc9cd79b8 Error creating: pods "wordpress-deployment-7fc9cd79b8-hr82k" is forbidden: maximum memory usage per Container is 2Gi, but limit is 3Gi
11m Warning FailedCreate replicaset/wordpress-deployment-7fc9cd79b8 Error creating: pods "wordpress-deployment-7fc9cd79b8-qbll5" is forbidden: maximum memory usage per Container is 2Gi, but limit is 3Gi
10m Warning FailedCreate replicaset/wordpress-deployment-7fc9cd79b8 (combined from similar events): Error creating: pods "wordpress-deployment-7fc9cd79b8-kmfpp" is forbidden: maximum memory usage per Container is 2Gi, but limit is 3Gi
# maximum memory usage per Container is 2Gi, but limit is 3Gi
# 容器的最大内存为 2G 但是这里设置为 3G 超出,所有未能成功创建
6.或者通过查看 deployment 并通过 json 格式输出
[15:38:07 root@k8s-master limit]#kubectl get deployments.apps -n limits wordpress-deployment -o json
"message": "pods \"wordpress-deployment-7b74888676-646bf\" is forbidden: maximum memory usage per Container is 2Gi, but limit is 3Gi,
13.2.4 错误的资源限制百分比演示
如果说我们在 Limit 中指定了 CPU 和 Mem 的申请百分比那么在 yaml 文件中也要指定,如在 13.2.1 创建 Limit 的时候指定了百分比为 2
1.删除 13.2.3 中创建的 pod
[15:20:52 root@k8s-master limit]#kubectl delete -f case4-pod-RequestRatio-limit.yaml
2.修改 yaml 文件中的资源申请百分比
这里我修改为了 4 被,也就是说 limits 资源是 requests 的 4 倍
3.创建
[15:31:02 root@k8s-master limit]#kubectl apply -f case4-pod-RequestRatio-limit.yaml
4.查看 pod 并没没有创建成功
[15:31:20 root@k8s-master limit]#kubectl get pod -n limits
No resources found in limits namespace.
5.通过 events 会发现警告信息
[15:32:05 root@k8s-master limit]#kubectl get events -n limits
4s Warning FailedCreate replicaset/wordpress-deployment-7b74888676 (combined from similar events): Error creating: pods "wordpress-deployment-7b74888676-zsmlc" is forbidden: cpu max limit to request ratio per Container is 2, but provided ratio is 4.000000
# 创建错误:pods“wordpress-deployment-7b74888676-zsmlc”被禁止:每个容器的cpu最大限制请求比率为2,但提供的比率为4.000000
6.或者通过查看 deployment 并通过 json 格式输出
[15:38:28 root@k8s-master limit]#kubectl get deployments.apps -n limits wordpress-deployment -o json
"message": "pods \"wordpress-deployment-7b74888676-646bf\" is forbidden: cpu max limit to request ratio per Container is 2, but provided ratio is 4.000000",
13.3 对 namespace 资源限制
https://kubernetes.io/zh/docs/concepts/policy/resource-quotas/
当多个用户或团队共享具有固定节点数目的集群时,人们会担心有人使用超过其基于公平原则所分配到的资源量。
资源配额是帮助管理员解决这一问题的工具。
资源配额,通过 ResourceQuota
对象来定义,对每个命名空间的资源消耗总量提供限制。 它可以限制命名空间中某种类型的对象的总数目上限,也可以限制命令空间中的 Pod 可以使用的计算资源的总上限。
资源配额的工作方式如下:
- 不同的团队可以在不同的命名空间下工作,目前这是非约束性的,在未来的版本中可能会通过 ACL (Access Control List 访问控制列表) 来实现强制性约束。
- 集群管理员可以为每个命名空间创建一个或多个 ResourceQuota 对象。
- 当用户在命名空间下创建资源(如 Pod、Service 等)时,Kubernetes 的配额系统会 跟踪集群的资源使用情况,以确保使用的资源用量不超过 ResourceQuota 中定义的硬性资源限额。
- 如果资源创建或者更新请求违反了配额约束,那么该请求会报错(HTTP 403 FORBIDDEN), 并在消息中给出有可能违反的约束。
- 如果命名空间下的计算资源 (如
cpu
和memory
)的配额被启用,则用户必须为 这些资源设定请求值(request)和约束值(limit),否则配额系统将拒绝 Pod 的创建。 提示: 可使用LimitRanger
准入控制器来为没有设置计算资源需求的 Pod 设置默认值。
限定某个对象类型(如Pod、service)可创建对象的总数;
限定某个对象类型可消耗的计算资源(CPU、内存)与存储资源(存储卷声明)总数,也就是数在当前 namespace 中最多可以创建多少的 PV PVC
ResourceQuota 对象的名称必须是合法的 DNS 子域名。
下面是使用命名空间和配额构建策略的示例:
- 在具有 32 GiB 内存和 16 核 CPU 资源的集群中,允许 A 团队使用 20 GiB 内存 和 10 核的 CPU 资源, 允许 B 团队使用 10 GiB 内存和 4 核的 CPU 资源,并且预留 2 GiB 内存和 2 核的 CPU 资源供将来分配。
- 限制 “testing” 命名空间使用 1 核 CPU 资源和 1GiB 内存。允许 “production” 命名空间使用任意数量。
在集群容量小于各命名空间配额总和的情况下,可能存在资源竞争。资源竞争时,Kubernetes 系统会遵循先到先得的原则。
不管是资源竞争还是配额的修改,都不会影响已经创建的资源使用对象。
也就是说我们在工作中不同的项目采用不同的 namspace ,并对该 namespace 下的资源进行限制
13.3.1 计算资源配额
用户可以对给定命名空间下的可被请求的 计算资源 总量进行限制。
配额机制所支持的资源类型:
资源名称 | 描述 |
---|---|
limits.cpu |
对所有运行中 Pod,其 CPU 限额总量不能超过该值。 |
limits.memory |
对所有运行中 Pod,其内存限额总量不能超过该值。 |
requests.cpu |
对所有运行中 Pod,其 CPU 需求总量不能超过该值。 |
requests.memory |
对所有运行中 Pod,其内存需求总量不能超过该值。 |
hugepages-<size> |
对所有运行中 Pod,针对指定尺寸的巨页请求总数不能超过此值。 |
cpu |
与 requests.cpu 相同。 |
memory |
与 requests.memory 相同。 |
用户可以对给定命名空间下的存储资源 总量进行限制。
此外,还可以根据相关的存储类(Storage Class)来限制存储资源的消耗。
资源名称 | 描述 |
---|---|
requests.storage |
所有 PVC,存储资源的需求总量不能超过该值。 |
persistentvolumeclaims |
在该命名空间中所允许的 PVC 总量。 |
<storage-class-name>.storageclass.storage.k8s.io/requests.storage |
在所有与 <storage-class-name> 相关的持久卷申领中,存储请求的总和不能超过该值。 |
<storage-class-name>.storageclass.storage.k8s.io/persistentvolumeclaims |
在与 storage-class-name 相关的所有持久卷申领中,命名空间中可以存在的持久卷申领总数。 |
例如,如果一个操作人员针对 gold
存储类型与 bronze
存储类型设置配额, 操作人员可以定义如下配额:
gold.storageclass.storage.k8s.io/requests.storage: 500Gi
bronze.storageclass.storage.k8s.io/requests.storage: 100Gi
以 GPU 拓展资源为例,如果资源名称为 nvidia.com/gpu
,并且要将命名空间中请求的 GPU 资源总数限制为 4,则可以如下定义配额:
requests.nvidia.com/gpu: 4
13.3.2 对象数量配额
你可以使用以下语法对所有标准的、命名空间域的资源类型进行配额设置:
count/<resource>.<group>
:用于非核心(core)组的资源count/<resource>
:用于核心组的资源
这是用户可能希望利用对象计数配额来管理的一组资源示例。
count/persistentvolumeclaims
count/services
count/secrets
count/configmaps
count/replicationcontrollers
count/deployments.apps
count/replicasets.apps
count/statefulsets.apps
count/jobs.batch
count/cronjobs.batch
相同语法也可用于自定义资源。 例如,要对 example.com
API 组中的自定义资源 widgets
设置配额,请使用 count/widgets.example.com
。
当使用 count/*
资源配额时,如果对象存在于服务器存储中,则会根据配额管理资源。 这些类型的配额有助于防止存储资源耗尽。例如,用户可能想根据服务器的存储能力来对服务器中 Secret 的数量进行配额限制。 集群中存在过多的 Secret 实际上会导致服务器和控制器无法启动。 用户可以选择对 Job 进行配额管理,以防止配置不当的 CronJob 在某命名空间中创建太多 Job 而导致集群拒绝服务。
对有限的一组资源上实施一般性的对象数量配额也是可能的。 此外,还可以进一步按资源的类型设置其配额。
支持以下类型:
资源名称 | 描述 |
---|---|
configmaps |
在该命名空间中允许存在的 ConfigMap 总数上限。 |
persistentvolumeclaims |
在该命名空间中允许存在的 PVC 的总数上限。 |
pods |
在该命名空间中允许存在的非终止状态的 Pod 总数上限。Pod 终止状态等价于 Pod 的 .status.phase in (Failed, Succeeded) 为真。 |
replicationcontrollers |
在该命名空间中允许存在的 ReplicationController 总数上限。 |
resourcequotas |
在该命名空间中允许存在的 ResourceQuota 总数上限。 |
services |
在该命名空间中允许存在的 Service 总数上限。 |
services.loadbalancers |
在该命名空间中允许存在的 LoadBalancer 类型的 Service 总数上限。 |
services.nodeports |
在该命名空间中允许存在的 NodePort 类型的 Service 总数上限。 |
secrets |
在该命名空间中允许存在的 Secret 总数上限。 |
例如,pods
配额统计某个命名空间中所创建的、非终止状态的 Pod
个数并确保其不超过某上限值。 用户可能希望在某命名空间中设置 pods
配额,以避免有用户创建很多小的 Pod, 从而耗尽集群所能提供的 Pod IP 地址。
13.3.3 创建 ResourceQuota
创建 ResourceQuota 来进行对每个命名空间的资源消耗总量提供限制。
1.编写 yaml
[16:03:57 root@k8s-master limit]#vim case6-ResourceQuota-magedu.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
name: quota-limits
namespace: limits # 对 limits 命名空间做限制
spec:
hard: # 硬限制
requests.cpu: "46" # 对运行中的 pod cpu 需求总量不能超过该值
limits.cpu: "46" # 对运行中的 pod cpu 限额总量不能超过该值。
requests.memory: 120Gi # 对运行中的 pod mem 其内存需求总量不能超过该值。
limits.memory: 120Gi # 对运行中的 pod mem 其内存限额总量不能超过该值。
requests.nvidia.com/gpu: 4 # 由于现在又人工智能的场景所以 K8S 中也限制 GPU
pods: "2" # 在该命名空间中有多少个 Pod 总数上限
services: "2" # 在该命名空间中允许存在的 Service 总数上限。
2.创建
[16:25:21 root@k8s-master limit]#kubectl apply -f case6-ResourceQuota-magedu.yaml
3.创建成功
[16:29:56 root@k8s-master limit]#kubectl get resourcequotas -n limits
NAME AGE REQUEST LIMIT
quota-limits 2s pods: 0/2, requests.cpu: 0/46, requests.memory: 0/120Gi, requests.nvidia.com/gpu: 0/4, services: 0/2 limits.cpu: 0/46, limits.memory: 0/120Gi
4.通过 describe 查看
[16:46:06 root@k8s-master limit]#kubectl describe resourcequotas -n limits
Name: quota-limits
Namespace: limits
Resource Used Hard
-------- ---- ----
limits.cpu 2 46
limits.memory 2Gi 120Gi
pods 2 2
requests.cpu 1 46
requests.memory 1Gi 120Gi
requests.nvidia.com/gpu 0 4
services 1 2
13.3.4 创建 pod 验证
这里我编写一个 yaml 文件,并在这个 yaml 文件中指定我们的 pod 数量
1.编写 yaml 文件
[16:31:56 root@k8s-master limit]#vim case7-namespace-pod-limit-test.yaml
kind: Deployment
apiVersion: apps/v1
metadata:
labels:
app: nginx-deployment-label
name: nginx-deployment
namespace: limits
spec:
replicas: 5 # 这里我指定了在当前的 deployment 中创建 5 个 pod
selector:
matchLabels:
app: nginx-selector
template:
metadata:
labels:
app: nginx-selector
spec:
containers:
- name: nginx-container
image: nginx:1.16.1
imagePullPolicy: Always
ports:
- containerPort: 80
protocol: TCP
name: http
env:
- name: "password"
value: "123456"
- name: "age"
value: "18"
resources:
limits:
cpu: 1
memory: 1Gi
requests:
cpu: 500m
memory: 512Mi
---
kind: Service
apiVersion: v1
metadata:
labels:
app: nginx-service-label
name: nginx-service
namespace: limits
spec:
type: NodePort
ports:
- name: http
port: 80
protocol: TCP
targetPort: 80
#nodePort: 30033
selector:
app: nginx-selector
2.创建
[16:31:59 root@k8s-master limit]#kubectl apply -f case7-namespace-pod-limit-test.yaml
3.验证
通过下图可以看到即使我在 yaml 文件中指定了 pod 的副本数量为 5 但是在创建的时候只能创建出两个 pod ,因为澡 resourceQuota 中明确中的了 pod 总数为 2