TIG 监控体系搭建:Telegraf + InfluxDB + Grafana 全链路实战

摘要:凌晨 2 点线上服务挂了但没告警------因为监控自己先挂了。TIG Stack 单点故障导致 45 分钟监控真空,这是我亲身经历的"盲飞"事故。本文从一台裸机开始,手把手搭建一套完整的服务器监控系统:Telegraf 采集 CPU/内存/磁盘/网络/容器/Nginx/MySQL 指标、InfluxDB 做数据存储和自动降采样、Grafana 做可视化 Dashboard 和告警通知,覆盖认证安全、Provisioning 自动化和 5 个生产踩坑案例。
环境说明:InfluxDB 2.7.x | Telegraf 1.28+ | Grafana 10.x | Docker 24.x | 操作系统 CentOS 7.9

版本提示:InfluxDB 3.x 已引入 SQL 支持,Grafana 10.x 原生支持 InfluxDB 3.x 数据源。本文基于 2.x 版本,如果你使用 3.x,Grafana 数据源配置方式略有不同(选择 SQL 查询模式而非 Flux)。

事故复盘:监控真空 45 分钟

凌晨 2:17,某业务系统核心接口 P99 飙到 5 秒,大量用户超时投诉。但值班人员没收到任何告警------InfluxDB 因 OOM 被 Kill,Telegraf 写入失败静默丢弃数据,Grafana 展示空白图表。

从故障发生到运维手动发现,整整 45 分钟处于监控真空状态。事后统计:直接影响订单 3,200+ 笔,间接损失约 18 万元。根本原因只有一个:TIG Stack 全部单点部署,没有任何健康检查和 Deadman 告警

这促使我重新审视监控体系的自保能力------监控系统自己挂了谁来告警?本文就是在这次事故后整理的完整搭建方案,重点解决"监控自身可靠性"问题。

TIG 全栈架构

为什么先画架构图再动手:TIG 三个组件之间有明确的上下游依赖,先理清数据流向,才能理解后续每一步配置的意图。
#mermaid-svg-Et9tFzZUNKSSwhds{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Et9tFzZUNKSSwhds .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Et9tFzZUNKSSwhds .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Et9tFzZUNKSSwhds .error-icon{fill:#552222;}#mermaid-svg-Et9tFzZUNKSSwhds .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Et9tFzZUNKSSwhds .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Et9tFzZUNKSSwhds .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Et9tFzZUNKSSwhds .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Et9tFzZUNKSSwhds .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Et9tFzZUNKSSwhds .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Et9tFzZUNKSSwhds .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Et9tFzZUNKSSwhds .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Et9tFzZUNKSSwhds .marker.cross{stroke:#333333;}#mermaid-svg-Et9tFzZUNKSSwhds svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Et9tFzZUNKSSwhds p{margin:0;}#mermaid-svg-Et9tFzZUNKSSwhds .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Et9tFzZUNKSSwhds .cluster-label text{fill:#333;}#mermaid-svg-Et9tFzZUNKSSwhds .cluster-label span{color:#333;}#mermaid-svg-Et9tFzZUNKSSwhds .cluster-label span p{background-color:transparent;}#mermaid-svg-Et9tFzZUNKSSwhds .label text,#mermaid-svg-Et9tFzZUNKSSwhds span{fill:#333;color:#333;}#mermaid-svg-Et9tFzZUNKSSwhds .node rect,#mermaid-svg-Et9tFzZUNKSSwhds .node circle,#mermaid-svg-Et9tFzZUNKSSwhds .node ellipse,#mermaid-svg-Et9tFzZUNKSSwhds .node polygon,#mermaid-svg-Et9tFzZUNKSSwhds .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Et9tFzZUNKSSwhds .rough-node .label text,#mermaid-svg-Et9tFzZUNKSSwhds .node .label text,#mermaid-svg-Et9tFzZUNKSSwhds .image-shape .label,#mermaid-svg-Et9tFzZUNKSSwhds .icon-shape .label{text-anchor:middle;}#mermaid-svg-Et9tFzZUNKSSwhds .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Et9tFzZUNKSSwhds .rough-node .label,#mermaid-svg-Et9tFzZUNKSSwhds .node .label,#mermaid-svg-Et9tFzZUNKSSwhds .image-shape .label,#mermaid-svg-Et9tFzZUNKSSwhds .icon-shape .label{text-align:center;}#mermaid-svg-Et9tFzZUNKSSwhds .node.clickable{cursor:pointer;}#mermaid-svg-Et9tFzZUNKSSwhds .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Et9tFzZUNKSSwhds .arrowheadPath{fill:#333333;}#mermaid-svg-Et9tFzZUNKSSwhds .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Et9tFzZUNKSSwhds .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Et9tFzZUNKSSwhds .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Et9tFzZUNKSSwhds .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Et9tFzZUNKSSwhds .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Et9tFzZUNKSSwhds .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Et9tFzZUNKSSwhds .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Et9tFzZUNKSSwhds .cluster text{fill:#333;}#mermaid-svg-Et9tFzZUNKSSwhds .cluster span{color:#333;}#mermaid-svg-Et9tFzZUNKSSwhds div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Et9tFzZUNKSSwhds .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Et9tFzZUNKSSwhds rect.text{fill:none;stroke-width:0;}#mermaid-svg-Et9tFzZUNKSSwhds .icon-shape,#mermaid-svg-Et9tFzZUNKSSwhds .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Et9tFzZUNKSSwhds .icon-shape p,#mermaid-svg-Et9tFzZUNKSSwhds .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Et9tFzZUNKSSwhds .icon-shape .label rect,#mermaid-svg-Et9tFzZUNKSSwhds .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Et9tFzZUNKSSwhds .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Et9tFzZUNKSSwhds .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Et9tFzZUNKSSwhds :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 可视化与告警层
数据存储层
数据采集层
outputs.influxdb_v2
webhook/邮件/企微
Telegraf

