Skip to content

K8s告警规则

K8s 告警关注的是集群对象和组件的持续异常。Pod 重建、滚动发布、镜像拉取、调度等待都会让指标短时间变化,告警规则要把正常波动和真正影响服务的状态区分开。节点 NotReady、Pod 反复重启、Deployment 可用副本不足、PVC 剩余空间偏低、API Server 错误率升高,才是监控里更常见的告警对象。

Prometheus Operator 使用 PrometheusRule 管理规则。PrometheusRule 写入 Kubernetes API 后,由 Operator 校验并转换成 Prometheus 能读取的规则文件;Prometheus 定期加载规则、计算表达式,再把进入 Firing 的告警发送到 Alertmanager。

一、规则来自哪些指标

K8s 告警不是直接读 kubectl get pod 的结果,而是使用监控组件暴露出来的指标。

指标来源常见指标适合告警的状态
kube-state-metricskube_deployment_status_replicas_availablekube_pod_container_status_restarts_totalkube_node_status_condition对象期望状态和当前状态不一致
kubelet / cAdvisorcontainer_cpu_usage_seconds_totalcontainer_memory_working_set_byteskubelet_volume_stats_available_bytes容器资源、PVC 容量、节点采集状态
API Serverapiserver_request_totalapiserver_request_duration_seconds_bucketAPI 错误率、请求延迟
etcdetcd_server_has_leaderetcd_disk_wal_fsync_duration_seconds_bucket控制平面存储状态

kube-state-metrics 更像 Kubernetes 对象状态的导出器。Deployment 期望几个副本、实际可用几个副本,Pod 当前是不是 CrashLoopBackOff,Node 是否 Ready,这些状态经常由它提供。kubelet 更贴近节点和容器运行时,PVC 容量、容器资源、cAdvisor 指标通常来自这一侧。

告警排查时经常要把 PromQL 和 Kubernetes 对象对起来看。例如 K8sDeploymentReplicasUnavailable 触发后,Prometheus 只能说明 deployment 的可用副本不足;真正原因可能是调度失败、镜像拉取失败、探针失败、容器启动失败、资源不足,还要回到 kubectl describe pod/deployment 和事件里确认。

二、测试对象

当前环境基于三节点 K3s 集群,监控栈部署过程参考 K3s部署监控栈。真实 K8s 告警链路用两个异常对象观察:

对象异常方式对应告警
crash-loop-app容器启动后退出,Pod 进入 CrashLoopBackOffK8sPodCrashLoopingK8sPodRestartingTooMuchK8sDeploymentReplicasUnavailable
replica-shortage-appnodeSelector 指向不存在的节点,两个 Pod 无法调度K8sDeploymentReplicasUnavailable

/root/monitoring/k8s-real-alerts.yaml

yaml
apiVersion: v1
kind: Namespace
metadata:
  name: k8s-alert-case
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: crash-loop-app
  namespace: k8s-alert-case
  labels:
    app: crash-loop-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: crash-loop-app
  template:
    metadata:
      labels:
        app: crash-loop-app
    spec:
      containers:
        - name: app
          image: busybox:1.38.0
          command:
            - sh
            - -c
            - |
              # 模拟应用启动失败,kubelet 会不断重启容器。
              echo "simulate app start failure"
              exit 1
          resources:
            requests:
              cpu: 10m
              memory: 16Mi
            limits:
              memory: 32Mi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: replica-shortage-app
  namespace: k8s-alert-case
  labels:
    app: replica-shortage-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: replica-shortage-app
  template:
    metadata:
      labels:
        app: replica-shortage-app
    spec:
      # 指向不存在的节点名,用来制造 Pending 和可用副本不足。
      nodeSelector:
        kubernetes.io/hostname: no-such-node
      containers:
        - name: app
          image: busybox:1.38.0
          command:
            - sh
            - -c
            - sleep 3600
          resources:
            requests:
              cpu: 10m
              memory: 16Mi
            limits:
              memory: 32Mi

应用后查看对象状态:

bash
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml

kubectl apply -f /root/monitoring/k8s-real-alerts.yaml
kubectl -n k8s-alert-case get pod,deploy -o wide

