监控日志 Runbook:Prometheus / Grafana / Loki / Alertmanager
这篇讲监控和日志怎么装、怎么看、怎么排障。
1. 组件作用
| 组件 | 作用 |
|---|---|
| Prometheus | 拉取和存储指标,比如 CPU、内存、Pod 状态 |
| Grafana | 展示指标和日志,看 dashboard |
| Alertmanager | 接收 Prometheus 告警,做分组、静默、通知 |
| kube-state-metrics | 把 K8s 对象状态转成指标 |
| node-exporter | 采集节点 CPU、内存、磁盘、网络 |
| Loki | 存日志 |
| Promtail | 每个节点采集 Pod 日志发给 Loki |
为什么用 kube-prometheus-stack:它一次性安装 Prometheus Operator、Prometheus、Grafana、Alertmanager、ServiceMonitor CRD 和默认 dashboard,学习和生产都常见。
2. 安装 kube-prometheus-stack
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm install kps prometheus-community/kube-prometheus-stack \
--namespace monitoring --create-namespace \
--version 85.3.3 \
--set prometheus.prometheusSpec.storageSpec.volumeClaimTemplate.spec.storageClassName=longhorn \
--set prometheus.prometheusSpec.storageSpec.volumeClaimTemplate.spec.resources.requests.storage=10Gi \
--set prometheus.service.type=NodePort \
--set prometheus.service.nodePort=30090 \
--set grafana.service.type=NodePort \
--set grafana.service.nodePort=32380 \
--set grafana.persistence.enabled=true \
--set grafana.persistence.storageClassName=longhorn \
--set grafana.persistence.size=2Gi \
--set grafana.deploymentStrategy.type=Recreate \
--set grafana.adminPassword=bootcamp \
--set 'prometheus.prometheusSpec.tolerations[0].operator=Exists' \
--set 'grafana.tolerations[0].operator=Exists'
为什么 grafana.deploymentStrategy.type=Recreate:Grafana 单副本挂 Longhorn RWO PVC,默认 RollingUpdate 会让新旧 Pod 抢同一个卷,出现 Multi-Attach。
前置依赖:这一步重度依赖前两步。①Longhorn 要能制备 PVC(Prometheus 10Gi + Grafana 2Gi);②Cilium MTU 必须已修对(见 Cilium 2.5 节),否则 Prometheus 跨节点抓取大量指标、写 TSDB 时会零星超时——又是"小请求通、大请求挂"的 MTU 症状。
chart 版本
85.3.3对应 app(Prometheus Operator)v0.90.1。装前可helm search repo prometheus-community/kube-prometheus-stack --versions | head确认版本仍在。
真实输出(装好后):
$ kubectl get pvc -n monitoring
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS
kps-grafana Bound pvc-4646.. 2Gi RWO longhorn ← 存储真的制备了
prometheus-...-0 Bound pvc-d817.. 10Gi RWO longhorn
$ kubectl get pods -n monitoring
alertmanager-...-0 2/2 Running
kps-grafana-... 3/3 Running ← 见下方说明
kps-kube-prometheus-stack-operator-… 1/1 Running
kps-kube-state-metrics-… 1/1 Running
kps-prometheus-node-exporter-… ×5 1/1 Running ← 每节点一个 (DaemonSet)
prometheus-...-0 2/2 Running
⚠️ Grafana 启动时会短暂停在
2/3 Running(实测,正常)。3 个容器里grafana主容器首次启动要在 PVC 上初始化 SQLite、跑一大串 DB migration(日志全是Migration successfully executed),几十秒后才 ready。grafana-sc-dashboard/grafana-sc-datasources两个 sidecar 早就 ready。看到2/3别急,kubectl logs ... -c grafana在刷 migration 就是在正常初始化。
3. 安装 Loki + Promtail
helm repo add grafana https://grafana.github.io/helm-charts
helm repo update
helm install loki grafana/loki-stack \
--namespace monitoring \
--set grafana.enabled=false \
--set grafana.sidecar.datasources.enabled=false \
--set prometheus.enabled=false \
--set loki.enabled=true \
--set loki.isDefault=false \
--set loki.persistence.enabled=true \
--set loki.persistence.storageClassName=longhorn \
--set loki.persistence.size=5Gi \
--set promtail.enabled=true \
--set 'loki.tolerations[0].operator=Exists' \
--set 'promtail.tolerations[0].operator=Exists'
自己创建 Grafana datasource:
cat <<'EOF' | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: loki-datasource
namespace: monitoring
labels:
grafana_datasource: "1"
data:
loki-datasource.yaml: |
apiVersion: 1
datasources:
- name: Loki
type: loki
access: proxy
url: http://loki:3100
isDefault: false
EOF
为什么关闭 loki-stack 自动 datasource:kube-prometheus-stack 已经把 Prometheus 设为默认 datasource。如果 Loki chart 也生成 isDefault: true,Grafana 会 CrashLoop。
4. 查看状态
helm list -n monitoring
kubectl get pods -n monitoring -o wide
kubectl get pvc -n monitoring
kubectl get svc -n monitoring
kubectl get prometheus,alertmanager,servicemonitor,prometheusrule -A
入口:
Grafana: http://154.201.73.31:32380
Prometheus: http://154.201.73.31:30090
Grafana 默认账号:
kubectl get secret -n monitoring kps-grafana \
-o jsonpath='{.data.admin-user}' | base64 -d; echo
kubectl get secret -n monitoring kps-grafana \
-o jsonpath='{.data.admin-password}' | base64 -d; echo
(本集群装时用 --set grafana.adminPassword=bootcamp,所以账号 admin / 密码 bootcamp。)
确认数据源没冲突(Loki 装好后必查):
G=$(kubectl get pods -n monitoring -l app.kubernetes.io/name=grafana --no-headers | awk '{print $1}')
kubectl exec -n monitoring $G -c grafana -- \
curl -sS -u admin:bootcamp http://localhost:3000/api/datasources | jq '.[]|{name,type,isDefault}'
真实输出(正确状态):
{ "name":"Prometheus", "type":"prometheus", "isDefault":true } ← 唯一默认
{ "name":"Loki", "type":"loki", "isDefault":false }
{ "name":"Alertmanager", "type":"alertmanager", "isDefault":false }
✅ 关键:只有 Prometheus 是 isDefault:true。这正是 3 节关掉 loki-stack 自动 datasource 的目的——两个默认源会让 Grafana CrashLoop(见 6.1)。
5. Prometheus 验收
PROM=$(kubectl get svc -n monitoring kps-kube-prometheus-stack-prometheus -o jsonpath='{.spec.clusterIP}')
curl -sS "http://$PROM:9090/-/ready"
curl -sS "http://$PROM:9090/api/v1/query?query=up" | jq '.status'
curl -sS "http://$PROM:9090/api/v1/query?query=count(up==1)" | jq '.data.result'
真实输出(本集群):
Prometheus Server is Ready.
count(up==1) = 32 ← 32 个健康抓取目标
# 各 job 抓到几个 target(按 up 指标聚合):
3 apiserver 3 kube-controller-manager
2 coredns 3 kube-scheduler
5 kube-proxy 3 kube-etcd
15 kubelet (5节点×3端点) 5 node-exporter
1 kube-state-metrics + grafana/alertmanager/operator/prometheus 自监控
逐项看什么(新手判断"监控是否真的全"):
| job | 期望数 | 含义 |
|---|---|---|
apiserver | 3 | 3 个控制面的 apiserver 都被抓 |
kube-etcd / kube-scheduler / kube-controller-manager | 各 3 | 控制面核心组件指标都采到了(这几个最容易因 bind 地址漏采,能到 3 说明 kubeadm 暴露正常) |
kubelet | 节点数×3 | 每节点 3 个 metrics 端点(kubelet / cadvisor / probes) |
node-exporter | =节点数 | 每节点一个,采机器级 CPU/内存/磁盘 |
kube-proxy | =节点数 | 5 |
如果 etcd / scheduler / controller-manager 的 up 是 0,多半是它们的 metrics 只 bind 在
127.0.0.1。kubeadm v1.30 默认 bind0.0.0.0,本集群直接能抓到,无需额外改。
看 targets:
kubectl port-forward -n monitoring svc/kps-kube-prometheus-stack-prometheus 9090:9090
浏览器打开:
http://127.0.0.1:9090/targets
6. Grafana 常见故障
6.1 datasource 默认值冲突
日志:
kubectl logs -n monitoring -l app.kubernetes.io/name=grafana -c grafana --tail=100
如果看到:
Only one datasource per organization can be marked as default
检查:
kubectl get configmap -n monitoring -l grafana_datasource=1 -o yaml \
| grep -n "name:\|isDefault"
处理原则:
- Prometheus 保持
isDefault: true。 - Loki 必须是
isDefault: false。 - 不要让 loki-stack 自动生成第二个默认 datasource。
6.2 Grafana Multi-Attach
kubectl describe pod -n monitoring -l app.kubernetes.io/name=grafana
kubectl get volumeattachments.storage.k8s.io | grep grafana || true
kubectl get pvc -n monitoring kps-grafana
固定值:
helm upgrade kps prometheus-community/kube-prometheus-stack \
--version 85.3.3 \
-n monitoring \
--reuse-values \
--set grafana.deploymentStrategy.type=Recreate
7. Loki 验收和排障
LOKI=$(kubectl get svc -n monitoring loki -o jsonpath='{.spec.clusterIP}')
curl -sS "http://$LOKI:3100/ready"
curl -sS "http://$LOKI:3100/loki/api/v1/labels" | jq
curl -sS "http://$LOKI:3100/loki/api/v1/label/namespace/values" | jq
真实输出(装好后):
ready ← Loki 就绪
namespace 标签值: ["kube-system","longhorn-system","monitoring"]
↑ 能列出 namespace = promtail 真的采到日志并发给 Loki 了
loki-promtail 是 DaemonSet,应每节点一个:
$ kubectl get ds -n monitoring loki-promtail
NAME DESIRED CURRENT READY AVAILABLE
loki-promtail 5 5 5 5 ← 5 节点全覆盖
✅ 验证点:Loki ready + namespace 标签能列出值 + promtail DaemonSet 5/5。三者齐 = 日志链路通。
查 Promtail:
kubectl get pods -n monitoring -l app=promtail -o wide
kubectl logs -n monitoring -l app=promtail --tail=100
常见问题:
| 现象 | 查什么 | 原因 |
|---|---|---|
| Grafana 查不到日志 | Loki labels API | Promtail 没采到或 datasource 错 |
| promtail readiness fail | promtail logs | 连不上 Loki 或节点日志路径异常 |
| Loki Pending | PVC / Longhorn | 卷挂载失败 |
8. Alertmanager 查看
kubectl get alertmanager -n monitoring
kubectl get pod -n monitoring | grep alertmanager
kubectl port-forward -n monitoring svc/kps-kube-prometheus-stack-alertmanager 9093:9093
浏览器:
http://127.0.0.1:9093
查看当前告警:
AM=$(kubectl get svc -n monitoring kps-kube-prometheus-stack-alertmanager -o jsonpath='{.spec.clusterIP}')
curl -sS "http://$AM:9093/api/v2/alerts" | jq 'length'
9. 一键健康检查
kubectl get pods -n monitoring -o wide
kubectl get pvc -n monitoring
kubectl get svc -n monitoring | grep NodePort
kubectl get configmap -n monitoring -l grafana_datasource=1 -o name
kubectl logs -n monitoring -l app.kubernetes.io/name=grafana -c grafana --tail=30