10.1 Kubernetes 资源管理
10.1.1 ResourcceQuota 简介
ResourceQuota 是什么
在使用 Kubernetes 时,公司可能会有多个项目组,每个项目组都有自己的命名空间,能够在其中创建资源。然而,他们通常对 Kubernetes 集群的规模和可调度资源的数量并不了解,这可能导致资源的过量分配,从而影响集群的可用性。为了解决这个问题,Kubernetes 引入了 ResourceQuota 的概念。
ResourceQuota 是 Kubernetes 中的一种资源对象,用于限制特定命名空间内的资源使用。
定义一个 ResourceQuota
使用以下 YAML 文件定义 ResourceQuota:
apiVersion: v1
kind: ResourceQuota
metadata:
name: my-resource-quota
namespace: my-namespace
spec:
hard:
requests.cpu: "10" # CPU 请求总量限制,10 表示 10000m
limits.cpu: "20" # CPU 限制总量限制
requests.memory: "20Gi" # 内存请求总量限制
limits.memory: "40Gi" # 内存限制总量限制
pods: "30" # Pod 总数量限制
replicationcontrollers: "5" # 复制控制器总数量限制
replicasets.apps: "10" # 副本集总数量限制
deployments.apps: "10" # Deployment 总数量限制
persistentvolumeclaims: "15" # PVC 总数量限制
requests.storage: "100Gi" # 存储请求总量限制
services: "10" # 服务总数量限制
secrets: "100" # 秘密总数量限制
configmaps: "100" # 配置映射总数量限制
networkpolicies: "5" # 网络策略总数量限制
ingresses.networking.k8s.io: "10" # Ingress 总数量限制
10.1.2 使用 ResourceQuota 案例
假设我们有一个命名空间 dev,我们希望限制该命名空间中使用的资源量,以确保不会超出集群资源的容量。
步骤 1: 创建命名空间
首先,我们需要创建一个命名空间:
apiVersion: v1
kind: Namespace
metadata:
name: dev
可以使用以下命令创建命名空间:
kubectl apply -f namespace.yaml
步骤 2: 创建 ResourceQuota
接下来,我们在该命名空间中定义一个 ResourceQuota。我们希望限制 dev 命名空间中 CPU 和内存的使用量,具体如下:
- 最大 CPU 限制为 4 个核心
- 最大内存限制为 8 GiB
- 最多允许 10 个 Pod
以下是 ResourceQuota 的 YAML 文件:
apiVersion: v1
kind: ResourceQuota
metadata:
name: dev-resource-quota
namespace: dev
spec:
hard:
requests.cpu: "4"
requests.memory: "8Gi"
limits.cpu: "4"
limits.memory: "8Gi"
pods: "10"
使用以下命令应用 ResourceQuota:
kubectl apply -f resource-quota.yaml
步骤 3: 验证 ResourceQuota
你可以通过以下命令查看命名空间的 ResourceQuota 状态:
kubectl get resourcequota -n dev
这将显示 dev 命名空间中当前的资源使用情况和限制。
[root@master-01 ~]# kubectl get resourcequota -n dev
NAME AGE REQUEST LIMIT
dev-resource-quota 12s pods: 0/10, requests.cpu: 0/4, requests.memory: 0/8Gi limits.cpu: 0/4, limits.memory: 0/8Gi
步骤 4: 尝试创建资源
现在,如果你在 dev 命名空间中尝试创建超出限制的 Pod 或者设置超出配额的资源请求,将会失败。例如,以下 Pod 定义将会被拒绝,因为它请求的 CPU 和内存超过了配额:
apiVersion: v1
kind: Pod
metadata:
name: example-pod
namespace: dev
spec:
containers:
- name: example-container
image: nginx
resources:
requests:
cpu: "2"
memory: "6Gi"
limits:
cpu: "2"
memory: "6Gi"
[root@master-01 ~]# kubectl get pod -n dev
NAME READY STATUS RESTARTS AGE
example-pod 0/1 Pending 0 10s
[root@master-01 ~]# kubectl describe pod example-pod -n dev
Name: example-pod
Namespace: dev
Priority: 0
Node: <none>
Labels: <none>
Annotations: <none>
Status: Pending
IP:
IPs: <none>
Containers:
example-container:
Image: nginx
Port: <none>
Host Port: <none>
Limits:
cpu: 2
memory: 6Gi
Requests:
cpu: 2
memory: 6Gi
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-zdbrw (ro)
Conditions:
Type Status
PodScheduled False
Volumes:
kube-api-access-zdbrw:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
ConfigMapOptional: <nil>
DownwardAPI: true
QoS Class: Guaranteed
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 22s default-scheduler 0/2 nodes are available: 2 Insufficient cpu, 2 Insufficient memory.
[root@master-01 ~]#
步骤 5: 创建资源成功的示例
如果你创建的 Pod 符合配额要求,比如:
apiVersion: v1
kind: Pod
metadata:
name: example-pod-2
namespace: dev
spec:
containers:
- name: example-container
image: nginx
resources:
requests:
cpu: "1"
memory: "2Gi"
limits:
cpu: "1"
memory: "2Gi"
这个 Pod 将会成功创建,因为它在配额限制内。
[root@master-01 ~]# kubectl -n dev get pod
NAME READY STATUS RESTARTS AGE
example-pod-2 1/1 Running 0 2m43s
[root@master-01 ~]# kubectl -n dev get resourcequotas
NAME AGE REQUEST LIMIT
dev-resource-quota 6m41s pods: 1/10, requests.cpu: 1/4, requests.memory: 2Gi/8Gi limits.cpu: 1/4, limits.memory: 2Gi/8Gi
[root@master-01 ~]# kubectl -n dev get resourcequotas dev-resource-quota -oyaml
apiVersion: v1
kind: ResourceQuota
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","kind":"ResourceQuota","metadata":{"annotations":{},"name":"dev-resource-quota","namespace":"dev"},"spec":{"hard":{"limits.cpu":"4","limits.memory":"8Gi","pods":"10","requests.cpu":"4","requests.memory":"8Gi"}}}
creationTimestamp: "2024-10-22T14:10:58Z"
name: dev-resource-quota
namespace: dev
resourceVersion: "38337"
uid: 2ecf9dc6-4017-42de-a9c7-4d6c939cde63
spec:
hard:
limits.cpu: "4"
limits.memory: 8Gi
pods: "10"
requests.cpu: "4"
requests.memory: 8Gi
status:
hard:
limits.cpu: "4"
limits.memory: 8Gi
pods: "10"
requests.cpu: "4"
requests.memory: 8Gi
used:
limits.cpu: "1"
limits.memory: 2Gi
pods: "1"
requests.cpu: "1"
requests.memory: 2Gi
[root@master-01 ~]#
10.2 Pod 的资源限制
通过使用 ResourceQuota,我们能够限制 Kubernetes 集群中命名空间的可用资源。然而,在同一个命名空间内,如果分配了 16 核 CPU 和 64 GB 内存,但某个 Pod 未设定资源限制,可能会导致该 Pod 占用整个命名空间的资源。为防止这种情况发生,我们应为每个 Pod 设置资源限制。
假设我们有一个 Java 进程,最低需要 1 核 CPU 和 2GB 内存才能正常启动,并且需要 10 个副本以支撑业务量。在这种情况下,我们需要创建一个 Deployment,设置副本数为 10,requests.cpu 为 1,requests.memory 为 2GB。在创建该资源时,Scheduler 会根据一系列算法将 Pod 部署到合适的节点上,而被调度的节点至少需要有 1 核 CPU 和 2GB 的空闲资源。
如果未设置 requests 字段,Scheduler 在调度时将不会检查节点上的资源是否满足应用程序的最低需求。此时,Pod 可能会随机部署到资源不足的节点上,导致容器无法启动,形成恶性循环。如果 10 个副本中有 3 个部署到资源紧张的节点上,它们之间会出现资源竞争,可能导致所有副本都无法启动。此外,启动过程中高 CPU 使用率可能会使正在运行的 Pod 和节点不可用,进而引发系统崩溃。因此,合理设置 requests 和 limits 是非常必要的。
10.2.1 Pod 的资源限制
在 Pod 的 YAML 文件中,你可以在 spec.containers 下的 resources 字段中设置请求和限制。例如:
yamlCopy CodeapiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
containers:
- name: my-container
image: nginx
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
在容器的 resources 字段下,我们定义了对内存和 CPU 的限制和请求。
limits 定义了对资源的最大限制:
- memory: "200Mi" 表示最大可用内存为 200MiB。
- cpu: "500m" 表示最大可用 CPU 为 500 毫核(0.5 核)。
requests 定义了对资源的请求:
-
memory: "100Mi" 表示请求 100MiB 的内存。
-
cpu: "250m" 表示请求 250 毫核(0.25 核)的 CPU。
10.2.2 服务质量等级 Qos
尽管我们实施了资源限制,但在实际使用中,节点资源仍然可能不足。为了解决资源不足的问题,Kubernetes 会通过重启或驱逐 Pod 来释放资源,然而这种操作可能导致一些关键服务的不可用。实际上,优先重启或驱逐一些不重要的 Pod 会更为理想,而这一决策是通过服务质量(QoS,Quality of Service)来决定的。因此,在生产环境中,QoS 是一个至关重要的环节。
服务质量等级(Quality of Service,QoS)在 Kubernetes 中也起着重要作用。在 Kubernetes 中,Pod 根据其请求的资源量和使用的资源情况,被分为以下三个 QoS 等级:
Kubernetes 中的 QoS 主要分为三种类别:
1. Guaranteed(保证型)
条件:
-
Pod 中每个容器都必须指定
limits.cpu、requests.cpu字段,并且需要两者需要相等; -
Pod 中每个容器都必须指定
limits.cpu、requests.cpu字段,并且需要两者需要相等;
特点:
- Pods 将始终获得其请求的资源(CPU 和内存)。
- 在资源不足的情况下,Kubernetes 优先保留保证型 Pods。
- 容器会在节点上的资源使用限制内运行。
适用场景:
- 适用于对性能要求较高的应用,比如数据库和关键业务服务。
案例:
apiVersion: v1
kind: Pod
metadata:
name: db-service
spec:
containers:
- name: postgres
image: postgres:latest
resources:
requests:
memory: "2Gi"
cpu: "1"
limits:
memory: "2Gi"
cpu: "1"
2. Burstable
条件:
- Pod 不符合 Guaranteed 的配置要求。
- Pod 中至少有一个容器配置了
limits.cpu、requests.cpu、limits.cpu、requests.cpu字段中的一个。
特点:
- Pods 在资源充足时可以使用超过
requests的资源,但在资源紧张时可能会被限制到请求的值。 - 在资源不足的情况下,Kubernetes 会第二优先驱逐此类 Pods。
适用场景:
- 适用于需要一定资源保证但也可以利用额外资源的应用,例如 Web 服务或后台处理任务。
案例:
apiVersion: v1
kind: Pod
metadata:
name: web-app
spec:
containers:
- name: nginx
image: nginx:latest
resources:
requests:
memory: "512Mi"
cpu: "0.5"
limits:
memory: "1Gi"
cpu: "1"
3. Best Effort
定义:
- 如果 Pod 中的容器没有设置
requests和limits。
特点:
- Pods 不会有任何资源保证,只有在资源充足时才能运行。
- 在资源不足的情况下,这类 Pods 最先被驱逐。
适用场景:
- 适用于不重要的应用,比如测试或批处理作业。
案例:
apiVersion: v1
kind: Pod
metadata:
name: batch-job
spec:
containers:
- name: data-processor
image: my-dataprocessor:latest
因此,当节点资源不足时,Kubernetes 将优先保证 Guaranteed Pod 的资源需求,然后是 Burstable Pod,最后是 BestEffort Pod。管理员可以根据应用的重要性和资源需求来设置 Pod 的 QoS 等级,以确保关键应用能够获得足够的资源,保障其服务质量。
通常情况下,依赖的底层中间件的 Qos 会配置为 Guaranteed,其他服务可能并不需要那么高的 Qos。
评论区