从零搭建一个单节点 K8S 可观测实验室(十二):告警与故障注入——从 Pending、Firing 到故障恢复

上一篇,我们让这套 Kubernetes 可观测实验室真正产生了变化。

通过 ApacheBench 压测:

复制代码
HTTP 请求

    ↓

Demo 应用负载变化

    ↓

Prometheus 采集 Metrics

    ↓

Grafana Dashboard

同时:

复制代码
Nginx Access Log

    ↓

Fluent Bit

    ↓

Loki

    ↓

Grafana Explore

也就是说,现在我们已经可以回答:

系统现在发生了什么?

但是还有一个问题:

如果没人一直盯着 Grafana 呢?

生产环境显然不能依靠人工不断刷新 Dashboard。

所以这一篇继续向前:

让 Kubernetes 监控系统自己发现异常,并进入告警流程。

最终完成:

复制代码
故障发生

    ↓

Metric 变化

    ↓

PrometheusRule

    ↓

Alert

    ↓

Grafana Alerting

    ↓

故障恢复

一、这一次继续使用 Grafana

上一篇已经创建:

复制代码
K8S Lab Runtime Overview

Dashboard。

其中已经包含:

复制代码
Demo Pod CPU

Demo Pod Memory

Host-Only RX

Host-Only TX

Demo Available Replicas

这一篇不重新创建 Dashboard。

因为:

告警实验真正需要观察的是状态变化,而不是继续增加大量 Panel。

只增加一个:

复制代码
CrashLoop Restart Count

用于后面的 CrashLoopBackOff 实验。


二、增加 Restart Count Panel

进入:

复制代码
Grafana

→ Dashboards

→ K8S Lab Runtime Overview

增加一个 Stat Panel。

PromQL:

复制代码
sum(
  kube_pod_container_status_restarts_total{
    namespace="production",
    pod=~"crashloop-demo-.*"
  }
) or vector(0)

名称:

复制代码
CrashLoop Restart Count

正常情况下:

复制代码
0

后面制造 CrashLoopBackOff 时:

复制代码
0

↓

1

↓

2

↓

3

可以直观看到 Container 是否不断重启。

最终 Dashboard:

复制代码
Demo Pod CPU

Demo Pod Memory

Host-Only RX

Host-Only TX

Demo Available Replicas

CrashLoop Restart Count

已经足够。


三、创建 PrometheusRule

前面我们已经安装:

复制代码
kube-prometheus-stack

所以这里不直接修改 Prometheus 配置。

使用 Kubernetes CRD:

复制代码
PrometheusRule

创建告警规则。

这一次只创建三个:

Rule 用途
DemoDeploymentUnavailable 服务不可用
LabCrashLoop Container 崩溃
LabHighCPU CPU 持续过高

3.1 创建 Rule

创建:

复制代码
nano k8s-lab-alerts.yaml

内容:

复制代码
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule

metadata:
  name: k8s-lab-alerts
  namespace: monitoring
  labels:
    release: prometheus

spec:
  groups:
    - name: k8s-lab-alerts

      rules:

      - alert: DemoDeploymentUnavailable
        expr: |
          kube_deployment_status_replicas_available{
            namespace="production",
            deployment="jenkins-k8s-demo"
          } < 1
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "Demo deployment unavailable"


      - alert: LabCrashLoop
        expr: |
          increase(
            kube_pod_container_status_restarts_total{
              namespace="production",
              pod=~"crashloop-demo-.*"
            }[5m]
          ) > 3
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "CrashLoopBackOff detected"


      - alert: LabHighCPU
        expr: |
          (
            sum by(namespace,pod)(
              rate(
                container_cpu_usage_seconds_total{
                  namespace="production",
                  pod=~"cpu-stress-.*"
                }[1m]
              )
            )
            * 1000
          ) > 300
        for: 1m
        labels:
          severity: warning
        annotations:
          summary: "CPU usage is high"

应用:

复制代码
kubectl apply -f k8s-lab-alerts.yaml

检查:

复制代码
kubectl get prometheusrule \
  -n monitoring

应该看到:

复制代码
k8s-lab-alerts

这里有一个容易踩坑的地方。

由于我们使用的是:

复制代码
kube-prometheus-stack

Prometheus 默认通过 Label Selector 查找需要加载的 PrometheusRule。

因此:

复制代码
labels:
  release: prometheus

非常重要。

如果 Label 不匹配:

  • Kubernetes 中可以看到 PrometheusRule 创建成功;

  • 但是 Prometheus 不会加载这条规则。


四、故障类型与对应指标

三个 Rule 分别模拟生产环境中常见的三类问题:

