内容

  1. Kubernetes Metric Server

  2. Quota and Limits

  3. 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 概述

一、问题提出与解决:

  1. 如何监控 Node 资源使用?

  2. 如何监控 Pod 资源使用?

  3. 如何依据 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. 工作流程
  1. Metrics Server 定时访问所有节点 kubelet;

  2. kubelet 读取容器运行时(containerd/docker)的 CPU、内存占用数据;

  3. 指标汇总存入内存(不持久化,只存最近窗口数据);

  4. APIServer 暴露标准 Metrics API;

  5. HPA 周期性调用 API 获取指标,计算是否需要扩缩容。

3. 关键特性(原文翻译 + 解释)
  1. 通用部署:一套 yaml 适配绝大多数标准 K8s 集群;

  2. 采集周期默认 15s:每 15s 拉取一轮全集群指标(可通过 controller-manager 参数修改);

  3. 极低资源消耗:全集群单实例运行,每个节点仅消耗 1m CPU、2MB 内存;

  4. 大规模集群支持:最多支撑 5000 节点生产集群,性能足够企业级使用。

4. 重要限制(原文重点:不能替代 Prometheus)

Metrics Server 只给自动扩缩容用,不能做长期监控、大盘展示、告警

  1. 数据只存在内存,集群重启 / 组件重启指标全部丢失,无持久存储;

  2. 仅采集 CPU、内存,没有磁盘、网络、业务自定义指标;

  3. 无法把指标转发到第三方监控系统(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 控制器

  1. HPA 每同步周期(默认 15s)拉取 Pod 平均负载;

  2. 对比你设定的阈值(如 CPU50%);

  3. 负载持续超标 → 自动新增 Pod(扩容);

  4. 负载长期低于阈值 → 自动删除多余 Pod(缩容)。

四、补充和你之前 HPA 参数联动
  1. Metrics Server 默认采集间隔 15s,可修改 kube-controller-manager 参数 --horizontal-pod-autoscaler-sync-period 缩短至 10s,指标刷新更快;

  2. HPA 所有扩缩容冷却、稳定窗口配置,都依赖 Metrics Server 提供的指标数据;

  3. 如果 Metrics Server 异常,kubectl top 报错、HPA 永远不会触发扩容 / 缩容。

Metrics-Server 部署

项目地址 kubernetes-sigs/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 分钟),缩容冷却时间。

持续高负载 → 扩容

  1. 15s 采集一次 CPU/内存指标

  2. Pod 刚启动前 30 秒,视为 “启动中”,不参与 HPA 计算。

  3. 一次扩容动作完成后,再次等待 45s 冷却 才能下一次扩容

👉 结论:K8s 1.30 扩容最小间隔 = 45s

持续低负载 → 缩容

  1. 同样 15s 采样

  2. 指标长期低于阈值

  3. 需要满足 300s(5分钟)稳定低位 才会触发缩容

  4. 每次缩容后,再次锁定 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 内置的自动扩缩容控制器,核心逻辑:

  1. 周期性从三类指标 API 获取监控数据:

    1. metrics.k8s.io:基础资源指标(CPU / 内存),由 Metrics Server 提供;

    2. custom.metrics.k8s.io:Pod 自定义业务指标(QPS、请求延迟等,需 Prometheus Adapter);

    3. external.metrics.k8s.io:集群外部指标(消息队列堆积量、第三方流量等)。

  2. 根据你配置的指标阈值,自动调整工作负载的副本数量;

  3. 支持管控资源:Deployment、ReplicaSet、ReplicationController、StatefulSet。

简单一句话:不用手动改 replicas,负载高自动加 Pod,负载低自动减 Pod

二、适用场景

  1. 无状态服务(主流) Nginx 网关、后端 API、微服务、前端服务,流量忽高忽低,适合自动扩容扛峰值、低峰缩容节省资源。

  2. 削峰填谷、应对突发并发 活动促销、定时批量任务、瞬时流量暴涨,避免人工扩容不及时导致服务卡顿。

  3. 有状态服务(StatefulSet) MySQL 从库、缓存集群等可横向扩展的有状态应用,HPA 同样支持副本自动调整。

不适合:无法横向扩容的单实例核心存储(单主数据库等)。

