运维实战

Kubernetes 生产监控实战:Prometheus + Grafana 完整部署、告警与容量治理

从零部署 kube-prometheus-stack,完成持久化、ServiceMonitor、PrometheusRule、Alertmanager、Grafana、容量治理、升级与回滚验收。

TY
Tycho
技术博主
• 2026-09-24 • 53 分钟阅读 • 3 次浏览
Kubernetes 生产监控实战:Prometheus + Grafana 完整部署、告警与容量治理

本文从一套空白 Kubernetes 集群开始,部署 Prometheus Operator、Prometheus、Alertmanager、Grafana、kube-state-metrics 与 node-exporter,再接入业务指标、告警规则和持久化存储。目标不是“页面能打开”,而是交付一套可验收、可升级、可回滚的监控基线。

版本原则:示例使用 Helm 3 与 kube-prometheus-stack。不要在生产环境直接安装 latest;先用 helm search repo ... -l 选定版本,再把版本写入环境变量和 Git 管理的 values 文件。大版本升级前必须阅读 chart 的 UPGRADE.md,并单独更新 CRD。

一、目标、边界与最终架构

1.1 数据链路

Kubernetes API ───────────────┐
kube-state-metrics ──────────┤
node-exporter / kubelet ─────┼─> Prometheus Operator
应用 /metrics ─ ServiceMonitor┘        │
                                      ├─> Prometheus TSDB ─> Grafana
                                      └─> PrometheusRule ─> Alertmanager ─> 通知渠道

Operator 负责把 ServiceMonitor、PodMonitor 和 PrometheusRule 转换为 Prometheus 的抓取与规则配置;Prometheus 保存时序数据并计算规则;Alertmanager 做分组、抑制和路由;Grafana 只负责查询与展示。把职责拆开后,故障定位就有明确顺序:发现对象、抓取目标、查询数据、规则计算、告警投递。

1.2 前置条件与容量基线

项目实验环境生产建议检查方式
Kubernetes官方仍受支持的版本至少 3 个工作节点kubectl version
HelmHelm 3固定 chart 版本helm version
存储20Gi RWO高性能块存储,按保留期估算kubectl get sc
资源4 vCPU / 8Gi 可用按活跃序列和采样率压测kubectl top nodes
时间NTP 同步节点时钟偏差小于抓取周期timedatectl
kubectl cluster-info
kubectl get nodes -o wide
kubectl get storageclass
kubectl auth can-i create customresourcedefinitions.apiextensions.k8s.io
helm version

二、固定版本并准备命名空间

2.1 选择并记录 chart 版本

helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm search repo prometheus-community/kube-prometheus-stack -l | head -n 10

# 将审核过的版本写在这里;不要省略 --version
export KPS_CHART_VERSION='<经验证的版本>'
kubectl create namespace monitoring --dry-run=client -o yaml | kubectl apply -f -

把 Chart.yaml 的 chart 版本、appVersion、集群版本和验证日期记录在变更单中。chart 版本不等于 Prometheus 版本;真正运行的镜像应在安装后通过 Pod 的 imageID 再确认。

2.2 生产 values.yaml

fullnameOverride: monitoring

prometheusOperator:
  resources:
    requests: {cpu: 100m, memory: 200Mi}
    limits: {cpu: 500m, memory: 500Mi}

prometheus:
  prometheusSpec:
    replicas: 2
    retention: 15d
    retentionSize: 45GB
    scrapeInterval: 30s
    evaluationInterval: 30s
    enableAdminAPI: false
    serviceMonitorSelectorNilUsesHelmValues: false
    podMonitorSelectorNilUsesHelmValues: false
    serviceMonitorNamespaceSelector: {}
    podMonitorNamespaceSelector: {}
    ruleNamespaceSelector: {}
    resources:
      requests: {cpu: 500m, memory: 2Gi}
      limits: {cpu: "2", memory: 4Gi}
    storageSpec:
      volumeClaimTemplate:
        spec:
          storageClassName: fast-rwo
          accessModes: ["ReadWriteOnce"]
          resources:
            requests:
              storage: 50Gi