故障 类型 观察指标 告警等级
Deployment 副本不可用 服务异常 Available Replica Critical
CrashLoopBackOff 应用异常 Restart Count Critical
CPU 持续升高 资源压力 CPU Usage Warning

可以看到:

不同问题,需要不同 Metrics。

监控系统不是简单判断:

复制代码
CPU 高

↓

报警

而是根据业务场景选择:

复制代码
什么指标

↓

代表什么异常

↓

什么情况下需要通知

五、在 Grafana 查看 Alert Rule

这一次不再主要使用 Prometheus Web UI。

直接打开:

复制代码
Grafana

→ Alerting

→ Alert rules

应该可以看到:

复制代码
DemoDeploymentUnavailable

LabCrashLoop

LabHighCPU

当前系统正常:

复制代码
No Alert

这是正确状态。

Grafana 这里主要用于:

  • 查看 Rule;

  • 查看当前 Alert;

  • 查看状态变化。

Prometheus Web UI 仍然可以用于:

  • PromQL 调试;

  • Rule 是否加载;

  • Target 检查。

但日常观察统一使用 Grafana。


六、为什么 Alert Rule 里面有 Pending period?

创建完三个 Rule 后,在 Grafana:

复制代码
Alerting

→ Alert rules

→ k8s-lab-alerts

展开其中任意一个 Rule,例如:

复制代码
LabCrashLoop

可以看到:

复制代码
Pending period

1m

这个值对应 YAML:

复制代码
for: 1m

这里需要理解 Prometheus Alert 的状态变化。

一个 Rule 主要由两部分组成:

expr

负责判断:

当前指标是否满足异常条件。

例如:

复制代码
Available Replica < 1

表示:

复制代码
Deployment 没有可用副本

或者:

复制代码
5分钟内 Restart Count 增加超过 3 次

表示:

复制代码
Container 可能持续崩溃

for

负责判断:

这个异常状态是否持续足够长时间。

例如:

复制代码
for: 1m

表示:

如果异常只持续几秒:

复制代码
异常

↓

Pending

↓

恢复

↓

Inactive

不会真正触发告警。

只有:

复制代码
异常条件满足

↓

持续超过 1 分钟

↓

Firing

所以一次完整状态变化是:

复制代码
正常

↓

Inactive


故障条件满足

↓

Pending


持续超过 Pending period

↓

Firing


故障恢复

↓

Resolved

这里还有一个细节:

本实验中的三个 Rule:

复制代码
DemoDeploymentUnavailable

LabCrashLoop

LabHighCPU

都设置:

复制代码
for: 1m

主要是为了让实验现象容易观察。

实际生产环境不会全部统一设置。

例如:

服务不可用:

复制代码
for: 5m

避免发布过程中短暂波动。

CPU 高:

复制代码
for: 10m

因为短时间 CPU 峰值很常见。

CrashLoop:

复制代码
for: 2m

因为持续崩溃通常需要更快响应。

所以:

复制代码
expr

决定:

什么情况算异常。

而:

复制代码
for

决定:

异常持续多久才值得报警。

这也是为什么 Prometheus Alert 不只是:

复制代码
Metric 超过阈值

↓

马上报警

而是:

复制代码
Metric 异常

↓

持续观察

↓

确认不是瞬态变化

↓

触发 Alert

七、故障实验一:Deployment Scale 到 0

删除 Pod 的实验容易受到 Kubernetes 自愈速度影响。

所以这一次直接制造一个确定持续的故障:

复制代码
Deployment 没有可用副本

执行:

复制代码
kubectl scale deployment \
  jenkins-k8s-demo \
  -n production \
  --replicas=0

查看:

复制代码
kubectl get deployment \
  -n production

应该看到:

复制代码
READY       0/0

AVAILABLE   0

7.1 Grafana 查看 Replica

打开:

复制代码
K8S Lab Runtime Overview

之前:

复制代码
Demo Available Replicas

1

现在:

复制代码
0

这就是故障状态。


7.2 Grafana 查看 Alert

进入:

复制代码
Alerting

→ Alert rules

找到:

复制代码
DemoDeploymentUnavailable

状态变化:

复制代码
正常

↓

Pending

↓

Firing

进入 Firing 后:

可以看到:

复制代码
severity=critical

说明:

复制代码
Deployment 没有可用实例

已经持续超过 Rule 设置时间。


八、恢复业务

恢复:

复制代码
kubectl scale deployment \
  jenkins-k8s-demo \
  -n production \
  --replicas=1

等待:

复制代码
kubectl get pods \
  -n production

恢复:

复制代码
ContainerCreating

↓

Running

Grafana:

复制代码
Demo Available Replicas

0

↓

1