CPU/内存/磁盘/网络
Telegraf

Docker 容器指标
Telegraf

Nginx/MySQL 应用指标
InfluxDB 2.x

原始数据: 7天

降采样: 1年
Grafana

Dashboard
Grafana

Alerting
值班人员

TIG 数据流时序

为什么需要时序图:很多同学搭完 TIG 发现数据没进来,但不知道断在哪一步。时序图能帮你快速定位------是 Telegraf 没采集到、还是写入失败、还是 Grafana 查询语法有误。
Alert Channel Grafana InfluxDB Telegraf Alert Channel Grafana InfluxDB Telegraf #mermaid-svg-8wWMeseiD7tUpza8{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-8wWMeseiD7tUpza8 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-8wWMeseiD7tUpza8 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-8wWMeseiD7tUpza8 .error-icon{fill:#552222;}#mermaid-svg-8wWMeseiD7tUpza8 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8wWMeseiD7tUpza8 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-8wWMeseiD7tUpza8 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8wWMeseiD7tUpza8 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8wWMeseiD7tUpza8 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-8wWMeseiD7tUpza8 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8wWMeseiD7tUpza8 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8wWMeseiD7tUpza8 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8wWMeseiD7tUpza8 .marker.cross{stroke:#333333;}#mermaid-svg-8wWMeseiD7tUpza8 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8wWMeseiD7tUpza8 p{margin:0;}#mermaid-svg-8wWMeseiD7tUpza8 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-8wWMeseiD7tUpza8 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-8wWMeseiD7tUpza8 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-8wWMeseiD7tUpza8 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-8wWMeseiD7tUpza8 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-8wWMeseiD7tUpza8 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-8wWMeseiD7tUpza8 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-8wWMeseiD7tUpza8 .sequenceNumber{fill:white;}#mermaid-svg-8wWMeseiD7tUpza8 #sequencenumber{fill:#333;}#mermaid-svg-8wWMeseiD7tUpza8 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-8wWMeseiD7tUpza8 .messageText{fill:#333;stroke:none;}#mermaid-svg-8wWMeseiD7tUpza8 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-8wWMeseiD7tUpza8 .labelText,#mermaid-svg-8wWMeseiD7tUpza8 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-8wWMeseiD7tUpza8 .loopText,#mermaid-svg-8wWMeseiD7tUpza8 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-8wWMeseiD7tUpza8 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-8wWMeseiD7tUpza8 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-8wWMeseiD7tUpza8 .noteText,#mermaid-svg-8wWMeseiD7tUpza8 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-8wWMeseiD7tUpza8 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-8wWMeseiD7tUpza8 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-8wWMeseiD7tUpza8 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-8wWMeseiD7tUpza8 .actorPopupMenu{position:absolute;}#mermaid-svg-8wWMeseiD7tUpza8 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-8wWMeseiD7tUpza8 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-8wWMeseiD7tUpza8 .actor-man circle,#mermaid-svg-8wWMeseiD7tUpza8 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-8wWMeseiD7tUpza8 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} loopDashboard刷新 alt触发阈值Deadman无数据 loop告警评估 每10s采集指标批量攒够1000条HTTP POST /api/v2/writegzip压缩写入WALCompaction为TSMFlux查询aggregateWindow聚合返回时序数据渲染图表告警规则查询返回指标值发送告警通知发送Deadman告警

第一步:部署 InfluxDB

为什么先部署 InfluxDB:它是整个 TIG 架构的核心存储层,Telegraf 和 Grafana 都依赖它。先确保存储层正常工作,再配置采集和展示。

yaml 复制代码
# docker-compose.yml --- InfluxDB 服务定义
# Why: 使用 init mode 自动完成初始化,避免手动 UI 操作
version: '3.8'
services:
  influxdb:
    image: influxdb:2.7
    ports:
      - "8086:8086"
    volumes:
      - ./data/influxdb:/var/lib/influxdb2
      - ./config/influxdb:/etc/influxdb2
    environment:
      DOCKER_INFLUXDB_INIT_MODE: setup
      DOCKER_INFLUXDB_INIT_USERNAME: admin
      DOCKER_INFLUXDB_INIT_PASSWORD: Admin123!
      DOCKER_INFLUXDB_INIT_ORG: myorg
      DOCKER_INFLUXDB_INIT_BUCKET: metrics
      DOCKER_INFLUXDB_INIT_RETENTION: 7d
    deploy:
      resources:
        limits:
          memory: 2g
    healthcheck:
      test: ["CMD", "influx", "ping"]
      interval: 30s
      timeout: 5s
      retries: 3

InfluxDB 初始化脚本

为什么用脚本而非手动 UI:生产环境需要可重复部署,手动操作无法版本管理,也不方便自动化 CI/CD 流程。

bash 复制代码
#!/bin/bash
# scripts/influxdb-init.sh
# Why: 自动创建 Token、Bucket、Retention Policy 和降采样 Task

INFLUX_URL="http://localhost:8086"
ADMIN_TOKEN=$(cat ./config/influxdb/admin-token)