三、核心特点

  1. 只改副本,不修改 Pod 内部配置 HPA 仅调整 replicas 数值,容器镜像、资源限制、启动命令等 Pod 模板完全不变;区别于 VPA(Vertical Pod Autoscaler,垂直扩容,修改 Pod CPU / 内存规格)。

  2. 响应速度快 依靠 Metrics Server 秒级采集指标,配合可调同步周期,快速感知负载变化。

  3. 成熟稳定、生产首选 属于 K8s 原生控制器,无需额外复杂组件,是企业最通用的自动扩缩容方案。

  4. 多重指标混合判断 可同时配置 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)

  1. Metrics Server 每 15s(可改)从 kubelet 拉取所有 Pod CPU、内存;

  2. 指标存入 metrics.k8s.io API;

  3. HPA 控制器周期性调用该 API,获取目标 Deployment 的平均负载;

  4. 和预设阈值对比,计算理想副本数;

  5. 若副本数需要调整,自动修改 Deployment 的 replicas

  6. 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个条件:

  1. 安装 metric server。

  2. 为 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

观察过程:

  1. 随着pod CPU使用率上升,自动扩展pod数量。

  2. 正常情况,30秒左右创建一个新pod,

  3. 即使pod CPU使用率超过阈值,但pod数量不会超过5。

  4. 停止压力测试,负载降低后,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

观察过程:

  1. 随着pod MEM使用率上升,自动扩展pod数量。

  2. 即使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.iocustom.metrics.k8s.ioexternal.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 纵向扩缩容
全称HorizontalVertical
扩缩方式增加 / 减少 Pod 数量调整 CPU / 内存 request/limit
是否重启 Pod❌ 不重启✅ 一般需要重启
适合负载流量波动大资源配置不合理
生效速度
稳定性
生产使用非常普遍较少
能否一起开不能同时开(会冲突)-
支持资源CPU、内存、自定义CPU、内存

环境清理

[root@master30 ~ 14:46:40]# kubectl delete ns metric
namespace "metric" deleted

Quota and Limits

学习参考:

环境准备

  1. 创建一个独立的名字空间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.
    
  2. 提前部署好 Metric Server

ResourceQuota

1、为什么需要 ResourceQuota

多团队共用一套 K8s 集群,不做限制会出现两类严重问题:

  1. 某个命名空间下所有 Pod 消耗的 CPU、内存总量无上限,直接占满集群所有机器硬件资源,其他命名空间的服务没有资源运行,直接宕机。

  2. 无限制重复创建 Pod、Service、配置文件、定时任务等资源:一是耗尽集群 Service 内网 IP;二是海量数据存入集群数据库 etcd,把数据库磁盘占满,整个集群控制面彻底瘫痪。 ResourceQuota 就是给每个独立命名空间设置资源使用硬上限,从根源限制资源消耗、资源创建数量。

2、ResourceQuota 是什么

K8s 原生的资源限制对象,作用范围仅限单个命名空间,专门定义两件事:

  1. 该命名空间最多能创建多少个 Pod、Deployment、Service 等各类资源;

  2. 该命名空间内所有运行 Pod,合计最多能占用多少 CPU、内存、存储。

3、完整工作流程

  1. 管理员给不同团队分配独立命名空间,配合 RBAC 权限控制,团队只能操作自己命名空间内资源。

  2. 管理员在目标命名空间创建 ResourceQuota,写明各项资源最大限额。

  3. 用户创建 / 修改 Pod、Deployment、Service 等资源时,集群 API 会自动拦截校验。

  4. 校验逻辑:计算当前命名空间已占用资源 + 本次新增资源的总和,对比 Quota 设置的上限:

    1. 总和没超过上限:允许创建资源,同步更新已占用资源统计;

    2. 总和超过上限:直接拒绝本次操作,返回 403 报错,明确标注哪一项资源超出配额。

  5. 生效规则:只要命名空间内存在任意一个 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

资源单位规则
  1. CPU:最小单位 m,1000m = 1 个 CPU 核心;

  2. 内存:两套单位标准

    1. 1024 进制:Mi、Gi(容器行业标准)

    2. 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、配额启用前提

  1. 集群 kube-apiserver 组件默认开启 ResourceQuota 准入插件,不需要额外修改控制面启动参数;

  2. 准入插件仅负责校验,不会主动限制资源:命名空间无 ResourceQuota 则无任何资源限制;只有存在 Quota 才开启校验。

8、单个命名空间多个 ResourceQuota 的规则

一个命名空间允许创建多个 ResourceQuota 对象,同一项资源的上限数值会累加。 举例:第一个 Quota 限制 requests.cpu=1000m,第二个 Quota 限制 requests.cpu=1000m,该命名空间 CPU 申请总上限等于 2000m。 实操建议:一个命名空间只创建 1 个 ResourceQuota,全部限制统一写在一份 yaml 中,避免累加逻辑混乱,不方便日常维护。

配额管理

重要说明: 如果项目级别配额限定了 requestlimit,那么创建pod的时候必须指定 requestlimit

创建 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

