一、技术介绍
Prometheus、Grafana及K8S服务发现详解
Prometheus简介
Prometheus是一个开源的监控系统和时间序列数据库,最初由SoundCloud开发,现已成为CNCF(云原生计算基金会)的毕业项目。它专注于实时监控和告警,特别适合云原生和分布式系统的监控。
Prometheus的核心功能
数据采集:通过Pull模型定期从目标服务拉取指标数据,支持HTTP端点、Pushgateway(用于短期任务)等多种采集方式
数据存储:使用高效的时间序列数据库(TSDB)存储指标数据,支持数据压缩和持久化
查询语言:提供强大的PromQL查询语言,用于分析和聚合时间序列数据
告警功能:支持基于PromQL的告警规则配置,告警信息可发送到Alertmanager进行分组、去重和路由
多维度数据模型:数据以键值对形式存储,支持多维度标签(Labels),便于灵活查询和聚合
Prometheus的架构特点
采用HTTP协议周期性抓取被监控组件的状态,任何提供HTTP接口的组件都可以接入
不依赖分布式存储,单个服务器节点可直接工作
支持服务发现或静态配置发现目标
适用于以机器为中心的监控以及高度动态面向服务架构的监控
Grafana简介
Grafana是一个开源的分析和可视化平台,允许用户从各种后端源(包括Prometheus)可视化数据。它提供了动态且交互式的仪表板,用于展示监控数据。
Grafana的核心特性
可自定义仪表板:创建视觉丰富、互动性强的仪表板
数据源灵活性:支持广泛的数据来源,包括Prometheus、Elasticsearch和InfluxDB等
警报和通知:根据可视化指标定义和触发警报
查询构建器:简化对支持的后端查询的创建过程
通用性:不仅适用于展示Prometheus数据,也适用于其他数据可视化需求
Prometheus与Grafana在K8S中的协同
在Kubernetes(K8S)环境中,Prometheus和Grafana是两个非常流行的开源工具组合:
Prometheus负责收集K8S集群和容器化应用的指标数据
Grafana负责展示这些数据,通过仪表盘直观呈现系统运行状况
这种组合为开发者和运维人员提供了强大而灵活的监控解决方案
基于K8S的服务发现作用
在Kubernetes环境中,基于服务发现的功能对Prometheus监控至关重要:
自动发现监控目标:Prometheus可以自动发现K8S集群中的Pod、Service等资源作为监控目标
动态适应环境变化:当K8S集群中的服务扩缩容或更新时,服务发现机制能自动更新监控目标列表
简化配置管理:无需手动维护监控目标列表,减少配置工作量
支持多集群监控:通过服务发现机制,Prometheus可以监控多个K8S集群
Prometheus通过定期从静态配置的监控目标或基于服务发现自动配置的目标中拉取数据,新拉取到的数据会先存储在内存缓存区,当数据量超过配置阈值时,就会持久化到存储设备中。
二、实战部署node-exporter
root@node1 \~\]# ctr -n k8s.io images import node-exporter.tar.gz unpacking docker.io/prom/node-exporter:v0.16.0 (sha256:efc8140e40b5c940d67056cb56d720ed66965eabe03865ab1595705f4f847009)...done \[root@master \~\]# kubectl create ns monitor-sa namespace/monitor-sa created \[root@master \~\]# kubectl get ns NAME STATUS AGE default Active 27m kube-node-lease Active 27m kube-public Active 27m kube-system Active 27m monitor-sa Active 4s \[root@master prometheus\]# cat node-export.yaml apiVersion: apps/v1 kind: DaemonSet metadata: name: node-exporter namespace: monitor-sa labels: name: node-exporter spec: selector: matchLabels: name: node-exporter template: metadata: labels: name: node-exporter spec: hostPID: true hostIPC: true hostNetwork: true containers: - name: node-exporter image: prom/node-exporter:v0.16.0 imagePullPolicy: IfNotPresent ports: - containerPort: 9100 resources: requests: cpu: 0.15 securityContext: privileged: true args: - --path.procfs - /host/proc - --path.sysfs - /host/sys - --collector.filesystem.ignored-mount-points - '"\^/(sys\|proc\|dev\|host\|etc)($\|/)"' volumeMounts: - name: dev mountPath: /host/dev - name: proc mountPath: /host/proc - name: sys mountPath: /host/sys - name: rootfs mountPath: /rootfs tolerations: - key: "node-role.kubernetes.io/control-plane" operator: "Exists" effect: "NoSchedule" volumes: - name: proc hostPath: path: /proc - name: dev hostPath: path: /dev - name: sys hostPath: path: /sys - name: rootfs hostPath: path: / \[root@master prometheus\]# kubectl get pods -n monitor-sa NAME READY STATUS RESTARTS AGE node-exporter-gwvcf 1/1 Running 0 3m9s node-exporter-wzck7 1/1 Running 0 4m24s   \[root@master prometheus\]# curl 192.168.40.180:9100/metrics \| grep node_load % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 81708 100 81708 0 0 10.3M 0 --:--:-- --:--:-- --:--:-- 11.1M # HELP node_load1 1m load average. # TYPE node_load1 gauge node_load1 0.13 # HELP node_load15 15m load average. # TYPE node_load15 gauge node_load15 0.26 # HELP node_load5 5m load average. # TYPE node_load5 gauge node_load5 0.14  Kubernetes 中部署 Node Exporter 的详细解释 操作流程概述 这段代码展示了在 Kubernetes 集群中部署 Prometheus Node Exporter 的完整过程,主要包括以下几个步骤: 导入 Node Exporter 镜像到容器运行时 创建监控专用的命名空间 部署 Node Exporter 的 DaemonSet 验证 Pod 运行状态 详细解释 1. 导入 Node Exporter 镜像 bash Copy Code ctr -n k8s.io images import node-exporter.tar.gz unpacking docker.io/prom/node-exporter:v0.16.0...done ctr 是 containerd 容器运行时的命令行工具 -n k8s.io 指定了命名空间为 k8s.io(Kubernetes 使用的命名空间) 从 node-exporter.tar.gz 文件中导入镜像 解压并加载了 prom/node-exporter:v0.16.0 镜像 2. 创建监控命名空间 bash Copy Code kubectl create ns monitor-sa kubectl get ns 创建了一个名为 monitor-sa 的命名空间 列出所有命名空间确认创建成功 命名空间用于隔离监控相关的资源 3. Node Exporter DaemonSet 配置 yaml Copy Code apiVersion: apps/v1 kind: DaemonSet metadata: name: node-exporter namespace: monitor-sa labels: name: node-exporter 定义了一个 DaemonSet 资源(确保每个节点运行一个 Pod 副本) 部署在 monitor-sa 命名空间 设置了 name: node-exporter 标签 yaml Copy Code spec: selector: matchLabels: name: node-exporter template: metadata: labels: name: node-exporter 使用标签选择器匹配 Pod Pod 模板中也设置了相同的标签 yaml Copy Code spec: hostPID: true hostIPC: true hostNetwork: true hostPID: true - 使用主机 PID 命名空间,可以查看主机进程 hostIPC: true - 使用主机 IPC 命名空间 hostNetwork: true - 使用主机网络栈,直接暴露主机网络信息 yaml Copy Code containers: - name: node-exporter image: prom/node-exporter:v0.16.0 imagePullPolicy: IfNotPresent ports: - containerPort: 9100 使用 prom/node-exporter:v0.16.0 镜像 IfNotPresent 拉取策略表示本地有镜像就不从仓库拉取 暴露 9100 端口(Node Exporter 默认端口) yaml Copy Code resources: requests: cpu: 0.15 securityContext: privileged: true 请求 0.15 个 CPU 核心 以特权模式运行(需要访问主机系统信息) yaml Copy Code args: - --path.procfs - /host/proc - --path.sysfs - /host/sys - --collector.filesystem.ignored-mount-points - '"\^/(sys\|proc\|dev\|host\|etc)($\|/)"' 指定 procfs 和 sysfs 的挂载路径 忽略特定挂载点的文件系统统计信息 yaml Copy Code volumeMounts: - name: dev mountPath: /host/dev - name: proc mountPath: /host/proc - name: sys mountPath: /host/sys - name: rootfs mountPath: /rootfs 挂载主机的 /dev、/proc、/sys 和 / 到容器内 使 Node Exporter 能访问主机系统信息 yaml Copy Code tolerations: - key: "node-role.kubernetes.io/control-plane" operator: "Exists" effect: "NoSchedule" 容忍控制平面节点的污点 确保 Node Exporter 也能在 master/control-plane 节点上运行 yaml Copy Code volumes: - name: proc hostPath: path: /proc - name: dev hostPath: path: /dev - name: sys hostPath: path: /sys - name: rootfs hostPath: path: / 定义主机路径卷,映射主机系统目录到容器 4. 验证 Pod 运行状态 bash Copy Code kubectl get pods -n monitor-sa NAME READY STATUS RESTARTS AGE node-exporter-gwvcf 1/1 Running 0 3m9s node-exporter-wzck7 1/1 Running 0 4m24s 列出 monitor-sa 命名空间中的 Pod 显示两个 Node Exporter Pod 正常运行(假设集群有两个节点) 每个节点一个 Pod(DaemonSet 的特性) 总结 这段配置实现了: 在每个 Kubernetes 节点(包括控制平面节点)上部署一个 Node Exporter Node Exporter 可以收集主机级别的监控指标(CPU、内存、磁盘、网络等) 通过 9100 端口暴露指标,供 Prometheus 抓取 使用适当的权限和挂载访问主机系统信息 这种部署方式是 Kubernetes 监控的常见模式,为集群提供了基础的主机级监控能力 三、实战部署Prometheus \[root@master prometheus\]# kubectl create sa monitor -n monitor-sa \[root@master prometheus\]# kubectl create clusterrolebinding monitor-binding --clusterrole=cluster-admin --serviceaccount=monitor-sa:monitor -n monitor-sa \[root@master prometheus\]# kubectl create clusterrolebinding monitor-binding-2 --clusterrole=cluster-admin --user=system:serviceaccount:monitor:monitor-sa -n monitor-sa \[root@node1 \~\]# mkdir -p /data \[root@node1 \~\]# chmod -R 777 /data/ \[root@node1 \~\]# ls -ld /data/ drwxrwxrwx 2 root root 6 Mar 13 03:44 /data/ \[root@master prometheus\]# kubectl apply -f prometheus-cfg.yaml configmap/prometheus-config created \[root@master prometheus\]# cat prometheus-cfg.yaml --- kind: ConfigMap apiVersion: v1 metadata: labels: app: prometheus name: prometheus-config namespace: monitor-sa data: prometheus.yml: \| global: scrape_interval: 15s scrape_timeout: 10s evaluation_interval: 1m scrape_configs: - job_name: 'kubernetes-node' kubernetes_sd_configs: - role: node relabel_configs: - source_labels: \[__address__
regex: '(.*):10250'
replacement: '${1}:9100'
target_label: address
action: replace
- action: labelmap
regex: _meta_kubernetes_node_label(.+)
- job_name: 'kubernetes-node-cadvisor'
kubernetes_sd_configs:
- role: node
scheme: https
tls_config:
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
relabel_configs:
- action: labelmap
regex: _meta_kubernetes_node_label(.+)
- target_label: address
replacement: kubernetes.default.svc:443
- source_labels: [__meta_kubernetes_node_name]
regex: (.+)
target_label: metrics_path
replacement: /api/v1/nodes/${1}/proxy/metrics/cadvisor
- job_name: 'kubernetes-apiserver'
kubernetes_sd_configs:
- role: endpoints
scheme: https
tls_config:
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
relabel_configs:
- source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name]
action: keep
regex: default;kubernetes;https
- job_name: 'kubernetes-service-endpoints'
kubernetes_sd_configs:
- role: endpoints
relabel_configs:
- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]
action: keep
regex: true
- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme]
action: replace
target_label: scheme
regex: (https?)
- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path]
action: replace
target_label: metrics_path
regex: (.+)
- source_labels: [address, __meta_kubernetes_service_annotation_prometheus_io_port]
action: replace
target_label: address
regex: ([^:]+)(?::\d+)?;(\d+)
replacement: 1:2
- action: labelmap
regex: _meta_kubernetes_service_label(.+)
- source_labels: [__meta_kubernetes_namespace]
action: replace
target_label: kubernetes_namespace
- source_labels: [__meta_kubernetes_service_name]
action: replace
target_label: kubernetes_name
root@master prometheus\]# cat prometheus-deploy.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: prometheus-server
namespace: monitor-sa
labels:
app: prometheus
spec:
replicas: 1
selector:
matchLabels:
app: prometheus
component: server
#matchExpressions:
#- {key: app, operator: In, values: \[prometheus\]}
#- {key: component, operator: In, values: \[server\]}
template:
metadata:
labels:
app: prometheus
component: server
annotations:
prometheus.io/scrape: 'false'
spec:
nodeName: node1
serviceAccountName: monitor
containers:
- name: prometheus
image: prom/prometheus:v2.33.5
imagePullPolicy: IfNotPresent
command:
- prometheus
- --config.file=/etc/prometheus/prometheus.yml
- --storage.tsdb.path=/prometheus
- --storage.tsdb.retention=720h
- --web.enable-lifecycle
ports:
- containerPort: 9090
protocol: TCP
volumeMounts:
- mountPath: /etc/prometheus
name: prometheus-config
- mountPath: /prometheus/
name: prometheus-storage-volume
volumes:
- name: prometheus-config
configMap:
name: prometheus-config
- name: prometheus-storage-volume
hostPath:
path: /data
type: Directory
\[root@master prometheus\]# cat prometheus-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: prometheus
namespace: monitor-sa
labels:
app: prometheus
spec:
type: NodePort
ports:
- port: 9090
targetPort: 9090
protocol: TCP
selector:
app: prometheus
component: server
\[root@master prometheus\]# kubectl get svc -n monitor-sa
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
prometheus NodePort 10.106.100.212 \
relabel_configs:
- source_labels: [address]
regex: '(.*):10250'
replacement: '${1}:9100' # 将kubelet端口(10250)替换为node-exporter端口(9100)
target_label: address
action: replace
- action: labelmap
regex: _meta_kubernetes_node_label(.+) # 保留节点标签
监控容器指标(cAdvisor)
- job_name: 'kubernetes-node-cadvisor'
kubernetes_sd_configs: [{role: node}]
scheme: https # 使用HTTPS
tls_config:
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
relabel_configs: [...]
重写指标路径为cAdvisor端点
监控API Server
- job_name: 'kubernetes-apiserver'
kubernetes_sd_configs: [{role: endpoints}]
scheme: https
tls_config: {...}
relabel_configs:
- source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name]
action: keep
regex: default;kubernetes;https # 只保留API Server的endpoint
监控服务端点
- job_name: 'kubernetes-service-endpoints'
kubernetes_sd_configs: [{role: endpoints}]
relabel_configs:
- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]
action: keep
regex: true # 只抓取注解了prometheus.io/scrape=true的服务
其他重标签配置...
- Prometheus部署(Deployment)
prometheus-deploy.yaml定义了Prometheus的部署:
yaml
Copy Code
apiVersion: apps/v1
kind: Deployment
metadata:
name: prometheus-server
namespace: monitor-sa
spec:
replicas: 1
selector:
matchLabels:
app: prometheus
component: server
template:
metadata:
labels:
app: prometheus
component: server
annotations:
prometheus.io/scrape: 'false' # 避免监控自己
spec:
nodeName: node1 # 指定部署节点
serviceAccountName: monitor # 使用之前创建的服务账号
containers:
- name: prometheus
image: prom/prometheus:v2.33.5
command:
-
prometheus
-
--config.file=/etc/prometheus/prometheus.yml # 配置文件路径
-
--storage.tsdb.path=/prometheus # 数据存储路径
-
--storage.tsdb.retention=720h # 数据保留30天
-
--web.enable-lifecycle # 启用配置热加载
ports:
- containerPort: 9090 # Prometheus Web端口
volumeMounts:
- mountPath: /etc/prometheus
name: prometheus-config # 挂载配置
- mountPath: /prometheus/
name: prometheus-storage-volume # 挂载数据卷
volumes:
- name: prometheus-config
configMap:
name: prometheus-config # 使用之前创建的ConfigMap
- name: prometheus-storage-volume
hostPath:
path: /data # 使用节点上的/data目录
type: Directory
- Prometheus服务(Service)
prometheus-svc.yaml(未完整显示)通常用于创建Service,暴露Prometheus的Web界面。
总结
这套配置实现了:
使用Kubernetes原生服务发现自动监控集群组件
监控节点指标、容器指标(cAdvisor)和API Server
通过注解(prometheus.io/scrape)选择性监控服务
使用主机路径实现数据持久化
通过ConfigMap管理配置,支持热更新
这种部署方式为Kubernetes集群提供了全面的监控能力,是云原生监控的典型实现
四、部署grafana
root@master prometheus\]# kubectl apply -f grafana.yaml
deployment.apps/monitoring-grafana configured
service/monitoring-grafana unchanged
\[root@master prometheus\]# cat grafana.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: monitoring-grafana
namespace: kube-system
spec:
replicas: 1
selector:
matchLabels:
task: monitoring
k8s-app: grafana
template:
metadata:
labels:
task: monitoring
k8s-app: grafana
spec:
nodeName:
containers:
- name: grafana
image: grafana/grafana:8.4.5
imagePullPolicy: IfNotPresent
ports:
- containerPort: 3000
protocol: TCP
volumeMounts:
- mountPath: /etc/ssl/certs
name: ca-certificates
readOnly: true
- mountPath: /var
name: grafana-storage
- mountPath: /var/lib/grafana/
name: lib
env:
- name: INFLUXDB_HOST
value: monitoring-influxdb
- name: GF_SERVER_HTTP_PORT
value: "3000"
# The following env variables are required to make Grafana accessible via
# the kubernetes api-server proxy. On production clusters, we recommend
# removing these env variables, setup auth for grafana, and expose the grafana
# service using a LoadBalancer or a public IP.
- name: GF_AUTH_BASIC_ENABLED
value: "false"
- name: GF_AUTH_ANONYMOUS_ENABLED
value: "true"
- name: GF_AUTH_ANONYMOUS_ORG_ROLE
value: Admin
- name: GF_SERVER_ROOT_URL
# If you're only using the API Server proxy, set this value instead:
# value: /api/v1/namespaces/kube-system/services/monitoring-grafana/proxy
value: /
volumes:
- name: ca-certificates
hostPath:
path: /etc/ssl/certs
- name: grafana-storage
hostPath:
path: /var/lib/grafana-storage
type: DirectoryOrCreate
- name: lib
hostPath:
path: /var/lib/grafana/
type: DirectoryOrCreate
---
apiVersion: v1
kind: Service
metadata:
labels:
# For use as a Cluster add-on (https://github.com/kubernetes/kubernetes/tree/master/cluster/addons)
# If you are NOT using this as an addon, you should comment out this line.
kubernetes.io/cluster-service: 'true'
kubernetes.io/name: monitoring-grafana
name: monitoring-grafana
namespace: kube-system
spec:
# In a production setup, we recommend accessing Grafana through an external Loadbalancer
# or through a public IP.
# type: LoadBalancer
# You could also use NodePort to expose the service at a randomly-generated port
# type: NodePort
ports:
- port: 80
targetPort: 3000
selector:
k8s-app: grafana
type: NodePort

grafana.yaml 文件解析
这个 grafana.yaml 文件是一个 Kubernetes 资源定义文件,用于在 Kubernetes 集群中部署 Grafana 监控仪表盘。文件包含两个部分:一个 Deployment 和一个 Service。
Deployment 部分
apiVersion: apps/v1 表示使用的 Kubernetes API 版本。
kind: Deployment 表示这是一个部署资源。
metadata:
name: 部署的名称,这里是 monitoring-grafana。
namespace: 部署所在的命名空间,这里是 kube-system。
spec:
replicas: 副本数量为 1,表示只会部署一个 Grafana 实例。
selector: 用于选择哪些 Pod 属于这个部署。
template: Pod 的模板。
metadata: Pod 的元数据,包括标签。
spec: Pod 的规格。
nodeName: 指定 Pod 部署在哪个节点上,这里是 xianchaonode1。
containers:
name: 容器的名称,这里是 grafana。
image: 容器使用的镜像,这里是 grafana/grafana:8.4.5。
imagePullPolicy: 镜像拉取策略,这里是 IfNotPresent,表示如果镜像已经存在则不拉取。
ports: 容器暴露的端口,这里是 TCP 协议的 3000 端口。
volumeMounts: 挂载的卷。
env: 环境变量,用于配置 Grafana。
volumes: 定义的卷。
Service 部分
apiVersion: v1 表示使用的 Kubernetes API 版本。
kind: Service 表示这是一个服务资源。
metadata:
labels: 服务的标签。
name: 服务的名称,这里是 monitoring-grafana。
namespace: 服务所在的命名空间,这里是 kube-system。
spec:
ports: 服务暴露的端口,这里是 80 端口,目标端口是 Grafana 容器的 3000 端口。
selector: 用于选择哪些 Pod 作为服务的后端,这里是选择标签 k8s-app: grafana 的 Pod。
type: 服务的类型,这里是 NodePort,表示服务会在每个节点的随机端口上暴露,并且可以通过 \