# ---- 创建专用 Token ----
# Why: 最小权限原则------Telegraf 只需写、Grafana 只需读
influx auth create \
  --org myorg \
  --write-bucket metrics \
  --description "telegraf-write-token" \
  --token "${ADMIN_TOKEN}"

influx auth create \
  --org myorg \
  --read-bucket metrics \
  --description "grafana-read-token" \
  --token "${ADMIN_TOKEN}"

# ---- 创建降采样 Bucket ----
# Why: 原始数据保留7天,降采样数据保留1年,节省80%存储
influx bucket create \
  --name metrics_downsampled \
  --org myorg \
  --retention 365d \
  --token "${ADMIN_TOKEN}"

# ---- 创建降采样 Task ----
# Why: 自动将10秒粒度聚合为1小时粒度,查询历史数据时不再扫描百万级数据点
influx task create \
  --org myorg \
  --name "downsample-1h" \
  --every 1h \
  --token "${ADMIN_TOKEN}" \
  - <<'FLUX'
option task = {name: "downsample-1h", every: 1h}

from(bucket: "metrics")
  |> range(start: -task.every)
  |> filter(fn: (r) => r._measurement == "cpu" or r._measurement == "mem" or r._measurement == "disk")
  |> aggregateWindow(every: 1h, fn: mean, createEmpty: false)
  |> to(bucket: "metrics_downsampled", org: "myorg")
FLUX

echo "✅ InfluxDB 初始化完成"
bash 复制代码
# 启动 InfluxDB
docker compose up -d influxdb

# 等待健康检查通过后执行初始化
bash scripts/influxdb-init.sh

第二步:配置 Telegraf

为什么需要仔细配置 Telegraf:Telegraf 是数据采集的入口,采集频率、字段过滤、输出压缩等参数直接影响 InfluxDB 的写入压力和存储空间。生产环境必须按需裁剪,否则磁盘和内存会很快被撑满。

toml 复制代码
# telegraf.conf --- 完整生产级配置
# Why: 覆盖系统/进程/Nginx/MySQL全量采集,fieldpass过滤只保留必要字段

[agent]
  interval = "10s"
  flush_interval = "10s"
  metric_batch_size = 1000
  metric_buffer_limit = 10000
  hostname = "$HOSTNAME"
  omit_hostname = false

# ====== CPU ======
# Why: 只保留4个关键字段,默认采集20+字段中大部分没用
[[inputs.cpu]]
  percpu = true
  totalcpu = true
  collect_cpu_time = false
  fieldpass = ["usage_user", "usage_system", "usage_idle", "usage_iowait"]

# ====== 内存 ======
[[inputs.mem]]
  fieldpass = ["total", "available", "used", "used_percent", "buffered", "cached"]

# ====== 磁盘 ======
[[inputs.disk]]
  ignore_fs = ["tmpfs", "devtmpfs", "overlay", "squashfs"]
  fieldpass = ["total", "used", "used_percent", "inodes_used"]

# ====== 磁盘IO ======
# Why: 磁盘IO延迟是数据库性能瓶颈的第一信号
[[inputs.diskio]]
  fieldpass = ["read_bytes", "write_bytes", "read_time", "write_time", "iops_in_progress"]

# ====== 网络 ======
[[inputs.net]]
  interfaces = ["eth0", "eth1"]
  fieldpass = ["bytes_sent", "bytes_recv", "packets_sent", "packets_recv", "err_in", "err_out"]

# ====== 系统负载 ======
[[inputs.system]]
  fieldpass = ["load1", "load5", "load15", "uptime"]

# ====== 进程 ======
# Why: 进程数异常飙升是内存泄漏/死锁的前兆
[[inputs.processes]]
  fieldpass = ["running", "sleeping", "stopped", "zombies"]

# ====== 内核 ------
[[inputs.kernel]]
  fieldpass = ["entropy_avail", "context_switches"]

# ====== Docker 容器 ======
[[inputs.docker]]
  endpoint = "unix:///var/run/docker.sock"
  container_names = []
  timeout = "5s"
  fieldpass = ["cpu_usage_total", "memory_usage", "net_rx_bytes", "net_tx_bytes", "container_status"]

# ====== Nginx 状态 ======
# Why: Nginx stub_status 提供实时连接数和请求量,是前端流量监控的基础
[[inputs.nginx]]
  urls = ["http://localhost:80/nginx_status"]
  response_timeout = "5s"
  fieldpass = ["active", "accepts", "handled", "requests", "reading", "writing", "waiting"]

# ====== MySQL 指标 ======
# Why: 慢查询、连接数、线程缓存是MySQL性能三驾马车
[[inputs.mysql]]
  servers = ["telegraf:Telegraf123!@tcp(127.0.0.1:3306)/"]
  metric_version = 2
  fieldpass = [
    "threads_running", "threads_connected", "threads_cached",
    "queries", "slow_queries", "connections",
    "buffer_pool_pages_total", "buffer_pool_pages_free"
  ]

# ====== 内部指标(监控Telegraf自身)======
# Why: 采集器自身挂了必须知道,这是Deadman告警的数据来源
[[inputs.internal]]
  collect_memstats = true

# ====== 写入 InfluxDB ======
[[outputs.influxdb_v2]]
  urls = ["http://influxdb:8086"]
  token = "${INFLUX_TOKEN}"
  organization = "myorg"
  bucket = "metrics"
  content_encoding = "gzip"
  influx_2x = true

