Kubernetes Metric Server、Quota and Limits、Kubernetes Health ChecKubernetes 认证和授权
内容
-
Kubernetes Metric Server
-
Quota and Limits
-
Kubernetes Health ChecKubernetes 认证和授权
Kubernetes Metric Server
学习参考:Metric Server
环境准备
[root@master30 ~ 10:06:03]# kubectl create ns metric namespace/metric created [root@master30 ~ 10:12:39]# kubectl config set-context --current --namespace metric Context "cluster1-context" modified.
Metrics-Server 概述
一、问题提出与解决:
-
如何监控 Node 资源使用?
-
如何监控 Pod 资源使用?
-
如何依据 Pod 负载自动扩缩容?
统一基础组件:Metrics Server
二、Metrics Server 核心定位解读
1. 核心作用
Metrics Server 是 K8s 官方轻量指标采集组件,专门服务于自动扩缩容链路,只采集 CPU、内存两类计算资源指标:
-
从每个节点的
kubelet /metrics/resource拉取 Node、Pod 的瞬时资源使用率; -
将指标聚合后注册到集群 API(Metrics API);
-
对外提供两条使用入口:
(1)命令行:
kubectl top node / kubectl top pod,手动查看负载;(2)控制器:给 HPA/VPA 提供指标,实现 Pod 自动扩容缩容。
2. 工作流程
-
Metrics Server 定时访问所有节点 kubelet;
-
kubelet 读取容器运行时(containerd/docker)的 CPU、内存占用数据;
-
指标汇总存入内存(不持久化,只存最近窗口数据);
-
APIServer 暴露标准 Metrics API;
-
HPA 周期性调用 API 获取指标,计算是否需要扩缩容。
3. 关键特性(原文翻译 + 解释)
-
通用部署:一套 yaml 适配绝大多数标准 K8s 集群;
-
采集周期默认 15s:每 15s 拉取一轮全集群指标(可通过 controller-manager 参数修改);
-
极低资源消耗:全集群单实例运行,每个节点仅消耗 1m CPU、2MB 内存;
-
大规模集群支持:最多支撑 5000 节点生产集群,性能足够企业级使用。
4. 重要限制(原文重点:不能替代 Prometheus)
Metrics Server 只给自动扩缩容用,不能做长期监控、大盘展示、告警
-
数据只存在内存,集群重启 / 组件重启指标全部丢失,无持久存储;
-
仅采集 CPU、内存,没有磁盘、网络、业务自定义指标;
-
无法把指标转发到第三方监控系统(Prometheus/Grafana)。
正确监控方案区分
-
临时查看负载 + HPA 自动扩缩容 → Metrics Server
-
长期监控、可视化大盘、告警、业务指标 → 直接采集 kubelet
/metrics/resource+ Prometheus
三、对应三大问题实操说明
问题 1:监控 Node 计算资源
安装 Metrics Server 后执行:
# 查看所有节点CPU、内存总使用量 kubectl top node # 实时持续刷新观察 watch -n 3 kubectl top node
输出字段:节点总 CPU 占用、总内存占用,用于判断节点资源是否打满、是否需要新增机器。
问题 2:监控 Pod 计算资源
# 查看所有PodCPU/内存占用 kubectl top pod # 看指定Pod单容器详情 kubectl top pod hpa-demo --containers
HPA 就是依靠这套 Pod CPU / 内存使用率做扩缩容判断。
问题 3:根据 Pod 负载自动扩缩容(HPA)
前置依赖:集群必须部署 Metrics Server,否则 HPA 获取不到指标,无法工作。 完整链路: Pod CPU / 内存指标 → kubelet → Metrics Server → Metrics API → HPA 控制器
-
HPA 每同步周期(默认 15s)拉取 Pod 平均负载;
-
对比你设定的阈值(如 CPU50%);
-
负载持续超标 → 自动新增 Pod(扩容);
-
负载长期低于阈值 → 自动删除多余 Pod(缩容)。
四、补充和你之前 HPA 参数联动
-
Metrics Server 默认采集间隔 15s,可修改
kube-controller-manager参数--horizontal-pod-autoscaler-sync-period缩短至 10s,指标刷新更快; -
HPA 所有扩缩容冷却、稳定窗口配置,都依赖 Metrics Server 提供的指标数据;
-
如果 Metrics Server 异常,
kubectl top报错、HPA 永远不会触发扩容 / 缩容。
Metrics-Server 部署
# 下载 Metrics-Server(下不了外网,老师给的components-v0.7.1.yaml 文件直接上传)
root@master30:~# wget https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.7.1/components.yaml
# 修改 Metrics-Server,不校验tls
root@master30:~# [root@master30 ~ 10:17:14]# sed -i '/metric-resolution/a\ - --kubelet-insecure-tls' components-v0.7.1.yaml
# 下载镜像
[root@master30 ~ 10:17:44]# grep image: components-v0.7.1.yaml
image: registry.k8s.io/metrics-server/metrics-server:v0.7.1
# 部署 Metrics-Server [root@master30 ~ 10:17:54]# kubectl apply -f components-v0.7.1.yaml serviceaccount/metrics-server created clusterrole.rbac.authorization.k8s.io/system:aggregated-metrics-reader created clusterrole.rbac.authorization.k8s.io/system:metrics-server created rolebinding.rbac.authorization.k8s.io/metrics-server-auth-reader created clusterrolebinding.rbac.authorization.k8s.io/metrics-server:system:auth-delegator created clusterrolebinding.rbac.authorization.k8s.io/system:metrics-server created service/metrics-server created deployment.apps/metrics-server created apiservice.apiregistration.k8s.io/v1beta1.metrics.k8s.io created
# 查看 Metrics-Server 状态 [root@master30 ~ 10:18:07]# kubectl get pods -n kube-system | grep metrics metrics-server-d994c478f-cwlml 1/1 Running 0 2m38s
Metrics-Server 使用
查看node状态
[root@master30 ~ 10:20:45]# kubectl top node NAME CPU(cores) CPU% MEMORY(bytes) MEMORY% master30.zy.cloud 119m 5% 1705Mi 45% worker31.zy.cloud 142m 7% 903Mi 23% worker32.zy.cloud 41m 2% 1152Mi 30%
查看 pod 状态
[root@master30 ~ 10:21:09]# kubectl top pods -n kube-system NAME CPU(cores) MEMORY(bytes) calico-kube-controllers-585df69d45-sjlcw 7m 77Mi calico-node-9n48n 32m 174Mi calico-node-b9txl 41m 174Mi calico-node-r7747 23m 173Mi coredns-7db6d8ff4d-f9ljv 2m 57Mi coredns-7db6d8ff4d-vshrt 1m 58Mi etcd-master30.zy.cloud 17m 95Mi kube-apiserver-master30.zy.cloud 39m 342Mi kube-controller-manager-master30.zy.cloud 10m 124Mi kube-proxy-hkxgn 11m 70Mi kube-proxy-lthx5 1m 70Mi kube-proxy-trghb 1m 70Mi kube-scheduler-master30.zy.cloud 2m 65Mi metrics-server-d994c478f-cwlml 2m 17Mi
单位:1cpu=1000m
扩容和减容控制
扩容和缩容时间参数
1. 指标采样周期
-
--horizontal-pod-autoscaler-sync-period duration -
默认:15s,HPA 每 15 秒拉取一次 metrics-server 指标。
2. 启动就绪窗口期
-
--horizontal-pod-autoscaler-initial-readiness-delay duration -
默认:30s,Pod 刚启动前 30 秒,视为 “启动中”,不参与 HPA 计算
3. 缩容稳定窗口期
-
--horizontal-pod-autoscaler-downscale-stabilization duration -
默认:5m0s(5 分钟),缩容冷却时间。
持续高负载 → 扩容
-
每 15s 采集一次 CPU/内存指标
-
Pod 刚启动前 30 秒,视为 “启动中”,不参与 HPA 计算。
-
一次扩容动作完成后,再次等待 45s 冷却 才能下一次扩容
👉 结论:K8s 1.30 扩容最小间隔 = 45s
持续低负载 → 缩容
-
同样 15s 采样
-
指标长期低于阈值
-
需要满足 300s(5分钟)稳定低位 才会触发缩容
-
每次缩容后,再次锁定 5 分钟
设置扩容和减容时间参数
扩容和减容pod数量由kube-controller-manager管理,如需调快/调慢,直接修改 controller-manager 启动参数即可。它是集群控制平面核心组件,以静态 Pod 运行在 master 节点:/etc/kubernetes/manifests/kube-controller-manager.yaml
扩容和缩容pod是有冷却时间的,目的是防止流量抖动、瞬间峰值导致频繁炸裂扩容。
为了演示扩容和缩容效果,这里设置扩容冷却时间为30s(10+20),缩容冷却时间为60秒。
[root@master30 ~ 10:21:46]# vim /etc/kubernetes/manifests/kube-controller-manager.yaml
spec:
containers:
- command:
- kube-controller-manager
- --allocate-node-cidrs=true
......
# 增加下面三行参数
- --horizontal-pod-autoscaler-sync-period=10s
- --horizontal-pod-autoscaler-initial-readiness-delay=20s
- --horizontal-pod-autoscaler-downscale-stabilization=60s
三个核心参数逐条拆解(默认值 + 作用 + 你修改后的配置)
1. --horizontal-pod-autoscaler-sync-period=10s
默认:15s,你改成 10s
全称:HPA 指标同步周期
作用
HPA 不会实时去查 CPU / 内存指标,每隔固定时间拉取一次 metrics-server 的监控数据,这个参数就是「多久采集一次负载数据」。
默认逻辑
每 15 秒采集一次所有 Pod 的负载,计算是否需要扩容 / 缩容。
修改后效果
缩短为 10 秒采集一次,监控刷新更快,负载变化能更快被 HPA 感知,扩缩容响应变灵敏。
2. --horizontal-pod-autoscaler-initial-readiness-delay=20s
默认:30s,你改成 20s
全称:Pod 初始化就绪延迟窗口期
作用
新扩容出来的 Pod 刚启动时,进程还没初始化完成,CPU / 内存指标不准(要么空载、要么瞬间冲高)。
为了避免刚启动的 Pod 干扰 HPA 计算,Pod 创建后的这段时间内,不纳入负载平均值计算。
默认逻辑
新 Pod 前 30 秒直接忽略,不参与负载统计。
修改后效果
缩短到 20 秒,新 Pod 更快加入负载计算,适合启动速度很快的应用。
3. --horizontal-pod-autoscaler-downscale-stabilization=60s
默认 5 分钟 = 300s,你改成 60 秒
全称:缩容稳定冷却窗口期(缩容防抖)
核心作用:防止频繁缩容
流量小幅波动会导致负载忽高忽低,如果负载一低立刻删 Pod,会反复创建、删除 Pod(抖动)。
K8s 要求:负载持续低于阈值,并且稳定一段时间,才允许缩容。
默认规则
必须连续 5 分钟指标都低于阈值,HPA 才会执行缩容;缩容完成后,再次锁定 5 分钟不能缩容。
修改后效果
缩容冷却缩短到 60 秒,低负载持续 1 分钟就会删 Pod,缩容速度大幅加快,适合测试环境快速观察缩容效果。
保存退出,静态 Pod 会自动重启生效。
Horizontal Pod Autoscaler
学习参考:hpa
一、HPA 核心作用
HPA 是 Kubernetes 内置的自动扩缩容控制器,核心逻辑:
-
周期性从三类指标 API 获取监控数据:
-
metrics.k8s.io:基础资源指标(CPU / 内存),由 Metrics Server 提供; -
custom.metrics.k8s.io:Pod 自定义业务指标(QPS、请求延迟等,需 Prometheus Adapter); -
external.metrics.k8s.io:集群外部指标(消息队列堆积量、第三方流量等)。
-
-
根据你配置的指标阈值,自动调整工作负载的副本数量;
-
支持管控资源:Deployment、ReplicaSet、ReplicationController、StatefulSet。
简单一句话:不用手动改 replicas,负载高自动加 Pod,负载低自动减 Pod。
二、适用场景
-
无状态服务(主流) Nginx 网关、后端 API、微服务、前端服务,流量忽高忽低,适合自动扩容扛峰值、低峰缩容节省资源。
-
削峰填谷、应对突发并发 活动促销、定时批量任务、瞬时流量暴涨,避免人工扩容不及时导致服务卡顿。
-
有状态服务(StatefulSet) MySQL 从库、缓存集群等可横向扩展的有状态应用,HPA 同样支持副本自动调整。
不适合:无法横向扩容的单实例核心存储(单主数据库等)。
三、核心特点
-
只改副本,不修改 Pod 内部配置 HPA 仅调整
replicas数值,容器镜像、资源限制、启动命令等 Pod 模板完全不变;区别于 VPA(Vertical Pod Autoscaler,垂直扩容,修改 Pod CPU / 内存规格)。 -
响应速度快 依靠 Metrics Server 秒级采集指标,配合可调同步周期,快速感知负载变化。
-
成熟稳定、生产首选 属于 K8s 原生控制器,无需额外复杂组件,是企业最通用的自动扩缩容方案。
-
多重指标混合判断 可同时配置 CPU + QPS 多指标,满足任意一条阈值就触发扩容,全部低于阈值才缩容。
四、支持的扩缩容指标分类
1. 基础内置指标(依赖 Metrics Server)
-
Pod CPU 使用率:对比容器
requests.cpu的百分比,最常用; -
Pod 内存使用率:对比容器
requests.memory。
2. 自定义业务指标(依赖 Prometheus + prometheus-adapter)
业务层监控指标,贴合真实用户流量:
-
HTTP QPS、每秒请求数;
-
接口响应延迟;
-
并发连接数;
-
接口错误率。
3. 外部指标(外部业务驱动扩缩容)
不依赖集群内部 Pod 数据:
-
Kafka/RabbitMQ 消息队列堆积条数;
-
云平台外部流量、定时任务待处理数量。
五、HPA 完整工作链路(串联 Metrics Server)
-
Metrics Server 每 15s(可改)从 kubelet 拉取所有 Pod CPU、内存;
-
指标存入
metrics.k8s.ioAPI; -
HPA 控制器周期性调用该 API,获取目标 Deployment 的平均负载;
-
和预设阈值对比,计算理想副本数;
-
若副本数需要调整,自动修改 Deployment 的
replicas; -
Deployment 控制器创建 / 销毁 Pod,完成扩缩容。
基于 CPU 使用率伸缩
准备资源
[root@master30 ~ 13:32:18]# kubectl create deployment web --image=docker.io/library/nginx deployment.apps/web created [root@master30 ~ 13:32:26]# kubectl get pod NAME READY STATUS RESTARTS AGE web-68b95c775c-c5m9x 1/1 Running 0 7s
创建 hpa
Usage: kubectl autoscale (-f FILENAME | TYPE NAME | TYPE/NAME) [--min=MINPODS]--max=MAXPODS [--cpu-percent=CPU] [options] [root@master30 ~ 13:32:33]# kubectl autoscale deployment web --max=5 --min=2 --cpu-percent=80 horizontalpodautoscalers.autoscaling hostendpoints.crd.projectcalico.org [root@master30 ~ 13:33:50]# kubectl get hpa web -o yaml | tee hpa-cpu.yaml # 省略部分不重要配置
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: web
spec:
maxReplicas: 5
metrics:
- resource:
name: cpu
target:
averageUtilization: 80
type: Utilization
type: Resource
minReplicas: 2
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: web
# 稍等片刻,创建一个新的pod [root@master30 ~ 13:35:13]# kubectl get pod NAME READY STATUS RESTARTS AGE web-68b95c775c-5854s 1/1 Running 0 95s web-68b95c775c-c5m9x 1/1 Running 0 3m9s [root@master30 ~ 13:35:35]# kubectl get hpa NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE web Deployment/web cpu: <unknown>/80% 2 5 2 2m3s # CPU目标为unknown
要想看到 HPA 的 TARGETS 值必须满足2个条件:
-
安装 metric server。
-
为 pod 设定资源限制。
[root@master30 ~ 13:39:04]# kubectl edit deployments.apps web
deployment.apps/web edited
# 修改spec.template.spec.containers.[N].resources属性,添加limit属性,如下:
resources:
limits:
cpu: 100m
memory: 200Mi
# 再次查看hpa
[root@master30 ~ 13:42:35]# kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
web Deployment/web cpu: 0%/80% 2 5 2 8m47s
[root@master30 ~ 13:42:37]# kubectl expose deployment web --port=80 --target-port=80
service/web exposed
[root@master30 ~ 13:44:34]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
web ClusterIP 10.96.33.123 <none> 80/TCP 15s
压力测试
# 打开一个监控窗口 [root@master30 ~ 13:45:12]# \ > while true do clear; kubectl get hpa;echo kubectl get pod;echo kubectl top pods sleep 1 done # 上 CPU 压力 [root@master30 ~ 13:46:01]# apt install -y apache2-utils [root@master30 ~ 13:46:17]# while true ; do ab -n 300000 -c 100 http://10.96.33.123/;sleep 1; done # -n 300000,总请求数 # -c 100,每次并发数 #刚开始的状态: NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE web Deployment/web cpu: 0%/80% 2 5 2 13m NAME READY STATUS RESTARTS AGE web-6dcdc45c94-qdw6q 1/1 Running 0 5m45s web-6dcdc45c94-x2mlz 1/1 Running 0 5m47s
CPU负载 cpu: 96%
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE web Deployment/web cpu: 96%/80% 2 5 2 23m NAME READY STATUS RESTARTS AGE web-6dcdc45c94-c2bkg 0/1 ContainerCreating 0 1s web-6dcdc45c94-qdw6q 1/1 Running 0 15m web-6dcdc45c94-x2mlz 1/1 Running 0 15m #0/1 ContainerCreating NAME CPU(cores) MEMORY(bytes) web-6dcdc45c94-qdw6q 100m 3Mi web-6dcdc45c94-x2mlz 92m 3Mi NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE web Deployment/web cpu: 96%/80% 2 5 2 23m NAME READY STATUS RESTARTS AGE web-6dcdc45c94-c2bkg 1/1 Running 0 3s web-6dcdc45c94-qdw6q 1/1 Running 0 15m web-6dcdc45c94-x2mlz 1/1 Running 0 15m #1/1 Running
超过目标值后,HPA 新建了一个pod
Every 2.0s: kubectl get hpa;echo;kubectl top pods master30.laoma.cloud: Sun Apr 19 09:58:04 2026 NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE web Deployment/web cpu: 95%/80% 2 5 3 9m14s NAME CPU(cores) MEMORY(bytes) web-6dcdc45c94-2zbmb 93m 3Mi web-6dcdc45c94-6gwd2 100m 3Mi web-6dcdc45c94-dvd2m 90m 3Mi
HPA又新建了一个pod
Every 2.0s: kubectl get hpa;echo;kubectl top pods master30.laoma.cloud: Sun Apr 19 09:58:59 2026 NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE web Deployment/web cpu: 99%/80% 2 5 4 24m NAME READY STATUS RESTARTS AGE web-6dcdc45c94-c2bkg 1/1 Running 0 90s web-6dcdc45c94-qdw6q 1/1 Running 0 16m web-6dcdc45c94-x2mlz 1/1 Running 0 16m web-6dcdc45c94-xm8dk 1/1 Running 0 50s
HPA又新建了一个pod
Every 2.0s: kubectl get hpa;echo;kubectl top pods master30.laoma.cloud: Sun Apr 19 09:59:59 2026 NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE web Deployment/web cpu: 99%/80% 2 5 5 25m NAME READY STATUS RESTARTS AGE web-6dcdc45c94-c2bkg 1/1 Running 0 2m8s web-6dcdc45c94-qdw6q 1/1 Running 0 17m web-6dcdc45c94-sstnd 1/1 Running 0 37s web-6dcdc45c94-x2mlz 1/1 Running 0 17m web-6dcdc45c94-xm8dk 1/1 Running 0 88s #即使pod CPU使用率超过阈值,但pod数量不会超过5。 [root@master30 ~ 13:58:05]# kubectl get pods NAME READY STATUS RESTARTS AGE web-6dcdc45c94-c2bkg 1/1 Running 0 2m19s web-6dcdc45c94-qdw6q 1/1 Running 0 17m web-6dcdc45c94-sstnd 1/1 Running 0 48s web-6dcdc45c94-x2mlz 1/1 Running 0 17m web-6dcdc45c94-xm8dk 1/1 Running 0 99s #停止压力测试,负载降低后,pod数量会逐步减少为2。 [root@master30 ~ 13:54:14]# kubectl get pods NAME READY STATUS RESTARTS AGE web-6dcdc45c94-qdw6q 1/1 Running 0 12m web-6dcdc45c94-x2mlz 1/1 Running 0 12m
观察过程:
-
随着pod CPU使用率上升,自动扩展pod数量。
-
正常情况,30秒左右创建一个新pod,
-
即使pod CPU使用率超过阈值,但pod数量不会超过5。
-
停止压力测试,负载降低后,pod数量会逐步减少为2。
清理资源
# 删除 hpa [root@master30 ~ 13:59:10]# kubectl delete hpa web horizontalpodautoscaler.autoscaling "web" deleted # 删除 deployment [root@master30 ~ 14:02:16]# kubectl delete deployments.apps web deployment.apps "web" deleted # 保留 svc,后续使用
基于 Mem 使用率伸缩
我们仍然以nginx应用实践。想要Nginx 内存涨,要访问会占用内存的页面。最简单方法:让 Nginx 返回一个超大响应体。
准备资源
# worker节点创建big.img [root@worker31 ~ 09:40:07]# mkdir /www [root@worker31 ~ 14:05:55]# dd if=/dev/zero of=/www/big.img bs=1M count=200 200+0 records in 200+0 records out 209715200 bytes (210 MB, 200 MiB) copied, 0.488752 s, 429 MB/s [root@worker32 ~ 09:40:09]# mkdir /www [root@worker32 ~ 14:08:18]# dd if=/dev/zero of=/www/big.img bs=1M count=200 200+0 records in 200+0 records out 209715200 bytes (210 MB, 200 MiB) copied, 0.755214 s, 278 MB/s [root@master30 ~ 14:11:28]# vim deployment-web.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 1
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
volumeMounts:
- name: big-file
mountPath: /usr/share/nginx/html
resources:
limits:
cpu: 100m
memory: 200Mi
volumes:
- name: big-file
hostPath:
path: /www
[root@master30 ~ 14:11:51]# kubectl apply -f deployment-web.yaml deployment.apps/web created
创建 hpa
root@master30:~# vim hpa-mem.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: web
spec:
minReplicas: 2
maxReplicas: 5
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: web
metrics:
- resource:
name: memory
target:
type: Utilization
averageUtilization: 60
type: Resource
[root@master30 ~ 14:13:15]# kubectl apply -f hpa-mem.yaml horizontalpodautoscaler.autoscaling/web created [root@master30 ~ 14:13:43]# kubectl get hpa NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE web Deployment/web memory: <unknown>/60% 2 5 1 20s # 目标值为空 # 稍等片刻,创建一个新的pod [root@master30 ~ 14:14:03]# kubectl get pod NAME READY STATUS RESTARTS AGE web-88cff78bb-87dl7 1/1 Running 0 3m3s web-88cff78bb-k9cll 1/1 Running 0 69s # 稍等一会,再次查看 [root@master30 ~ 14:15:02]# kubectl get hpa NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE web Deployment/web memory: 1%/60% 2 5 2 84s
压力测试
# 打开一个监控窗口 root@master30:~# watch -n 4 'kubectl get hpa;echo;kubectl top pods' # 上 MEM 压力 [root@master30 ~ 14:16:37]# while true ;do ab -n 300000 -c 100 http://10.96.33.123/big.img;sleep 1;done
负载上来了
Every 4.0s: kubectl get hpa;echo;kubectl top pods master30.laoma.cloud: Sun Apr 19 11:13:29 2026 NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE web Deployment/web memory: 86%/60% 2 5 2 16m NAME CPU(cores) MEMORY(bytes) web-88cff78bb-2b7vh 15m 174Mi web-88cff78bb-76pg2 15m 171Mi
新建了一个pods
Every 4.0s: kubectl get hpa;echo;kubectl top pods master30.laoma.cloud: Sun Apr 19 11:13:46 2026 NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE web Deployment/web memory: 58%/60% 2 5 3 16m NAME CPU(cores) MEMORY(bytes) web-88cff78bb-2b7vh 15m 174Mi web-88cff78bb-4hcm5 5m 3Mi web-88cff78bb-76pg2 15m 171Mi
又新建了一个pods
Every 4.0s: kubectl get hpa;echo;kubectl top pods master30.laoma.cloud: Sun Apr 19 11:15:46 2026 NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE web Deployment/web memory: 58%/60% 2 5 4 16m NAME CPU(cores) MEMORY(bytes) web-88cff78bb-2b7vh 15m 174Mi web-88cff78bb-4hcm5 5m 3Mi web-88cff78bb-76pg2 15m 171Mi web-88cff78bb-ws6t4 1m 3Mi
又新建了一个pods
Every 4.0s: kubectl get hpa;echo;kubectl top pods master30.laoma.cloud: Sun Apr 19 11:17:46 2026 NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE web Deployment/web memory: 58%/60% 2 5 5 16m NAME CPU(cores) MEMORY(bytes) web-88cff78bb-2b7vh 15m 174Mi web-88cff78bb-4hcm5 5m 3Mi web-88cff78bb-76pg2 15m 171Mi web-88cff78bb-ws6t4 1m 3Mi web-88cff78bb-t4q2p 100m 3Mi
观察过程:
-
随着pod MEM使用率上升,自动扩展pod数量。
-
即使pod MEM使用率超过阈值,但pod数量不会超过5。
清理资源
# 删除 hpa [root@master30 ~ 14:46:10]# kubectl delete hpa web horizontalpodautoscaler.autoscaling "web" deleted # 删除 deployment [root@master30 ~ 14:46:30]# kubectl delete deployment web deployment.apps "web" deleted # 删除 svc [root@master30 ~ 14:46:36]# kubectl delete svc web service "web" deleted
Vertical Pod Autoscaler
VPA 使用场景不多,这里不做实践演示。
VPA 作用
控制器将从一系列API(metrics.k8s.io、custom.metrics.k8s.io 和 external.metrics.k8s.io)中获取度量值,metrics.k8s.io API 通常由Metrics Server提供。根据 VPA 中定义的指标动态调整 Pod 的 requests 和 limits。
VPA 适用场景
-
资源配置不合理的服务
-
长期运行、流量稳定的服务
-
Java、Go 等内存占用固定的应用
-
不适合频繁扩缩的服务。
VPA 核心特点
-
不改变 Pod 数量
-
自动给 Pod 推荐 / 设置 CPU / 内存
-
生效需要重启 Pod(默认)
-
比 HPA 少用,因为有侵入性
HPA 与 VPA 对比
| 项目 | HPA 横向扩缩容 | VPA 纵向扩缩容 |
|---|---|---|
| 全称 | Horizontal | Vertical |
| 扩缩方式 | 增加 / 减少 Pod 数量 | 调整 CPU / 内存 request/limit |
| 是否重启 Pod | ❌ 不重启 | ✅ 一般需要重启 |
| 适合负载 | 流量波动大 | 资源配置不合理 |
| 生效速度 | 快 | 慢 |
| 稳定性 | 高 | 中 |
| 生产使用 | 非常普遍 | 较少 |
| 能否一起开 | 不能同时开(会冲突) | - |
| 支持资源 | CPU、内存、自定义 | CPU、内存 |
环境清理
[root@master30 ~ 14:46:40]# kubectl delete ns metric namespace "metric" deleted
Quota and Limits
学习参考:
环境准备
-
创建一个独立的名字空间quota,并切换到该ns
[root@master30 ~ 14:47:58]# kubectl create ns quota namespace/quota created [root@master30 ~ 14:52:45]# kubectl config set-context --current --namespace quota Context "cluster1-context" modified.
-
提前部署好 Metric Server
ResourceQuota
1、为什么需要 ResourceQuota
多团队共用一套 K8s 集群,不做限制会出现两类严重问题:
-
某个命名空间下所有 Pod 消耗的 CPU、内存总量无上限,直接占满集群所有机器硬件资源,其他命名空间的服务没有资源运行,直接宕机。
-
无限制重复创建 Pod、Service、配置文件、定时任务等资源:一是耗尽集群 Service 内网 IP;二是海量数据存入集群数据库 etcd,把数据库磁盘占满,整个集群控制面彻底瘫痪。 ResourceQuota 就是给每个独立命名空间设置资源使用硬上限,从根源限制资源消耗、资源创建数量。
2、ResourceQuota 是什么
K8s 原生的资源限制对象,作用范围仅限单个命名空间,专门定义两件事:
-
该命名空间最多能创建多少个 Pod、Deployment、Service 等各类资源;
-
该命名空间内所有运行 Pod,合计最多能占用多少 CPU、内存、存储。
3、完整工作流程
-
管理员给不同团队分配独立命名空间,配合 RBAC 权限控制,团队只能操作自己命名空间内资源。
-
管理员在目标命名空间创建 ResourceQuota,写明各项资源最大限额。
-
用户创建 / 修改 Pod、Deployment、Service 等资源时,集群 API 会自动拦截校验。
-
校验逻辑:计算当前命名空间已占用资源 + 本次新增资源的总和,对比 Quota 设置的上限:
-
总和没超过上限:允许创建资源,同步更新已占用资源统计;
-
总和超过上限:直接拒绝本次操作,返回 403 报错,明确标注哪一项资源超出配额。
-
-
生效规则:只要命名空间内存在任意一个 ResourceQuota,该命名空间永久开启配额校验;删除命名空间全部 ResourceQuota,校验功能直接关闭,不再做任何限制。
4、两类配额详细说明
第一类:资源数量配额
管控该命名空间最多能创建多少个指定类型资源。 可管控资源包含:pod、service、configmap、secret、deployment、statefulset、一次性任务 Job、定时任务 CronJob、存储 PVC 等。 核心作用:控制资源总数,避免无限创建资源耗尽内网 IP、撑爆 etcd 数据库。 示例:配置pods: "20",代表这个命名空间最多同时存在 20 个 Pod。
第二类:硬件计算资源配额
管控命名空间内所有正常运行 Pod 的 CPU、内存总消耗,4 个核心统计项:
-
requests.cpu:所有 Pod 申请预留 CPU 总和上限
-
requests.memory:所有 Pod 申请预留内存总和上限
-
limits.cpu:所有 Pod 最大可用 CPU 总和上限
-
limits.memory:所有 Pod 最大可用内存总和上限 简写规则:直接写
cpu等价requests.cpu,直接写memory等价requests.memory。
资源单位规则
-
CPU:最小单位 m,1000m = 1 个 CPU 核心;
-
内存:两套单位标准
-
1024 进制:Mi、Gi(容器行业标准)
-
1000 进制:M、G 只填写纯数字(如 2),默认按 1000 进制 G 计算。
-
5、强制硬性规则(极易踩坑)
只要 ResourceQuota 配置了 requests.cpu 或 requests.memory,该命名空间内所有新建 Pod 必须手动填写 resources.requests(CPU、内存申请值)。 不填写会直接拒绝创建 Pod。 底层原因:配额统计总资源占用量完全依赖每个 Pod 的 requests 数值,无 requests 则无法计算占用总量,校验流程无法执行。
6、配套工具 LimitRanger,解决强制填写资源的麻烦
痛点
每次创建 Pod、Deployment 都手动写 requests、limits,操作繁琐,开发容易遗漏。
LimitRanger 功能
和 ResourceQuota 部署在同一个命名空间,Pod 创建时如果没有手动定义 CPU、内存资源,自动给 Pod 补上默认 requests 和 limits,自动满足配额校验要求,不用人工填写资源参数。 标准落地规范:每个业务命名空间同时创建 ResourceQuota + LimitRanger。
7、配额启用前提
-
集群 kube-apiserver 组件默认开启 ResourceQuota 准入插件,不需要额外修改控制面启动参数;
-
准入插件仅负责校验,不会主动限制资源:命名空间无 ResourceQuota 则无任何资源限制;只有存在 Quota 才开启校验。
8、单个命名空间多个 ResourceQuota 的规则
一个命名空间允许创建多个 ResourceQuota 对象,同一项资源的上限数值会累加。 举例:第一个 Quota 限制 requests.cpu=1000m,第二个 Quota 限制 requests.cpu=1000m,该命名空间 CPU 申请总上限等于 2000m。 实操建议:一个命名空间只创建 1 个 ResourceQuota,全部限制统一写在一份 yaml 中,避免累加逻辑混乱,不方便日常维护。
配额管理
重要说明: 如果项目级别配额限定了 request 和 limit,那么创建pod的时候必须指定 request 和 limit。
创建 ResourceQuota 对象
[root@master30 ~ 14:52:48]# kubectl create quota myquota --hard=pods=2,services=3,secrets=5,persistentvolumeclaims=10 resourcequota/myquota created [root@master30 ~ 15:14:14]# kubectl get resourcequotas NAME AGE REQUEST LIMIT myquota 9s persistentvolumeclaims: 0/10, pods: 0/2, secrets: 0/5, services: 0/3 [root@master30 ~ 15:14:23]# kubectl describe quota myquota Name: myquota Namespace: quota Resource Used Hard -------- ---- ---- persistentvolumeclaims 0 10 pods 0 2 secrets 0 5 services 0 3
通过 yaml 文件创建
apiVersion: v1
kind: ResourceQuota
metadata:
name: myquota
spec:
hard:
persistentvolumeclaims: "10"
pods: "2"
secrets: "5"
services: "3"
测试配额
[root@master30 ~ 15:15:00]# kubectl create deployment web --image=docker.io/library/nginx --replicas=3
deployment.apps/web created
[root@master30 ~ 15:16:40]# kubectl get all
NAME READY STATUS RESTARTS AGE
pod/web-68b95c775c-dfg42 1/1 Running 0 5s
pod/web-68b95c775c-jps4v 1/1 Running 0 5s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/web 2/3 2 2 5s
NAME DESIRED CURRENT READY AGE
replicaset.apps/web-68b95c775c 3 2 2 5s
[root@master30 ~ 15:16:45]# kubectl describe rs web-68b95c775c
------------------------------------------------------------------------------
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreate 43s replicaset-controller Created pod: web-6-jps4v
Warning FailedCreate 43s replicaset-controller Error creating: po8b95c775c-7grpr" is forbidden: exceeded quota: myquota, requested: pods=1, used: pods=2, pods=2
Normal SuccessfulCreate 43s replicaset-controller Created pod: web-6-dfg42
Warning FailedCreate 43s replicaset-controller Error creating: po8b95c775c-5vr74" is forbidden: exceeded quota: myquota, requested: pods=1, used: pods=2, pods=2
Warning FailedCreate 43s replicaset-controller Error creating: po8b95c775c-8647s" is forbidden: exceeded quota: myquota, requested: pods=1, used: pods=2, pods=2
Warning FailedCreate 43s replicaset-controller Error creating: po8b95c775c-cwdxk" is forbidden: exceeded quota: myquota, requested: pods=1, used: pods=2, pods=2
Warning FailedCreate 43s replicaset-controller Error creating: po8b95c775c-rc5kj" is forbidden: exceeded quota: myquota, requested: pods=1, used: pods=2, pods=2
Warning FailedCreate 43s replicaset-controller Error creating: po8b95c775c-67xxh" is forbidden: exceeded quota: myquota, requested: pods=1, used: pods=2, pods=2
Warning FailedCreate 43s replicaset-controller Error creating: po8b95c775c-m7qgf" is forbidden: exceeded quota: myquota, requested: pods=1, used: pods=2, pods=2
Warning FailedCreate 43s replicaset-controller Error creating: po8b95c775c-pn8fd" is forbidden: exceeded quota: myquota, requested: pods=1, used: pods=2, pods=2
Warning FailedCreate 43s replicaset-controller Error creating: po8b95c775c-h9b8r" is forbidden: exceeded quota: myquota, requested: pods=1, used: pods=2, pods=2
Warning FailedCreate 38s (x7 over 43s) replicaset-controller (combined from simts): Error creating: pods "web-68b95c775c-42g44" is forbidden: exceeded quota: myquota, r pods=1, used: pods=2, limited: pods=2
# 超过配额,创建失败
# 修改配额 pod数量为10
[root@master30 ~ 15:17:23]# kubectl patch resourcequotas myquota -p '{"spec":{"hard":{"pods":10}}}'
resourcequota/myquota patched
# 此时重新扩展rs
[root@master30 ~ 15:22:28]# kubectl scale rs web-68b95c775c --replicas 3
replicaset.apps/web-68b95c775c scaled
# 再次验证pod数量
[root@master30 ~ 15:22:47]# kubectl get pod
NAME READY STATUS RESTARTS AGE
web-68b95c775c-dfg42 1/1 Running 0 6m15s
web-68b95c775c-jps4v 1/1 Running 0 6m15s
web-68b95c775c-qvhsv 1/1 Running 0 8s
# 清理环境
[root@master30 ~ 15:22:55]# kubectl delete deployments.apps web
deployment.apps "web" deleted
[root@master30 ~ 15:23:27]# kubectl delete resourcequotas myquota
resourcequota "myquota" deleted
思考: 如果一个用户可以管理多个 namespace,能否限定该用户配额呢?
Request 和 Limits
如果命名空间下的计算资源 (如 cpu 和 memory)的配额被启用, 则用户必须为这些资源设定请求值(request)和约束值(limit),否则配额系统将拒绝 Pod 的创建。
pod.containers.resources 定义包含两部分:
-
requests,指明pod运行需要的最少计算资源,调度器查找具有充足计算资源的nodes。
-
limits,指明pod运行可以获得节点最多计算资源,用于阻止pod占用node太多计算资源。node使用Linux内核功能cgroup,限制pod资源使用。
一、requests(资源请求)
1. 含义
Pod 向集群申请最低保障资源,代表这个容器正常稳定运行最少需要多少 CPU、内存。
2. 给谁用?作用于调度阶段
kube-scheduler(调度器)分配节点时,只看 requests:
-
每个节点会统计已部署所有 Pod 的 requests 总和;
-
节点总硬件资源减去已占用 requests,剩下的就是节点空闲预留资源;
-
只有节点空闲预留 ≥ 当前 Pod 的 requests,调度器才会把 Pod 调度到这台机器;
-
如果所有节点剩余预留都小于 Pod 的 requests,Pod 会一直处于 Pending 等待状态。
3. 核心作用
给 Pod 保底资源,避免同节点其他服务抢占资源,导致自身基础运行资源不足、卡顿。
4. 和 ResourceQuota 的关联(关键)
ResourceQuota 统计整个命名空间资源占用总量时,完全依靠每个 Pod 的 requests 数值计算。 只要命名空间开启 CPU / 内存配额,不填写 requests,系统无法统计占用量,直接拒绝创建 Pod。
二、limits(资源上限约束)
1. 含义
Pod 允许使用的资源最大值,超过这个数值会被系统惩罚,底层依赖 Linux cgroup 内核机制强制限制。
2. 给谁用?作用于 Pod 运行全过程
Pod 运行期间实时监控资源消耗,分两种资源的处罚规则:
-
CPU 超过 limits 不会杀死 Pod,内核直接限制 CPU 使用率,容器运行速度大幅变慢、接口卡顿;
-
内存 超过 limits 内核直接触发 OOM,立刻杀死 Pod,容器重启。
3. 核心作用
硬性锁死单个 Pod 的资源天花板,防止某个程序异常疯狂占用 CPU / 内存,吃光整台节点机器资源,导致同节点其他全部服务崩溃。
三、requests 和 limits 核心区别对照表
| 配置项 | 生效阶段 | 底层实现 | 超量后果 | 核心用途 |
|---|---|---|---|---|
| requests | Pod 调度时 | 调度器计算节点预留 | 无足够节点则 Pod Pending | 保证基础运行资源、配额统计依据 |
| limits | Pod 运行全程 | Linux cgroup 内核限制 | CPU 限速 / 内存超限杀 Pod | 限制资源占用峰值,保护节点 |
四、三种常用配置方案
-
requests < limits(最常用) 例:requests.cpu=100m,limits.cpu=500m Pod 最低保证 0.1 核,流量峰值最多用到 0.5 核,兼顾稳定与突发流量。
-
requests = limits(固定资源) 例:cpu 均为 200m Pod 资源完全固定,不允许临时扩容资源,适合性能敏感、不能波动的服务。
-
只写 requests,不配置 limits(生产禁止) Pod 有最低资源保障,但无上限,程序异常会吃光节点全部资源,引发集群故障。
五、极简总结
-
requests = 调度器分配节点的最低资源门槛,是 ResourceQuota 统计资源的标准;
-
limits = Linux 内核硬限制的资源天花板,防止单个 Pod 拖垮整台机器;
-
开启资源配额后,requests 必须填写,limits 生产环境强制配套填写。
测试-不指定计算资源
配额示例
[root@master30 ~ 15:23:32]# vim resourcequota.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"
[root@master30 ~ 15:49:20]# kubectl apply -f resourcequota.yaml resourcequota/myquota created
pod 示例
[root@master30 ~ 15:49:38]# vim pod-without-quota.yaml
apiVersion: v1
kind: Pod
metadata:
name: web
labels:
app: web
spec:
containers:
- name: web
image: httpd
imagePullPolicy: IfNotPresent
ports:
- name: web
containerPort: 80
protocol: TCP
[root@master30 ~ 15:50:06]# 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 ~ 15:50:11]# kubectl delete resourcequotas myquota resourcequota "myquota" deleted
测试-Request
配额示例
[root@master30 ~ 15:50:55]# vim resourcequota.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
name: myquota
spec:
hard:
requests.cpu: 1000m
requests.memory: "2048Mi" #设置内存最大为2048Mi(2G)
[root@master30 ~ 15:51:26]# kubectl apply -f resourcequota.yaml resourcequota/myquota created [root@master30 ~ 15:51:38]# kubectl get resourcequotas myquota NAME AGE REQUEST LIMIT myquota 19s requests.cpu: 0/1, requests.memory: 0/2Gi
pod 示例1:超上限
root@master30:~# vim pod-request-1.yaml
apiVersion: v1
kind: Pod
metadata:
name: web
labels:
app: web
spec:
containers:
- name: web
image: docker.io/library/httpd
imagePullPolicy: IfNotPresent
resources:
requests:
cpu: 2000m
memory: 4096Mi #设置内存为4G,超过了内存
ports:
- name: web
containerPort: 80
protocol: TCP
[root@master30 ~ 15:53:09]# 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:未超上限
root@master30:~# vim pod-request-2.yaml
apiVersion: v1
kind: Pod
metadata:
name: web
labels:
app: web
spec:
containers:
- name: web
image: docker.io/library/httpd
imagePullPolicy: IfNotPresent
resources:
requests:
cpu: 200m
memory: 1024Mi #设置内存为1G,没超2G内存
ports:
- name: web
containerPort: 80
protocol: TCP
[root@master30 ~ 15:54:25]# kubectl apply -f pod-request-2.yaml pod/web created [root@master30 ~ 15:54:55]# kubectl get pod NAME READY STATUS RESTARTS AGE web 1/1 Running 0 6s # 验证使用情况 [root@master30 ~ 15:55:00]# kubectl describe resourcequotas myquota Name: myquota Namespace: quota Resource Used Hard -------- ---- ---- requests.cpu 200m 1 requests.memory 1Gi 2Gi # 删除pod和quota [root@master30 ~ 15:55:33]# kubectl delete pod web pod "web" deleted [root@master30 ~ 15:55:52]# kubectl delete resourcequotas myquota resourcequota "myquota" deleted
测试-Limits
压力测试镜像
可以直接使用镜像 docker.io/progrium/stress 进行压力测试,该镜像中运行stress命令。
找讲师索取镜像。
#直接拉取老马hub里面的镜像 [root@master30 ~ 16:01:29]# nerdctl pull hub.laoma.cloud/progrium/stress@sha256:48a71454d405dbe1c756dd728cadeb577f429f61313ac62b413b52fbaa8a3b44 --insecure-registry [root@master30 ~ 16:02:38]# nerdctl images | grep stress hub.laoma.cloud/progrium/stress <none> 48a71454d405 About a minute ago linux/amd64 298.4 MiB 87.2 MiB
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)
示例:
# 压力测试内存 $ docker run --name stress docker.io/progrium/stress -m 1 --vm-bytes 512M # 压力测试CPU $ docker run --name stress docker.io/progrium/stress -c 1 # 压力测试IO $ docker run --name stress docker.io/progrium/stress –d 1 --hdd-bytes 3G
对于 pod:
apiVersion: v1
kind: Pod
metadata:
name: stress
spec:
containers:
- name: stress
image: docker.io/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']
配额示例
root@master30:~# vim resourcequota.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
name: myquota
spec:
hard:
limits.cpu: 1000m
limits.memory: "2048Mi"
[root@master30 ~ 16:09:43]# kubectl apply -f resourcequota.yaml resourcequota/myquota created [root@master30 ~ 16:09:52]# kubectl get resourcequotas NAME AGE REQUEST LIMIT myquota 10s limits.cpu: 0/1, limits.memory: 0/2Gi
测试 CPU 资源
pod示例:
root@master30:~# vim pod-limit-cpu.yaml
apiVersion: v1
kind: Pod
metadata:
name: stress
spec:
containers:
- name: stress
image: hub.laoma.cloud/progrium/stress #这里用laoma的hub,去docker.io上面拉不上来
imagePullPolicy: IfNotPresent
args: ['-c','1']
resources:
limits:
cpu: 200m
memory: "256Mi"
[root@master30 ~ 16:13:01]# kubectl apply -f pod-limit-cpu.yaml pod/stress configured
打开一个终端监控
kubectl top 命令需要提前部署Metrics-Server
[root@master30 ~ 16:13:25]# kubectl top pod NAME CPU(cores) MEMORY(bytes) stress 201m 0Mi
可以发现:CPU 使用率维持在 200m 左右。
# 删除 pod [root@master30 ~ 16:17:50]# kubectl delete pod stress --force Warning: Immediate deletion does not wait for confirmation that the running resource has been terminated. The resource may continue to run on the cluster indefinitely. pod "stress" force deleted
测试 MEMORY 资源
pod示例:
[root@master30 ~ 16:17:54]# vim pod-limit-memory.yaml
apiVersion: v1
kind: Pod
metadata:
name: stress
spec:
containers:
- name: stress
image: docker.io/progrium/stress
imagePullPolicy: IfNotPresent
args: ['-m','1','--vm-bytes','512M']
resources:
limits:
cpu: 200m
memory: "256Mi"
[root@master30 ~ 16:18:20]# kubectl apply -f pod-limit-memory.yaml pod/stress created
打开一个终端监控
[root@master30 ~ 16:18:25]# kubectl get pods -w NAME READY STATUS RESTARTS AGE stress 0/1 OOMKilled 1 (9s ago) 11s stress 0/1 CrashLoopBackOff 1 (12s ago) 17s stress 1/1 Running 2 (13s ago) 18s stress 0/1 OOMKilled 2 (14s ago) 19s stress 0/1 CrashLoopBackOff 2 (16s ago) 34s
可以发现: Pod 状态为 OOMKilled,并进行restart。
整体结论: Pod 因为内存超出 limits 上限被系统杀死(OOMKilled),kubelet 反复重启它,反复被杀,最终进入 CrashLoopBackOff 减速重启状态。 每一行状态拆解 1、stress 0/1 OOMKilled 1 (9s ago) 11s OOMKilled = Out Of Memory Killed,内存溢出杀死 容器运行时占用内存超过你配置的 resources.limits.memory Linux cgroup 触发内核机制,直接杀掉这个容器进程 READY 0/1:容器已经死掉,不就绪 RESTARTS=1:已经重启过 1 次 2、stress 0/1 CrashLoopBackOff 1 (12s ago) 17s CrashLoopBackOff:崩溃循环退避 Pod 连续启动、连续崩溃,K8s 不会立刻无限重启,会拉长重启间隔(等待几秒再尝试启动,避免疯狂消耗节点资源) 3、stress 1/1 Running 2 (13s ago) 18s 等待冷却时间结束,kubelet 再次拉起容器,短暂正常 Running RESTARTS 变成 2,重启次数 + 1 4、stress 0/1 OOMKilled 2 (14s ago) 19s 容器一跑起来,程序又疯狂吃内存,再次超过内存上限,又被内核杀掉 5、stress 0/1 CrashLoopBackOff 2 (16s ago) 34s 第二次崩溃,再次进入退避等待,循环往复
# 删除 pod he [root@master30 ~ 16:48:15]# kubectl delete pod stress --force Warning: Immediate deletion does not wait for confirmation that the running resource has been terminated. The resource may continue to run on the cluster indefinitely. pod "stress" force deleted [root@master30 ~ 16:50:51]# kubectl delete resourcequotas myquota resourcequota "myquota" deleted
总结
-
计算资源的 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
一、存在的背景问题
-
没配置任何限制时,创建 Pod 可以不写
requests、limits; -
一旦命名空间加了
ResourceQuota资源配额,不写 requests 的 Pod 直接创建失败; -
开发经常忘记手动填写 CPU、内存参数,部署会频繁报错。
LimitRange 就是用来解决这个矛盾的对象,作用范围仅限单个命名空间。
二、LimitRange 能干什么
给当前命名空间定下单容器 / 单 Pod 的 4 套资源规则:
-
default:Pod 没写资源时,自动填充的默认requests和limits; -
defaultRequest:Pod 只写了 limits、没写 requests 时,自动补全 requests; -
min:单个容器允许设置的最小 CPU、内存,不能比这个值更小; -
max:单个容器允许设置的最大 CPU、内存,不能比这个值更大。
所有校验只针对单个容器,Pod 总资源是内部所有容器资源相加。
LimitRange 资源,也称为limits,定义了单个pod的资源请求和资源限制default、minimum、maximum值。pod的资源请求是其中所有容器请求的总和。
三、开启 LimitRange 后,3 条强制创建规则:
规则 1:Pod 完全没写 resources 资源
系统自动套用 LimitRange 里default配置,给容器补上 requests 和 limits。 补完后满足 ResourceQuota 要求,Pod 可以正常创建,不会报缺少 request 的错误。
举例: LimitRange 默认 cpu:req=100m,limit=500m 创建 nginx 不写任何资源,自动生成:
resources:
requests:
cpu: 100m
limits:
cpu: 500m
规则 2:手动填的资源 < LimitRange 设置的 min 最小值
拒绝创建 Pod。 例:LimitRange 规定 min.cpu=100m,你手动写 requests.cpu=50m,低于最小值,创建失败。
规则 3:手动填的资源 > LimitRange 设置的 max 最大值
拒绝创建 Pod。 例:LimitRange 规定 max.memory=1Gi,你写 limits.memory=2Gi,超过上限,创建失败。
四、LimitRange 和 ResourceQuota 分工区分(重点)
-
LimitRange:管单个 Pod / 单个容器 控制每一个 Pod 的资源上下限、自动补全默认资源;
-
ResourceQuota:管整个命名空间总和 控制该命名空间所有 Pod 加起来,CPU、内存总占用不能超过设定值;
两者搭配标准流程:
-
ResourceQuota:限制全命名空间总资源,强制 Pod 必须带 requests;
-
LimitRange:自动给忘记写资源的 Pod 补默认值,同时约束单个 Pod 资源不能过大 / 过小。
五、LimitRange 资源用于限定特定 namespace。
namespace设定了LimitRange,创建资源规则:
-
如果项目中请求一个未提供计算资源的对象,那么此时namespace将使用limit范围default值创建该对象。
-
如果项目中请求一个计算资源的对象,请求的资源小于limit最小值,那么该资源无法创建。
-
如果项目中请求一个计算资源的对象,请求的资源大于limit最大值,那么该资源无法创建。
LimitRange 示例
root@master30:~# vim limits.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
-
四项核心参数含义
-
min:单个容器资源最低下限,手动设置资源不能比这个更小,否则创建失败 CPU 最小 100m,内存最小 128Mi -
max:单个容器资源最高上限,手动设置资源不能超过这个数值,否则创建失败 CPU 最大 1 核,内存最大 1024Mi -
defaultRequest:Pod 完全不写 request 时,系统自动给它补上的申请资源 默认申请 CPU 200m、内存 256Mi -
default:Pod 完全不写 limit 时,系统自动给它补上的资源上限 默认上限 CPU 500m、内存 512Mi
数值强制关系 min ≤ defaultRequest ≤ default ≤ max 这个顺序不能乱,配置数值颠倒会报错。
root@master30 ~ 17:00:47]# kubectl apply -f limits.yaml limitrange/mylimit created [root@master30 ~ 17:01:05]# kubectl get limitranges NAME CREATED AT mylimit 2026-07-02T09:01:05Z [root@master30 ~ 17:01:14]# 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 - 打印规则明细表格,表格每列含义: Min:单个容器资源最小值 Max:单个容器资源最大值 Default Request:自动补的 requests 值 Default Limit:自动补的 limits 值 表格证明配置完全加载成功,规则已生效。
未指定 resources
示例1:
[root@master30 ~ 17:01:35]# vim pod-without-limits.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 指定的相关默认值一致。
[root@master30 ~ 17:13:12]# kubectl apply -f pod-without-limits.yaml pod/stress created [root@master30 ~ 17:13:45]# kubectl top pods NAME CPU(cores) MEMORY(bytes) stress 500m 0Mi #kubectl top pods:查看 Pod 实时资源占用,当前程序没压内存,所以内存占用 0Mi,CPU 运行在限制 500m 范围内。 [root@master30 ~ 17:15:32]# kubectl get pod stress -o yaml
......
spec:
containers:
- args:
- -c
- "1"
image: hub.laoma.cloud/progrium/stress
imagePullPolicy: IfNotPresent
name: stress
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 200m
memory: 256Mi
......
# 清理资源 [root@master30 ~ 17:15:51]# kubectl delete limitranges mylimit limitrange "mylimit" deleted [root@master30 ~ 17:16:44]# kubectl delete pod stress --force Warning: Immediate deletion does not wait for confirmation that the running resource has been terminated. The resource may continue to run on the cluster indefinitely. pod "stress" force deleted
只指定 limit 值
示例 2-1:limit 值大于 max 值
[root@master30 ~ 18:47:59]# vim limit.yaml
apiVersion: v1
kind: Pod
metadata:
name: web
spec:
containers:
- name: web
image: docker.io/library/nginx
resources:
limits:
cpu: 1.1
memory: 1100Mi
[root@master30 ~ 18:50:35]# kubectl apply -f limit.yaml Error from server (Forbidden): error when creating "limit.yaml": 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 1100Mi]
示例 2-2:limit 值小于 min 值
[root@master30 ~ 18:50:40]# vim limit.yaml
apiVersion: v1
kind: Pod
metadata:
name: web
spec:
containers:
- name: web
image: docker.io/library/nginx
resources:
limits:
cpu: 60m
memory: 60Mi
[root@master30 ~ 18:52:44]# kubectl apply -f limit.yaml Error from server (Forbidden): error when creating "limit.yaml": 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 值
apiVersion: v1
kind: Pod
metadata:
name: web
spec:
containers:
- name: web
image: docker.io/library/nginx
resources:
limits:
cpu: 600m
memory: 600Mi
结论:
-
创建的容器limits值必须满足条件:min值<指定的limit值<max值
-
当只指定limits值时,requests值与limits值保持一致,而不是default request。
root@master30:~# kubectl get pod web -o yaml ...... spec: containers: - image: docker.io/library/nginx imagePullPolicy: Always name: web resources: limits: cpu: 600m memory: 600Mi requests: cpu: 600m memory: 600Mi ......
只指定 requests
示例 3-1:requests 大于 max 值
[root@master30 ~ 18:52:47]# vim limit4.yaml
apiVersion: v1
kind: Pod
metadata:
name: web
spec:
containers:
- name: web
image: docker.io/library/nginx
resources:
requests:
cpu: 1600m
memory: 600Mi
[root@master30 ~ 18:54:30]# kubectl apply -f limit4.yaml The Pod "web" is invalid: * spec.containers[0].resources.requests: Invalid value: "600Mi": must be less than or equal to memory limit of 512Mi * spec.containers[0].resources.requests: Invalid value: "1600m": must be less than or equal to cpu limit of 500m
示例 3-2:requests 小于 min 值
[root@master30 ~ 18:54:40]# vim limit.yaml
[root@master30 ~ 19:04:00]# kubectl apply -f limit.yaml
Error from server (Forbidden): error when creating "limit.yaml": 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]
apiVersion: v1
kind: Pod
metadata:
name: web
spec:
containers:
- name: web
image: docker.io/library/nginx
resources:
requests:
cpu: 60m
memory: 60Mi
[root@master30 ~ 19:04:00]# kubectl apply -f limit.yaml Error from server (Forbidden): error when creating "limit.yaml": 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 值
apiVersion: v1
kind: Pod
metadata:
name: web
spec:
containers:
- name: web
image: docker.io/library/nginx
resources:
requests:
cpu: 400m
memory: 400Mi
结论:
-
创建的容器requests值必须满足条件:min值<requests值<limits值
-
当只指定requests值时,limits值与default值保持一致。
root@master30:~# kubectl get pod web -o yaml ...... spec: containers: - image: docker.io/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 示例:
apiVersion: v1
kind: LimitRange
metadata:
name: storagelimits
spec:
limits:
- type: PersistentVolumeClaim
max:
storage: 2Gi
min:
storage: 1Gi
环境清理
[root@master30 ~ 19:04:13]# kubectl delete ns quota
Kubernetes Health Check
学习参考:配置存活、就绪和启动探针
环境准备
[root@master30 ~ 19:09:11]# kubectl create ns health namespace/health created [root@master30 ~ 19:09:26]# kubectl config set-context --current --namespace health Context "cluster1-context" modified.
Health Check
应用可能会因为各种问题,变的 unhealthy,例如临时连接断开,配置错误,应用本身错误。
kubelet 使用 probes(探针),周期性地监控容器中应用是否为healthy状态,进一步决定什么时候要重启容器。 例如,当存活探针可以探测到应用死锁(应用在运行,但是无法继续执行后面的步骤)情况,进而重启pod,有助于提高应用的可用性,即使其中存在缺陷。
没有探测的情况,看一个例子:
# 创建一个普通 pod [root@master30 ~ 19:09:31]# kubectl run web --image=docker.io/library/httpd --image-pull-policy=IfNotPresent pod/web created [root@master30 ~ 19:10:40]# kubectl describe pod web | grep '^IP:' IP: 10.224.215.171 [root@master30 ~ 19:11:04]# curl 10.224.215.171 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <title>It works! Apache httpd</title> </head> <body> <p>It works!</p> </body> </html> # 删除主页文件,即使pod中应用数据丢失,pod状态依然为Running root@master30:~# kubectl exec web -- rm -f htdocs/index.html [root@master30 ~ 19:12:05]# curl 10.224.215.171 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <title>Index of /</title> </head> <body> <h1>Index of /</h1> <ul></ul> </body></html> [root@master30 ~ 19:12:23]# kubectl get pod NAME READY STATUS RESTARTS AGE web 1/1 Running 0 2m8s #1现象说明 删除网站首页后,网页已经异常,不再展示正常页面,但执行kubectl get pod查看:Pod 状态依然是Running,重启次数 0,K8s 完全没发现服务坏掉。 #2底层根本原因(重点) 没有配置存活探针 livenessProbe; K8s 判断 Pod 存活,只看容器主进程有没有崩溃退出; 你只是删了网页文件,httpd 主进程还在正常运行,没有宕机; K8s 识别不到「业务逻辑故障、页面打不开」这种内部异常,不会自动重启修复容器。 #3整条实验想表达的结论 只靠容器进程存活判断健康是不靠谱的: 进程没崩不代表业务服务正常,内部文件丢失、接口报错、页面失效这类问题,无探针时集群完全感知不到,故障会一直存在,业务持续异常。 解决方案:配置livenessProbe存活探针,定时访问网页,访问失败就判定容器故障,自动重启 Pod 恢复业务。 # 清理环境 [root@master30 ~ 19:12:48]# kubectl delete pods web --force
Probe Type
启动慢的容器配置 StartupProbe,初始化期间暂停执行存活、就绪探针,防止程序还没加载完就被误杀;等启动探针检测到程序初始化成功后,再正常开启日常健康检测。
一:三种探针(Liveness/Readiness/Startup)作用区分
1. LivenessProbe 存活探针
作用:判断容器里的应用有没有卡死、内部故障
-
探测失败 → kubelet 直接杀死容器,重启 Pod
-
对应你刚才的实验:删掉网页文件、服务异常但主进程没死,不加存活探针集群发现不了;配了 httpGet 探针访问页面失败,就会自动重启 Pod 恢复业务。 适用场景:程序死锁、内存泄漏、页面无法访问,但进程还在跑的隐性故障。
2. ReadinessProbe 就绪探针
作用:判断容器能不能对外正常提供业务流量
-
探测失败:不会杀 Pod、不会重启,只是把这个 Pod 的 IP 从 Service 后端地址列表(endpoints)移除
-
效果:流量不再转发到这个 Pod,即便 Pod 状态是 Running,外部访问不到它 适用场景:应用正在启动加载数据、数据库连接没完成、还没初始化完毕,暂时不能接收请求。
3. StartupProbe 启动探针
作用:专门给启动很慢的容器做初始化检测 规则:
-
只要启动探针没探测成功,存活、就绪探针完全不执行;
-
启动探针连续失败,会像存活探针一样重启 Pod;
-
启动成功后,才会正常执行 liveness、readiness 探针。 解决痛点:慢启动程序(比如加载大量缓存、大数据),启动耗时久,还没初始化完就被存活探针误判卡死、反复重启。
二、三种探针执行优先级
StartupProbe(初始化阶段)优先执行,成功后,才会持续循环执行 LivenessProbe + ReadinessProbe。
三:Checking Methods
探针检查容器主要有三种不同的方法:
1. httpGet 方式(网页服务最常用)
kubelet 发起 http GET 请求访问 Pod 内部指定端口 + 路径
-
返回码 200~399:探测成功,健康
-
4xx/5xx:探测失败 例:httpd/nginx 容器访问首页,页面丢失返回 404,判定不健康。
2. exec 命令方式(通用任意容器)
在容器内部执行一条自定义命令
-
命令退出码 = 0:成功
-
非 0:失败 例:执行
curl -s 127.0.0.1/index.html,页面不存在返回非 0,触发探针失败。
3. tcpSocket TCP 端口探测(只检测端口是否监听)
kubelet 尝试连接容器指定端口
-
端口正常打开、能建立连接:健康
-
端口没启动、连接被拒绝:失败 仅能判断端口进程是否启动,无法检测业务是否正常。
四、三者核心一句话区别
-
Liveness:坏了就重启 Pod,修复故障;
-
Readiness:没准备好就切走流量,不接收请求;
-
Startup:启动慢时保护 Pod,避免初始化中途被误重启。
HTTP Checks-httpGet
httpGet 探测基础概念
-
适用场景:跑网页、HTTP 接口的容器(nginx/httpd/java 后端等)
-
判断规则:kubelet 发起 GET 请求,返回码 200~399 = 健康;4xx/5xx / 超时 = 不健康
-
本次用的是
livenessProbe存活探针:连续探测失败达到阈值,直接杀掉 Pod 并重建。
livenessProbe
[root@master30 ~ 19:13:06]# vim deploy-httpGet-liveness.yaml
apiVersion: apps/v1 kind: Deployment metadata: labels: app: web name: web spec: replicas: 1 selector: matchLabels: app: web template: metadata: labels: app: web spec: containers: - image: docker.io/library/httpd imagePullPolicy: IfNotPresent name: httpd # 存活探针配置 livenessProbe: failureThreshold: 3 # 连续失败3次才判定为不健康 initialDelaySeconds: 5 # 容器启动后延迟5秒开始探测 periodSeconds: 5 # 每5秒执行一次探测 successThreshold: 1 # 连续成功1次即判定为健康(探针恢复时) timeoutSeconds: 10 # 单次HTTP请求超时时间(秒) httpGet: path: /index.html # 探测的访问路径 port: 80 # 探测的容器端口 scheme: HTTP # 协议(HTTP/HTTPS)
每个探针参数直白解释
-
initialDelaySeconds:5容器刚启动不会立刻检测,等待 5 秒,给程序留出启动时间,避免刚开机就误判失败。 -
periodSeconds:5每隔 5 秒发一次 HTTP 请求检查页面。 -
timeoutSeconds:10单次访问最多等待 10 秒,页面卡住无响应直接判定探测失败。 -
successThreshold:1只要一次访问正常,就认为容器健康。 -
failureThreshold:3连续 3 次访问失败,kubelet 认定容器卡死,执行重启操作。
[root@master30 ~ 19:27:45]# kubectl apply -f deploy-httpGet-liveness.yaml deployment.apps/web created [root@master30 ~ 19:29:20]# kubectl get pod NAME READY STATUS RESTARTS AGE web-d746d549f-zgcwg 1/1 Running 0 8s [root@master30 ~ 19:29:28]# kubectl describe pod web-d746d549f-zgcwg | grep '^IP:' IP: 10.224.215.162 # 手动破坏业务:删除首页文件 root@master30:~# kubectl exec web-85c6ff748f-qwszz -- bash -c 'rm htdocs/index.html' # 观察pod状态,RESTARTS次数变位1,再次访问 [root@master30 ~ 19:30:46]# kubectl get pod NAME READY STATUS RESTARTS AGE web-d746d549f-zgcwg 1/1 Running 0 92s [root@master30 ~ 19:30:52]# kubectl get pod NAME READY STATUS RESTARTS AGE web-d746d549f-zgcwg 1/1 Running 1 (13s ago) 114s #现象解读: - RESTARTS 从 0 变成 1,代表 Pod 被杀死、重新拉起了一次; - 流程:连续 3 次 httpGet 探测 404 失败 → 触发存活探针规则 → kubelet 杀掉旧容器 → Deployment 自动新建一个全新 httpd 容器。 # 容器删除需要一些时间,由参数terminationGracePeriodSeconds设定,默认值为30s。 # 只有等容器删除,并创建完成后才会继续检测 #curl 访问恢复正常的原因:旧容器被销毁,新容器是全新镜像,自带原始默认index.html首页文件,所以再次 curl 能正常输出It works!。 [root@master30 ~ 19:31:14]# curl 10.224.215.162 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <title>It works! Apache httpd</title> </head> <body> <p>It works!</p> </body> </html> # 清理环境 root@master30:~# kubectl delete deployments.apps web
readinessProbe(就绪探针)
[root@master30 ~ 19:33:19]# vim deploy-httpGet-readiness.yaml
apiVersion: apps/v1 kind: Deployment metadata: labels: app: web name: web spec: replicas: 3 # 启动3个Pod做负载均衡对比 selector: matchLabels: app: web template: metadata: labels: app: web spec: containers: - image: docker.io/library/httpd imagePullPolicy: IfNotPresent name: httpd # 添加readinessProbe部分 readinessProbe: failureThreshold: 3 # 连续失败3次判定未就绪 initialDelaySeconds: 5 # 启动5秒后开始第一次检测 periodSeconds: 5 # 每5秒检测一次页面 successThreshold: 1 timeoutSeconds: 10 httpGet: path: /index.html port: 80 scheme: HTTP
# 创建应用
#Deployment 创建 3 副本 Pod;
#kubectl expose 创建 ClusterIP 类型 Service,统一入口 10.98.146.216,实现负载均衡分发流量到 3 个 Pod。
[root@master30 ~ 19:50:16]# kubectl apply -f deploy-httpGet-readiness.yaml
deployment.apps/web created
[root@master30 ~ 19:50:37]# kubectl expose deployment web --port=80 --target-port=80
service/web exposed
[root@master30 ~ 19:51:08]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
web ClusterIP 10.102.216.105 <none> 80/TCP 8s
[root@master30 ~ 19:51:15]# kubectl get pod
NAME READY STATUS RESTARTS AGE
web-679db8c59c-6s6cg 1/1 Running 0 44s
web-679db8c59c-d99fd 1/1 Running 0 44s
web-679db8c59c-jxpgz 1/1 Running 0 44s
#每个 Pod 首页写入自身 Pod 名称,curl 访问时可以直观看到当前请求落到哪个 Pod。
root@master30:~# for pod in $(kubectl get pods -o name|awk -F / '{print $2}'); do kubectl exec $pod -- bash -c "echo $pod > htdocs/index.html"; done
#循环访问 Service 90 次,统计流量分布:
[root@master30 ~ 19:52:18]# for i in {1..90};do curl -s 10.102.216.105;done|sort |uniq -c
30 web-679db8c59c-6s6cg
30 web-679db8c59c-d99fd
30 web-679db8c59c-jxpgz
[root@master30 ~ 19:52:47]# kubectl get endpoints web
NAME ENDPOINTS AGE
web 10.224.215.169:80,10.224.215.179:80,10.224.215.180:80 114s
[root@master30 ~ 19:53:02]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web-679db8c59c-6s6cg 1/1 Running 0 2m42s 10.224.215.179 worker31.zy.cloud <none> <none>
web-679db8c59c-d99fd 1/1 Running 0 2m42s 10.224.215.169 worker31.zy.cloud <none> <none>
web-679db8c59c-jxpgz 1/1 Running 0 2m42s 10.224.215.180 worker31.zy.cloud <none> <none>
# 手动破坏其中一个 Pod(web-6bpg7)首页,删除 web-9479dc55c-6bpg7主页文件
[root@master30 ~ 19:53:19]# kubectl exec -it web-679db8c59c-6s6cg -- rm -f htdocs/index.html
# web 服务的后端没有pod的ip,原本 3 个 IP 只剩 2 个,故障 Pod 的 IP 被直接移除,Service 不再转发流量给它。
[root@master30 ~ 19:55:04]# kubectl get endpoints web
NAME ENDPOINTS AGE
web 10.224.215.169:80,10.224.215.180:80 4m28s
# 访问svc,看不到后端故障 Pod节点(web-9479dc55c-6bpg7) 输出
[root@master30 ~ 19:55:58]# for i in {1..90};do curl -s 10.102.216.105;done|sort |uniq -c
45 web-679db8c59c-d99fd
45 web-679db8c59c-jxpgz
# 观察web1状态,READY为0,RESTARTS数量为0
[root@master30 ~ 19:56:02]# kubectl get pod
NAME READY STATUS RESTARTS AGE
web-679db8c59c-6s6cg 0/1 Running 0 5m36s
web-679db8c59c-d99fd 1/1 Running 0 5m36s
web-679db8c59c-jxpgz 1/1 Running 0 5m36s
#READY 变为 0/1:容器运行中,但业务未就绪,不接收流量
#RESTARTS=0:Pod 没有被杀死、没有重启
#对比 livenessProbe:探测失败会重启 Pod;readiness 只切流量,不重启容器。
实验核心结论(考试重点)
1.readinessProbe 管控流量分发,不销毁 Pod;livenessProbe 管控Pod 存活,故障直接重启;
2.Pod 未就绪时:状态 0/1 Running,Endpoints 剔除 IP,负载均衡绕开该 Pod;
3.适合场景:应用启动慢、正在初始化、临时故障但不需要重启,只临时切断流量。
# 清理环境
[root@master30 ~ 19:56:27]# kubectl delete deployments.apps web
deployment.apps "web" deleted
Execution Checks-exec
前置基础:exec 探测规则
kubelet 进入容器内部执行你写的 command 命令:
-
命令退出码 = 0 → 探测成功,容器健康
-
命令非 0 / 执行超时 → 探测失败 搭配 livenessProbe:连续失败达到 failureThreshold,直接杀死 Pod、重建。
示例1:检测容器自带文件
[root@master30 ~ 19:56:32]# vim deploy-exec-liveness.yaml
apiVersion: apps/v1 kind: Deployment metadata: labels: app: web name: web spec: replicas: 1 selector: matchLabels: app: web template: metadata: labels: app: web spec: containers: - image: docker.io/library/httpd imagePullPolicy: IfNotPresent name: httpd # 添加livenessProbe部分 livenessProbe: failureThreshold: 3 initialDelaySeconds: 5 #容器启动后,先等 5 秒再第一次探测 periodSeconds: 5 #每隔 5 秒执行一次探测命令 successThreshold: 1 timeoutSeconds: 10 #- 文件存在:cat 正常输出,返回码 0 → 探测健康 # - 文件被删除:cat 找不到文件,命令报错,返回非 0 → 探测失败 exec: command: - cat - /usr/local/apache2/htdocs/index.html
# 创建应用 [root@master30 ~ 20:17:32]# kubectl apply -f deploy-exec-liveness.yaml deployment.apps/web created [root@master30 ~ 20:17:37]# kubectl get pods NAME READY STATUS RESTARTS AGE web-6dff956c58-zbfhd 1/1 Running 0 4s # 手动破坏业务:删除首页文件 root@master30:~# kubectl exec web-8c9ff9b76-nm6s2 -- bash -c 'rm htdocs/index.html' # 观察pod状态,RESTARTS次数变位1 [root@master30 ~ 20:18:17]# kubectl get pod NAME READY STATUS RESTARTS AGE web-6dff956c58-zbfhd 1/1 Running 1 (16s ago) 63s 现象解释完整流程: 1. 删除文件后,kubelet 每 5 秒执行一次 cat; 2. 第一次失败、第二次失败、第三次失败,凑满 failureThreshold=3; 3. kubelet 判定容器内部故障,杀死当前旧容器; 4. Deployment 控制器根据镜像重新拉起全新 httpd Pod; 5. 新 Pod 自带原始 index.html,探针恢复正常; 6. RESTARTS 从 0 变成 1,代表重启了一次; 7. Pod 状态依旧 Running,只是底层容器被替换。
总结:exec 探针通过命令校验容器内文件是否存在,文件丢失导致探测连续失败,触发存活探针自动重启 Pod 恢复业务。
示例2:检测自定义文件
#导出基础 busybox yaml 模板,busybox.yml:把输出内容写入文件,快速拿到基础模板,不用手写所有字段 root@master30:~# kubectl run busybox --image=busybox --image-pull-policy=IfNotPresent -o yaml --dry-run=client > busybox.yml #修改 yaml deploy-exec-busybox.yml root@master30:~# vim deploy-exec-busybox.yml
apiVersion: v1 kind: Pod metadata: creationTimestamp: null labels: run: busybox name: busybox spec: containers: - image: busybox imagePullPolicy: IfNotPresent # 本地存在镜像则不重新拉取镜像 name: busybox # 容器启动执行的shell脚本,整段命令写在同一个数组项中,不可拆分 args: - /bin/sh - -c - touch /tmp/healthy; sleep 10; rm -rf /tmp/healthy; sleep 100 # 脚本逻辑注释: # touch /tmp/healthy 创建健康标记文件 # sleep 10 等待10秒 # rm -rf /tmp/healthy 删除健康标记文件,制造业务故障 # sleep 100 长时间休眠,保证容器主进程不退出,Pod一直为Running状态 # 配置exec方式存活探针livenessProbe,用于检测容器内部是否故障 livenessProbe: failureThreshold: 3 # 连续探测失败3次,则判定容器异常,重启Pod initialDelaySeconds: 5 # 容器启动完成后,延迟5秒才开始第一次探测 periodSeconds: 5 # 探测周期,每隔5秒执行一次探测命令 successThreshold: 1 # 连续1次探测成功,就标记容器健康 timeoutSeconds: 10 # 单次探测命令最多执行10秒,超时直接判定失败 exec: # exec探测方式:在容器内执行指定命令,依靠命令返回码判断健康状态 command: - ls - /tmp/healthy # 探测判断规则: # 1. 文件存在,ls命令执行成功,返回码0 → 探测成功,容器健康 # 2. 文件被删除,ls找不到文件,返回非0码 → 探测失败 dnsPolicy: ClusterFirst # DNS策略:优先使用集群内部DNS解析 restartPolicy: Always # 容器无论何种情况退出,都自动重启
exec 探针通用优缺点
优点:
适配所有类型容器,无 web 服务、无端口也能检测,自定义命令灵活(校验文件、进程、脚本、权限)。
缺点:
容器镜像必须内置探测用到的命令(ls、cat 等),极简镜像缺少命令会直接探测失败。
exec 和 httpGet 探针区分
-
httpGet:针对 web 服务,访问接口 / 页面,通过 HTTP 状态码判断;
-
exec:通用命令检测,文件、进程、自定义脚本校验场景。
TCP Socket Checks-tcpSocket
当使用TCP socket checks,kubelet代理尝试打开容器socket。如果check可以建立连接,判定check成功。
示例:liveness probe使用TCP Socket check
root@master30:~# vim deploy-tcpSocket-liveness.yaml
apiVersion: apps/v1 kind: Deployment metadata: labels: app: web name: web spec: replicas: 1 selector: matchLabels: app: web template: metadata: labels: app: web spec: containers: - image: docker.io/library/httpd imagePullPolicy: IfNotPresent name: httpd # 添加livenessProbe部分 livenessProbe: failureThreshold: 3 initialDelaySeconds: 5 periodSeconds: 5 successThreshold: 1 timeoutSeconds: 10 tcpSocket: port: 80
Health Check Case(健康检查)
一、健康检查在扩容(Scale Up)里的作用
原文核心意思
多副本服务执行扩容,会新建 Pod,Service 默认会直接把新 Pod 加入负载均衡,流量直接打过去。 但应用启动不是瞬间就绪的,要加载缓存、连数据库、初始化配置,这段时间收到请求会报错、超时。 readiness 就绪探针就是用来拦住流量,等初始化完成再接入流量。
举例子
你现在 3 个 Pod,扩容到 5 个,新增 2 个 Pod:
-
没配就绪探针:Pod 刚启动、数据库还没连上,Service 直接把流量分给它,用户访问大量报错;
-
配就绪探针:新 Pod 初始化没完成,探测失败,状态
0/1 Running,从 Endpoints 剔除,不分发流量;等探针检测成功,才加入负载均衡接收请求。
二、健康检查在滚动更新(Rolling Update)里的作用
滚动更新逻辑:Deployment 逐步创建新版本 Pod,确认新版本正常后,再删除旧版本 Pod,分批替换,保证服务不中断。 这里分两种场景:
-
正常场景:新版本启动要 10 秒初始化,这段时间不能处理请求;
-
故障场景:新版本有 bug(连不上数据库、配置错误),永远初始化失败。
三、不配置健康检查的灾难性后果
K8s 原生只判断:容器进程有没有跑起来,只要进程没崩溃,就默认这个 Pod 能对外提供服务。
-
发布有问题的新版本镜像,新 Pod 进程正常运行,但业务完全不可用;
-
K8s 认为新版本 Pod 就绪,持续创建新 Pod、删除旧 Pod;
-
等到所有旧 Pod 全部删完,集群里全是故障新版本 Pod;
-
整个服务瘫痪,全部请求报错,生产业务中断。
四、配置就绪探针后的保护机制
readiness 探针会校验业务真实可用性,不只是看进程是否存活:
-
新版本 Pod 初始化失败、数据库连不上,探针持续失败;
-
故障新 Pod 不会被加入 Service 后端 Endpoints,不会接收流量;
-
Deployment 会判定新版本是否可用,不会继续删除旧 Pod(“新 Pod 没准备好,旧 Pod 就当‘备胎’继续顶着,绝不贸然下岗。)
-
存量正常旧 Pod 保留,流量依旧正常分发,业务完全不受影响;
-
运维可以及时发现新版本发布异常,回滚版本,避免全站故障。
总结Health Check Case(健康检查)两个核心价值
-
扩容场景:防止刚启动未初始化的 Pod 接收流量,减少用户报错;
-
滚动更新发布场景:作为发布安全屏障,新版本异常时保留旧副本,避免业务全量瘫痪,是生产环境必备保障。
环境清理
root@master30:~# kubectl delete ns health
root@master30:~# kubectl delete ns auth
更多推荐
所有评论(0)