如果命名空间下的计算资源 (如 cpumemory)的配额被启用, 则用户必须为这些资源设定请求值(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

  1. 每个节点会统计已部署所有 Pod 的 requests 总和;

  2. 节点总硬件资源减去已占用 requests,剩下的就是节点空闲预留资源;

  3. 只有节点空闲预留 ≥ 当前 Pod 的 requests,调度器才会把 Pod 调度到这台机器;

  4. 如果所有节点剩余预留都小于 Pod 的 requests,Pod 会一直处于 Pending 等待状态。

3. 核心作用

给 Pod 保底资源,避免同节点其他服务抢占资源,导致自身基础运行资源不足、卡顿。

4. 和 ResourceQuota 的关联(关键)

ResourceQuota 统计整个命名空间资源占用总量时,完全依靠每个 Pod 的 requests 数值计算。 只要命名空间开启 CPU / 内存配额,不填写 requests,系统无法统计占用量,直接拒绝创建 Pod。

二、limits(资源上限约束)

1. 含义

Pod 允许使用的资源最大值,超过这个数值会被系统惩罚,底层依赖 Linux cgroup 内核机制强制限制。

2. 给谁用?作用于 Pod 运行全过程

Pod 运行期间实时监控资源消耗,分两种资源的处罚规则:

  1. CPU 超过 limits 不会杀死 Pod,内核直接限制 CPU 使用率,容器运行速度大幅变慢、接口卡顿;

  2. 内存 超过 limits 内核直接触发 OOM,立刻杀死 Pod,容器重启。

3. 核心作用

硬性锁死单个 Pod 的资源天花板,防止某个程序异常疯狂占用 CPU / 内存,吃光整台节点机器资源,导致同节点其他全部服务崩溃。

三、requests 和 limits 核心区别对照表

配置项生效阶段底层实现超量后果核心用途
requestsPod 调度时调度器计算节点预留无足够节点则 Pod Pending保证基础运行资源、配额统计依据
limitsPod 运行全程Linux cgroup 内核限制CPU 限速 / 内存超限杀 Pod限制资源占用峰值,保护节点

四、三种常用配置方案

  1. requests < limits(最常用) 例:requests.cpu=100m,limits.cpu=500m Pod 最低保证 0.1 核,流量峰值最多用到 0.5 核,兼顾稳定与突发流量。

  2. requests = limits(固定资源) 例:cpu 均为 200m Pod 资源完全固定,不允许临时扩容资源,适合性能敏感、不能波动的服务。

  3. 只写 requests,不配置 limits(生产禁止) Pod 有最低资源保障,但无上限,程序异常会吃光节点全部资源,引发集群故障。

五、极简总结

  1. requests = 调度器分配节点的最低资源门槛,是 ResourceQuota 统计资源的标准;

  2. limits = Linux 内核硬限制的资源天花板,防止单个 Pod 拖垮整台机器;

  3. 开启资源配额后,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
总结
  1. 计算资源的 limits 总和是否会超过节点上资源总和?

    答案:可能会。 假设 node可用MEMORY为1G。

    每个pod内存 requests是256M,limit是512M。创建5个pod,pod实际占用内存也为256M(有可能小于256M)。

    node上大概可以创建4个pod,而此时的limits总和是2G。

  2. 当计算资源的limits总和超过节点上资源总和时,kubernetes如何处理?

    • 对于 cpu,kubernetes 认为 cpu 是可被压缩的资源,在应用达到limits时,减少该容器的调度时间,并不会杀死应用。

    • 对于 memory,kubernetes 认为 memory 是无法被压缩的资源,此时k8s会杀死占用资源超过其request的应用(1.9版本之后的版本)。首当其冲的是没有指定request的container,然后是使用资源超过其request更多的container。同等情况下优先级更低的container更容易被杀死。

LimitRange

一、存在的背景问题
  1. 没配置任何限制时,创建 Pod 可以不写requestslimits

  2. 一旦命名空间加了ResourceQuota资源配额,不写 requests 的 Pod 直接创建失败

  3. 开发经常忘记手动填写 CPU、内存参数,部署会频繁报错。

LimitRange 就是用来解决这个矛盾的对象,作用范围仅限单个命名空间。

二、LimitRange 能干什么

给当前命名空间定下单容器 / 单 Pod 的 4 套资源规则:

  1. default:Pod 没写资源时,自动填充的默认requestslimits

  2. defaultRequest:Pod 只写了 limits、没写 requests 时,自动补全 requests;

  3. min:单个容器允许设置的最小 CPU、内存,不能比这个值更小;

  4. 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 分工区分(重点)

  1. LimitRange:管单个 Pod / 单个容器 控制每一个 Pod 的资源上下限、自动补全默认资源;

  2. ResourceQuota:管整个命名空间总和 控制该命名空间所有 Pod 加起来,CPU、内存总占用不能超过设定值;

两者搭配标准流程:

  1. ResourceQuota:限制全命名空间总资源,强制 Pod 必须带 requests;

  2. 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
  1. 四项核心参数含义

  • 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 资源可以限定如下资源:

TypeResource NameDescription
containercpu、memory限定容器 cpu、memroy
Podcpu、memory限定 Pod 中所有容器cpu、memroy的总和
PVCstorage限定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 启动探针

作用:专门给启动很慢的容器做初始化检测 规则:

  1. 只要启动探针没探测成功,存活、就绪探针完全不执行;

  2. 启动探针连续失败,会像存活探针一样重启 Pod;

  3. 启动成功后,才会正常执行 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 尝试连接容器指定端口

  • 端口正常打开、能建立连接:健康

  • 端口没启动、连接被拒绝:失败 仅能判断端口进程是否启动,无法检测业务是否正常。

四、三者核心一句话区别

  1. Liveness:坏了就重启 Pod,修复故障;

  2. Readiness:没准备好就切走流量,不接收请求;

  3. Startup:启动慢时保护 Pod,避免初始化中途被误重启。

HTTP Checks-httpGet

httpGet 探测基础概念

  1. 适用场景:跑网页、HTTP 接口的容器(nginx/httpd/java 后端等)

  2. 判断规则:kubelet 发起 GET 请求,返回码 200~399 = 健康;4xx/5xx / 超时 = 不健康

  3. 本次用的是 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)