命令结果里,crash-loop-app 的 Pod 是 CrashLoopBackOffreplica-shortage-app 的两个 Pod 是 Pending

text
NAME                                       READY   STATUS             RESTARTS
pod/crash-loop-app-6f58b5f7f5-f8jrg        0/1     CrashLoopBackOff   5
pod/replica-shortage-app-67c46c5cf-qq8s9   0/1     Pending            0
pod/replica-shortage-app-67c46c5cf-zlv9t   0/1     Pending            0

NAME                                   READY   UP-TO-DATE   AVAILABLE
deployment.apps/crash-loop-app         0/1     1            0
deployment.apps/replica-shortage-app   0/2     2            0

三、PrometheusRule

告警规则同样放在 /root/monitoring/k8s-real-alerts.yaml

yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: k8s-real-alerts
  namespace: monitoring
  labels:
    app.kubernetes.io/name: k8s-real-alerts
spec:
  groups:
    - name: k8s-real.rules
      rules:
        - alert: K8sNodeNotReady
          expr: kube_node_status_condition{condition="Ready", status="true"} == 0
          for: 3m
          labels:
            severity: critical
            category: kubernetes
          annotations:
            summary: "Kubernetes node is not Ready"
            description: "node={{ $labels.node }} has not been Ready for 3 minutes."

        - alert: K8sPodCrashLooping
          expr: kube_pod_container_status_waiting_reason{reason="CrashLoopBackOff"} == 1
          for: 1m
          labels:
            severity: warning
            category: kubernetes
          annotations:
            summary: "Pod container is CrashLoopBackOff"
            description: "namespace={{ $labels.namespace }}, pod={{ $labels.pod }}, container={{ $labels.container }}."

        - alert: K8sPodRestartingTooMuch
          expr: increase(kube_pod_container_status_restarts_total[15m]) > 3
          for: 1m
          labels:
            severity: warning
            category: kubernetes
          annotations:
            summary: "Pod container restarts too much"
            description: "namespace={{ $labels.namespace }}, pod={{ $labels.pod }}, container={{ $labels.container }}, restarts={{ $value }}."

        - alert: K8sDeploymentReplicasUnavailable
          expr: (kube_deployment_spec_replicas - kube_deployment_status_replicas_available) > 0
          for: 1m
          labels:
            severity: warning
            category: kubernetes
          annotations:
            summary: "Deployment available replicas are lower than expected"
            description: "namespace={{ $labels.namespace }}, deployment={{ $labels.deployment }}, unavailable={{ $value }}."

        - alert: K8sPVCSpaceLow
          expr: (kubelet_volume_stats_available_bytes / kubelet_volume_stats_capacity_bytes) < 0.15
          for: 5m
          labels:
            severity: warning
            category: kubernetes
          annotations:
            summary: "PVC available space is low"
            description: "namespace={{ $labels.namespace }}, pvc={{ $labels.persistentvolumeclaim }}, available ratio={{ $value }}."

关键字段:

字段说明
groups.namePrometheus 页面里的规则组名称,排查时经常按它定位规则文件
alert告警名,进入 Alertmanager 后会变成 alertname 标签
exprPromQL 表达式,返回非空并且值满足条件时进入 Pending
for条件持续多久才进入 Firing,避免短暂波动直接报警
labels.severity告警级别,常用于 Alertmanager 路由
labels.category加上 kubernetes 分类,方便过滤 K8s 告警
annotations.summary告警摘要,适合放一句话现象
annotations.description告警详情,适合放 namespace、pod、deployment、当前值等排查字段

应用规则并确认 Operator 校验:

bash
kubectl apply -f /root/monitoring/k8s-real-alerts.yaml

kubectl -n monitoring get prometheusrule k8s-real-alerts \
  -o jsonpath='{.metadata.annotations.prometheus-operator-validated}'
echo

输出为 true,说明 Prometheus Operator 已经接受规则。

四、规则加载验证

操作路径:

Status -> Rule health (中文:状态 -> 规则健康)

操作步骤:

  1. 打开 http://192.168.10.11:30900/rules
  2. 在规则列表里找到 k8s-real.rules
  3. 展开 K8sDeploymentReplicasUnavailable,查看表达式、for 时间、标签和注解。