alertmanager:
  alertmanagerSpec:
    replicas: 3
    resources:
      requests: {cpu: 100m, memory: 200Mi}
      limits: {cpu: 500m, memory: 500Mi}
    storage:
      volumeClaimTemplate:
        spec:
          storageClassName: fast-rwo
          accessModes: ["ReadWriteOnce"]
          resources:
            requests: {storage: 5Gi}

grafana:
  enabled: true
  persistence:
    enabled: true
    type: pvc
    storageClassName: fast-rwo
    accessModes: [ReadWriteOnce]
    size: 10Gi
  resources:
    requests: {cpu: 100m, memory: 256Mi}
    limits: {cpu: "1", memory: 1Gi}
  admin:
    existingSecret: grafana-admin
    userKey: admin-user
    passwordKey: admin-password

kube-state-metrics:
  resources:
    requests: {cpu: 100m, memory: 128Mi}
    limits: {cpu: 500m, memory: 512Mi}

prometheus-node-exporter:
  resources:
    requests: {cpu: 50m, memory: 64Mi}
    limits: {cpu: 250m, memory: 256Mi}

serviceMonitorSelectorNilUsesHelmValues: false 表示允许发现不带 Helm release 标签的自定义 ServiceMonitor;若组织要求强隔离,可改为显式 matchLabels。两个 Prometheus 副本是高可用采集,不会自动去重,查询同一指标时应理解副本标签的含义。retentionSize 要小于 PVC 可用空间,为 WAL、压缩临时文件和文件系统预留余量。

2.3 先创建 Grafana 管理员 Secret

read -rsp 'Grafana admin password: ' GRAFANA_PASSWORD; echo
kubectl -n monitoring create secret generic grafana-admin \
  --from-literal=admin-user=admin \
  --from-literal=admin-password="$GRAFANA_PASSWORD" \
  --dry-run=client -o yaml | kubectl apply -f -
unset GRAFANA_PASSWORD

不要把真实密码写入 values.yaml 或提交到 Git。生产环境应由 External Secrets、Sealed Secrets 或同等秘密管理系统生成该 Secret。

三、安装、等待就绪并确认实际版本

helm upgrade --install monitoring prometheus-community/kube-prometheus-stack \
  --namespace monitoring \
  --version "$KPS_CHART_VERSION" \
  --values values-monitoring.yaml \
  --atomic --timeout 15m

kubectl -n monitoring get pods,pvc
kubectl -n monitoring wait --for=condition=Available deployment/monitoring-operator --timeout=5m
kubectl -n monitoring get pods -o custom-columns='NAME:.metadata.name,IMAGE:.spec.containers[*].image,IMAGE_ID:.status.containerStatuses[*].imageID'
helm -n monitoring list
helm -n monitoring get values monitoring

如果 Pod Pending,先看 PVC 和调度事件,不要反复重装。kubectl describe pod 末尾的 Events 能直接区分存储类不存在、资源不足、镜像拉取失败和节点污点。

四、接入第一个业务指标

4.1 示例应用与 Service

apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-api
  namespace: demo
spec:
  replicas: 2
  selector:
    matchLabels: {app: demo-api}
  template:
    metadata:
      labels: {app: demo-api}
    spec:
      containers:
        - name: app
          image: quay.io/brancz/prometheus-example-app:v0.5.0
          ports:
            - name: http
              containerPort: 8080
          readinessProbe:
            httpGet: {path: /, port: http}
          resources:
            requests: {cpu: 20m, memory: 32Mi}
            limits: {cpu: 200m, memory: 128Mi}
---
apiVersion: v1
kind: Service
metadata:
  name: demo-api
  namespace: demo
  labels: {app: demo-api}
