3. 数据采集层:Agent部署、日志采集、API接口对接、SNMP Trap接收

数据采集层是整个监控系统的「眼睛」和「耳朵」。说白了,这一层要是没做好,后面再牛的告警算法、再炫的仪表盘,都是空中楼阁。我做了这么多年运维,见过太多团队在采集层偷懒,结果线上出问题时数据对不上,那叫一个抓瞎。

今天咱们就把这四块东西掰开揉碎讲清楚:Agent怎么部署、日志怎么采、API怎么对接、SNMP Trap怎么收。

3.1 Agent部署:轻量、安全、可管理

Agent是部署在被监控机器上的采集程序。我个人习惯把它分成两类:侵入式Agent和非侵入式Agent。

类型 典型代表 优点 缺点
侵入式Agent Telegraf、Prometheus Node Exporter 采集指标丰富,可自定义插件 占用系统资源,需维护版本
非侵入式Agent SNMP、IPMI、SSH远程采集 无需安装,对业务零影响 采集粒度粗,性能开销在管理端

我在项目中遇到过一种情况:某团队给2000台服务器全装了侵入式Agent,结果每台Agent默认开启了一堆不必要的插件,CPU占用直接飙了5%。嗯,这里要注意——Agent部署的第一原则:只采集你需要的指标。

我的部署建议:
  • 使用容器化部署(Docker + systemd),方便版本回滚
  • Agent配置统一由配置中心下发,不要手动改每台机器的配置文件
  • 开启Agent自身的健康检查接口,监控系统要能监控自己

下面这张图是我常用的Agent部署架构,你可以参考一下:

Agent部署架构图 Agent 部署架构 配置中心 Agent 集群(容器化 + systemd 托管) 健康检查接口 /metrics /health 采集服务(Kafka / MQ) 时序数据库(InfluxDB / VictoriaMetrics) Agent 从配置中心拉取采集规则,上报到采集服务,最终落入时序库

3.2 日志采集:从混乱到有序

日志采集比指标采集麻烦得多。指标是结构化的,日志是非结构化的。你想想看,同样是「连接超时」,不同开发写的日志格式可能完全不一样。

我常用的日志采集方案是 Filebeat + Logstash + Elasticsearch 这条链路。不过现在也有不少人直接用 Vector 或 Fluentd,性能更好。

日志采集的核心原则:

  • 所有日志必须包含时间戳、日志级别、服务名、TraceID
  • 日志文件必须按天或按小时滚动,避免单文件过大
  • 采集端必须做限流,防止日志洪峰打挂采集管道

我曾经遇到过一件事:某次大促,业务日志量突然暴涨10倍,Filebeat没做限流,直接把Logstash打OOM了。从那以后,我所有日志采集配置里都会加这么一段:

# Filebeat 限流配置示例
filebeat.inputs:
- type: log
  enabled: true
  paths:
    - /var/log/app/*.log
  # 每秒最多采集1000行
  harvester_limit: 1000
  # 背压机制:当输出阻塞时暂停采集
  backoff.init: 1s
  backoff.max: 60s

output.logstash:
  hosts: ["logstash:5044"]
  # 启用压缩,减少网络带宽
  compression_level: 6
  # 批量发送,提高吞吐
  bulk_max_size: 2048

嗯,这里有个小技巧:日志采集一定要做「采样」和「降级」。比如DEBUG日志可以全量采集,但ERROR日志必须全量保留。我习惯在Logstash里做过滤,把WARN以下的日志直接丢弃,只保留ERROR和业务关键日志。

3.3 API接口对接:标准化是王道

API对接这块,说白了就是「别人把数据准备好了,你去拉」。最常见的场景是对接云厂商的监控API、对接内部CMDB、对接第三方SaaS服务。

我个人习惯把API对接分成三步走:

  1. 认证与鉴权:、OAuth2、JWT,选一种就行。别搞太复杂,但也不能裸奔。
  2. 数据拉取:全量拉取还是增量拉取?全量适合数据量小的场景,增量适合大数据量。我一般先用全量做一次基线,后面全走增量。
  3. 数据转换:把第三方API返回的数据格式,转成内部统一的指标模型。这一步最容易踩坑。
避坑指南:
  • 第三方API可能有调用频率限制,一定要做重试和退避
  • API返回的数据可能包含空值或异常值,采集层要做数据清洗
  • 接口变更时要有版本管理,我见过API升级后字段名变了,采集直接崩了

下面是一个简单的API对接代码示例,我用Python写的,你可以参考:

import requests
import time
from typing import Dict, List

class APICollector:
    def __init__(self, base_url: str, : str):
        self.base_url = base_url
        self.headers = {"Authorization": f"Bearer {}"}
        self.session = requests.Session()
        self.session.headers.update(self.headers)

    def fetch_metrics(self, endpoint: str, params: Dict = None) -> List[Dict]:
        """拉取指标数据,带重试和退避"""
        max_retries = 3
        for attempt in range(max_retries):
            try:
                resp = self.session.get(
                    f"{self.base_url}/{endpoint}",
                    params=params,
                    timeout=10
                )
                resp.raise_for_status()
                return resp.json().get("data", [])
            except requests.exceptions.RequestException as e:
                if attempt == max_retries - 1:
                    raise
                time.sleep(2 ** attempt)  # 指数退避
        return []

    def transform(self, raw_data: List[Dict]) -> List[Dict]:
        """转换为内部统一格式"""
        transformed = []
        for item in raw_data:
            transformed.append({
                "metric": item.get("name"),
                "value": float(item.get("value", 0)),
                "timestamp": int(time.time()),
                "tags": {
                    "host": item.get("host"),
                    "region": item.get("region", "unknown")
                }
            })
        return transformed

3.4 SNMP Trap接收:被动采集的艺术

SNMP Trap跟前面几种方式不太一样。Agent和API都是主动去拉数据,而Trap是设备主动推数据给你。说白了,它是「被动采集」。

我最早接触SNMP Trap是在管网络设备的时候。交换机、路由器、防火墙出了故障,会主动发一个Trap报文到你的Trap Receiver。你想想看,如果靠轮询去查设备状态,等发现故障的时候黄花菜都凉了。

SNMP Trap的接收流程大概是这样的:

  • 设备配置Trap目标地址(你的Receiver IP)
  • 设备发生事件时,发送UDP报文到Receiver的162端口
  • Receiver解析报文,提取OID、Value、时间戳
  • 将解析结果转成内部事件,写入消息队列

我的SNMP Trap实践建议:

  • 使用 snmptrapd 作为Receiver,配合 snmpTT 做事件格式化
  • 一定要配置Trap的过滤规则,否则设备的风扇告警能把你刷屏
  • Trap报文是UDP的,可能会丢包。建议在发送端做重传,或者在Receiver端做去重

我曾经遇到过一个问题:某厂商的防火墙每5分钟发一次「设备正常运行」的Trap,结果Receiver没做去重,告警系统每5分钟收到一条「设备正常」的告警。嗯,这其实是个设计缺陷——Trap只应该发异常事件,不要发心跳。

下面是一个简单的snmptrapd配置示例:

# /etc/snmp/snmptrapd.conf
# 允许所有来源的Trap
authCommunity log,execute,net public

# 格式化输出
format2 %V\n%v\n%.4y-%.2m-%.2l %.2h:%.2j:%.2k %A [%B] %N\n\t%W Trap: %q\n\t%v\n

# 执行外部脚本处理Trap
traphandle default /usr/local/bin/trap_handler.sh

# 忽略某些OID(比如设备心跳)
ignoreOID .1.3.6.1.4.1.9.9.43.1.1.1.0

最后说一句:数据采集层是整个监控系统的地基。Agent部署要轻量,日志采集要限流,API对接要标准化,SNMP Trap要去重。这四块做好了,后面的数据处理和告警才能站得住脚。