装之前先搞清楚三件事:谁连谁、数据往哪流、谁离了谁不能工作。下面这张图就是三件套的全景 —— ①②③ 圈号即推荐安装顺序,箭头即数据流向:
up==0up==0 持续 1m),触发后主动推送给 AlertManager315(集群) 8588(Prometheus) 1860(节点)图:监控三件套数据流与依赖全景 —— ①②③ = 推荐安装顺序;箭头 = 数据流向;蓝框 = 核心(一切的依赖根)
| 顺序 | 组件 | 角色 | 为什么排这个位置 | 装完验证 |
|---|---|---|---|---|
| ① 第 1 个 | Prometheus | 核心引擎 | 另两个都依赖它;单独装即完整可用(采集+存储+自带查询页) | Web UI 9090 → Status/Targets 全 UP;能查到 up 等指标 |
| ② 第 2 个 | AlertManager | 告警下游 | 被动收 Prometheus 推送;装好后在 Prometheus 配 alertmanagers 地址 |
造一条必触发规则(如 up==0),AlertManager UI(9093)能看到 alert |
| ③ 第 3 个 | Grafana | 展示层 | 无任何组件依赖它;只需加数据源,随时可装 | Data Source 测试连通;导入 Dashboard 315 能出图 |
有个看似反过来的理由:Prometheus 配置里要写 AlertManager 的地址,似乎该先把 AlertManager 建好?注意区分两件事:
connection refused)—— 不影响核心功能。alertmanager.monitoring.svc:9093),再装 Prometheus 时一次性写好 alerting 配置 —— 避免后改配置重启。这是"Service 先行",不是"组件先行"。| 组件 | 依赖谁 | 被谁依赖 | 单独存在是否有意义 | 结论 |
|---|---|---|---|---|
| Prometheus | 无(零依赖,只依赖被抓对象活着) | AlertManager(收它的推送)、Grafana(查它的数据) | ✅ 完整可用:采集+存储+自带 9090 查询页 | 第 1 个装 |
| AlertManager | Prometheus(功能上:没它推送就永远无事可做) | 无人依赖 | ❌ 空转:一个告警都不会有 | 第 2 个装 |
| Grafana | Prometheus(数据上:没数据源的看板是空壳) | 无人依赖 | ❌ 空壳:连了 MySQL/Loki 才另当别论 | 第 3 个装 |
数据流向(运行时,数据怎么走) 依赖方向(部署时,谁必须先在)
───────────────────────────── ─────────────────────────────
Target ←─ pull ── Prometheus Prometheus(根,0 依赖)
Prometheus ── push ──► AlertManager ├─► AlertManager(等它来推)
Prometheus ←─ query ── Grafana └─► Grafana(来查数据)
AlertManager ── webhook ──► 钉钉/邮件
部署序 = 从根往下:① P → ② A → ③ G
推 push / 拉 pull 的主动方都是箭头尾 (数据流的主动方 ≠ 先装的一方!)
实战环境:测试集群 qingfeng 单节点(v1.35.9 · containerd),安装时间 2026-10-30 16:58,命令与输出均为真实记录,复制即可用。路线:用 Helm 单装 prometheus-community/prometheus(想三件套一键装齐见第 07 章)。
# ① 集群里是否已有三件套?(防与 Rancher monitoring 撞车 —— 有输出就要考虑复用而非重装)
kubectl get pods -A | grep -iE "prometheus|alertmanager|grafana|node-exporter"
# → 无任何输出 = 环境干净,可全新安装 ✅
# ② 默认存储类是谁?(决定 Prometheus 数据落在哪、能不能扩容)
kubectl get storageclass
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
local-path (default) rancher.io/local-path Delete WaitForFirstConsumer false 7d2h
| local-path 特性(读表要点) | 对安装的影响 |
|---|---|
| 本地路径存储(数据落节点磁盘) | 单节点无风险;以后多节点,Pod 会被"钉"在数据所在节点 |
ALLOWVOLUMEEXPANSION: false | PVC 不能在线扩容 → 第一次就把 size 给够(本章给 10Gi) |
WaitForFirstConsumer | PVC 等 Pod 起来才建 PV,get pvc 起初 Pending 是正常的,别慌 |
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm search repo prometheus-community/prometheus
这个 chart 是"一父多子"结构:server(Prometheus 本体)、alertmanager、nodeExporter、pushgateway、kube-state-metrics,各自一个开关。按第 01 节的顺序,先只装 Prometheus 本体 + 采集端:
mkdir -p ~/monitor && cat > ~/monitor/prom-values.yaml <<'EOF'
## ============ Prometheus 本体 ============
server:
enabled: true
retention: "7d" # 测试环境 7 天足够,生产 15d~30d
persistentVolume:
enabled: true
size: 10Gi # local-path 不能扩容,一次给够
# storageClass 不写 = 用默认 local-path
resources:
requests: {cpu: 200m, memory: 768Mi}
limits: {cpu: "1", memory: 2Gi}
service:
type: ClusterIP # 先 ClusterIP 验证,要域名见本章第 5 步 ingress 段
## ============ 周边组件按需裁剪 ============
alertmanager:
enabled: false # ← 第二步再开(第 05 章)
nodeExporter:
enabled: true # 节点指标,保留
pushgateway:
enabled: false # 临时任务推送用,暂时不需要
EOF
helm upgrade 即可 —— 这正是按"①②③"顺序分步装三件套的底气。helm install prometheus prometheus-community/prometheus \
-n monitoring --create-namespace \
-f ~/monitor/prom-values.yaml
NAME: prometheus
LAST DEPLOYED: Fri Oct 30 16:58:43 2026
NAMESPACE: monitoring
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
The Prometheus server can be accessed via port 80 on the following DNS name from within your cluster:
prometheus-server.monitoring.svc.cluster.local
Get the Prometheus server URL by running these commands in the same shell:
export POD_NAME=$(kubectl get pods --namespace monitoring -l "app.kubernetes.io/name=prometheus,app.kubernetes.io/instance=prometheus" -o jsonpath="{.items[0].metadata.name}")
kubectl --namespace monitoring port-forward $POD_NAME 9090
...(NOTES 里还有 Pushgateway 一段,见下方误区)
prometheus-server.monitoring.svc.cluster.local(端口 80)—— 以后 AlertManager/Grafana 对接、加数据源全靠它。kubectl get svc/pods 为准(实测教训):NOTES 模板是无条件打印的,但本集群 get svc 里真的出现了 prometheus-prometheus-pushgateway —— 说明实际 values 漏写了 pushgateway.enabled: false(漏写 = 默认 true)。处理:values 补一段 pushgateway: {enabled: false} 再 helm upgrade,该组件即被移除。# ① 实际装了哪些 Pod?预期 3 类:server / node-exporter / kube-state-metrics
# (不该有 alertmanager 和 pushgateway —— values 关掉了)
kubectl get pods -n monitoring
# NAME READY STATUS RESTARTS AGE
# prometheus-kube-state-metrics-xxxxx 1/1 Running 0 2m (示意,后缀以实际为准)
# prometheus-prometheus-node-exporter-xxxxx 1/1 Running 0 2m
# prometheus-server-xxxxxxx-xxxxx 2/2 Running 0 2m ← 2/2:主容器+config-reload
# ② Service 清单,记住 prometheus-server 这个名字(port 80 → 9090)
kubectl get svc -n monitoring
# ③ PVC:Pod Running 之后应为 Bound(起初 Pending 是 WaitForFirstConsumer 特性,正常)
kubectl get pvc -n monitoring
# ④ Helm 视角:release 状态与版本
helm list -n monitoring
先用 port-forward 把页面拉到本地(仅验证用,长期访问见第 6 步):
# 方式一:chart NOTES 推荐的(转发 Pod)
export POD_NAME=$(kubectl get pods -n monitoring -l "app.kubernetes.io/name=prometheus,app.kubernetes.io/instance=prometheus" -o jsonpath="{.items[0].metadata.name}")
kubectl port-forward $POD_NAME -n monitoring 9090
# Forwarding from 127.0.0.1:9090 -> 9090 ← 真实输出
# 方式二:转发 Service(更短,效果一样)
kubectl port-forward svc/prometheus-server -n monitoring 9090:80
在服务器的另一个窗口先用命令行过三关(不开浏览器也能验收):
# 关① 活性
curl -s http://localhost:9090/-/healthy
# 期望: Prometheus is Healthy.
# 关② Targets 健康(命令行版"看绿点")
curl -s http://localhost:9090/api/v1/targets | grep -o '"health":"[a-z]*"' | sort | uniq -c
# 期望: 若干 "health":"up"
# 关③ up 指标查询(命令行版 Graph 查询)
curl -s 'http://localhost:9090/api/v1/query?query=up' | head -c 600
# 期望: "value":[时间戳,"1"] 的若干条
| # | 页面操作(浏览器版) | 合格标准 |
|---|---|---|
| ① | Status → Targets | 各 job 的 target 全 UP(绿点),尤其 kubernetes-nodes / kubernetes-pods |
| ② | Graph 查 up | 所有返回值 = 1 |
| ③ | 查 container_cpu_usage_seconds_total | 有数据行(cAdvisor 抓通了) |
ssh -L 9090:127.0.0.1:9090 用户@节点IP,然后 Windows 浏览器开 http://localhost:9090。链路:Windows 浏览器 →(SSH)→ 服务器 localhost:9090 →(port-forward)→ Pod:9090。port-forward 一断就失效,还占终端。"全开放"= 照 站内 Ingress 文档同款链路加一条子域名规则,测试集群入口 31588 是现成的:
浏览器 http://prometheus.ergesanbaishou.com:31588/
│ hosts 解析到节点 IP
▼
ingress-nginx-controller (NodePort 31588 ← ingress.yaml 里已固定的入口)
│ 按 Host: prometheus.ergesanbaishou.com 匹配新 Ingress
▼
svc prometheus-server (monitoring ns, port 80)
▼
Pod :9090 ← 和 port-forward 看到的是同一个页面
不用手写 YAML,values 的 server.ingress 段打开即可(Helm 统一管理,卸载不残留):
# ~/monitor/prom-values.yaml 的 server: 段里追加(与 retention 等字段平级):
ingress:
enabled: true
className: nginx # 对应 ingress-nginx
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "false" # 先 HTTP,同 k8s-web 站
hosts:
- prometheus.ergesanbaishou.com # ★ 新子域名
path: /
pathType: Prefix
# 生效
helm upgrade prometheus prometheus-community/prometheus -n monitoring -f ~/monitor/prom-values.yaml
# 确认 Ingress 已生成
kubectl get ingress -n monitoring
# NAME CLASS HOSTS ADDRESS PORTS AGE
# prometheus nginx prometheus.ergesanbaishou.com 节点IP 80 ... (示意)
# 先在服务器上验证(不等 DNS,Host 头模拟 —— 和验证 k8s-web 的手法一模一样)
curl -s -H "Host: prometheus.ergesanbaishou.com" http://127.0.0.1:31588/-/healthy
# 期望: Prometheus is Healthy. ← 通了 = Ingress→Service→Pod 全链路 OK
最后在 Windows 用管理员编辑 C:\Windows\System32\drivers\etc\hosts 加一行(IP 换成实际节点 IP),浏览器访问 http://prometheus.ergesanbaishou.com:31588/:
<节点IP> prometheus.ergesanbaishou.com
http://prometheus.ergesanbaishou.com/ 是不通的。要"无端口"体验需外层 NGINX 反代 —— 配置已备好:k8s/prometheus-nginx.conf(80 版可直接用 + 443 升级注释段),放到外层 NGINX 机器的 /etc/nginx/conf.d/(必须 .conf 后缀),nginx -t && nginx -s reload 生效;hosts 解析改指外层 NGINX 机器 IP。完整说明见 证书文档。alertmanager.ergesanbaishou.com、Grafana 加 grafana.ergesanbaishou.com —— 三个域名都从 31588 进,Ingress 按 Host 分流,正是第 01 节图里"三件套各自有门"的落地形态。server.ingress.enabled 改回 false 再 upgrade,然后 kubectl apply -f k8s/prometheus-ingress.yaml —— 同 host 双开会规则重复、管理混乱。高频疑问:Ingress 里为什么不写端口?31588 已被 k8s-web 用了怎么办?先纠正概念:Ingress 资源只有 host/path → Service 的映射,没有入口端口参数(设计使然),端口属于 controller 的 Service —— 31588 被多域名共享按 Host 分流本不算冲突。但本集群有更优雅的先例:harbor 单独装了一套 controller(入口 32588);照抄该模式给 Prometheus 配专属 controller + 独立端口 30990,端口与规则全隔离,还不丢 Ingress 层能力:
| 方案 | 链路 | 特点 | 入口 |
|---|---|---|---|
| A · 共享 | 域名:31588 → 共享 controller → Ingress → svc:80 | 一个入口管所有域名,Host 分流;最省资源 | prometheus.ergesanbaishou.com:31588 |
| B · 独立 controller(★本环境采用) | 域名:30990 → 专属 controller → Ingress(独立 class)→ svc:80 | 同 harbor(32588) 模式;端口/规则全隔离;保留 basic auth 等 Ingress 能力 | prometheus.ergesanbaishou.com:30990 |
| C · svc NodePort 直连 | 节点:30990 → svc(跳过 controller) | 最轻量,一条 helm upgrade 就有;但绕过 Ingress 层,无 basic auth | http://节点IP:30990(无域名) |
# ① 先加仓库(★本机第一次用 ingress-nginx 仓库必须这步!)
# repo 是"按机器"记的:装 Prometheus 时加的 prometheus-community 不通用;
# 漏了这步 = Error: repo ingress-nginx not found(实测踩坑,已登记第 8 节坑速查)
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm repo list # 期望看到 ingress-nginx + prometheus-community 两行
# ② 装第三套 ingress-nginx:专属 class=prometheus-nginx,HTTP 固定 30990(同 harbor 32588 模式)
helm upgrade --install ingress-prometheus-nginx ingress-nginx/ingress-nginx \
-n ingress-nginx \
--set controller.ingressClassResource.name=prometheus-nginx \
--set controller.ingressClassResource.controllerValue=k8s.io/ingress-prometheus-nginx \
--set controller.ingressClassResource.default=false \
--set controller.service.type=NodePort \
--set controller.service.nodePorts.http=30990 \
--set controller.service.nodePorts.https=30490 \
--set controller.admissionWebhooks.enabled=false \
--set controller.replicaCount=1
# 三个关键参数:
# ingressClassResource.name → 只认领 class=prometheus-nginx 的 Ingress,不与共享款(nginx)抢
# nodePorts.http=30990 → 专属入口;https 顺手固定 30490,以后上证书直接用
# admissionWebhooks=false → 新 controller 不装"安检门"(webhook),避开多实例抢校验
# 的经典坑(那两个 443 svc 是什么 → 见下方 5.6 全景表)
# ③ 等新 controller 就绪(单节点约 30s)
kubectl -n ingress-nginx get pods,svc | grep prometheus
# 期望: pod Running;svc ingress-prometheus-nginx-controller → 80:30990/TCP,443:30490/TCP
# ④ Ingress 切到新 class —— ★前提:服务器上的 yaml 已是新版!(仓库双 clone 实测坑:
# apply 了旧版 class=nginx,controller 装好了 30990 也死活不通 —— 规则还挂在共享款上)
grep ingressClassName prometheus-ingress.yaml
# 期望输出: prometheus-nginx;若还是 nginx → git pull 或 vim 手动改这一行
kubectl apply -f prometheus-ingress.yaml # ★ apply 的是 .yaml,不是 .conf!(见下表)
kubectl -n monitoring get ingress prometheus-ingress
# CLASS 列: nginx → prometheus-nginx;换 class = 规则"搬家",31588 上随之失效
# ⑤ 验证专属入口 + 佐证搬家
curl -s -H "Host: prometheus.ergesanbaishou.com" http://10.18.170.34:30990/-/healthy
# 期望: Prometheus is Healthy.
curl -s -H "Host: prometheus.ergesanbaishou.com" -o /dev/null -w '%{http_code}\n' http://10.18.170.34:31588/
# 期望: 404(共享款已不管这个 Host —— 是佐证不是故障)
两个配套文件分工(实测坑:kubectl apply -f prometheus-nginx.conf 报 invalid object to validate —— .conf 根本不是 K8S 资源):
| 文件 | 归属 | 正确用法 |
|---|---|---|
| k8s/prometheus-ingress.yaml | K8S 资源(Ingress) | kubectl apply -f prometheus-ingress.yaml |
| k8s/prometheus-nginx.conf | 外层 NGINX 配置 | scp 到外层机器 /etc/nginx/conf.d/(必须 .conf 后缀)→ nginx -t && nginx -s reload —— 永不进 kubectl |
kubectl -n ingress-nginx get svc 装完第三套后会看到 5 个 Service,高频疑问:"下面两个 443 的没开放端口,nginx 是怎么指定的?" —— 答案:它们不是流量入口,是 admission webhook(准入校验"安检门"),只被 API Server 在 kubectl apply Ingress 的那一瞬间集群内部调用(校验注解/snippet 语法对不对),通过即退场,与业务流量零关系,所以 ClusterIP 足够、没 NodePort 属正常:
| Service | 类型 / 端口 | 身份 | 要 NodePort 吗 |
|---|---|---|---|
| ingress-nginx-controller | NodePort 80:31588 / 443:30443 | 共享款 controller —— 业务流量入口(k8s-web/hello/xl18z) | ★要,这就是 31588 |
| ingress-harbor-nginx-controller | NodePort 80:32588 / 443:31443 | harbor 专属 controller —— 业务流量入口 | ★要(32588) |
| ingress-prometheus-nginx-controller | NodePort 80:30990 / 443:30490 | Prometheus 专属 controller(5.5 节新装) | ★要(30990) |
| ingress-nginx-controller-admission | ClusterIP 443 | 共享款的 admission webhook("安检门") | 不要 —— API Server 内网调用即可 |
| ingress-nginx-ergesanbaishou | ClusterIP 443 | 与 harbor controller 同期出现的另一个 webhook | 不要 —— 同上,非流量入口 |
一句话:认业务入口只看带 80:xxxxx 的 controller Service;443 的 ClusterIP 是"安检门"。这也正是 5.5 节新 controller 关 admissionWebhooks.enabled=false 的原因 —— 三扇"安检门"会抢校验(多 controller 共存的经典坑)。验证某扇门背后挂谁:
kubectl -n ingress-nginx get svc ingress-nginx-ergesanbaishou -o yaml | grep -A3 selector
kubectl get validatingwebhookconfigurations # 看集群里有几扇"安检门"
ingressClassName 改回 nginx 再 apply、外层 NGINX upstream 同步改 31588。注意 30990 在装好 controller 前必然不通,顺序是加仓库 → 装 controller → 切 class → 验端口。# ① 生成密码文件并建 Secret(服务器没 htpasswd 就用 openssl,密码自己定)
printf "admin:$(openssl passwd -apr1 '你的密码')\n" > ~/monitor/auth
kubectl create secret generic prom-basic-auth --from-file=~/monitor/auth -n monitoring
# ② values 的 ingress.annotations 追加三行:
# nginx.ingress.kubernetes.io/auth-type: basic
# nginx.ingress.kubernetes.io/auth-secret: prom-basic-auth
# nginx.ingress.kubernetes.io/auth-realm: "Prometheus Login"
# ③ 再 upgrade 一次,浏览器会弹登录框
helm upgrade prometheus prometheus-community/prometheus -n monitoring -f ~/monitor/prom-values.yaml
helm list -n monitoring # 装了什么、什么版本
helm history prometheus -n monitoring # 改过几次(revision)
helm upgrade prometheus prometheus-community/prometheus -n monitoring -f ~/monitor/prom-values.yaml # 一切变更走这里
helm rollback prometheus 1 -n monitoring # 改坏了回第 1 版
# 卸载(注意:PVC/PV 不会被删,数据还在;重装同名 release 若 PVC 未删会沿用或报错,按需清理)
helm uninstall prometheus -n monitoring
kubectl delete pvc -n monitoring -l app.kubernetes.io/instance=prometheus
| 症状 | 原因/处理 |
|---|---|
Pod 卡 ContainerCreating / ImagePullBackOff | 拉 quay.io/registry.k8s.io 镜像失败;kubectl describe pod 看 Events,走代理或改 Harbor 中转(见 Harbor 文档) |
get pvc 一直 Pending | Prometheus Pod 还没调度起来(local-path 是 WaitForFirstConsumer);若 Pod 已 Running 仍 Pending,看 events 是否 SC 名写错 |
helm repo add/update 超时 | github.io 托管的 index 国内偶发超时,多重试几次 |
| 浏览器开域名不通 | ① 忘带 :31588 端口;② hosts 没解析到节点 IP;③ 先用 curl Host 头验证定位是 DNS 还是链路问题 |
| 不想装的组件还是出现了(如 Pushgateway) | values 漏写 enabled: false = 默认 true;本集群实测 svc 里出现了 pushgateway —— 补上 values 再 helm upgrade 即可移除 |
Error: repo ingress-nginx not found | 本机没加过该仓库(装 Prometheus 时只加过 prometheus-community)。先 helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx && helm repo update,helm repo list 确认后再装 —— repo 是按机器记的,换机器要重加 |
kubectl apply -f prometheus-nginx.conf 报 invalid object to validate | 文件角色搞混:.conf 是外层 NGINX 的配置(scp 到外层机器 /etc/nginx/conf.d/ + nginx -s reload),kubectl 只吃 K8S YAML —— 该 apply 的是 prometheus-ingress.yaml(实测踩坑) |
| apply 了旧版 yaml,class 没变 | 仓库是两个 clone(本地改、服务器跑),apply 前 grep ingressClassName prometheus-ingress.yaml 确认是 prometheus-nginx —— 旧版 nginx 会让 30990 不通(规则还在共享款上) |
| 数据盘满了想扩容 | local-path 不支持在线扩容:短期调小 retention,长期换支持扩容的 SC 或重建更大 PVC |
alertmanager.enabled: true 打开升级,进入第 05 章。本章将覆盖:
alerting.alertmanagers 对接本章将覆盖:
生产环境更推荐 Helm 一个 Chart 装齐(三件套 + Operator + 采集端),本章将覆盖:
monitoring 命名空间