本文从一套空白 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 |
| Helm | Helm 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: http4.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: falsekubectl 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 Pending | Pod Events、PVC | StorageClass/资源/污点 | 修正存储类或调度约束 |
| Target 缺失 | ServiceMonitor selector | 标签或命名端口不匹配 | 核对 CR、Service 与 EndpointSlice |
| Target DOWN | Targets 错误文本 | 网络策略、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 15mHelm 默认不会替你升级 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 基线,再根据数据规模决定是否引入长期存储。