上一篇,我们让这套 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 的完整搭建过程,以及这个实验室对于日常学习、技术积累和实际工作的价值。