每个探针参数直白解释

  1. initialDelaySeconds:5 容器刚启动不会立刻检测,等待 5 秒,给程序留出启动时间,避免刚开机就误判失败。

  2. periodSeconds:5 每隔 5 秒发一次 HTTP 请求检查页面。

  3. timeoutSeconds:10 单次访问最多等待 10 秒,页面卡住无响应直接判定探测失败。

  4. successThreshold:1 只要一次访问正常,就认为容器健康。

  5. 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 命令:

  1. 命令退出码 = 0 → 探测成功,容器健康

  2. 命令非 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 探针区分

  1. httpGet:针对 web 服务,访问接口 / 页面,通过 HTTP 状态码判断;

  2. 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:

  1. 没配就绪探针:Pod 刚启动、数据库还没连上,Service 直接把流量分给它,用户访问大量报错;

  2. 配就绪探针:新 Pod 初始化没完成,探测失败,状态0/1 Running,从 Endpoints 剔除,不分发流量;等探针检测成功,才加入负载均衡接收请求。

二、健康检查在滚动更新(Rolling Update)里的作用

滚动更新逻辑:Deployment 逐步创建新版本 Pod,确认新版本正常后,再删除旧版本 Pod,分批替换,保证服务不中断。 这里分两种场景:

  1. 正常场景:新版本启动要 10 秒初始化,这段时间不能处理请求;

  2. 故障场景:新版本有 bug(连不上数据库、配置错误),永远初始化失败。

三、不配置健康检查的灾难性后果

K8s 原生只判断:容器进程有没有跑起来,只要进程没崩溃,就默认这个 Pod 能对外提供服务。

  1. 发布有问题的新版本镜像,新 Pod 进程正常运行,但业务完全不可用;

  2. K8s 认为新版本 Pod 就绪,持续创建新 Pod、删除旧 Pod;

  3. 等到所有旧 Pod 全部删完,集群里全是故障新版本 Pod;

  4. 整个服务瘫痪,全部请求报错,生产业务中断。

四、配置就绪探针后的保护机制

readiness 探针会校验业务真实可用性,不只是看进程是否存活:

  1. 新版本 Pod 初始化失败、数据库连不上,探针持续失败;

  2. 故障新 Pod 不会被加入 Service 后端 Endpoints,不会接收流量;

  3. Deployment 会判定新版本是否可用,不会继续删除旧 Pod(“新 Pod 没准备好,旧 Pod 就当‘备胎’继续顶着,绝不贸然下岗。)

  4. 存量正常旧 Pod 保留,流量依旧正常分发,业务完全不受影响;

  5. 运维可以及时发现新版本发布异常,回滚版本,避免全站故障。

总结Health Check Case(健康检查)两个核心价值

  1. 扩容场景:防止刚启动未初始化的 Pod 接收流量,减少用户报错;

  2. 滚动更新发布场景:作为发布安全屏障,新版本异常时保留旧副本,避免业务全量瘫痪,是生产环境必备保障。

环境清理

 root@master30:~# kubectl delete ns health
 root@master30:~# kubectl delete ns auth

更多推荐