Prometheus 监控入门与 K8s 实践
Prometheus 监控入门与 K8s 实践
本文涵盖 Prometheus 监控理论、架构、二进制部署、K8s 部署、应用监控及 K8s 集群监控等核心知识点。
一、监控理论
监控分为两大类
| 类型 | 说明 | 特点 |
|---|---|---|
| 黑盒监控 | 身体行不行,拉出来溜溜,有病没病走两步 | 侧重于眼前问题,尽早发现(早于用户)、第一时间处理 |
| 白盒监控 | 身体行不行,先做个全身体检,拿检测结果说话 | 了解系统内部细节,做好预防,防患于未然 |
区别:
- 黑盒:不需要了解系统内部细节 → 早于真正的用户找到问题
- 白盒:要了解系统内部细节 → 预防
监控的目的
- 长期趋势分析 — 预测未来的扩容
- 故障根因分析 — 查历史监控
- 对照分析 — 明确不同版本的运行情况
- 告警通知 — 超过阈值则报警,提前处理问题
- 系统状态了然于胸 — 实时掌握运行状态
总结:
- 通过白盒监控能够提前预知业务瓶颈
- 通过黑盒监控能够第一时间发现业务故障并通过告警通告运维人员进行紧急恢复
二、Prometheus 架构
┌─────────────────────────────────────────────────────────────┐
│ Prometheus 架构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 上游(采集) Prometheus Server 下游(使用)│
│ ┌──────────┐ ┌──────────┐│
│ │ targets │ ┌────────────────┐ │ ││
│ │ (监控目标)│─────→ │ Retrieval │ │ Grafana ││
│ │ │ │ (周期性拉取) │ │ ││
│ │ - K8s │ └───────┬────────┘ └──────────┘│
│ │ - Node │ │ ┌──────────┐│
│ │ - App │ ┌───────┴────────┐ │ ││
│ │ - Exporter│ │ TSDB │ │Alertmanager│
│ │ - Pushgateway│ │ (时序存储) │ │ ││
│ └──────────┘ └───────┬────────┘ └──────────┘│
│ │ │
│ ┌───────┴────────┐ │
│ │ HTTP Server │ │
│ │ :9090 │ │
│ └────────────────┘ │
└─────────────────────────────────────────────────────────────┘架构三段式
| 阶段 | 说明 |
|---|---|
| 上游 | targets:监控目标(K8s 组件、各种 Exporter、Pushgateway、Service Discovery) |
| 中间 | Prometheus Server:Retrieval(周期性拉取)→ TSDB(时序存储)→ HTTP Server(对外暴露接口) |
| 下游 | Alertmanager(告警发送)、Grafana(可视化出图) |
三、二进制安装 Prometheus Server
1. 下载安装
# 下载(推荐 LTS 版本 2.53.x)
wget https://github.com/prometheus/prometheus/releases/download/v2.53.0/prometheus-2.53.0.linux-amd64.tar.gz
mkdir /monitor
tar xf prometheus-2.53.0.linux-amd64.tar.gz -C /monitor
ln -s /monitor/prometheus-2.53.0.linux-amd64 /monitor/prometheus
mkdir /monitor/prometheus/data2. 配置系统服务
cat > /usr/lib/systemd/system/prometheus.service << 'EOF'
[Unit]
Description=prometheus server daemon
[Service]
Restart=on-failure
ExecStart=/monitor/prometheus/prometheus \
--config.file=/monitor/prometheus/prometheus.yml \
--storage.tsdb.path=/monitor/prometheus/data \
--storage.tsdb.retention.time=30d \
--web.enable-lifecycle
[Install]
WantedBy=multi-user.target
EOF3. 启动服务
systemctl daemon-reload
systemctl enable prometheus.service
systemctl start prometheus.service
systemctl status prometheus
netstat -tunalp | grep 9090访问: http://IP:9090
4. 添加监控目标
编译测试程序(提供 /metrics 接口):
yum install golang -y
git clone https://github.com/prometheus/client_golang.git
cd client_golang/examples/random
export GO111MODULE=on
export GOPROXY=https://goproxy.cn
go build启动 3 个服务:
./random -listen-address=:8080 # http://localhost:8080/metrics
./random -listen-address=:8081 # http://localhost:8081/metrics
./random -listen-address=:8082 # http://localhost:8080/metrics配置 prometheus.yml:
scrape_configs:
- job_name: 'example-random'
scrape_interval: 5s
static_configs:
- targets: ['192.168.71.101:8080', '192.168.71.101:8081']
labels:
group: 'production'
- targets: ['192.168.71.101:8082']
labels:
group: 'canary'systemctl restart prometheus访问 http://192.168.71.101:9090 → Status → Targets 查看监控目标。
四、安装 Prometheus Server 到 K8s
1. 创建命名空间
kubectl create namespace monitor2. ConfigMap 配置
# prometheus-cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: prometheus-config
namespace: monitor
data:
prometheus.yml: |
global:
scrape_interval: 15s
scrape_timeout: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: "prometheus"
static_configs:
- targets: ["localhost:9090"]3. PV 和 PVC
# prometheus-pv-pvc.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: prometheus-local
labels:
app: prometheus
spec:
accessModes:
- ReadWriteOnce
capacity:
storage: 20Gi
storageClassName: local-storage
local:
path: /data/k8s/prometheus
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- master01
persistentVolumeReclaimPolicy: Retain
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: prometheus-data
namespace: monitor
spec:
selector:
matchLabels:
app: prometheus
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
storageClassName: local-storage4. RBAC 权限
# prometheus-rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: prometheus
namespace: monitor
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: prometheus
rules:
- apiGroups:
- ''
resources:
- nodes
- services
- endpoints
- pods
- nodes/proxy
verbs:
- get
- list
- watch
- apiGroups:
- 'extensions'
resources:
- ingresses
verbs:
- get
- list
- watch
- apiGroups:
- ''
resources:
- configmaps
- nodes/metrics
verbs:
- get
- nonResourceURLs:
- /metrics
verbs:
- get
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: prometheus
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: prometheus
subjects:
- kind: ServiceAccount
name: prometheus
namespace: monitor5. Deployment 部署
# prometheus-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: prometheus
namespace: monitor
spec:
selector:
matchLabels:
app: prometheus
template:
metadata:
labels:
app: prometheus
spec:
serviceAccountName: prometheus
securityContext:
runAsUser: 0
containers:
- image: prom/prometheus:v2.53.0
name: prometheus
args:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--storage.tsdb.retention.time=24h'
- '--web.enable-admin-api'
- '--web.enable-lifecycle'
ports:
- containerPort: 9090
name: http
volumeMounts:
- mountPath: '/etc/prometheus'
name: config-volume
- mountPath: '/prometheus'
name: data
resources:
requests:
cpu: 100m
memory: 512Mi
limits:
cpu: 100m
memory: 512Mi
volumes:
- name: data
persistentVolumeClaim:
claimName: prometheus-data
- configMap:
name: prometheus-config
name: config-volume6. Service 暴露
# prometheus-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: prometheus
namespace: monitor
spec:
selector:
app: prometheus
type: NodePort
ports:
- name: web
port: 9090
targetPort: 90907. 部署命令
# 在 PV 所亲和的节点上创建目录
mkdir /data/k8s/prometheus
kubectl apply -f prometheus-cm.yaml
kubectl apply -f prometheus-pv-pvc.yaml
kubectl apply -f prometheus-rbac.yaml
kubectl apply -f prometheus-deploy.yaml
kubectl apply -f prometheus-svc.yaml
# 停掉二进制部署的 prometheus
systemctl stop prometheus
systemctl disable prometheus8. 热更新配置
修改 ConfigMap 后执行:
# 获取 prometheus pod IP
kubectl -n monitor get pods -o wide
# 热重载配置
curl -X POST "http://<PROMETHEUS_POD_IP>:9090/-/reload"五、监控应用软件
监控目标分类
| 类别 | 监控对象 | 方式 |
|---|---|---|
| 应用自带 /metrics | Redis、MySQL、K8s 组件 | 直接配置 |
| 无 /metrics 接口 | 大多数应用 | 部署对应 Exporter |
5.1 监控 K8s 组件(自带接口)
- job_name: "coredns"
static_configs:
- targets: ["kube-dns.kube-system.svc.cluster.local:9153"]5.2 监控 Redis(使用 Exporter)
安装 Redis:
yum install redis -y
sed -ri 's/bind 127.0.0.1/bind 0.0.0.0/g' /etc/redis.conf
sed -ri 's/port 6379/port 16379/g' /etc/redis.conf
echo "requirepass 123456" >> /etc/redis.conf
systemctl restart redis安装 Redis Exporter:
wget https://github.com/oliver006/redis_exporter/releases/download/v1.61.0/redis_exporter-v1.61.0.linux-amd64.tar.gz
tar xf redis_exporter-v1.61.0.linux-amd64.tar.gz
mv redis_exporter /usr/bin/
cat > /usr/lib/systemd/system/redis_exporter.service << 'EOF'
[Unit]
Description=Redis Exporter
After=network.target
[Service]
ExecStart=/usr/bin/redis_exporter \
--redis.addr=redis://127.0.0.1:16379 \
--redis.password=123456 \
--web.listen-address=0.0.0.0:9122
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl restart redis_exporterPrometheus 配置:
- job_name: "redis-server"
static_configs:
- targets: ["192.168.71.101:9122"]5.3 监控 MySQL
安装 MySQL:
yum install mariadb-server -y
systemctl start mariadb
# 创建账号
mysql -e "CREATE USER 'mysql_exporter'@'localhost' IDENTIFIED BY '123456'"
mysql -e "GRANT ALL ON *.* TO 'mysql_exporter'@'localhost'"
cat >> /etc/my.cnf << 'EOF'
[client]
user=mysql_exporter
password=123456
EOF安装 MySQL Exporter:
wget https://github.com/prometheus/mysqld_exporter/releases/download/v0.15.1/mysqld_exporter-0.15.1.linux-amd64.tar.gz
tar xf mysqld_exporter-0.15.1.linux-amd64.tar.gz
mv mysqld_exporter /usr/bin/
cat > /etc/systemd/system/mysqld_exporter.service << 'EOF'
[Unit]
Description=Prometheus MySQL Exporter
After=network.target
[Service]
ExecStart=/usr/bin/mysqld_exporter --config.my-cnf=/etc/my.cnf
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl restart mysqld_exporterPrometheus 配置:
- job_name: "mysql-server"
static_configs:
- targets: ["192.168.71.102:9104"]5.4 Sidecar 模式监控 K8s 中的应用
# redis-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
namespace: monitor
spec:
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:4
ports:
- containerPort: 6379
- name: redis-exporter
image: oliver006/redis_exporter:latest
ports:
- containerPort: 9121
---
apiVersion: v1
kind: Service
metadata:
name: redis
namespace: monitor
spec:
selector:
app: redis
ports:
- name: redis
port: 6379
targetPort: 6379
- name: prom
port: 9121
targetPort: 9121Prometheus 配置(使用 Service 名字):
- job_name: 'redis'
static_configs:
- targets: ['redis:9121']六、监控物理节点(node-exporter)
二进制安装(每个节点都要)
wget https://github.com/prometheus/node_exporter/releases/download/v1.3.1/node_exporter-1.3.1.linux-amd64.tar.gz
tar xf node_exporter-1.3.1.linux-amd64.tar.gz
mv node_exporter /usr/bin/
cat > /usr/lib/systemd/system/node_exporter.service << 'EOF'
[Unit]
Description=Prometheus Node Exporter
After=network.target
[Service]
ExecStart=/usr/bin/node_exporter
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl restart node_exporterPrometheus 配置(手动):
- job_name: "node-exporter"
static_configs:
- targets: ["192.168.71.101:9100", "192.168.71.102:9100", "192.168.71.103:9100"]K8s 部署(DaemonSet 方式)
# prometheus-node-exporter.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-exporter
namespace: monitor
spec:
selector:
matchLabels:
app: node-exporter
template:
metadata:
labels:
app: node-exporter
spec:
hostPID: true
hostIPC: true
hostNetwork: true
tolerations:
- operator: 'Exists'
nodeSelector:
kubernetes.io/os: linux
containers:
- name: node-exporter
image: quay.io/prometheus/node-exporter:latest
args:
- --web.listen-address=$(HOSTIP):9100
- --path.procfs=/host/proc
- --path.sysfs=/host/sys
- --path.rootfs=/host/root
- --no-collector.hwmon
- --no-collector.nfs
- --no-collector.nfsd
- --no-collector.nvme
- --no-collector.dmi
- --no-collector.arp
- --collector.filesystem.ignored-mount-points=^/(dev|proc|sys|var/lib/containerd/.+|/var/lib/docker/.+|var/lib/kubelet/pods/.+)($|/)
- --collector.filesystem.ignored-fs-types=^(autofs|binfmt_misc|cgroup|configfs|debugfs|devpts|devtmpfs|fusectl|hugetlbfs|mqueue|overlay|proc|procfs|pstore|rpc_pipefs|securityfs|sysfs|tracefs)$
ports:
- containerPort: 9100
env:
- name: HOSTIP
valueFrom:
fieldRef:
fieldPath: status.hostIP
resources:
requests:
cpu: 150m
memory: 180Mi
limits:
cpu: 150m
memory: 180Mi
volumeMounts:
- name: proc
mountPath: /host/proc
- name: sys
mountPath: /host/sys
- name: root
mountPath: /host/root
mountPropagation: HostToContainer
readOnly: true
volumes:
- name: proc
hostPath:
path: /proc
- name: dev
hostPath:
path: /dev
- name: sys
hostPath:
path: /sys
- name: root
hostPath:
path: /Prometheus 配置(自动发现):
- job_name: 'nodes'
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_(.+)七、采集 K8s 集群指标
监控 kubelet/cAdvisor
- job_name: 'kubelet'
kubernetes_sd_configs:
- role: node
scheme: https
tls_config:
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
insecure_skip_verify: true
bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
relabel_configs:
- action: labelmap
regex: __meta_kubernetes_node_label_(.+)监控容器指标(cAdvisor)
- job_name: 'kubernetes-cadvisor'
kubernetes_sd_configs:
- role: node
scheme: https
tls_config:
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
insecure_skip_verify: true
bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
relabel_configs:
- action: labelmap
regex: __meta_kubernetes_node_label_(.+)
- source_labels: [__meta_kubernetes_node_name]
regex: (.+)
replacement: /metrics/cadvisor
target_label: __metrics_path__cAdvisor 详解
cAdvisor = Container Advisor,K8s 内置的容器监控组件,自动采集容器运行指标。
┌─────────────────────────────────────────────────────────┐
│ Node │
│ ┌─────────────────────────────────────────┐ │
│ │ kubelet │ │
│ │ ┌─────────────────────────────┐ │ │
│ │ │ cAdvisor │ │ │
│ │ │ (kubelet 内置组件) │ │ │
│ │ └─────────────────────────────┘ │ │
│ │ ↑ │ │
│ │ 采集本节点所有容器的指标 │ │
│ └─────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘自动发现与认证流程
# 1. 自动发现节点(role: node)
kubernetes_sd_configs:
- role: node
# 自动获取:
# __address__ = 192.168.71.101:10250
# __meta_kubernetes_node_name = node1# 2. HTTPS + Token 认证
scheme: https
tls_config:
ca_file: /var/run/secrets/.../ca.crt
insecure_skip_verify: true
bearer_token_file: /var/run/secrets/.../token认证流程:
Prometheus 用 ServiceAccount 运行
→ K8s 自动挂载 token 到 Pod 内
→ Prometheus 访问 kubelet 时带上 Bearer Token
→ kubelet 验证 Token 权限
→ 有权限返回数据,无权限返回 403relabel_configs 详解
第一步:复制 Node 标签
- action: labelmap
regex: __meta_kubernetes_node_label_(.+)K8s Node 标签:
kubernetes.io/os: linux
hostname: master01
Prometheus 标签:
os: linux
hostname: master01第二步:修改采集路径
- source_labels: [__meta_kubernetes_node_name]
regex: (.+)
replacement: /metrics/cadvisor
target_label: __metrics_path__替换前:/metrics
替换后:/metrics/cadvisor最终采集地址生成
┌─────────────────────────────────────────────────────────┐
│ 最终采集地址生成过程 │
├─────────────────────────────────────────────────────────┤
│ │
│ 自动发现的 Node: │
│ __address__ = 192.168.71.101:10250 │
│ │
│ relabel 后: │
│ __metrics_path__ = /metrics │
│ ↓ │
│ __metrics_path__ = /metrics/cadvisor │
│ │
│ 最终地址: │
│ https://192.168.71.101:10250/metrics/cadvisor │
│ │
└─────────────────────────────────────────────────────────┘cAdvisor 返回的常用指标
container_cpu_usage_seconds_total # CPU 使用累计
container_memory_usage_bytes # 内存使用字节
container_network_receive_bytes_total # 网络接收字节累计
container_network_transmit_bytes_total # 网络发送字节累计
container_fs_reads_bytes_total # 磁盘读取字节累计
container_fs_writes_bytes_total # 磁盘写入字节累计
container_spec_memory_limit_bytes # 内存限制
container_spec_cpu_quota # CPU 配额常用 PromQL 查询
# CPU 使用率
100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
# 内存使用率
100 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100
# Pod CPU 使用率
sum(rate(container_cpu_usage_seconds_total{image!="",pod!=""}[5m])) by (namespace, pod)
# Pod 内存使用
container_memory_usage_bytes{image!="",pod!=""}
# 节点磁盘使用率
100 - (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) * 100八、总结
┌─────────────────────────────────────────────────────────┐
│ Prometheus 监控核心要点 │
├─────────────────────────────────────────────────────────┤
│ │
│ 监控分类: │
│ ├─ 黑盒监控:早于用户发现问题 │
│ └─ 白盒监控:提前预防 │
│ │
│ Prometheus 架构: │
│ ├─ 上游:targets(K8s/Node/Exporter) │
│ ├─ 中间:Retrieval → TSDB → HTTP Server │
│ └─ 下游:Grafana / Alertmanager │
│ │
│ 监控方式: │
│ ├─ 应用自带 /metrics → 直接配置 │
│ ├─ 无 /metrics → 部署对应 Exporter │
│ └─ K8s 集群 → ServiceMonitor / 自动发现 │
│ │
│ K8s 集群监控: │
│ ├─ kubelet:节点基础指标 │
│ ├─ cAdvisor:容器指标 │
│ ├─ kube-state-metrics:K8s 资源状态 │
│ └─ node-exporter:节点硬件/系统指标 │
│ │
│ 常用 Exporter: │
│ ├─ node-exporter:物理节点 │
│ ├─ redis_exporter:Redis │
│ └─ mysqld_exporter:MySQL │
│ │
└─────────────────────────────────────────────────────────┘九、补充说明
1. Prometheus Server 本质是什么
本质上是一个:时序数据库 + HTTP 服务器
┌─────────────────────────────────────┐
│ Prometheus Server │
├─────────────────────────────────────┤
│ │
│ 时序数据库(TSDB) │
│ = 存储带时间戳的数据 │
│ = 存的不是文档,是"指标随时间变化" │
│ │
│ HTTP 服务器 │
│ = 提供查询接口 │
│ = Grafana/Alertmanager 来调用 │
│ │
└─────────────────────────────────────┘| 不是 | 说明 |
|---|---|
| 不是消息队列 | 拉模式,不是推模式 |
| 不是传统数据库 | 不是存业务数据的 |
| 不是监控 agent | 它是服务端,不是装在每台机器上的采集器 |
Prometheus Server = 一个专门存"监控指标历史"的数据库 + 查询接口。
2. PV 和 PVC 为什么要分离
PV 和 PVC 分离是为了"解耦",让不同角色做不同的事。
| 对比 | 直接绑定 PV | PV + PVC 分离 |
|---|---|---|
| 开发者 | 要知道存储细节(路径、存储类型) | 只管申请"我需要 20Gi 存储" |
| 灵活性 | 差 | 好 |
| 换存储类型 | 要改 Pod 配置 | 换个 StorageClass 就行 |
类比:租房
- 直接绑定 PV = 买房子(不灵活)
- PV + PVC = 租房(灵活,Pod 删除后 PVC 可以回收)
3. ConfigMap 中 prometheus.yml: | 是什么意思
| 是 YAML 语法,表示"多行字符串"。
data:
prometheus.yml: | # 文件名
global: # 文件内容从这里开始
scrape_interval: 15s
scrape_configs:
- job_name: 'prometheus'展开后等价于:
data:
"prometheus.yml": "global:\n scrape_interval: 15s\n..."K8s 挂载后,Pod 里会看到文件:
/etc/prometheus/
└── prometheus.yml ← 文件内容就是 | 后面的内容4. redis_exporter 是谁开发的
开源项目,由个人维护。
| 信息 | 内容 |
|---|---|
| 作者 | Oliver |
| GitHub | oliver006/redis_exporter |
| Stars | 7000+ |
| 语言 | Go |
所有 Exporter 汇总: https://prometheus.io/docs/instrumenting/exporters/
5. node-exporter DaemonSet 配置详解
为什么要用 DaemonSet
Deployment:部署 N 个 Pod,不一定在哪
DaemonSet:每个节点都部署 1 个 Pod(自动保证)三个关键配置
(1)共享宿主机的命名空间:
hostPID: true # 容器能看到主机的进程
hostIPC: true # 容器能与主机共享 IPC
hostNetwork: true # 容器使用主机网络原因: 监控需要读取 /proc/stat、/proc/meminfo 等主机目录。
(2)挂载宿主机目录:
volumes:
- name: proc
hostPath:
path: /proc # 容器内 /host/proc → 主机 /proc这样容器内读取 /host/proc 就等于读取主机的 /proc。
(3)容忍所有污点:
tolerations:
- operator: 'Exists'作用: 让 Pod 能调度到 Master 节点(Master 有污点,普通 Pod 调度不上去)。
6. relabel_configs 原理
自动发现节点后,默认端口是 10250(kubelet),需要改成 9100(node-exporter)。
relabel_configs:
- source_labels: [__address__] # 原始值:192.168.71.101:10250
regex: '(.*):10250' # 匹配 :10250
replacement: '${1}:9100' # 替换成 :9100
target_label: __address__ # 重新赋值转换过程:
192.168.71.101:10250
↓ regex (.*):10250
192.168.71.101 (.*) 捕获
↓ replacement: ${1}:9100
192.168.71.101:9100 最终结果7. 自动发现 vs 手动配置
| 场景 | 用哪种 |
|---|---|
| K8s 集群内部的服务 | ✅ 自动发现(推荐) |
| K8s 集群外部的服务(物理机/虚拟机/云数据库) | ❌ 必须手动配置 |
┌─────────────────────────────────────────┐
│ K8s 集群(Prometheus 在这) │
│ │
│ 自动发现 → 只能发现集群内部的东西 │
│ │
└─────────────────────────────────────────┘
│
│ 集群外部(自动发现管不到)
▼
┌─────────────────────────────────────────┐
│ 物理机 MySQL、Redis │
│ 云数据库 RDS │
│ → 只能用 static_configs 手动配置 │
└─────────────────────────────────────────┘8. K8s 内部 DNS 访问格式
完整格式:
<service-name>.<namespace>.svc.<cluster-domain>| 部分 | 示例 |
|---|---|
<service-name> | redis |
<namespace> | monitor |
svc | 固定写法 |
<cluster-domain> | cluster.local |
不同写法:
# 同命名空间
redis:6379
# 不同命名空间
redis.monitor:6379
redis.monitor.svc.cluster.local:63799. 副本调度会到同一节点吗
默认不会,K8s 调度器会尽量分散。
Deployment replicas: 4
理想情况:
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Node A │ │ Node B │ │ Node C │
│ Pod 1 │ │ Pod 2 │ │ Pod 3 │
│ │ │ │ │ Pod 4 │
└─────────┘ └─────────┘ └─────────┘但如果只有 2 个 Node,只能分散到 2 个节点上。
10. 制作系统服务是什么意思
让程序可以用 systemctl 管理,开机自启。
# 制作后可以用这些命令
systemctl start prometheus # 启动
systemctl stop prometheus # 停止
systemctl restart prometheus # 重启
systemctl enable prometheus # 开机自启本质:把程序交给 systemd 管理。
八、K8s 监控详解
K8s 监控要做什么
| 监控对象 | 说明 |
|---|---|
| 组件服务状态 | kube-apiserver、kube-scheduler、kube-controller-manager、etcd、coredns |
| 业务服务状态 | 业务 Pod → Endpoint |
| K8s 资源状态 | Pod、DaemonSet、Deployment、Job、CronJob 等资源的状态 |
| 容器状态 | cAdvisor 内置于 kubelet,负责采集容器指标 |
监控业务服务的条件
- 业务服务必须暴露 /metrics 接口
- 创建一个 Service,Service 会自动生成 Endpoint 资源
- 让 Service 带上指定的注解或标签
annotations:
prometheus.io/scrape: "true" # 开启监控
prometheus.io/port: "9121" # 监控端口监控 API Server
- job_name: 'kubernetes-apiservers'
kubernetes_sd_configs:
- role: endpoints
scheme: https
tls_config:
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
insecure_skip_verify: true
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监控 kube-controller-manager
1. 创建 Service:
apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/component: kube-controller-manager
app.kubernetes.io/name: kube-controller-manager
k8s-app: kube-controller-manager
name: kube-controller-manager
namespace: kube-system
spec:
clusterIP: None
ports:
- name: https-metrics
port: 10257
targetPort: 10257
selector:
component: kube-controller-manager2. 修改默认监听地址:
# 每台 Master 节点都要改
vi /etc/kubernetes/manifests/kube-controller-manager.yaml
# 修改 --bind-address=127.0.0.1 → --bind-address=0.0.0.03. 添加监控配置:
- job_name: 'kube-controller-manager'
kubernetes_sd_configs:
- role: endpoints
scheme: https
tls_config:
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
insecure_skip_verify: true
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: kube-system;kube-controller-manager;https-metrics监控 etcd
1. 修改 etcd 静态配置:
# 每台 Master 节点都要改
vi /etc/kubernetes/manifests/etcd.yaml
# 添加 --listen-metrics-urls=http://127.0.0.1:23812. 创建 etcd Service:
apiVersion: v1
kind: Service
metadata:
namespace: kube-system
name: etcd
labels:
k8s-app: etcd
spec:
selector:
component: etcd
type: ClusterIP
clusterIP: None
ports:
- name: http
port: 2381
targetPort: 23813. 添加监控配置:
- job_name: 'etcd'
kubernetes_sd_configs:
- role: endpoints
scheme: http
relabel_configs:
- source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name]
action: keep
regex: kube-system;etcd;http自动发现业务服务
- job_name: 'kubernetes-endpoints'
kubernetes_sd_configs:
- role: endpoints
relabel_configs:
# 1. 只保留 prometheus.io/scrape="true" 的 Service
- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]
action: keep
regex: true
# 2. 从注解获取协议(http/https)
- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme]
action: replace
target_label: __scheme__
regex: (https?)
# 3. 从注解获取路径
- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path]
action: replace
target_label: __metrics_path__
regex: (.+)
# 4. 拼接 IP:Port
- source_labels: [__address__, __meta_kubernetes_service_annotation_prometheus_io_port]
action: replace
target_label: __address__
regex: ([^:]+)(?::\d+)?;(\d+)
replacement: $1:$2
# 5. 复制 Service 标签
- action: labelmap
regex: __meta_kubernetes_service_label_(.+)
# 6. 替换标签名
- source_labels: [__meta_kubernetes_namespace]
action: replace
target_label: kubernetes_namespace
- source_labels: [__meta_kubernetes_service_name]
action: replace
target_label: kubernetes_name
- source_labels: [__meta_kubernetes_pod_name]
action: replace
target_label: kubernetes_pod_name示例:Redis Service 自动发现
apiVersion: v1
kind: Service
metadata:
name: redis
namespace: monitor
annotations:
prometheus.io/scrape: 'true'
prometheus.io/port: '9121'
spec:
selector:
app: redis
ports:
- name: redis
port: 6379
targetPort: 6379
- name: prom
port: 9121
targetPort: 9121kube-state-metrics(监控 K8s 资源状态)
kube-state-metrics 擅长历史数据分析,不适合实时 HPA 决策。
# 1. 下载
git clone https://github.com/kubernetes/kube-state-metrics.git
cd kube-state-metrics/examples/standard
# 2. 修改 deployment.yaml 中的镜像地址(可选)
# 原镜像:registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.12.0
# 国内可改为阿里云镜像
# 3. Service 添加注解
apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/component: exporter
app.kubernetes.io/name: kube-state-metrics
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8080"
name: kube-state-metrics
namespace: kube-system
spec:
clusterIP: None
ports:
- name: http-metrics
port: 8080
targetPort: http-metrics
selector:
app.kubernetes.io/name: kube-state-metrics4. 部署:
kubectl apply -f service-account.yaml
kubectl apply -f cluster-role-binding.yaml
kubectl apply -f cluster-role.yaml
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml常用查询:
# 节点状态错误
kube_node_status_condition{condition="Ready", status="true"}==1
kube_node_status_condition{condition="Ready", status!="true"}==1
# 启动失败的 Pod
kube_pod_status_phase{phase=~"Failed|Unknown"}==1
# 最近 30 分钟有容器重启
changes(kube_pod_container_status_restarts_total[30m])>0九、PromQL 详解
数据格式
HELP node_cpu_seconds_total Seconds the CPUs spent in each mode.
TYPE node_cpu_seconds_total counter
node_cpu_seconds_total{cpu="0",mode="idle"} 3853.38
node_cpu_seconds_total{cpu="0",mode="iowait"} 0.26监控样本 = 指标 + 标签 + 值
| 部分 | 说明 | 示例 |
|---|---|---|
| 指标名 | 反映监控含义 | node_cpu_seconds_total |
| 标签 | 描述样本特征/维度 | {cpu="0",mode="idle"} |
| 值 | 样本的具体数值 | 3853.38 |
TSDB 时序数据库
Prometheus 将数据以时间序列保存在内存数据库中。
时间序列 = 同一指标按时间顺序存放的多个值
X轴 = 时间
Y轴 = 值样本的三个组成部分:
- 指标(metric):指标名 + 标签
- 时间戳(timestamp):精确到毫秒
- 样本值(value):float64 浮点数
<metric name>{<labels>} @ <timestamp> => <value>
http_request_total{status="200", method="GET"} @ 1434417560938 => 94355
http_request_total{status="200", method="GET"} @ 1434417561287 => 94334四种指标类型
1. Counter(计数器)
只增不减的计数器,适合累计值。
应用场景:
- CPU 使用时间
- 网络流量
- HTTP 请求总数
常用操作:
rate(node_cpu_seconds_total[5m]) # 增长率
topk(3, kubelet_http_requests_total) # 排名前32. Gauge(仪表盘)
可增可减的仪表盘,适合瞬时值。
应用场景:
- 内存使用量
- CPU 负载
- 当前连接数
常用操作:
delta(node_memory_MemFree_bytes[1h]) # 变化量
predict_linear(node_filesystem_free_bytes[1h], 4*3600) # 趋势预测3. Histogram(直方图)
将样本分段统计,用于分析数据分布。
# 示例:HTTP 请求延迟分布
http_request_duration_seconds_bucket{le="0.1"} 20 # 20 个请求 < 0.1s
http_request_duration_seconds_bucket{le="0.5"} 40 # 40 个请求 < 0.5s
http_request_duration_seconds_bucket{le="1"} 45 # 45 个请求 < 1s
http_request_duration_seconds_bucket{le="+Inf"} 50 # 总共 50 个请求
# 计算 99 分位数
histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])) by (le))4. Summary(摘要)
预先计算百分位数,提供实时延迟分布。
# 示例
go_gc_duration_seconds{quantile="0"} 2.1928e-05 # 0% 分位
go_gc_duration_seconds{quantile="0.25"} 3.225e-05 # 25% 分位
go_gc_duration_seconds{quantile="0.5"} 3.5691e-05 # 50% 分位(中位数)
go_gc_duration_seconds{quantile="0.75"} 3.7185e-05 # 75% 分位
go_gc_duration_seconds{quantile="0.99"} 0.001 # 99% 分位向量类型
| 类型 | 说明 | 示例 |
|---|---|---|
| 瞬时向量(Instant Vector) | 每个时间序列只有一个最新值 | node_cpu_seconds_total{mode="idle"} |
| 区间向量(Range Vector) | 每个时间序列包含一段时间的数据 | node_cpu_seconds_total{mode="idle"}[5m] |
| 标量(Scalar) | 单独的浮点数 | 100 |
PromQL 语法
标签过滤
# 精确匹配
node_cpu_seconds_total{instance="master01", mode="idle"}
# 不等于
node_cpu_seconds_total{instance!="node01"}
# 正则匹配
node_cpu_seconds_total{instance=~"master.*"}
# 正则不匹配
node_cpu_seconds_total{instance!~"master.*"}时间范围
# 默认(当前)
node_cpu_seconds_total{instance="master01"}
# 最近 5 分钟
node_cpu_seconds_total{instance="master01"}[5m]
# 偏移量(查过去)
node_cpu_seconds_total{instance="master01"} offset 5m
# 5 小时前到 5 分钟前的数据
node_cpu_seconds_total[5m] offset 5hrate vs irate
# rate:计算平均速率(稳定、平滑)
rate(node_cpu_seconds_total{mode="idle"}[5m])
# irate:计算瞬时速率(对变化敏感)
irate(node_cpu_seconds_total{mode="idle"}[5m])运算符
算术运算符
# 单位转换
node_memory_MemFree_bytes / 1024 / 1024 # 字节转 MB
# 磁盘读写总量
node_disk_read_bytes_total{device="sda"} + node_disk_written_bytes_total{device="sda"}
# 计算百分比
(node_memory_MemFree_bytes / node_memory_MemTotal_bytes) * 100比较运算符
# 内存使用率
(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > 80逻辑运算符
# and:同时满足
# or:满足其一
# unless:排除聚合运算
# sum:求和
sum(rate(node_cpu_seconds_total[5m]))
# count:计数
count(node_cpu_seconds_total)
# avg:平均值
avg(rate(node_cpu_seconds_total[5m])) by (instance)
# max/min:最大/最小值
max(node_network_receive_bytes_total) by (instance)
# topk/bottomk:最大/最小的 N 个
topk(3, node_cpu_seconds_total)
bottomk(5, node_memory_MemFree_bytes)
# by/without:按标签分组/排除
sum(rate(node_network_receive_bytes_total[5m])) by (instance)
sum(rate(node_network_receive_bytes_total[5m])) without (device)常用 PromQL 示例
# CPU 使用率
100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
# 内存使用率
100 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100
# Pod CPU 使用率
sum(rate(container_cpu_usage_seconds_total{image!=""}[5m])) by (namespace, pod)
# Pod 内存使用
sum(container_memory_usage_bytes{image!=""}) by (namespace, pod)
# HTTP 请求增长率
rate(http_requests_total[5m])
# 99 分位延迟
histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])) by (le))
# 预测磁盘空间(4 小时后)
predict_linear(node_filesystem_free_bytes{mountpoint="/"}[1h], 4*3600)长尾问题与 Histogram/Summary
长尾问题: 大部分请求正常,但少量请求极慢,平均值无法反映真实情况。
比如 100 个请求:
- 99 个请求 100ms
- 1 个请求 10s
平均值 = (99*0.1 + 10) / 100 = 199ms(看起来还行)
实际用户体验很差Histogram: 统计每个区间的请求数量,精确分析分布。 Summary: 预先计算百分位数,快速判断长尾。
# Histogram:查看 99% 请求在多少秒内完成
histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])) by (le))
# 结果:0.5s 表示 99% 请求 < 0.5s