PLG: Prometheus + Loki + Grafana 可观测体系
PLG
快速入门
简介
PLG指以Prometheus+Loki+Grafana为核心的可观测性体系,分别负责指标、日志与可视化/告警,采集日志还需要Promtail,四者的关系:1
2
3应用/容器 ──(pull 指标)──> Prometheus ──┐
应用/容器 ──(tail 日志)──> Promtail ──push──> Loki ──┐
├──> Grafana(查询/仪表盘/告警)相比
ELK全文索引的重量级方案,Loki只对标签建索引、日志正文压缩存储,以极低的成本换来”够用”的日志检索能力,与k8s的标签体系天然契合Grafana是唯一面向用户的入口:指标与日志统一查询、仪表盘与告警规则统一管理,且全部资源支持配置化供给(provisioning),可以像管理应用一样用git+helm管理监控体系
Prometheus
简介
pull模型:Prometheus主动周期性抓取(scrape)目标的/metrics端点,而非等待推送,天然适合k8s的自注册环境- 数据以时序形式本地存储(
TSDB),配合PromQL做聚合查询 - 基本配置结构:
scrape_configs定义一组抓取任务(job),每个任务通过服务发现找到目标,再通过relabel改写标签
服务发现与注解发现
k8s环境用kubernetes_sd_configs发现目标,role可选pod/service/endpoints等生产实践是注解驱动:应用只在
Service/Pod上打prometheus.io/*注解即可被发现,零额外配置1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16additionalScrapeConfigs:
- job_name: kubernetes-service-endpoints
kubernetes_sd_configs:
- role: endpoints
relabel_configs:
- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]
action: keep # 只保留声明了注解的 Service
regex: true
- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_port]
action: replace
target_label: __address__
regex: (.+)(?::\d+);(\d+) # 用注解端口替换默认地址
replacement: $1:$2
- source_labels: [__meta_kubernetes_service_name]
action: replace
target_label: application # 服务名映射为 application 标签,告警信息直接可用Istio网格的指标同样可以低成本接入:按容器端口名过滤,抓取sidecar暴露的Envoy指标1
2
3
4
5
6
7
8- job_name: envoy-stats
metrics_path: /stats/prometheus
kubernetes_sd_configs:
- role: pod
relabel_configs:
- source_labels: [__meta_kubernetes_pod_container_port_name]
action: keep
regex: .*-envoy-prom
存储
数据目录挂在
PVC上即可持久化;重装时应复用已有云盘:PVC通过volumeName+selector钉死既有的PV,监控历史不因重装丢失1
2
3
4
5
6
7
8
9
10
11storageSpec:
volumeClaimTemplate:
spec:
selector:
matchLabels:
app.kubernetes.io/name: monitoring
storageClassName: monitoring
volumeName: monitoring # 直接绑定已有 PV
resources:
requests:
storage: 300Gi
Promtail
简介
Promtail是Loki官方采集代理,以DaemonSet跑在每个节点,tail容器日志文件并push给Loki- 采集时把
k8s元数据(命名空间、Pod名、标签)作为日志的标签附上,这就是Loki查询的主要维度
基本配置
1
2
3
4
5
6config:
clients:
- url: http://loki-write-headless:3100/loki/api/v1/push # 推送地址
snippets:
pipelineStages:
- cri: {} # 解析 CRI 日志格式(containerd 运行时的标准格式,拆出时间戳/级别/正文)
pipelineStages是加工管道,常用阶段:cri/docker:解析容器运行时日志头regex:正则提取字段,配合labels阶段提升为标签(标签基数要克制,高基数值不要做标签)timestamp/multiline:修正时间戳、合并多行日志(如Java堆栈)
- 简单可扩展模式下的
Loki有多个写入副本,推送地址用headless service(loki-write-headless)让客户端负载均衡到所有write实例
Loki
简介
Loki只索引标签,日志正文按块(chunk)压缩存储到对象存储,查询时按标签定位再过滤正文(LogQL)LogQL速记:{namespace="production", app=~"api.+"}选流,| json解析正文,| line_format格式化,指标查询再套聚合如rate(...[5m])
部署模式
单体模式(单体SingleBinary):所有组件一个进程,小规模够用简单可扩展模式(SimpleScalable):拆为read/write/backend三组,read/write可独立扩容,日志量大时的性价比之选;注意两条数据面都要配持久化,且write的PVC开启随StatefulSet删除(enableStatefulSetAutoDeletePVC)
关键配置
存储与索引结构,对象存储放
chunk,TSDB索引按天滚动:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18loki:
storage:
type: s3
bucketNames:
chunks: loki-chunks # 日志块
ruler: loki-ruler
admin: loki-admin
s3:
s3: s3://loki
endpoint: http://s3.internal # 兼容 S3 协议即可(各云厂商对象存储均可)
schemaConfig:
configs:
- from: 2024-01-01
store: tsdb
object_store: s3
schema: v13
index:
period: 24h保留策略:全局保留 + 按流精细覆盖,低价值环境短保留,成本立省
1
2
3
4
5
6
7
8
9
10
11limits_config:
retention_period: 744h # 默认保留 31 天
retention_stream:
- selector: '{namespace="staging"}'
priority: 1
period: 168h # staging 只留 7 天
- selector: '{app=~".+job.+"}'
priority: 2
period: 168h # 任务类 Pod 日志也只留 7 天
compactor:
retention_enabled: true # 老版本用 tableManager,新版统一由 compactor 执行删除
Grafana
数据源
数据源用
provisioning配置,不做手工点击;uid显式固定,仪表盘与告警规则都靠uid引用数据源1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16datasources:
datasources.yaml:
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
uid: prometheus
url: http://monitoring-kube-prometheus-prometheus:9090
isDefault: true
- name: Loki
type: loki
uid: loki
url: http://loki-read-headless:3100
jsonData:
timeout: 60
maxLines: 1000 # 限制单次查询行数,防止误查打爆浏览器
仪表盘与配置即代码
仪表盘
json放进ConfigMap,打上标签(如grafana_dashboard: "1"),由Grafana的sidecar(k8s-sidecar)监听全集群的同标签ConfigMap,自动加载与更新目录归类用注解指定,文件结构即目录结构:
1
2
3
4
5
6
7
8
9
10kind: ConfigMap
metadata:
name: grafana-logs
labels:
grafana_dashboard: "1" # sidecar 按该标签发现
annotations:
k8s-sidecar-target-directory: "/tmp/dashboards/Logs" # 归入 Logs 目录
data:
logs.json: |
{ "title": "Logs", ... } # 仪表盘 JSON 全文1
2
3
4
5
6
7sidecar:
dashboards:
enabled: true
provider:
foldersFromFilesStructure: true # 按文件结构生成目录
datasources:
enabled: true社区仪表盘(
grafana.com的gnetId)可直接导入json后沉淀为ConfigMap,再做定制这套”标签 +
sidecar“的供给模式不仅用于仪表盘与数据源,同样适用于告警规则等其余Grafana资源