动手实践:创建第一个 ResourceQuota
理论讲完了,咱们直接上手。这一节我会带你从零创建一个 ResourceQuota,限制命名空间里的 CPU 和内存总量。别怕,操作很简单,但背后的逻辑值得你细品。
准备工作:确认环境
动手之前,先确认你的集群状态。我个人习惯用 kubectl version 看一眼客户端和服务端版本,确保都在 v1.10 以上——ResourceQuota 这个特性从那个版本开始就稳定了。
kubectl version --short
# 输出示例:
# Client Version: v1.28.2
# Server Version: v1.28.2
然后检查一下集群有没有启用 ResourceQuota 准入控制器。大部分发行版默认都开着,但万一你用的是自建集群,最好确认一下:
kubectl -n kube-system get pod -l component=kube-apiserver -o yaml | grep -A5 "enable-admission-plugins"
如果输出里没有 ResourceQuota,嗯,那得先加上再重启 API Server。不过这种情况我很少遇到,主流云厂商的托管集群都默认启用了。
第一步:创建测试命名空间
我建议你单独搞一个命名空间来做实验,别在生产命名空间里瞎折腾。我曾经手滑在 default 空间里删过东西,那叫一个酸爽……
kubectl create namespace quota-demo
或者用 YAML 也行:
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Namespace
metadata:
name: quota-demo
EOF
第二步:编写 ResourceQuota 配置
这是核心。咱们先写一个最基础的 ResourceQuota,限制 CPU 和内存的总量:
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ResourceQuota
metadata:
name: compute-quota
namespace: quota-demo
spec:
hard:
requests.cpu: "4"
requests.memory: "8Gi"
limits.cpu: "8"
limits.memory: "16Gi"
EOF
你可能会问:为什么同时设 requests 和 limits?
说白了,requests 是“最低保障”,limits 是“最高上限”。集群调度器根据 requests 决定 Pod 能不能调度到某个节点,而 limits 控制 Pod 实际能用多少。两个都限制,才能做到真正的“软硬兼施”。
第三步:验证 ResourceQuota 是否生效
创建完马上查一下:
kubectl get resourcequota compute-quota -n quota-demo -o yaml
你会看到类似这样的输出:
status:
hard:
limits.cpu: "8"
limits.memory: 16Gi
requests.cpu: "4"
requests.memory: 8Gi
used:
limits.cpu: "0"
limits.memory: 0Gi
requests.cpu: "0"
requests.memory: 0Gi
注意那个 used 字段,现在全是 0。这说明集群已经认了这个配额,只是还没人用。
第四步:测试配额是否真的能拦住超额
光创建不算完,得试试它到底能不能拦住超额的 Pod。咱们先创建一个合规的 Pod:
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: normal-pod
namespace: quota-demo
spec:
containers:
- name: nginx
image: nginx:latest
resources:
requests:
cpu: "1"
memory: "1Gi"
limits:
cpu: "2"
memory: "2Gi"
EOF
这个 Pod 的 requests 是 1C/1Gi,limits 是 2C/2Gi,远低于配额上限。创建后看看配额使用情况:
kubectl get resourcequota compute-quota -n quota-demo
输出会变成:
NAME AGE REQUEST LIMIT
compute-quota 5m requests.cpu: 1/4 limits.cpu: 2/8
requests.memory: 1Gi/8Gi limits.memory: 2Gi/16Gi
看到没?used 字段从 0 变成了 1/4 和 1Gi/8Gi。配额系统在实时跟踪。
接下来,试试创建一个超额的 Pod:
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: greedy-pod
namespace: quota-demo
spec:
containers:
- name: stress
image: polinux/stress
resources:
requests:
cpu: "3"
memory: "6Gi"
limits:
cpu: "6"
memory: "12Gi"
command: ["stress"]
args: ["--cpu", "1", "--vm", "1", "--vm-bytes", "500M"]
EOF
这个 Pod 的 requests 是 3C/6Gi,加上之前那个 1C/1Gi,总共 4C/7Gi。CPU 刚好卡在配额线上,但内存超了——配额是 8Gi,这里已经用了 1Gi,再申请 6Gi 就变成 7Gi,没超。等等,我算错了?
嗯,重新算一下:已用 1Gi + 新申请 6Gi = 7Gi,确实没超 8Gi。那这个 Pod 应该能创建成功。咱们换个真正超额的:
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: greedy-pod-2
namespace: quota-demo
spec:
containers:
- name: stress
image: polinux/stress
resources:
requests:
cpu: "4"
memory: "8Gi"
limits:
cpu: "8"
memory: "16Gi"
command: ["stress"]
args: ["--cpu", "1", "--vm", "1", "--vm-bytes", "500M"]
EOF
这次会收到一个错误:
Error from server (Forbidden): error when creating "STDIN": pods "greedy-pod-2" is forbidden: exceeded quota: compute-quota, requested: requests.cpu=4,requests.memory=8Gi, used: requests.cpu=1,requests.memory=1Gi, limited: requests.cpu=4,requests.memory=8Gi
完美!配额系统直接拒绝了创建请求。这就是 ResourceQuota 的威力——它不是在 Pod 运行后去 kill,而是在创建时就拦住。
核心逻辑:一张图看懂
下面这张 SVG 图展示了 ResourceQuota 的完整工作流程:
清理实验环境
实验做完了,记得清理:
kubectl delete namespace quota-demo
删除命名空间会连带删除里面所有的 Pod、ResourceQuota 和其他资源。干净利落。
常见问题与避坑
- Pod 创建失败但没报错? 检查一下是不是用了
kubectl run命令,它有时候不会显示配额拒绝的详细信息。建议用kubectl create -f或kubectl apply -f。 - 配额设了但没生效? 确认 ResourceQuota 和 Pod 在同一个命名空间。配额是命名空间级别的,跨空间不生效。
- Pod 运行中突然被 kill? ResourceQuota 不会 kill 运行中的 Pod。如果 Pod 被 kill,通常是节点资源不足触发了 OOM Killer,跟配额无关。
核心要点回顾:
- ResourceQuota 是准入控制,在创建时拦截超额请求
- 同时限制
requests和limits才能做到完整管控 - 配额用尽后,新 Pod 创建会返回
Forbidden错误 - 配额是命名空间级别的,不同空间互不影响
好了,第一个 ResourceQuota 已经跑起来了。你可以在自己的集群里多试试不同的配额组合,比如只限制 CPU 不限制内存,或者反过来。实践出真知,动手试试比看十遍文档都管用。