验证重点:

页面字段当前值含义
Rule groupk8s-real.rulesPrometheus 已加载规则组
Rule healthOK最近一次规则计算没有语法错误
Expression(kube_deployment_spec_replicas - kube_deployment_status_replicas_available) > 0可用副本低于期望副本时触发
Labelscategory="kubernetes"severity="warning"Alertmanager 后续可按标签分组和路由

Prometheus 规则加载

排错入口:

bash
kubectl -n monitoring get prometheusrule k8s-real-alerts -o yaml
kubectl -n monitoring logs deploy/kps-operator
kubectl -n monitoring logs prometheus-kps-prometheus-0 -c prometheus

规则不存在,通常先看 PrometheusRule 是否被创建;规则存在但页面没有加载,继续看 Operator 日志和 Prometheus 日志。表达式写错时,Rules 页面通常能看到 unhealthy 状态或解析错误。

五、告警状态验证

操作路径:

Alerts (中文:告警)

操作步骤:

  1. 打开 http://192.168.10.11:30900/alerts
  2. 展开 K8sPodRestartingTooMuch,查看 Pod 重启告警的表达式、标签、当前值和 Firing 状态。
  3. 展开 K8sDeploymentReplicasUnavailable,查看两个 Deployment 的副本不足告警。

验证重点:

告警状态当前对象
K8sPodRestartingTooMuchFIRINGpod="crash-loop-app-6f58b5f7f5-f8jrg"
K8sDeploymentReplicasUnavailableFIRINGdeployment="crash-loop-app"deployment="replica-shortage-app"
K8sPodCrashLoopingPENDINGFIRING取决于 kube-state-metrics 抓取到 CrashLoopBackOff 状态后的持续时间

Prometheus 真实 K8s 告警

命令行查询:

bash
curl -sG http://127.0.0.1:30900/api/v1/query \
  --data-urlencode 'query=ALERTS{namespace="k8s-alert-case"}'

返回结果能看到 alertstate="firing"K8sPodRestartingTooMuchK8sDeploymentReplicasUnavailableK8sPodCrashLooping 依赖 kube_pod_container_status_waiting_reason{reason="CrashLoopBackOff"},Pod 状态在 ErrorCrashLoopBackOff 之间切换时,可能会比重启次数告警更不稳定。

六、Alertmanager 验证

操作路径:

Alerts -> Group -> Info (中文:告警 -> 分组 -> 信息)

操作步骤:

  1. 打开 http://192.168.10.11:30903/#/alerts
  2. 在分组里查看 namespace="k8s-alert-case"
  3. 点击某条告警旁边的 Info
  4. 查看 descriptionsummaryalertnamecategorydeploymentseverity 等标签。

验证重点:

页面字段当前值说明
分组标签namespace="k8s-alert-case"Alertmanager 按 namespace 聚合当前告警
descriptiondeployment=crash-loop-app, unavailable=1告警正文能直接看到异常对象和当前值
alertnameK8sDeploymentReplicasUnavailablePrometheus 规则名已经流转到 Alertmanager
severitywarning后续通知路由可按级别匹配

Alertmanager 真实 K8s 告警详情

API 查询:

bash
curl -s http://127.0.0.1:30903/api/v2/alerts | grep k8s-alert-case

Alertmanager 有告警但没有对外通知时,继续检查 Status 里的路由配置、receiver、静默和抑制状态。当前环境 receiver 是 null,只表示告警已进入 Alertmanager,不代表已经配置短信、邮件、飞书或企业微信。

七、常用规则理解

1. 节点 NotReady

promql
kube_node_status_condition{condition="Ready", status="true"} == 0

kube_node_status_condition 来自 kube-state-metrics。condition="Ready" 表示节点 Ready 条件,status="true" 表示 Ready 为真。值等于 0 时,说明节点没有处于 Ready 状态。

这类告警通常保留 node 标签。排查入口是:

bash
kubectl get nodes -o wide
kubectl describe node <node-name>
kubectl get events -A --sort-by=.lastTimestamp | tail -50