Alert:

复制代码
Firing

↓

Resolved

到这里完成第一个完整闭环:

复制代码
业务故障

↓

Metric 异常

↓

PrometheusRule

↓

Alert

↓

恢复

九、故障实验二:CrashLoopBackOff

第二个实验模拟:

Container 不断崩溃。

创建:

复制代码
nano crashloop-demo.yaml

内容:

复制代码
apiVersion: apps/v1
kind: Deployment

metadata:
  name: crashloop-demo
  namespace: production

spec:
  replicas: 1

  selector:
    matchLabels:
      app: crashloop-demo

  template:
    metadata:
      labels:
        app: crashloop-demo

    spec:
      containers:

      - name: crashloop-demo

        image: busybox:1.36

        command:
        - sh
        - -c

        args:
        - |
          echo "$(date) simulated crash"
          sleep 5
          exit 1

应用:

复制代码
kubectl apply \
  -f crashloop-demo.yaml

观察:

复制代码
kubectl get pods \
  -n production \
  -w

状态:

复制代码
Running

↓

Error

↓

CrashLoopBackOff

十、Grafana 查看 Restart Count

回到 Dashboard:

复制代码
Grafana

→ Dashboards

→ K8S Lab Runtime Overview

查看:

复制代码
CrashLoop Restart Count

可以看到:

复制代码
0

↓

1

↓

2

↓

3

说明:

复制代码
Container 正在不断重启。

这时候 Kubernetes 并不是简单地认为:

Container 退出一次就是故障。

而是观察:

  • Container 是否持续退出;

  • Restart Count 是否持续增加;

  • 是否进入 CrashLoopBackOff。

这也是为什么我们使用:

复制代码
increase(
  kube_pod_container_status_restarts_total[5m]
)

作为告警条件。


十一、LabCrashLoop Alert

进入:

复制代码
Grafana

→ Alerting

→ Alert rules

查看:

复制代码
LabCrashLoop

状态:

复制代码
Inactive

↓

Pending

↓

Firing

展开以后:

复制代码
severity=critical

说明:

复制代码
Container 在短时间内发生多次重启。

此时 Metrics 已经告诉我们:

哪个 Pod 出问题。

但是还不知道:

为什么会崩溃。

这就需要 Logs。


十二、Metrics 找到问题,Logs 找原因

现在进入:

复制代码
Grafana

→ Explore

→ Loki

查询:

复制代码
{namespace="production"} |= "simulated crash"

可以看到:

复制代码
simulated crash

simulated crash

simulated crash

因为每次 Container 重启都会执行:

复制代码
echo "$(date) simulated crash"

所以排障流程变成:

复制代码
Alert

↓

Restart Count 增加

↓

CrashLoopBackOff

↓

Loki 查看日志

↓

找到 simulated crash

这就是:

Metrics 告诉你哪里异常,Logs 帮你找到原因。

在真实生产环境中也是类似流程:

复制代码
监控发现异常

↓

定位服务

↓

查看指标变化

↓

查看日志

↓

分析根因

十三、恢复 CrashLoop

删除:

复制代码
kubectl delete deployment \
  crashloop-demo \
  -n production

之后:

复制代码
LabCrashLoop

Firing

↓

Resolved

到这里完成第二个故障闭环:

复制代码
Container 异常退出

↓

Restart Count 增加

↓

PrometheusRule

↓

Alert

↓

Loki 定位原因

↓

恢复

十四、故障实验三:CPU Stress

最后测试资源告警。

创建:

复制代码
nano cpu-stress.yaml

内容:

复制代码
apiVersion: apps/v1
kind: Deployment

metadata:
  name: cpu-stress
  namespace: production

spec:
  replicas: 1

  selector:
    matchLabels:
      app: cpu-stress

  template:

    metadata:
      labels:
        app: cpu-stress

    spec:

      containers:

      - name: cpu-stress

        image: busybox:1.36

        command:
        - sh
        - -c

        args:
        - |
          while true; do
            :
          done

        resources:

          requests:
            cpu: 100m

          limits:
            cpu: 500m

这里增加:

复制代码
requests:
  cpu: 100m

是为了让 Kubernetes 调度时明确知道:

这个 Pod 至少需要多少 CPU。

同时:

复制代码
limits:
  cpu: 500m

限制最大 CPU 使用量。

应用:

复制代码
kubectl apply \
  -f cpu-stress.yaml

十五、观察 CPU

Grafana:

复制代码
Explore

→ Prometheus

查询:

复制代码
sum by(pod)(
  rate(
    container_cpu_usage_seconds_total{
      namespace="production",
      pod=~"cpu-stress-.*"
    }[1m]
  )
) * 1000