Telegraf 完整 Docker 配置:

yaml 复制代码
# docker-compose.yml --- Telegraf 服务定义
# Why: 只读挂载Docker socket和/proc,遵循最小权限原则
services:
  telegraf:
    image: telegraf:1.28
    volumes:
      - ./config/telegraf/telegraf.conf:/etc/telegraf/telegraf.conf:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - ./config/telegraf/mysql.cnf:/etc/telegraf/mysql.cnf:ro
    environment:
      HOST_PROC: /host/proc
      HOST_SYS: /host/sys
      INFLUX_TOKEN: "${INFLUX_TOKEN}"
    privileged: false
    depends_on:
      influxdb:
        condition: service_healthy
bash 复制代码
# 验证 Telegraf 采集是否正常
docker compose logs telegraf | grep "metrics"
# 输出类似:...[agent] 124 metrics collected, 124 metrics sent

# 验证数据是否写入 InfluxDB
flux 复制代码
from(bucket: "metrics")
  |> range(start: -5m)
  |> filter(fn: (r) => r._measurement == "cpu")
  |> yield(name: "test")

第三步:配置 Grafana

为什么用 Provisioning 配置数据源:手动在 Grafana UI 中配置数据源不方便版本管理和迁移,通过 YAML 文件可以一键还原配置,新环境部署零手工操作。

yaml 复制代码
# docker-compose.yml --- Grafana 服务定义
# Why: 挂载provisioning目录实现数据源和Dashboard自动导入
services:
  grafana:
    image: grafana/grafana:10.3
    ports:
      - "3000:3000"
    volumes:
      - ./data/grafana:/var/lib/grafana
      - ./config/grafana/provisioning:/etc/grafana/provisioning
      - ./config/grafana/dashboards:/var/lib/grafana/dashboards
    environment:
      GF_SECURITY_ADMIN_USER: admin
      GF_SECURITY_ADMIN_PASSWORD: "${GRAFANA_ADMIN_PASSWORD}"
      GF_INSTALL_PLUGINS: "grafana-clock-panel,grafana-piechart-panel"
      GF_LOG_LEVEL: info
      GF_ALERTING_ENABLED: "true"
      GF_UNIFIED_ALERTING_ENABLED: "true"
    depends_on:
      - influxdb
    deploy:
      resources:
        limits:
          memory: 512m

数据源 Provisioning

yaml 复制代码
# config/grafana/provisioning/datasources/influxdb.yml
# Why: Grafana启动时自动注册InfluxDB数据源,无需手动配置
apiVersion: 1
datasources:
  - name: InfluxDB
    type: influxdb
    access: proxy
    url: http://influxdb:8086
    isDefault: true
    jsonData:
      version: Flux
      organization: myorg
      defaultBucket: metrics
      tlsSkipVerify: false
    secureJsonData:
      token: "${INFLUX_TOKEN}"
  - name: InfluxDB-Downsampled
    type: influxdb
    access: proxy
    url: http://influxdb:8086
    jsonData:
      version: Flux
      organization: myorg
      defaultBucket: metrics_downsampled
    secureJsonData:
      token: "${INFLUX_TOKEN}"

Dashboard Provisioning

为什么自动导入 Dashboard:手动配 20+ 面板的 Dashboard 至少要 2 小时,用 JSON 模板 30 秒搞定,还能版本管理。

yaml 复制代码
# config/grafana/provisioning/dashboards/dashboards.yml
# Why: 告诉Grafana从哪个目录加载Dashboard JSON文件
apiVersion: 1
providers:
  - name: 'default'
    orgId: 1
    folder: ''
    type: file
    disableDeletion: false
    editable: true
    options:
      path: /var/lib/grafana/dashboards
      foldersFromFilesStructure: false
json 复制代码
{
  "annotations": { "list": [] },
  "editable": true,
  "fiscalYearStartMonth": 0,
  "graphTooltip": 1,
  "links": [],
  "panels": [
    {
      "title": "CPU Usage",
      "type": "timeseries",
      "datasource": { "type": "influxdb", "uid": "${DS_INFLUXDB}" },
      "targets": [
        {
          "query": "from(bucket: \"metrics\")\n  |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n  |> filter(fn: (r) => r._measurement == \"cpu\")\n  |> filter(fn: (r) => r._field == \"usage_user\" or r._field == \"usage_system\")\n  |> aggregateWindow(every: v.windowPeriod, fn: mean)\n  |> yield(name: \"mean\")",
          "refId": "A"
        }
      ],
      "fieldConfig": {
        "defaults": {
          "unit": "percent",
          "max": 100,
          "min": 0
        }
      },
      "gridPos": { "h": 8, "w": 12, "x": 0, "y": 0 }
    },
    {
      "title": "Memory Usage",
      "type": "timeseries",
      "datasource": { "type": "influxdb", "uid": "${DS_INFLUXDB}" },
      "targets": [
        {
          "query": "from(bucket: \"metrics\")\n  |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n  |> filter(fn: (r) => r._measurement == \"mem\")\n  |> filter(fn: (r) => r._field == \"used_percent\")\n  |> aggregateWindow(every: v.windowPeriod, fn: mean)\n  |> yield(name: \"mean\")",
          "refId": "A"
        }
      ],
      "fieldConfig": {
        "defaults": {
          "unit": "percent",
          "max": 100,
          "min": 0
        }
      },
      "gridPos": { "h": 8, "w": 12, "x": 12, "y": 0 }
    }
  ],
  "title": "TIG Server Monitoring",
  "uid": "tig-server-monitor"
}

第四步:告警配置

为什么需要告警:监控的核心价值不在于"看到问题",而在于"被通知问题"。没有告警的监控系统只是事后分析工具,无法在故障发生时第一时间响应。

告警链路全景

为什么画告警链路图:告警链路涉及数据采集→规则评估→通知发送三个阶段,任一环节断链都会导致"有监控无告警"。这张图帮你快速定位断点。
#mermaid-svg-BWNy4IUv6AhkZd7o{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-BWNy4IUv6AhkZd7o .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-BWNy4IUv6AhkZd7o .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-BWNy4IUv6AhkZd7o .error-icon{fill:#552222;}#mermaid-svg-BWNy4IUv6AhkZd7o .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-BWNy4IUv6AhkZd7o .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-BWNy4IUv6AhkZd7o .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-BWNy4IUv6AhkZd7o .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-BWNy4IUv6AhkZd7o .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-BWNy4IUv6AhkZd7o .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-BWNy4IUv6AhkZd7o .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-BWNy4IUv6AhkZd7o .marker{fill:#333333;stroke:#333333;}#mermaid-svg-BWNy4IUv6AhkZd7o .marker.cross{stroke:#333333;}#mermaid-svg-BWNy4IUv6AhkZd7o svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-BWNy4IUv6AhkZd7o p{margin:0;}#mermaid-svg-BWNy4IUv6AhkZd7o .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-BWNy4IUv6AhkZd7o .cluster-label text{fill:#333;}#mermaid-svg-BWNy4IUv6AhkZd7o .cluster-label span{color:#333;}#mermaid-svg-BWNy4IUv6AhkZd7o .cluster-label span p{background-color:transparent;}#mermaid-svg-BWNy4IUv6AhkZd7o .label text,#mermaid-svg-BWNy4IUv6AhkZd7o span{fill:#333;color:#333;}#mermaid-svg-BWNy4IUv6AhkZd7o .node rect,#mermaid-svg-BWNy4IUv6AhkZd7o .node circle,#mermaid-svg-BWNy4IUv6AhkZd7o .node ellipse,#mermaid-svg-BWNy4IUv6AhkZd7o .node polygon,#mermaid-svg-BWNy4IUv6AhkZd7o .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-BWNy4IUv6AhkZd7o .rough-node .label text,#mermaid-svg-BWNy4IUv6AhkZd7o .node .label text,#mermaid-svg-BWNy4IUv6AhkZd7o .image-shape .label,#mermaid-svg-BWNy4IUv6AhkZd7o .icon-shape .label{text-anchor:middle;}#mermaid-svg-BWNy4IUv6AhkZd7o .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-BWNy4IUv6AhkZd7o .rough-node .label,#mermaid-svg-BWNy4IUv6AhkZd7o .node .label,#mermaid-svg-BWNy4IUv6AhkZd7o .image-shape .label,#mermaid-svg-BWNy4IUv6AhkZd7o .icon-shape .label{text-align:center;}#mermaid-svg-BWNy4IUv6AhkZd7o .node.clickable{cursor:pointer;}#mermaid-svg-BWNy4IUv6AhkZd7o .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-BWNy4IUv6AhkZd7o .arrowheadPath{fill:#333333;}#mermaid-svg-BWNy4IUv6AhkZd7o .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-BWNy4IUv6AhkZd7o .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-BWNy4IUv6AhkZd7o .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BWNy4IUv6AhkZd7o .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-BWNy4IUv6AhkZd7o .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BWNy4IUv6AhkZd7o .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-BWNy4IUv6AhkZd7o .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-BWNy4IUv6AhkZd7o .cluster text{fill:#333;}#mermaid-svg-BWNy4IUv6AhkZd7o .cluster span{color:#333;}#mermaid-svg-BWNy4IUv6AhkZd7o div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-BWNy4IUv6AhkZd7o .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-BWNy4IUv6AhkZd7o rect.text{fill:none;stroke-width:0;}#mermaid-svg-BWNy4IUv6AhkZd7o .icon-shape,#mermaid-svg-BWNy4IUv6AhkZd7o .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BWNy4IUv6AhkZd7o .icon-shape p,#mermaid-svg-BWNy4IUv6AhkZd7o .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-BWNy4IUv6AhkZd7o .icon-shape .label rect,#mermaid-svg-BWNy4IUv6AhkZd7o .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BWNy4IUv6AhkZd7o .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-BWNy4IUv6AhkZd7o .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-BWNy4IUv6AhkZd7o :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 写入
Flux查询
阈值触发
数据正常
无数据超5m
severity=critical
severity=warning
severity=info
Telegraf 采集指标
InfluxDB
Grafana Alert Rule
Alert State: Firing
Alert State: Normal
Alert State: NoData

Deadman Switch
Notification Policy
📞 电话+企微
📧 邮件+企微
📊 企微群消息
值班人员响应
技术群知会

Deadman Switch 告警

为什么 Deadman 是最高优先级:开头的 45 分钟监控真空事故就是没有 Deadman 导致的。监控系统自己挂了,没有任何人知道------这是最致命的故障模式。

yaml 复制代码
# config/grafana/provisioning/alerting/rules.yml
# Why: Deadman检测Telegraf是否存活,阈值告警检测业务是否正常
apiVersion: 1
groups:
  - orgId: 1
    name: tig-deadman
    interval: 1m
    rules:
      - uid: deadman-switch
        title: "Telegraf Deadman Switch"
        condition: C
        data:
          - refId: A
            relativeTimeRange:
              from: 600
              to: 0
            datasourceUid: "${DS_INFLUXDB}"
            model:
              queryType: ""
              query: |
                from(bucket: "metrics")
                  |> range(start: -10m)
                  |> filter(fn: (r) => r._measurement == "internal_agent")
                  |> filter(fn: (r) => r._field == "metrics_gathered")
                  |> aggregateWindow(every: 1m, fn: last, createEmpty: true)
          - refId: B
            datasourceUid: "-1"
            model:
              conditions:
                - evaluator:
                    params: [1, 0]
                    type: "lt"
                  operator: { type: "and" }
                  query: { params: ["B"] }
                  type: "query"
              type: "classic_conditions"
          - refId: C
            datasourceUid: "-1"
            model:
              type: "reduce"
              reducer: "last"
        noDataState: Alerting
        executionErrorState: Alerting
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "Telegraf 已停止上报数据超过 5 分钟"
          description: "可能原因:Telegraf 进程崩溃、InfluxDB 不可用、网络中断"

  - orgId: 1
    name: server-alerts
    interval: 30s
    rules:
      - uid: cpu-high
        title: "CPU 使用率 > 90%"
        condition: C
        data:
          - refId: A
            relativeTimeRange:
              from: 300
              to: 0
            datasourceUid: "${DS_INFLUXDB}"
            model:
              query: |
                from(bucket: "metrics")
                  |> range(start: -5m)
                  |> filter(fn: (r) => r._measurement == "cpu")
                  |> filter(fn: (r) => r._field == "usage_user")
                  |> aggregateWindow(every: 30s, fn: mean)
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "CPU 使用率持续超过 90%"
          runbook_url: "https://wiki.internal/runbook/cpu-high"

      - uid: disk-high
        title: "磁盘使用率 > 85%"
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "磁盘使用率持续超过 85%"

      - uid: mem-high
        title: "内存使用率 > 90%"
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "内存使用率持续超过 90%"

      - uid: mysql-slow-queries
        title: "MySQL 慢查询激增"
        for: 3m
        labels:
          severity: warning
        annotations:
          summary: "MySQL 慢查询数量异常增长"

通知渠道配置

为什么用 Notification Policy 分级:凌晨 3 点的 CPU 告警必须打电话,而磁盘 85% 的 warning 发企微即可。分级通知避免"告警疲劳"------告警太多反而没人看。

yaml 复制代码
# config/grafana/provisioning/alerting/notification-policies.yml
# Why: 按severity分级通知,critical打电话,warning发企微
apiVersion: 1
policies:
  - receiver: "电话+企微"
    match:
      severity: critical
    group_by: ["alertname", "host"]
    group_wait: 30s
    group_interval: 5m
    repeat_interval: 30m

  - receiver: "企微群"
    match:
      severity: warning
    group_by: ["alertname"]
    group_wait: 1m
    group_interval: 10m
    repeat_interval: 1h

  - receiver: "企微群"
    match:
      severity: info
    group_by: ["..."]
    group_wait: 5m
    repeat_interval: 4h

性能基线数据

为什么需要量化数据:搭建完 TIG 不代表万事大吉,你需要知道每个组件吃多少资源、能扛多少流量,才能合理规划容量。以下是我在一组 8 核 16G 服务器上的实测数据。

测试时间 :2026 年 5 月 12 日

测试环境:阿里云 ECS c6.2xlarge (8 核 16G) × 1 台监控服务器 + 50 台被监控节点

指标 Telegraf InfluxDB Grafana 说明
CPU 占用 1-3% 5-15% 2-5% 50 节点 × 10s 采集间隔
内存占用 45MB 800MB-1.5GB 180MB InfluxDB 随数据量增长
数据吞吐 500 points/s 5,000 writes/s - 写入含 gzip 压缩
写入延迟 P99 - 12ms - 批量 1000 条写入
查询延迟 P99 - 85ms (5m) / 1.2s (30d) - 30d 查询须用降采样
磁盘写入 - 2MB/s - 含 WAL + TSM Compaction
网络带宽 0.5Mbps 3Mbps 0.2Mbps 50 节点总入流量

关键结论:50 节点规模下 InfluxDB 内存 2G 够用,但数据量翻倍时内存线性增长。超过 500 节点建议考虑 InfluxDB 集群。

踩坑案例

踩坑 1:Grafana Dashboard 加载慢

现象:Grafana 首页加载一个展示 30 天 CPU 趋势的 Dashboard,等了 10 秒才出图。

原因:Grafana 直接查询原始数据(10 秒间隔 × 30 天 = 约 26 万个数据点)。Grafana 需要把所有数据点拉到前端渲染。

解决 :降采样后加上 Grafana 的 $__interval 变量自动控制查询粒度。

为什么用 $__interval:Grafana 会根据 Dashboard 的时间范围和面板像素宽度自动计算合适的聚合间隔,避免查询过多数据点。

flux 复制代码
# Why: aggregateWindow将数据点从26万降到200以内,查询从10秒降到0.3秒
from(bucket: "metrics")
  |> range(start: v.timeRangeStart, stop: v.timeRangeStop)
  |> filter(fn: (r) => r._measurement == "cpu")
  |> filter(fn: (r) => r._field == "usage_user")
  |> aggregateWindow(every: v.windowPeriod, fn: mean)
  |> yield(name: "mean")

教训 :任何超过 1 天时间范围的查询都必须用 aggregateWindow 聚合。将此规则写入团队 Dashboard 开发规范,新 Dashboard 上线前必须用 30 天范围做加载速度测试。

踩坑 2:Telegraf 采集的字段名跟预期不同

现象:Grafana 中 CPU 使用率一直显示 0%,但服务器负载明明很高。

原因 :Telegraf 默认采集的 CPU 字段名是 usage_userusage_system,我写 Dashboard 时查的字段名是 cpu_usage。字段名不匹配导致查询结果为空,Grafana 显示 0 而非报错------这种静默失败最坑。

解决 :先查实际字段名再配 Dashboard。Telegraf 的字段命名规则跟直觉不同(如 CPU 用 usage_user 而非 cpu_usage),猜字段名是最常见的 Dashboard 配置错误。

flux 复制代码
# Why: 查询实际字段名,避免猜错导致Dashboard空数据
from(bucket: "metrics")
  |> range(start: -5m)
  |> filter(fn: (r) => r._measurement == "cpu")
  |> keep(columns: ["_field", "_value"])
  |> distinct(column: "_field")

输出结果:

bash 复制代码
# _field: usage_user
# _field: usage_system
# _field: usage_idle
# _field: usage_iowait

教训 :每次新增 Telegraf input 插件后,先用 Flux 查询确认 _measurement_field 的实际命名,再配 Dashboard。我在团队内定了规矩:新 Dashboard 必须附带字段名截图,Code Review 时检查。

踩坑 3:Telegraf Docker 插件权限不足

现象 :Telegraf 容器日志报 permission denied while reading /var/run/docker.sock

原因:Telegraf 容器没有挂载 Docker socket 或者挂载了但权限不够,宿主机的 docker 组 GID 跟容器内不一致。

解决:只读挂载 Docker socket + 指定正确的用户组 ID。

为什么用只读挂载而非特权模式:Docker socket 挂载本身就存在安全风险(容器可以控制宿主机 Docker),使用只读挂载 + 非 root 用户是最小权限原则的体现。

yaml 复制代码
# Why: ro只读防止容器篡改Docker,994是宿主机docker组的GID
services:
  telegraf:
    image: telegraf:1.28
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
    privileged: false
    user: "telegraf:994"

教训 :Docker socket 权限问题在不同宿主机上 GID 可能不同,部署脚本必须动态获取宿主机 docker 组的 GID:getent group docker | cut -d: -f3,写死 994 在某些机器上会失败。这条我写进了部署自动化脚本。

踩坑 4:InfluxDB OOM 被 Kill(进阶)

现象 :InfluxDB 进程突然消失,docker ps 显示 Exited,dmesg 日志有 oom-killer 记录。这正是开头 45 分钟监控真空事故的直接原因。

原因 :InfluxDB 2.x 默认不限制内存,当查询涉及大时间范围或降采样 Task 并发执行时,内存可以飙到宿主机的 80%。如果 Docker 没设 memory 限制,Linux OOM Killer 会优先杀掉内存占用最高的进程。

解决:设置 Docker 内存限制 + InfluxDB 配置项。

yaml 复制代码
# Why: 限制InfluxDB内存上限,防止OOM Killer误杀其他服务
services:
  influxdb:
    deploy:
      resources:
        limits:
          memory: 2g
        reservations:
          memory: 512m
toml 复制代码
# /etc/influxdb2/config.toml
# Why: 限制查询内存和并发,防止单个大查询吃掉所有内存
[storage]
  cache-max-memory-size = 1073741824  # 1GB,超过拒绝写入
  cache-snapshot-memory-size = 26214400

[query]
  memory-bytes = 536870912  # 单次查询最大512MB
  concurrency-quota = 10   # 最大并发查询数
  queue-size = 20

教训 :所有时序数据库都必须设内存上限。InfluxDB OOM 的前兆是 cache-max-memory-size 接近上限,建议在 Grafana 配一个 InfluxDB 自身内存使用率告警。这个告警让我在后续一次流量高峰中提前扩容,避免了第二次监控真空。

踩坑 5:Grafana 变量查询拖慢整个 Dashboard(进阶)

现象:Dashboard 打开要 15 秒,但去掉变量后 2 秒就出来了。

原因 :Dashboard 用了 3 个模板变量(hostmeasurementfield),每个变量刷新时都向 InfluxDB 发起一次 Flux 查询。3 个变量 × 每次打开 Dashboard 刷新 = 额外 3 次查询。如果变量查询本身没做 aggregateWindow,每次都扫全量数据。

解决:变量查询必须做限制,不能用全量扫描。

flux 复制代码
# Why: 变量查询只取tag值,不取数据值,且限制时间范围减少扫描量
from(bucket: "metrics")
  |> range(start: -1h)
  |> filter(fn: (r) => r._measurement == "cpu")
  |> keep(columns: ["host"])
  |> distinct(column: "host")

同时在变量设置中勾选"Refresh on Dashboard load"而非"Refresh on time range change"------后者每次切换时间范围都会重新查变量。

教训 :变量是 Dashboard 性能的隐形杀手。团队约定变量查询必须加 range(start: -1h) 限制,禁止用全量 range(start: -365d) 查变量。这条规则让 20+ 个 Dashboard 的平均加载时间从 8 秒降到 2 秒。

常见问题

Q1:Telegraf 采集频率设多少合适?

默认 10 秒,对服务器监控足够。如果采集 IoT 设备可以降到 1-5 秒。频率越高 InfluxDB 写入压力越大:10 秒间隔 × 200 台服务器 ≈ 2,000 点/秒,InfluxDB 轻松应对;1 秒间隔 × 200 台 ≈ 20,000 点/秒,需要调优。

Q2:Grafana 能不能直接查询降采样后的数据?

可以。在 Grafana Dashboard 中配置两个数据源查询:近 6 小时查原始数据(高精度),6 小时以上查降采样数据(低精度)。通过 Grafana 的混合数据源功能实现。

Q3:监控数据占用太多磁盘怎么办?

三层存储策略:

  • 原始数据(10 秒粒度):保留 7 天
  • 小时聚合(1 小时粒度):保留 90 天
  • 天聚合(1 天粒度):保留 1 年

用 InfluxDB Task 自动降采样,存储空间可以节省 80% 以上。

Q4:TIG 怎么防止单点故障?

这是开头事故的核心教训。三层防护:

  • Deadman Switch:检测 Telegraf 是否还在上报数据
  • InfluxDB 内存限制:防 OOM 被 Kill(踩坑 4)
  • 进程守护 :Docker restart: unless-stopped + healthcheck

更完善的方案是 InfluxDB 集群部署,详见下一篇高可用篇。

TIG 搭建检查清单

markdown 复制代码
【InfluxDB】
□ Docker 部署完成,8086 端口可访问
□ Token 已创建,Telegraf 和 Grafana 已配置
□ Bucket retention 已设置(不要默认 0)
□ 认证开启(生产环境必须)
□ 内存限制已设置(防 OOM)
□ 降采样 Task 已创建

【Telegraf】
□ CPU/内存/磁盘/网络 基础采集正常
□ Docker 容器指标采集正常
□ Nginx/MySQL 应用指标采集正常
□ 采集频率与 InfluxDB 写入能力匹配
□ 数据压缩开启(content_encoding = gzip)
□ internal 插件已启用(Deadman 依赖)

【Grafana】
□ InfluxDB 数据源 Provisioning 配置正常
□ Dashboard JSON 自动导入
□ 告警规则已配置(CPU/磁盘/内存 + Deadman)
□ 通知渠道已配置(分级:电话/企微/邮件)
□ 变量查询已加时间范围限制

总结

TIG 整套搭建下来大约需要 2 小时(含初始化脚本和 Provisioning)。这套组合的优势是全部开源、社区活跃、文档齐全

选型对比:相比 Prometheus + AlertManager 方案,TIG 的优势在于 InfluxDB 支持更灵活的查询语言(InfluxQL/Flux/SQL)和更长的数据保留周期;劣势是告警能力不如 AlertManager 成熟,需要依赖 Grafana Alerting 补充。如果你的场景以基础设施监控为主,Prometheus 生态更完善;如果需要存储 IoT/APM 等非监控类时序数据,TIG 更合适。

如果你正在做信创改造,InfluxDB 在鲲鹏 ARM64 上也能正常跑(见本专栏信创篇)。如果因为合规要求必须用国产时序数据库,openGemini 兼容 InfluxDB 协议,迁移成本相对较低。

适用边界:TIG 方案适合中小规模(< 500 台服务器、< 10TB 数据量)的监控场景。超过这个规模,建议考虑 InfluxDB Enterprise 集群或 Prometheus + Thanos 方案。

📜 真实性声明

本文开头的"45 分钟监控真空"事故基于 2025 年 11 月我在某电商平台项目中亲身经历的真实故障。为保护商业机密,具体业务数据和项目名称已做脱敏处理,但技术细节、故障原因和修复方案保持完整和真实。

下一篇文章进入进阶篇 --- InfluxDB 高可用架构:单点故障怎么避免、开源版和企业版的差距到底有多大。

💬 互动:你的监控系统选的是什么组合?TIG、Prometheus + AlertManager、还是商业产品?有没有遇到过"监控自己挂了"的事故?

相关推荐
Elastic 中国社区官方博客1 天前
将你的 Grafana Kubernetes 仪表板迁移到 Elastic Observability:相同的 PromQL,30 倍更快的查询
大数据·人工智能·elasticsearch·搜索引擎·容器·kubernetes·grafana
做个文艺程序员3 天前
Linux第24篇:Java应用监控体系搭建:Prometheus+Grafana可视化运维
java·grafana·prometheus
取谖慕12.4 天前
Prometheus+Alertmanager+node_exporter+grafana高可用
linux·运维·grafana·prometheus
爱莉希雅&&&5 天前
Prometheus高可用(alertmanager+node_exporter+grafana)
运维·服务器·grafana·prometheus
行者-全栈开发6 天前
【码动四季】Spring Boot 可观测性体系:Micrometer + OpenTelemetry + Grafana 全链路搭建
grafana·opentelemetry·micrometer·全链路追踪·分布式追踪·atomcode·spring boot可观测性
todoitbo7 天前
让数据看板随时可见:Grafana + cpolar 远程访问实战
ui·网络安全·grafana·数据看板·远程访问
维C°9 天前
Grafana实例-Gauge仪表盘面板
grafana
微三云 - 廖会灵 (私域系统开发)9 天前
电商系统监控告警体系从0到1:Prometheus+Grafana+AlertManager的全链路可观测性实践
oracle·grafana·prometheus
维C°9 天前
Grafana-Text面板
grafana