2. Pod CrashLoopBackOff

promql
kube_pod_container_status_waiting_reason{reason="CrashLoopBackOff"} == 1

CrashLoopBackOff 表示容器启动后失败退出,kubelet 正在按退避时间重启它。这个告警能说明 Pod 当前处于等待重启状态,但采样时机可能受 Pod 状态切换影响。实际排查还要看上一轮容器退出原因、退出码和事件:

bash
kubectl -n k8s-alert-case describe pod <pod-name>
kubectl -n k8s-alert-case logs <pod-name> -c app --previous

本次 crash-loop-app 的事件里能看到:

text
Back-off restarting failed container app

3. Pod 重启过多

promql
increase(kube_pod_container_status_restarts_total[15m]) > 3

kube_pod_container_status_restarts_total 是容器重启总次数。increase(...[15m]) > 3 表示最近 15 分钟重启次数增加超过 3 次。相比只看 CrashLoopBackOff,这条规则对“反复失败但状态瞬间切换”的情况更稳。

通知里适合保留 namespacepodcontainer。缺少这些标签时,收到告警还要回 Prometheus 里查对象,排查会慢一截。

4. Deployment 可用副本不足

promql
(
  kube_deployment_spec_replicas
  -
  kube_deployment_status_replicas_available
) > 0

kube_deployment_spec_replicas 是期望副本数,kube_deployment_status_replicas_available 是当前可用副本数。两者相减大于 0,说明有副本没有变成 Available。

这个告警比较适合做服务可用性兜底,但它不直接说明原因。常见原因包括镜像拉取失败、调度失败、探针失败、容器启动失败、资源不足。排查入口:

bash
kubectl -n k8s-alert-case describe deploy replica-shortage-app
kubectl -n k8s-alert-case get pods -o wide
kubectl -n k8s-alert-case describe pod <pod-name>

本次 replica-shortage-appnodeSelector 指向不存在的节点,两个 Pod 处于 Pending,所以可用副本是 0/2

5. PVC 空间不足

promql
(
  kubelet_volume_stats_available_bytes
  /
  kubelet_volume_stats_capacity_bytes
) < 0.15

PVC 空间指标来自 kubelet。available / capacity 小于 0.15 表示剩余空间低于 15%。这条规则需要集群里有真实 PVC,并且 kubelet 能采集到卷容量。存储类不同,指标可见性也会有差异。

排查入口:

bash
kubectl get pvc -A
kubectl describe pvc -n <namespace> <pvc-name>
kubectl get pv

如果 PromQL 没有数据,常见原因是没有挂载中的 PVC、CSI 没有暴露容量信息、kubelet 没采集到 kubelet_volume_stats_*

八、排查表

现象检查入口常见原因处理方向
Rules 页面没有规则kubectl -n monitoring get prometheusrule规则没有创建、namespace 写错、selector 没选中修正 YAML 后重新 apply,查看 Operator 日志
Rules 页面 unhealthyPrometheus Status -> Rule healthPromQL 语法错误、指标名写错Query 页面单独执行表达式,修正后观察加载状态
Alerts 长期 InactivePrometheus Query表达式结果为空、标签过滤过窄去掉部分标签逐步查询,确认指标真实标签
一直 Pendingfor 时间、表达式结果变化条件没有持续满足、指标采样时断时续调整 for 或换成更稳定的指标组合
Prometheus Firing,Alertmanager 没有Prometheus targets、Alertmanager APIAlertmanager 地址异常、告警发送失败查看 Prometheus 配置和 Alertmanager Pod 日志
Alertmanager 有告警,没有通知Alertmanager Status、receiver 配置路由没匹配、receiver 是 null、被静默或抑制检查 route、receiver、silence、inhibit_rules
告警信息看不出对象Alertmanager labels、annotations规则没有保留 namespacepoddeployment 等标签调整表达式聚合方式和 annotations 内容

K8s 告警规则写完后,至少要经过三层验证:Prometheus 是否加载规则,Prometheus 是否计算出 Pending/Firing,Alertmanager 是否收到带完整标签的事件。只有 YAML 存在,说明不了告警链路已经可用。