可以看到:

复制代码
CPU

接近

500 mCPU

说明:

复制代码
cpu-stress

正在持续占用 CPU。


十六、LabHighCPU Alert

等待:

复制代码
CPU > 300m

持续 1 分钟

Grafana:

复制代码
LabHighCPU

状态:

复制代码
Inactive

↓

Pending

↓

Firing

这一次:

复制代码
severity=warning

因为 CPU 高并不一定代表服务已经不可用。

它和:

复制代码
DeploymentUnavailable

属于不同严重程度。

例如:

复制代码
服务不可用

=
业务已经中断

Critical


CPU 偏高

=
可能存在风险

Warning

生产环境通常会根据影响范围设置不同等级。


十七、清理实验环境

删除测试 Deployment:

复制代码
kubectl delete deployment \
  crashloop-demo \
  cpu-stress \
  -n production

确认业务恢复:

复制代码
kubectl get deployment \
  -n production

应该只剩:

复制代码
jenkins-k8s-demo

并且:

复制代码
READY

1/1

十八、总结

上一篇:

让监控数据真正动起来。

这一篇:

让监控系统自己发现问题。

我们没有增加新的组件。

只是利用已有的:

复制代码
Prometheus

Grafana

Alertmanager

Loki

完成三个故障实验。


实验一:Deployment 不可用

复制代码
replicas=0

↓

Available Replica = 0

↓

DemoDeploymentUnavailable

↓

Alert

验证:

服务副本异常时,监控系统可以发现。


实验二:CrashLoopBackOff

复制代码
Container crash

↓

Restart Count 增加

↓

LabCrashLoop

↓

Loki 查看原因

验证:

Metrics 负责发现异常,Logs 负责定位原因。


实验三:CPU Stress

复制代码
CPU 持续升高

↓

LabHighCPU

↓

warning Alert

验证:

资源类问题也可以通过指标提前发现。


最终完整链路:

复制代码
Kubernetes

↓

Metrics

↓

Prometheus

↓

PrometheusRule

↓

Alert

↓

Grafana Alerting

↓

Loki 排障

↓

Recovery

到了这里,这套单节点 K8S 可观测实验室已经不只是:

能看到系统状态。

而是:

能发现问题,并帮助定位问题。

从最开始:

复制代码
kubectl get pods

只能看到:

复制代码
Running

Pending

CrashLoopBackOff

到现在:

复制代码
Metric

↓

Rule

↓

Alert

↓

Log

↓

Recovery

已经形成了一套完整的 Kubernetes 故障发现流程。

到这里,这套单节点 K8S 可观测实验室也已经完成了从:

复制代码
状态查看

↓

指标采集

↓

Metrics 可视化

↓

日志分析

↓

告警发现

↓

故障定位

↓

恢复验证

的一整套闭环。

从最开始只能通过:

复制代码
kubectl get pods

查看:

复制代码
Running

Pending

CrashLoopBackOff

到现在:

复制代码
故障发生

↓

Metric 异常

↓

PrometheusRule

↓

Alert

↓

Grafana 查看

↓

Loki 定位

↓

恢复验证

我们已经搭建了一套完整的 Kubernetes 可观测实验环境。

下一篇,我们将对整个实验室进行一次总结:

从零搭建一个单节点 K8S 可观测实验室(十三):总结------这个实验室如何服务工作、学习和开源项目

回顾这一路从 Kubernetes 基础环境、CI/CD、镜像仓库,到 Metrics、Logs 和 Alert 的完整搭建过程,以及这个实验室对于日常学习、技术积累和实际工作的价值。

相关推荐
吃不吃早饭1 小时前
RHEL9 搭建 Harbor 私有仓库与 Ansible 自动化部署 Kubernetes 运行环境完整实践
kubernetes·自动化·ansible
分布式存储与RustFS3 小时前
RustFS 监控实战:OpenTelemetry Collector 把指标接入 Prometheus + Grafana
云原生·开源·对象存储·分布式存储·s3·rustfs·性能基准
奇特認12 小时前
kubernetes 环境部署
云原生·容器·kubernetes
ltl12 小时前
etcd 深度解剖:Watch、MVCC 与 Kubernetes 里的角色
kubernetes
mohesashou12 小时前
红帽linux的K8S部署
linux·kubernetes·ansible
CJY62115 小时前
k8s集群部署的方法原理
云原生·容器·kubernetes
JavaPub-rodert15 小时前
使用 Docker 部署 Ollama:Docker Compose 一键运行 DeepSeek-R1 1.5B
运维·docker·容器
ZY小袁16 小时前
K8S集群部署(脚本方法)
云原生·容器·kubernetes