阶段9:Prometheus + Grafana + Jenkins 自动部署实施笔记
0. 阶段开始前的基础环境
项目之前已经完成:
阶段1 基础环境
阶段2 Ansible
阶段3 Docker
阶段4 Kubernetes
阶段5 Spring Boot
阶段6 Harbor / MySQL
阶段7 Jenkins CI/CD
阶段8 ELK + Filebeat
当前继续建设:
阶段9 Prometheus + Grafana 监控
服务器规划:
| 192.168.205.141 | k8s-master | Kubernetes Control Plane |
| 192.168.205.142 | k8s-node1 | Worker |
| 192.168.205.143 | k8s-node2 | Worker |
| 192.168.205.144 | devops | Ansible + Jenkins |
| 192.168.205.145 | elk | Kafka + ELK |
| 192.168.205.146 | infra | Harbor + MySQL |
Kubernetes:
v1.36.2
Docker + cri-dockerd
Flannel
Pod CIDR: 10.244.0.0/16
1. 安装 Helm
首先在 k8s-master 安装 Helm。
验证:
helm version
结果:
version.BuildInfo{
Version:"v3.17.3"
}
添加 Prometheus Helm 仓库:
helm repo add prometheus-community \\
https://prometheus-community.github.io/helm-charts
更新:
helm repo update
检查:
helm repo list
结果:
prometheus-community
https://prometheus-community.github.io/helm-charts
1.1 问题:Ansible 显示 repo 已添加,但手工 helm repo list 看不到
原因:
Ansible 当时可能使用了不同用户环境执行 Helm,Helm repo 配置存储在用户 HOME 下。
Helm repo 配置不是全局配置。
最终以 master 用户重新执行:
helm repo add prometheus-community \\
https://prometheus-community.github.io/helm-charts
然后:
helm repo list
确认 master 用户可见。
2. 使用 Ansible 安装 kube-prometheus-stack
监控采用:
prometheus-community/kube-prometheus-stack
Namespace:
monitoring
Release:
monitoring
核心安装命令:
helm upgrade –install monitoring \\
prometheus-community/kube-prometheus-stack \\
-n monitoring \\
–create-namespace \\
-f /tmp/prometheus-values.yaml
安装后检查:
kubectl get pods -n monitoring
核心组件:
monitoring-grafana
monitoring-kube-prometheus-operator
monitoring-kube-state-metrics
monitoring-prometheus-node-exporter
prometheus-monitoring-kube-prometheus-prometheus-0
alertmanager…
3. 第一次安装后遇到资源不足
最初所有 Kubernetes 节点内存约:
2Gi
node2 上同时运行:
Grafana
Prometheus
node-exporter
出现:
Grafana readiness/liveness timeout
Prometheus readiness/liveness timeout
NodeNotReady
kubelet housekeeping took too long
node2:
free -h
结果大约:
Mem: 1.9Gi
used: 1.7Gi
available: 65Mi
Swap: 0
top:
load average 非常高
kubelet CPU高
kswapd0 CPU高
Prometheus / Grafana CPU高
判断:
节点资源不足
3.1 处理方式
先降低监控组件资源占用,并给 master 增加内存。
最终:
k8s-master: 3Gi
k8s-node1: 2Gi
k8s-node2: 2Gi
devops: 2Gi
infra: 2Gi
并采用较低资源配置:
prometheus:
prometheusSpec:
resources:
requests:
cpu: 100m
memory: 512Mi
limits:
cpu: 1000m
memory: 1Gi
4. Helm upgrade 卡 pending-upgrade
曾出现:
Error:
another operation (install/upgrade/rollback) is in progress
查看:
helm history monitoring -n monitoring
看到:
pending-upgrade
原因:
上一次 Helm upgrade 没正常结束。
处理:
helm rollback monitoring <正常revision> -n monitoring
恢复后:
helm status monitoring -n monitoring
确认:
STATUS: deployed
5. 网络问题导致 Chart 下载失败
曾出现:
read tcp … github.com:443:
connection reset by peer
虽然:
ping github.com
可以正常。
说明 ICMP 通不代表 HTTPS 下载稳定。
后来更换可用网络源后成功下载 kube-prometheus-stack Chart。
最终 Ansible 安装成功。
6. 监控组件成功运行
最终:
kubectl get pods -n monitoring -o wide
达到:
Grafana 3/3 Running
Prometheus 2/2 Running
Operator 1/1 Running
kube-state-metrics 1/1 Running
node-exporter master 1/1 Running
node-exporter node1 1/1 Running
node-exporter node2 1/1 Running
7. 暴露 Prometheus 和 Grafana
最初 Service 为:
ClusterIP
实验环境为了浏览器直接访问,改为 NodePort。
Grafana:
32015
Prometheus:
32093
检查:
kubectl get svc -n monitoring
应看到:
monitoring-grafana
NodePort
80:32015/TCP
monitoring-kube-prometheus-prometheus
NodePort
9090:32093/TCP
访问:
Grafana:
http://192.168.205.141:32015
Prometheus:
http://192.168.205.141:32093
8. Grafana Dashboard 验证
Grafana 安装后首先验证:
Node Exporter Full
Kubernetes Cluster
Spring Boot Dashboard
Node Exporter Full 用于查看三台 Kubernetes 虚拟机资源:
k8s-master
k8s-node1
k8s-node2
可以看到:
CPU
Memory
Disk
Network
Load
Filesystem
Kubernetes Cluster Dashboard 用于查看:
Cluster
Namespace
Node
Pod
Deployment
CPU
Memory
Pod状态
9. Spring Boot 接入 Actuator + Prometheus
Spring Boot 增加:
spring-boot-starter-actuator
micrometer-registry-prometheus
并暴露:
/actuator/prometheus
验证:
curl http://<pod-ip>:8080/actuator/prometheus
成功时会看到:
jvm_memory_used_bytes
process_cpu_usage
http_server_requests_seconds_count
…
10. 创建 ServiceMonitor
Spring Boot Service 必须有命名端口:
ports:
– name: http
port: 8080
targetPort: 8080
ServiceMonitor:
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: devops–demo
namespace: monitoring
labels:
release: monitoring
spec:
selector:
matchLabels:
app: devops–demo
namespaceSelector:
matchNames:
– default
endpoints:
– port: http
path: /actuator/prometheus
interval: 15s
应用:
kubectl apply -f servicemonitor-devops-demo.yml
检查:
kubectl get servicemonitor -n monitoring
Prometheus Targets 中出现:
serviceMonitor/monitoring/devops-demo/0
并达到:
2 / 2 UP
说明两个 Spring Boot 副本都被 Prometheus 自动发现。
11. Spring Boot Grafana Dashboard 成功
导入 Spring Boot Dashboard 后,已经能够看到:
Heap Used
Non-Heap Used
CPU
Uptime
GC
Process Files
JVM Memory
应用:
devops-demo
实例:
10.244.x.x:8080
阶段 Spring Boot → Prometheus → Grafana 链路完成。
12. 修改 Kubernetes YAML 时引发应用回退
最初业务 Deployment YAML 是很早以前手工部署时留下的:
image:
192.168.205.146/devops/devops–demo:v1
但 Jenkins 实际已经自动部署:
devops-demo:6
因此出现:
Git / 本地 YAML:
v1
Kubernetes实际运行:
6
属于配置漂移。
后来修改标签并:
kubectl apply -f devops-demo-deployment.yml
导致 Deployment 从:
devops-demo:6
被重新覆盖为:
devops-demo:v1
结果:
curl /actuator
返回:
404
Prometheus Target:
0/2 UP
HTTP 404
13. 使用 Kubernetes Rollout 恢复业务
查看:
kubectl rollout history deployment devops-demo
出现:
REVISION 1
2
3
4
5
6
通过历史 revision 找到正常版本并回滚。
回滚:
kubectl rollout undo deployment devops-demo \\
–to-revision=<正常revision>
之后:
kubectl rollout status deployment devops-demo
恢复成功。
确认镜像:
kubectl describe pod <pod> | grep Image
结果:
192.168.205.146/devops/devops-demo:6
验证:
curl http://<pod-ip>:8080/actuator/prometheus
恢复指标。
14. 从 Kubernetes 恢复当前真实 YAML
因为原 YAML 已经和线上状态不一致,所以从当前集群导出。
安装 kubectl-neat 后:
kubectl get deploy devops-demo -o yaml \\
| kubectl neat \\
> devops-demo-deployment.yml
Service:
kubectl get svc devops-demo-service -o yaml \\
| kubectl neat \\
> devops-demo-service.yml
然后人工清理运行时字段:
creationTimestamp
resourceVersion
uid
status
revision annotation
保留真实业务配置:
image
env
database
imagePullSecrets
ports
replicas
strategy
15. 确认 Jenkins 实际使用的仓库
发现一个关键事实:
Jenkins 拉取的是:
https://gitee.com/knight-puno/devops-demo.git
而不是之前存放 K8s YAML 的基础设施仓库。
因此之前:
Jenkins
只负责 set image
K8s YAML
仍然是手工部署时代遗留
这也是配置漂移的根本原因之一。
16. 将 K8s Manifest 纳入 Spring Boot 仓库
项目结构调整为:
devops-demo/
├── src/
├── pom.xml
├── Dockerfile
├── Jenkinsfile
└── k8s/
├── devops-demo-deployment.yml
├── devops-demo-service.yml
└── servicemonitor-devops-demo.yml
这样应用代码和部署配置统一版本管理。
17. Jenkins 自动部署 Kubernetes
Jenkins 节点:
devops
没有 kubectl。
所以不能直接:
kubectl apply
最终采用:
Jenkins
|
| SSH / SCP
v
k8s-master
|
kubectl
Jenkins 将:
k8s/*
传到:
/tmp/devops-demo-k8s
而不是覆盖:
/home/master/devops-demo-k8s
因为后者还有历史文件,需要保留。
18. Jenkins 部署流程最终顺序
Pipeline:
Checkout
↓
Maven Build
↓
Docker Build
↓
Push Harbor
↓
scp k8s/* -> master:/tmp/devops-demo-k8s
↓
kubectl apply -f /tmp/devops-demo-k8s
↓
kubectl set image
↓
kubectl rollout status
镜像版本:
${BUILD_NUMBER}
例如:
devops-demo:7
devops-demo:9
19. Jenkins kubectl not found
第一次 Jenkins 直接执行:
kubectl apply -f k8s/
报:
kubectl: not found
解决:
所有 kubectl 命令都改为:
ssh master@192.168.205.141 "kubectl …"
包括:
apply
set image
rollout status
20. Jenkins 自动发布验证成功
一次构建生成:
192.168.205.146/devops/devops-demo:9
Harbor push:
digest: sha256:…
然后:
deployment.apps/devops-demo configured
service/devops-demo-service configured
servicemonitor.monitoring.coreos.com/devops-demo unchanged
随后:
deployment.apps/devops-demo image updated
Kubernetes:
kubectl get pods
出现:
devops-demo-847cb9979d-xxxx
1/1 Running
说明:
Git
→ Jenkins
→ Docker
→ Harbor
→ Kubernetes
自动发布闭环完成。
21. Kubernetes 标签接入 Prometheus
业务 Pod 使用标签:
labels:
app: devops–demo
environment: prod
查看:
kubectl get pods –show-labels
但 Prometheus 查询:
kube_pod_labels{label_app="devops-demo"}
最初返回:
Empty query result
与此同时:
kube_pod_info
有数据。
说明 kube-state-metrics 正常,但自定义 Pod label 没暴露。
22. kube-state-metrics 增加 Label Allowlist
检查 kube-state-metrics:
kubectl get deployment \\
-n monitoring \\
monitoring-kube-state-metrics \\
-o yaml | grep args -A30
最初只有:
–port=8080
–resources=…
没有:
–metric-labels-allowlist
于是 Helm values 增加:
kube-state-metrics:
metricLabelsAllowlist:
– pods=[app,environment]
然后:
helm upgrade monitoring \\
prometheus-community/kube-prometheus-stack \\
-n monitoring \\
-f /tmp/prometheus-values.yaml
目标 Prometheus 查询:
kube_pod_labels
能够看到:
label_app="devops-demo"
label_environment="prod"
namespace="default"
pod="devops-demo-…"
23. Helm Upgrade 导致 NodePort 消失
一次:
helm upgrade ...
-f /tmp/prometheus-values.yaml
之后:
kubectl get svc -n monitoring
发现 Grafana / Prometheus 从:
NodePort
变回:
ClusterIP
原因:
新的 values.yaml 没保留旧 NodePort 配置。
因此 Helm 按默认值恢复。
修复:
grafana:
service:
type: NodePort
nodePort: 32015
prometheus:
service:
type: NodePort
nodePort: 32093
然后再次 Helm upgrade。
24. Grafana Dashboard 丢失
Helm upgrade 后:
之前导入的 Grafana Dashboard 全部消失。
检查:
kubectl get pvc -n monitoring
结果:
No resources found
原因:
Grafana 使用非持久化存储。
Pod 重建后:
grafana.db
Dashboard
用户数据
全部丢失。
同时意识到:
Prometheus 没 PVC 时,Pod 重建也会丢历史监控数据。
因此开始补持久化。
25. 配置 Grafana / Prometheus PVC
考虑每台虚拟机磁盘只有约:
20G
最终实验环境配置:
Grafana: 1Gi
Prometheus: 5Gi
Retention: 7d
Grafana:
grafana:
adminPassword: "123456"
persistence:
enabled: true
type: pvc
storageClassName: local–path
size: 1Gi
service:
type: NodePort
nodePort: 32015
Prometheus:
prometheus:
service:
type: NodePort
nodePort: 32093
prometheusSpec:
retention: 7d
resources:
requests:
cpu: 100m
memory: 512Mi
limits:
cpu: 1000m
memory: 1Gi
storageSpec:
volumeClaimTemplate:
spec:
storageClassName: local–path
accessModes:
– ReadWriteOnce
resources:
requests:
storage: 5Gi
26. PVC Pending:没有 StorageClass
开启 PVC 后:
kubectl get pvc -n monitoring
出现:
Pending
Pod Events:
pod has unbound immediate PersistentVolumeClaims
原因:
裸 Kubernetes 集群没有动态 StorageClass。
27. 安装 local-path-provisioner
安装:
kubectl apply -f \\
https://raw.githubusercontent.com/rancher/local-path-provisioner/master/deploy/local-path-storage.yaml
验证:
kubectl get storageclass
结果:
local-path
rancher.io/local-path
WaitForFirstConsumer
之后 PVC 可以动态创建 PV。
28. Prometheus PVC 成功
最终:
kubectl get pvc -n monitoring
Prometheus:
Bound
5Gi
local-path
PV:
pvc-6fc10540-7496-434c-89eb-20565f42e43b
查看:
kubectl describe pv pvc-6fc10540-7496-434c-89eb-20565f42e43b
确认:
local.path.provisioner/selected-node: k8s-node1
也就是:
Prometheus数据
→ k8s-node1本地磁盘
29. Prometheus Pending:PV Node Affinity 冲突
PVC 已 Bound 后,Prometheus 又 Pending:
1 node didn't match PersistentVolume's node affinity
2 nodes didn't match Pod's node selector
原因:
之前 values 强制:
nodeSelector:
kubernetes.io/hostname: k8s–master
但是 PV 位于:
k8s-node1
冲突。
解决:
删除:
nodeSelector:
让 Prometheus 跟随 local-path PV 调度。
Grafana 同理不再强制固定节点。
30. Grafana PVC 处理
Grafana PVC:
1Gi
local-path
曾出现 PVC 挂载后 Grafana:
2/3 Running
检查:
grafana false
grafana-sc-dashboard true
grafana-sc-datasources true
主 Grafana:
readiness probe :3000/api/health
connection refused
中间 PVC 删除时还出现:
Terminating
在确认 Grafana 数据可丢弃的情况下:
kubectl patch pvc monitoring-grafana \\
-n monitoring \\
-p '{"metadata":{"finalizers":null}}'
然后重新:
helm upgrade monitoring ...
最终 Grafana:
3/3 Running
PVC:
Bound
1Gi
31. node-exporter Pending
恢复监控过程中:
monitoring-prometheus-node-exporter-xxx
Pending
原因:
k8s-node2 上有旧 node-exporter Pod 残留。
node-exporter 是 DaemonSet:
每个节点一个
删除 node2 残留后:
master node-exporter Running
node1 node-exporter Running
node2 node-exporter Running
全部恢复。
32. 最终 monitoring 状态
阶段结束时:
kubectl get pods -n monitoring
全部正常:
Grafana 3/3 Running
Prometheus 2/2 Running
Operator 1/1 Running
kube-state-metrics 1/1 Running
node-exporter master 1/1 Running
node-exporter node1 1/1 Running
node-exporter node2 1/1 Running
PVC:
Grafana Bound 1Gi
Prometheus Bound 5Gi
StorageClass:
local-path
33. 最终监控 Values
最终 values 必须注意:
grafana 和 prometheus 只能各写一次。
曾经因为:
grafana:
…
grafana:
…
导致前面配置被后面覆盖。
最终结构:
grafana:
enabled: true
adminPassword: "123456"
persistence:
enabled: true
type: pvc
storageClassName: local–path
size: 1Gi
service:
type: NodePort
nodePort: 32015
prometheus:
enabled: true
service:
type: NodePort
nodePort: 32093
prometheusSpec:
retention: 7d
resources:
requests:
cpu: 100m
memory: 512Mi
limits:
cpu: 1000m
memory: 1Gi
storageSpec:
volumeClaimTemplate:
spec:
storageClassName: local–path
accessModes:
– ReadWriteOnce
resources:
requests:
storage: 5Gi
alertmanager:
enabled: false
kubeControllerManager:
enabled: false
kubeScheduler:
enabled: false
kubeEtcd:
enabled: false
kube-state-metrics:
metricLabelsAllowlist:
– pods=[app,environment]
34. 当前项目最终链路
Gitee
|
v
Jenkins
|
├── Maven Build
├── Docker Build
├── Push Harbor
├── SCP Kubernetes Manifest
├── SSH k8s-master
├── kubectl apply
├── kubectl set image
└── kubectl rollout status
|
v
Kubernetes
|
v
Spring Boot
|
/actuator/prometheus
|
v
ServiceMonitor
|
v
Prometheus
|
v
Grafana
同时:
node-exporter
kube-state-metrics
PVC
local-path
均已完成。