spec:
  selector: {app: demo-api}
  ports:
    - name: http
      port: 8080
      targetPort: http

4.2 ServiceMonitor

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: demo-api
  namespace: demo
  labels: {team: platform}
spec:
  namespaceSelector:
    matchNames: [demo]
  selector:
    matchLabels: {app: demo-api}
  endpoints:
    - port: http
      path: /metrics
      interval: 30s
      scrapeTimeout: 10s
      honorLabels: false
kubectl create namespace demo --dry-run=client -o yaml | kubectl apply -f -
kubectl apply -f demo-api.yaml
kubectl apply -f demo-api-servicemonitor.yaml
kubectl -n demo get endpointslices,servicemonitor
kubectl -n monitoring port-forward svc/monitoring-prometheus 9090:9090
# 浏览器访问 http://127.0.0.1:9090/targets,查询 up{namespace="demo"}

Target 不出现时依次检查:ServiceMonitor 是否被 selector 选中、Service 的命名端口是否与 endpoints.port 一致、EndpointSlice 是否存在 Pod IP、网络策略是否允许 monitoring 命名空间访问目标端口。Target 出现但为 DOWN,再查看错误是 DNS、超时、TLS 还是 HTTP 状态码。

五、告警规则、路由与抑制

5.1 编写可操作的 PrometheusRule

apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: demo-api-rules
  namespace: demo
spec:
  groups:
    - name: demo-api.availability
      rules:
        - alert: DemoApiTargetDown
          expr: min_over_time(up{namespace="demo",service="demo-api"}[5m]) == 0
          for: 10m
          labels:
            severity: critical
            team: platform
          annotations:
            summary: "demo-api 指标目标持续不可用"
            description: "实例 {{ $labels.instance }} 已连续 10 分钟抓取失败"
            runbook_url: "https://runbooks.example.com/demo-api/target-down"

for 用于过滤短抖动;告警标签决定路由与聚合;annotation 应告诉值班人员发生了什么、影响谁、去哪里查。不要把瞬时 CPU 高于阈值直接设为 critical,也不要在告警表达式里使用会产生无限维度的标签。

5.2 Alertmanager 配置原则

route:
  receiver: default
  group_by: [alertname, cluster, namespace]
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h
  routes:
    - matchers: ['severity="critical"']
      receiver: oncall
      continue: false
inhibit_rules:
  - source_matchers: ['severity="critical"']
    target_matchers: ['severity="warning"']
    equal: [alertname, cluster, namespace]
receivers:
  - name: default
  - name: oncall
    webhook_configs:
      - url_file: /etc/alertmanager/secrets/oncall-webhook/url
        send_resolved: true

通知地址必须由 Secret 挂载,不要直接写进 ConfigMap。上线前用专门的测试告警验证 firing 与 resolved 两条链路,并确认抑制规则不会把不同集群的告警互相压制。

六、Grafana 查询、仪表盘与访问控制

kubectl -n monitoring get svc | grep grafana
kubectl -n monitoring port-forward svc/monitoring-grafana 3000:80
# 打开 http://127.0.0.1:3000

先用 Explore 验证 up、rate(container_cpu_usage_seconds_total[5m]) 和 sum by (namespace) (kube_pod_status_phase{phase="Running"}),再制作仪表盘。面板应同时展示请求率、错误率、延迟和饱和度,不要只显示 CPU。生产访问使用 Ingress TLS、SSO/OAuth 和最小角色;关闭匿名管理员权限。

6.1 记录规则减少重复计算

- record: namespace:container_cpu_usage_seconds_total:sum_rate5m
  expr: |
    sum by (cluster, namespace) (
      rate(container_cpu_usage_seconds_total{container!="",image!=""}[5m])
    )

复杂 PromQL 被多个面板反复执行时,记录规则可降低查询 CPU 并统一口径。规则名包含聚合维度和窗口,变更时用新名称并并行验证,避免仪表盘静默改变语义。

