Prometheus + Grafana / 容器日志收集
Prometheus 核心概念
Pull 模型
Prometheus 采用 Pull(拉取) 模式采集监控数据,与传统的 Push(推送)模式相反。Prometheus Server 定期通过 HTTP 端点从目标(Target)拉取指标数据。
┌─────────────────┐
│ Prometheus │
│ Server │
│ │
│ ┌───────────┐ │
│ │ 采集器 │──┼── HTTP GET /metrics ──→ 目标A(:9100)
│ └───────────┘ │
│ ┌───────────┐ │
│ │ 存储引擎 │ │
│ └───────────┘ │
│ ┌───────────┐ │
│ │ PromQL │ │
│ │ 查询引擎 │ │
│ └───────────┘ │
└─────────────────┘Pull 模型的优势:
- 故障隔离 — 监控系统主动拉取,目标宕机立即感知
- 去中心化 — 无需维护中心化推送队列
- 健康检测 — 拉取失败本身即为告警信号
TSDB 时序数据库
Prometheus 内置自研的 TSDB(Time Series Database),专门用于存储时序数据。
存储特性:
| 特性 | 说明 |
|---|---|
| 存储格式 | 自定义磁盘格式,基于 block 的追加写 |
| 压缩算法 | Gorilla 变种压缩,float64 压缩至平均 1.37 字节 |
| 数据保留 | 默认 15 天(可通过 --storage.tsdb.retention.time 配置) |
| 存储结构 | Block(2h 跨度)→ chunk(256 samples)→ sample |
| 内存映射 | mmap 方式加载索引,降低内存占用 |
数据模型
Prometheus 每条时间序列通过 metric name 和 labels 唯一标识。
<metric_name>{<label_name>=<label_value>, ...} <sample_value> <timestamp>| 组成部分 | 说明 | 示例 |
|---|---|---|
| Metric(指标名) | 标识监控指标含义 | http_requests_total |
| Label(标签) | 键值对维度,用于区分不同实例/路径/方法 | method="GET", status="200" |
| Sample(样本) | 具体的数值 + 时间戳 | 1024 @ 1710000000 |
示例:
http_requests_total{method="POST", handler="/api/order", instance="10.0.1.5:8080"} 2048 1710000000PromQL 基础
PromQL(Prometheus Query Language)是 Prometheus 的查询语言。
Instant Vector(瞬时向量)
某一时刻的指标值集合。
# 查询当前时刻所有 http_requests_total 指标
http_requests_total
# 带标签过滤
http_requests_total{job="api-server", method="GET"}
# 标签匹配操作符:=、!=、=~(正则匹配)、!~(正则排除)
http_requests_total{environment=~"prod|staging"}Range Vector(范围向量)
一段时间范围内的指标值集合。
# 过去 5 分钟的指标值
http_requests_total[5m]
# 可用时间单位:s(秒)、m(分)、h(时)、d(天)、w(周)
http_requests_total{job="api-server"}[1h]聚合运算
# 速率计算 — 常用在 Counter 类型上计算 QPS
rate(http_requests_total[5m])
# 增长率(适用于 counter 重置场景)
irate(http_requests_total[5m])
# 聚合操作:sum / avg / min / max / count / quantile
sum by (status) (rate(http_requests_total[5m]))
# 分位数
histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))
# 算术运算
(rate(http_requests_total[5m]) > 100)四种指标类型
| 类型 | 说明 | 值特点 | 适用场景 |
|---|---|---|---|
| Counter | 只增不减的计数器 | 单调递增 | 请求总数、错误次数、任务完成数 |
| Gauge | 可增可减的测量值 | 任意变化 | CPU 使用率、内存使用量、连接数 |
| Histogram | 直方图,分桶统计 | 多 bucket + sum + count | 请求延迟分布、响应大小分布 |
| Summary | 汇总,直接计算分位数 | 多 quantile + sum + count | 已知分位数需求的延迟统计 |
Counter 示例:
# HTTP 总请求数(只增)
http_requests_total{method="GET"} 10000
# 使用 rate() 计算每秒速率
rate(http_requests_total[1m])Gauge 示例:
# 当前内存使用量(可增可减)
node_memory_Active_bytes 8589934592Histogram 示例:
# 请求延迟分布在 [0.1, 0.5, 1, 2.5, 5, 10] 秒的桶中
http_request_duration_seconds_bucket{le="0.1"} 500
http_request_duration_seconds_bucket{le="0.5"} 1500
http_request_duration_seconds_bucket{le="+Inf"} 2000
http_request_duration_seconds_sum 850.0
http_request_duration_seconds_count 2000
# 计算 P95 延迟
histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))Summary 示例:
# 直接暴露分位数(由客户端计算)
rpc_duration_seconds{quantile="0.5"} 0.05
rpc_duration_seconds{quantile="0.9"} 0.12
rpc_duration_seconds{quantile="0.99"} 0.35
rpc_duration_seconds_sum 120.0
rpc_duration_seconds_count 2400Prometheus 部署
Docker Compose 完整示例
# docker-compose.yml
version: "3.8"
services:
prometheus:
image: prom/prometheus:v2.53.0
container_name: prometheus
restart: always
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
command:
- "--config.file=/etc/prometheus/prometheus.yml"
- "--storage.tsdb.path=/prometheus"
- "--storage.tsdb.retention.time=30d"
- "--web.console.libraries=/etc/prometheus/console_libraries"
- "--web.console.templates=/etc/prometheus/consoles"
- "--web.enable-lifecycle"
volumes:
prometheus_data:prometheus.yml 配置
# prometheus.yml
global:
scrape_interval: 15s # 采集间隔
evaluation_interval: 15s # 规则评估间隔(用于告警)
scrape_timeout: 10s # 采集超时
# 告警规则文件
rule_files:
- "alert_rules.yml"
# 采集目标配置
scrape_configs:
# Prometheus 自身监控
- job_name: "prometheus"
static_configs:
- targets: ["localhost:9090"]
labels:
service: "monitoring"
# Node Exporter — 主机监控
- job_name: "node_exporter"
scrape_interval: 10s
static_configs:
- targets:
- "10.0.1.10:9100" # 生产节点 1
- "10.0.1.11:9100" # 生产节点 2
- "10.0.1.12:9100" # 生产节点 3
labels:
env: "production"
# cAdvisor — 容器监控
- job_name: "cadvisor"
scrape_interval: 15s
static_configs:
- targets:
- "10.0.1.10:8080"
- "10.0.1.11:8080"
- "10.0.1.12:8080"
# JMX Exporter — Java 应用监控
- job_name: "jmx_exporter"
scrape_interval: 30s
metrics_path: "/metrics"
static_configs:
- targets:
- "10.0.1.20:5556" # 应用 A
- "10.0.1.21:5556" # 应用 B
labels:
app: "java-service"
# Kubernetes — 通过 K8s API 自动发现(kube-state-metrics)
- 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: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
action: replace
target_label: __address__
regex: (.+)(?::\d+)?;(\d+)
replacement: $1:$2
# Alertmanager 配置
alerting:
alertmanagers:
- static_configs:
- targets:
- "alertmanager:9093"监控目标
cAdvisor — 容器监控
cAdvisor(Container Advisor)由 Google 开源,用于收集容器的 CPU、内存、网络、磁盘等资源使用指标。
# docker-compose.yml 中 cAdvisor 配置
cadvisor:
image: gcr.io/cadvisor/cadvisor:v0.49.1
container_name: cadvisor
restart: always
ports:
- "8080:8080"
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
- /dev/disk/:/dev/disk:ro
privileged: true
devices:
- /dev/kmsg关键指标:
| 指标 | 含义 |
|---|---|
container_cpu_usage_seconds_total | 容器 CPU 累计使用秒数 |
container_memory_usage_bytes | 容器当前内存使用量 |
container_network_receive_bytes_total | 容器网络接收字节数 |
container_fs_usage_bytes | 容器文件系统使用量 |
Node Exporter — 主机监控
用于采集 Linux 主机系统指标。
# docker-compose.yml 中 Node Exporter 配置
node_exporter:
image: prom/node-exporter:v1.8.0
container_name: node_exporter
restart: always
ports:
- "9100:9100"
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- "--path.procfs=/host/proc"
- "--path.sysfs=/host/sys"
- "--path.rootfs=/rootfs"
- "--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)"关键指标:
| 指标 | 含义 |
|---|---|
node_cpu_seconds_total | CPU 时间(按 mode 分) |
node_memory_MemTotal_bytes | 总内存 |
node_memory_MemAvailable_bytes | 可用内存 |
node_disk_read_bytes_total | 磁盘读取字节数 |
node_network_receive_bytes_total | 网络接收字节数 |
node_load1 / node_load5 / node_load15 | 系统平均负载 |
常用 PromQL:
# CPU 使用率(排除 idle)
100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
# 内存使用率
(1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100
# 磁盘空间使用率
(node_filesystem_size_bytes{fstype=~"ext[34]|xfs"} - node_filesystem_free_bytes{fstype=~"ext[34]|xfs"})
/ node_filesystem_size_bytes{fstype=~"ext[34]|xfs"} * 100kube-state-metrics — K8s 集群监控
kube-state-metrics 监听 Kubernetes API Server,生成集群资源对象的状态指标。
# kube-state-metrics 部署示例 (Deployment)
apiVersion: apps/v1
kind: Deployment
metadata:
name: kube-state-metrics
namespace: monitoring
spec:
replicas: 1
selector:
matchLabels:
app: kube-state-metrics
template:
metadata:
labels:
app: kube-state-metrics
spec:
serviceAccountName: kube-state-metrics
containers:
- name: kube-state-metrics
image: registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.13.0
ports:
- containerPort: 8080
name: http-metrics
args:
- "--resources=deployments,pods,services,nodes,namespaces"关键指标:
| 指标 | 含义 |
|---|---|
kube_deployment_status_replicas_available | Deployment 可用副本数 |
kube_pod_container_status_restarts_total | Pod 重启次数 |
kube_node_status_condition | 节点状态条件 |
kube_deployment_status_replicas_unavailable | Deployment 不可用副本数 |
JMX Exporter — Java 应用监控
JMX Exporter 以 Java Agent 方式集成,将 JVM 指标暴露为 Prometheus 格式。
# prometheus-jmx-config.yml
---
startDelaySeconds: 0
hostPort: 127.0.0.1:5556
username:
password:
jmxUrl: service:jmx:rmi:///jndi/rmi://127.0.0.1:5556/jmxrmi
ssl: false
lowercaseOutputName: true
lowercaseOutputLabelNames: true
whitelistObjectNames:
- "java.lang:*"
- "java.lang.management:*"Java 启动参数:
java -javaagent:jmx_prometheus_javaagent-1.0.1.jar=5556:prometheus-jmx-config.yml \
-jar myapp.jar关键指标:
| 指标 | 含义 |
|---|---|
jvm_memory_bytes_used | JVM 已用内存(按区域:heap / nonheap) |
jvm_gc_collection_seconds_sum | GC 累计耗时 |
jvm_threads_current | 当前线程数 |
jvm_classes_loaded_classes | 已加载类数量 |
Grafana
安装配置
# docker-compose.yml 中 Grafana 配置
grafana:
image: grafana/grafana:11.1.0
container_name: grafana
restart: always
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=admin123
- GF_INSTALL_PLUGINS=grafana-clock-panel,grafana-piechart-panel
- GF_SERVER_ROOT_URL=http://localhost:3000
volumes:
- grafana_data:/var/lib/grafana
- ./grafana/provisioning:/etc/grafana/provisioning
depends_on:
- prometheus
volumes:
grafana_data:数据源添加
通过 provisioning 方式自动添加 Prometheus 数据源:
# grafana/provisioning/datasources/datasources.yml
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
access: proxy
url: http://prometheus:9090
isDefault: true
editable: true
- name: Loki
type: loki
access: proxy
url: http://loki:3100
editable: true仪表盘导入
Grafana 社区提供大量现成仪表盘模板,通过 Dashboard ID 快速导入。
| 监控场景 | Dashboard ID | 说明 |
|---|---|---|
| Node Exporter 主机监控 | 1860 | 最流行的 Linux 主机监控模板 |
| cAdvisor 容器监控 | 14282 | 容器资源使用率监控 |
| Kubernetes 集群监控 | 315 | K8s 集群节点/Pod 概览 |
| Spring Boot / JVM 监控 | 4701 | JVM 内存、GC、线程监控 |
| 业务应用概览 | 6417 | 通用 HTTP 服务监控 |
导入方式(命令行):
# 通过 Grafana HTTP API 导入
curl -X POST http://admin:admin123@localhost:3000/api/dashboards/import \
-H "Content-Type: application/json" \
-d '{"dashboard":{"id":null,"uid":null},"overwrite":true,"inputs":[{"name":"DS_PROMETHEUS","type":"datasource","pluginId":"prometheus","value":"Prometheus"}]}'告警规则配置
Grafana 支持在 UI 中配置告警规则,也可通过 provisioning 文件自动配置:
# grafana/provisioning/alerting/alert_rules.yml
apiVersion: 1
groups:
- name: host_alerts
interval: 30s
rules:
- uid: high_cpu_usage
title: "CPU 使用率过高"
condition: "A"
data:
- refId: "A"
datasourceUid: "prometheus"
model:
expr: "100 - (avg by (instance) (rate(node_cpu_seconds_total{mode='idle'}[5m])) * 100) > 80"
intervalMs: 30000
noDataState: "Alerting"
execErrState: "Alerting"
for: 5m
annotations:
summary: "实例 {{ $labels.instance }} CPU 使用率超过 80%"
labels:
severity: "warning"
- uid: high_memory_usage
title: "内存使用率过高"
condition: "A"
data:
- refId: "A"
datasourceUid: "prometheus"
model:
expr: "(1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100 > 85"
intervalMs: 30000
noDataState: "Alerting"
execErrState: "Alerting"
for: 5m
annotations:
summary: "实例 {{ $labels.instance }} 内存使用率超过 85%"
labels:
severity: "warning"
- name: container_alerts
interval: 30s
rules:
- uid: container_restart
title: "容器频繁重启"
condition: "A"
data:
- refId: "A"
datasourceUid: "prometheus"
model:
expr: "rate(container_last_seen{name!=\"\"}[5m]) > 0"
intervalMs: 30000
noDataState: "OK"
execErrState: "OK"
for: 1m
annotations:
summary: "容器 {{ $labels.name }} 正在重启"
labels:
severity: "critical"EFK(Elasticsearch + Filebeat / Fluentd + Kibana)
Docker Compose 完整部署
# docker-compose-efk.yml
version: "3.8"
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.14.0
container_name: elasticsearch
restart: always
environment:
- cluster.name=efk-cluster
- node.name=es-node-1
- discovery.type=single-node
- bootstrap.memory_lock=true
- "ES_JAVA_OPTS=-Xms1g -Xmx1g"
- xpack.security.enabled=false
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- es_data:/usr/share/elasticsearch/data
ports:
- "9200:9200"
- "9300:9300"
kibana:
image: docker.elastic.co/kibana/kibana:8.14.0
container_name: kibana
restart: always
ports:
- "5601:5601"
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
- I18N_LOCALE=zh-CN
depends_on:
- elasticsearch
filebeat:
image: docker.elastic.co/beats/filebeat:8.14.0
container_name: filebeat
restart: always
user: root
volumes:
- ./filebeat.yml:/usr/share/filebeat/filebeat.yml:ro
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
depends_on:
- elasticsearch
volumes:
es_data:Filebeat 配置采集容器日志
# filebeat.yml
filebeat.inputs:
- type: container
enabled: true
paths:
- /var/lib/docker/containers/*/*.log
json.message_key: log
json.overwrite_keys: true
json.add_error_key: true
json.keys_under_root: true
processors:
- add_docker_metadata:
host: "unix:///var/run/docker.sock"
match_fields:
container_id: container.id
- decode_json_fields:
fields: ["message"]
target: ""
overwrite_keys: true
- timestamp:
field: time
layouts:
- "2006-01-02T15:04:05.000Z"
- "2006-01-02T15:04:05.000Z07:00"
test:
- "2024-03-10T08:30:15.123Z"
output.elasticsearch:
hosts: ["elasticsearch:9200"]
index: "filebeat-%{[agent.version]}-%{+yyyy.MM.dd}"
# 使用 ILM 管理索引生命周期
ilm.enabled: true
ilm.rollover_alias: "filebeat"
ilm.pattern: "{now/d}-000001"
setup.template.settings:
index.number_of_shards: 1
index.number_of_replicas: 0
# 加载默认仪表盘到 Kibana(可选)
setup.dashboards.enabled: false
# 确保索引模板被创建
setup.template.enabled: true
setup.template.name: "filebeat"
setup.template.pattern: "filebeat-*"Kibana 索引模式和数据可视化
创建索引模式(Index Pattern):
- 打开 Kibana -> Stack Management -> Index Patterns
- 点击 "Create index pattern"
- 输入
filebeat-*匹配所有 Filebeat 索引 - 选择
@timestamp作为时间字段 - 完成后即可在 Discover 中搜索日志
常用可视化:
| 可视化类型 | 配置 | 用途 |
|---|---|---|
| 柱状图 | X 轴:@timestamp(按小时聚合),Y 轴:计数 | 日志数量时间分布 |
| 饼图 | 分片:container.name.keyword | 各容器日志量占比 |
| 数据表 | 列:message、container.name、@timestamp | 日志明细检索 |
| 折线图 | X 轴:@timestamp,Y 轴:平均值(event.duration) | 应用响应时间趋势 |
Loki + Promtail
轻量级日志方案
Loki 是 Grafana Labs 推出的日志聚合系统,受 Prometheus 设计启发,使用相同的标签机制。
核心优势:
- 不对日志内容建立全文索引,而是通过标签索引
- 与 Prometheus 相同的标签模型,实现指标与日志的无缝切换
- 存储成本低,适合 Kubernetes 环境
架构
┌───────────┐
│ 应用服务 │
└─────┬─────┘
│ 日志文件
┌─────┴─────┐
│ Promtail │ ← 日志采集器(DaemonSet 模式)
└─────┬─────┘
│ HTTP Push 日志流
┌─────┴─────┐
│ Loki │ ← 聚合、存储、查询
│ ┌───────┐ │
│ │Ingester│ │
│ │Querier │ │
│ └───────┘ │
└─────┬─────┘
│ LogQL 查询
┌─────┴─────┐
│ Grafana │ ← 统一 UI 展示
└───────────┘Docker Compose 部署
# docker-compose-loki.yml
version: "3.8"
services:
loki:
image: grafana/loki:3.1.0
container_name: loki
restart: always
ports:
- "3100:3100"
volumes:
- ./loki-config.yml:/etc/loki/loki-config.yml
- loki_data:/loki
command:
- "-config.file=/etc/loki/loki-config.yml"
promtail:
image: grafana/promtail:3.1.0
container_name: promtail
restart: always
user: root
volumes:
- ./promtail-config.yml:/etc/promtail/promtail-config.yml
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- /var/log:/var/log:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
command:
- "-config.file=/etc/promtail/promtail-config.yml"
volumes:
loki_data:Loki 配置
# loki-config.yml
auth_enabled: false
server:
http_listen_port: 3100
grpc_listen_port: 9095
common:
instance_addr: 127.0.0.1
path_prefix: /loki
storage:
filesystem:
chunks_directory: /loki/chunks
rules_directory: /loki/rules
replication_factor: 1
ring:
kvstore:
store: inmemory
# 存储配置
schema_config:
configs:
- from: 2024-01-01
store: tsdb
object_store: filesystem
schema: v13
index:
prefix: index_
period: 24h
# 查询超时
query_range:
results_cache:
cache:
embedded_cache:
enabled: true
max_size_mb: 100
# 限制配置
limits_config:
max_query_length: 721h # 30 天
max_entries_limit_per_query: 5000
reject_old_samples: true
reject_old_samples_max_age: 168h
# 压缩保留
compactor:
working_directory: /loki/compactor
compactor_ring:
kvstore:
store: inmemory
retention_enabled: true
retention_delete_delay: 2h
retention_delete_worker_count: 150
# 数据保留(通过 compactor 实现)
# 注意版本:Loki 3.x 使用 compactor 配置 retentionPromtail 采集日志配置
# promtail-config.yml
server:
http_listen_port: 9080
grpc_listen_port: 0
positions:
filename: /tmp/positions.yaml
clients:
- url: http://loki:3100/loki/api/v1/push
scrape_configs:
# 采集 Docker 容器日志
- job_name: docker-containers
docker_sd_configs:
- host: "unix:///var/run/docker.sock"
refresh_interval: 15s
filters:
- name: label
values: ["logging=promtail"]
relabel_configs:
- source_labels: ["__meta_docker_container_name"]
regex: "/(.*)"
target_label: "container"
- source_labels: ["__meta_docker_container_log_stream"]
target_label: "log_stream"
- action: labelmap
regex: "__meta_docker_container_label_(.+)"
- source_labels: ["__meta_docker_container_id"]
target_label: "container_id"
# 采集主机日志文件
- job_name: system-logs
static_configs:
- targets: ["localhost"]
labels:
job: "system"
__path__: "/var/log/*.log"
# 采集指定应用日志目录
- job_name: application-logs
static_configs:
- targets: ["localhost"]
labels:
job: "application"
__path__: "/var/log/app/**/*.log"
pipeline_stages:
# 使用正则解析日志行,提取字段
- regex:
expression: '^(?P<timestamp>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2},\d{3})\s+(?P<level>[A-Z]+)\s+(?P<logger>[\w.]+)\s+-\s+(?P<message>.*)$'
- labels:
level:
- timestamp:
source: timestamp
format: "2006-01-02 15:04:05,000"Grafana 集成 Loki 查询
在 Grafana 中配置 Loki 数据源后,可通过 LogQL 查询日志。
LogQL 基础:
# 查询所有日志
{job="application"}
# 带标签过滤
{container="api-server"} |= "ERROR"
# 排除关键词
{container="api-server"} != "healthcheck"
# 正则匹配
{container="api-server"} |~ "(NullPointerException|OutOfMemoryError)"
# 解析 JSON 日志字段
{container="api-server"} | json | response_time > 1000
# 聚合统计
rate({container="api-server"} |= "ERROR" [5m])Grafana 面板集成:
- 在 Dashboard 中添加 Loki 数据源的 Logs 面板
- 使用 LogQL 查询,支持实时流日志
- 点击日志行可直接跳转到关联的指标面板
- 通过
{instance="..."}标签从 Prometheus 指标面板穿透到对应主机的日志
告警
Alertmanager 配置
Alertmanager 负责处理 Prometheus 发送的告警,支持分组、抑制、静默和路由。
# alertmanager.yml
global:
resolve_timeout: 5m
smtp_smarthost: 'smtp.example.com:587'
smtp_from: 'alert@example.com'
smtp_auth_username: 'alert@example.com'
smtp_auth_password: 'password'
smtp_require_tls: true
# 根路由
route:
receiver: 'default'
group_by: ['alertname', 'severity']
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
routes:
# 严重告警发送到紧急通道
- receiver: 'critical'
match:
severity: critical
repeat_interval: 10m
# 中间件告警发送到中间件团队
- receiver: 'middleware'
match:
job: node_exporter
group_by: ['instance', 'alertname']
# 接收器配置
receivers:
- name: 'default'
email_configs:
- to: 'team@example.com'
- name: 'critical'
email_configs:
- to: 'oncall@example.com'
- to: 'manager@example.com'
webhook_configs:
- url: 'http://dingtalk-webhook:8080/alert'
- name: 'middleware'
email_configs:
- to: 'middleware-team@example.com'
# 抑制规则 — 当某实例宕机时,抑制其衍生告警
inhibit_rules:
- source_match:
severity: critical
target_match:
severity: warning
equal: ['instance', 'job']Docker Compose 部署 Alertmanager
# docker-compose.yml 中 Alertmanager 配置
alertmanager:
image: prom/alertmanager:v0.27.0
container_name: alertmanager
restart: always
ports:
- "9093:9093"
volumes:
- ./alertmanager.yml:/etc/alertmanager/alertmanager.yml
- alertmanager_data:/alertmanager
command:
- "--config.file=/etc/alertmanager/alertmanager.yml"
- "--storage.path=/alertmanager"
- "--web.external-url=http://localhost:9093"
volumes:
alertmanager_data:告警分组、抑制与静默
分组(Grouping):
- 将同类告警合并为一条通知,避免告警风暴
- 通过
group_by指定分组维度,如['alertname', 'severity'] group_wait:分组等待时间(攒一批再发)group_interval:同组告警再次发送间隔
抑制(Inhibition):
- 当某个告警已触发,抑制其他相关的低优先级告警
- 典型场景:主机宕机时,抑制该主机上所有服务的不可达告警
- 通过
inhibit_rules配置源和目标匹配规则
静默(Silences):
- 在 Alertmanager UI(:9093)中创建静默规则
- 按标签匹配,可设置过期时间
- 适用于计划内维护窗口
Prometheus 告警规则
# alert_rules.yml
groups:
- name: host_alerts
interval: 30s
rules:
- alert: HostDown
expr: up == 0
for: 1m
labels:
severity: critical
annotations:
summary: "主机 {{ $labels.instance }} 离线"
description: "{{ $labels.instance }} 已无法从 Prometheus 访问,持续超过 1 分钟"
- alert: HighCpuUsage
expr: 100 - (avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
for: 5m
labels:
severity: warning
annotations:
summary: "主机 {{ $labels.instance }} CPU 使用率过高"
description: "CPU 使用率: {{ $value | humanizePercentage }}"
- alert: DiskSpaceFull
expr: (node_filesystem_size_bytes{fstype=~"ext[34]|xfs"} - node_filesystem_free_bytes{fstype=~"ext[34]|xfs"}) / node_filesystem_size_bytes{fstype=~"ext[34]|xfs"} * 100 > 90
for: 5m
labels:
severity: critical
annotations:
summary: "主机 {{ $labels.instance }} 磁盘空间即将用尽"
description: "磁盘使用率: {{ $value | humanizePercentage }}"
- name: container_alerts
interval: 30s
rules:
- alert: ContainerOOM
expr: container_oom_events_total > 0
for: 0s
labels:
severity: critical
annotations:
summary: "容器 {{ $labels.name }} 发生 OOM"
- alert: ContainerRestartHigh
expr: rate(container_last_seen{name!=""}[15m]) > 0.01
for: 2m
labels:
severity: warning
annotations:
summary: "容器 {{ $labels.name }} 重启频繁"钉钉通知配置
通过 Webhook 转发 Alertmanager 告警到钉钉:
# 使用 prometheus-alertmanager-dingtalk 或自建 webhook
dingtalk-webhook:
image: timonwong/prometheus-webhook-dingtalk:v2.1.0
container_name: dingtalk-webhook
restart: always
ports:
- "8060:8060"
environment:
- PROME_URL=http://alertmanager:9093
- DINGTALK_TOKEN=your_dingtalk_robot_token
command:
- "--ding.profile=webhook1=https://oapi.dingtalk.com/robot/send?access_token=your_dingtalk_robot_token"
- "--log.level=info"Alertmanager 配置 webhook 接收器:
receivers:
- name: 'critical'
webhook_configs:
- url: 'http://dingtalk-webhook:8060/dingtalk/webhook1/send'
send_resolved: true邮件通知配置
# Alertmanager 邮件接收器
receivers:
- name: 'email-notify'
email_configs:
- to: 'oncall@example.com'
send_resolved: true
headers:
subject: '[{{ .GroupLabels.severity }}] Prometheus 告警 - {{ .GroupLabels.alertname }}'
html: >
<h2>Prometheus 告警通知</h2>
<table border="1" cellpadding="8" cellspacing="0">
<tr><th>告警名称</th><td>{{ .GroupLabels.alertname }}</td></tr>
<tr><th>告警级别</th><td>{{ .GroupLabels.severity }}</td></tr>
<tr><th>告警时间</th><td>{{ .StartsAt }}</td></tr>
</table>
<h3>告警详情</h3>
<pre>{{ range .Alerts }}{{ .Annotations.description }}
{{ end }}</pre>对比:EFK vs Loki + Grafana
| 对比维度 | EFK(Elasticsearch + Filebeat + Kibana) | Loki + Grafana |
|---|---|---|
| 存储引擎 | Elasticsearch 全文索引引擎 | Loki 类 Prometheus 的标签索引 |
| 索引方式 | 倒排索引,对日志内容建立全文索引 | 仅索引标签,日志内容不建索引 |
| 查询语言 | DSL(基于 JSON 的查询语法) | LogQL(类 PromQL 语法) |
| 资源消耗 | 高 — ES 需要大量内存和磁盘 IO | 低 — 适合资源受限环境 |
| 存储成本 | 高,日志全文索引占用较大磁盘 | 低,仅存储原始日志和标签 |
| 部署复杂度 | 中等 — ES 集群需调优 | 简单 — 单二进制文件即可运行 |
| 全文搜索 | 支持 — 适合复杂文本检索 | 部分支持 — 通过 LogQL 过滤器 |
| 数据保留 | ES ILM 生命周期管理 | 通过 compactor 配置保留周期 |
| 与 Prometheus 集成 | 需单独关联,通过标签关联 | 原生集成 — 共享标签模型 |
| K8s 适配 | 良好 — 可通过 Elastic Operator 部署 | 优秀 — DaemonSet + 自动发现 |
| 适合场景 | 业务日志分析、审计日志、复杂查询 | 基础设施日志、K8s 容器日志快速排查 |
| 扩展性 | 集群模式支持 PB 级 | 通过微服务组件水平扩展 |
| 可视化 | Kibana 功能丰富,支持图表和仪表盘 | Grafana 统一指标和日志视图 |
| 典型部署资源 | ES:4C 8G 起,Kibana:2C 4G | Loki:2C 4G,Promtail:256M |
| 社区生态 | 成熟,ELK 生态完善 | 快速发展,Grafana 生态加持 |
选型建议:
- 选择 EFK:需要全文搜索、复杂聚合查询、业务日志审计、历史日志长期归档
- 选择 Loki:已有 Grafana 监控体系、Kubernetes 环境、日志主要用于故障排查、希望降低存储成本
- 混合方案:EFK 处理业务日志(7天热存 + 30天冷存),Loki 处理容器和系统日志(30天),通过 Grafana 统一展示
完整 Docker Compose 整合示例
以下将所有组件整合到一个完整的监控和日志收集栈中:
# docker-compose-monitoring.yml
version: "3.8"
services:
# ========== Prometheus 生态 ==========
prometheus:
image: prom/prometheus:v2.53.0
container_name: prometheus
restart: always
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- ./alert_rules.yml:/etc/prometheus/alert_rules.yml
- prometheus_data:/prometheus
command:
- "--config.file=/etc/prometheus/prometheus.yml"
- "--storage.tsdb.retention.time=30d"
- "--web.enable-lifecycle"
alertmanager:
image: prom/alertmanager:v0.27.0
container_name: alertmanager
restart: always
ports:
- "9093:9093"
volumes:
- ./alertmanager.yml:/etc/alertmanager/alertmanager.yml
- alertmanager_data:/alertmanager
node_exporter:
image: prom/node-exporter:v1.8.0
container_name: node_exporter
restart: always
ports:
- "9100:9100"
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- "--path.procfs=/host/proc"
- "--path.sysfs=/host/sys"
- "--path.rootfs=/rootfs"
cadvisor:
image: gcr.io/cadvisor/cadvisor:v0.49.1
container_name: cadvisor
restart: always
ports:
- "8080:8080"
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
privileged: true
# ========== Grafana ==========
grafana:
image: grafana/grafana:11.1.0
container_name: grafana
restart: always
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=admin123
volumes:
- grafana_data:/var/lib/grafana
- ./grafana/provisioning/datasources:/etc/grafana/provisioning/datasources
# ========== Loki 日志方案 ==========
loki:
image: grafana/loki:3.1.0
container_name: loki
restart: always
ports:
- "3100:3100"
volumes:
- ./loki-config.yml:/etc/loki/loki-config.yml
- loki_data:/loki
promtail:
image: grafana/promtail:3.1.0
container_name: promtail
restart: always
user: root
volumes:
- ./promtail-config.yml:/etc/promtail/promtail-config.yml
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- /var/log:/var/log:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
volumes:
prometheus_data:
alertmanager_data:
grafana_data:
loki_data: