欢迎光临
我们一直在努力

DevOps 阶段9:Prometheus + Grafana + Jenkins 自动部署实施笔记

阶段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 监控

服务器规划:

IPhostname作用
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: devopsdemo
namespace: monitoring
labels:
release: monitoring

spec:
selector:
matchLabels:
app: devopsdemo

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/devopsdemo: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: devopsdemo
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: localpath
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: localpath
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: k8smaster

但是 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: localpath
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: localpath
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

均已完成。

赞(0)
未经允许不得转载:171主机测评 » DevOps 阶段9:Prometheus + Grafana + Jenkins 自动部署实施笔记
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址