七、性能、容量与高基数治理

  • 估算样本量:每秒样本数约等于活跃序列数除以抓取间隔。抓取周期从 30s 改成 15s,写入量近似翻倍。
  • 先删无价值标签:用户 ID、请求 ID、完整 URL、时间戳不能作为 metric label;它们应进入日志或 trace。
  • 限制发现范围:为团队设置 namespace selector、sampleLimit、targetLimit 与 labelLimit,防止一个错误 exporter 拖垮 Prometheus。
  • 保留双约束:同时配置时间和大小上限,并对 PVC 使用量、WAL 错误、规则耗时和 remote write backlog 告警。
  • 长周期存储:本地 TSDB 保留 7–15 天,长期数据使用兼容 remote_write 的后端;下一篇将给出 VictoriaMetrics 实施方案。
# 每个 job 的活跃序列估算
count by (job) ({__name__=~".+"})

# Prometheus 头部活跃序列
prometheus_tsdb_head_series

# 规则组计算耗时与间隔的比值
prometheus_rule_group_last_duration_seconds
  / prometheus_rule_group_interval_seconds

八、故障排查、升级与回滚

现象首查常见根因处理
Pod PendingPod Events、PVCStorageClass/资源/污点修正存储类或调度约束
Target 缺失ServiceMonitor selector标签或命名端口不匹配核对 CR、Service 与 EndpointSlice
Target DOWNTargets 错误文本网络策略、TLS、路径错误从 Prometheus Pod 内 curl
查询慢query log、活跃序列高基数或超大时间范围降维、记录规则、限制范围
告警未通知Alerts 与 Alertmanager规则未 firing、路由不匹配逐段验证规则、路由、接收器
# 备份当前值与 manifest
helm -n monitoring get values monitoring -a > monitoring-values-backup.yaml
helm -n monitoring get manifest monitoring > monitoring-manifest-backup.yaml

# 升级前先检查 CRD 差异,并阅读目标版本 UPGRADE.md
helm show crds prometheus-community/kube-prometheus-stack \
  --version "$NEW_CHART_VERSION" | kubectl diff -f -

helm upgrade monitoring prometheus-community/kube-prometheus-stack \
  -n monitoring --version "$NEW_CHART_VERSION" \
  -f values-monitoring.yaml --atomic --timeout 15m

helm -n monitoring history monitoring
helm -n monitoring rollback monitoring <上一成功修订号> --wait --timeout 15m

Helm 默认不会替你升级 chart 中的 CRD。CRD 变更必须先评估兼容性并按上游升级说明执行。回滚应用资源不等于回滚数据格式;任何涉及 TSDB 或 PVC 的变更,都应先做快照并在隔离环境恢复演练。

九、上线验收清单

  • 所有 Pod Ready,PVC Bound,Prometheus 两副本分布在不同节点或可用区。
  • 核心 Kubernetes targets 为 UP,业务 ServiceMonitor 能被发现且指标可查询。
  • Grafana 数据源健康,核心仪表盘在 1h、24h、7d 范围都能正常打开。
  • 测试告警能从 pending 进入 firing,通知到达,并在恢复后发送 resolved。
  • 记录了 chart、镜像、CRD、values 与集群版本;升级和回滚命令经过演练。
  • 已对活跃序列、WAL、磁盘、规则耗时、通知失败和抓取失败建立自监控。

十、总结

一套可靠监控系统的完成标准不是 Grafana 有图,而是从目标发现、样本写入、查询、规则计算到通知的每一段都有可验证证据,并且容量、权限、升级和回滚都有边界。先用本文建立短周期、可运营的 Prometheus 基线,再根据数据规模决定是否引入长期存储。

十一、官方资料

TY

Tycho

热爱分享技术知识,帮助开发者成长。

评论 (0)

评论功能当前已关闭
暂无评论,快来抢沙发吧!