Nightingale 集成 Canal 监控:基于 Categraf Prometheus 采集的 MySQL binlog 全流程实践
【免费下载链接】nightingaleNightingale is to monitoring and alerting what Grafana is to visualization.项目地址: https://gitcode.com/GitHub_Trending/ni/nightingale
导读
本文围绕 Nightingale 仓库内置的 Canal 集成包(integrations/Canal)展开,完整讲解如何让 Canal Server 通过 Prometheus 格式端点暴露自身指标、如何用 Categraf 的input.prometheus插件采集并打上可区分的标签、以及如何借助仓库自带的 "Canal instances" 仪表盘查看 binlog 解析 TPS、延迟、Client 吞吐与 Store 内存水位。读完本文,你将掌握一套可复制、可排障的 Canal 监控接入方案,并理解为什么不能随意覆盖destination、instance这两个标签。
监控链路总览
Canal 是阿里巴巴开源的 MySQL binlog 增量订阅与消费组件,运行时会持续解析主库 binlog 并向下游 Client 推送格式化事件。要观测它的运行健康度,监控链路分三段:
MySQL binlog │ dump/parse ▼ Canal Server ──(HTTP /metrics, 默认 11112)──► Categraf input.prometheus │ interval=15 拉取 ▼ Prometheus 兼容时序库(Nightingale) │ ▼ "Canal instances" 内置仪表盘 / 告警规则Canal Server 本身就提供 Prometheus 格式的指标端点(官方默认端口11112),因此无需额外安装 exporter——这正是本项目集成包选择“直接用 Categraf 的 Prometheus input 采集”的原因,integrations/Canal/markdown/README.md 开篇即明确了这一思路。
在 Nightingale 仓库中,integrations/目录是各组件 Categraf 采集配置与指标命名的“事实来源”(ground truth):aiagent/tools/integrations_loader.go 的注释明确写道,每个组件的markdown/README.md和collect/*/*.toml会被转成文档条目并入索引,LLM 通过search_n9e_docs检索时能直接搜到真实的[[instances]]写法;而 center/integration/init.go 则会在中心端启动时读取每个组件的markdown/README.md作为内置组件说明,读取dashboards/下的 JSON 作为内置仪表盘模板。也就是说,本文下面要用的配置与仪表盘,都是随仓库发布的、可直接落地的标准做法。
Canal Server 端配置:打开指标拉取端口
Canal 的指标端口由canal.properties中的canal.metrics.pull.port控制,官方示例默认值为11112。确认配置文件中已启用该端口:
canal.metrics.pull.port=11112修改配置后需重启 Canal Server 使其生效。启动完成后,先确认端点能正常返回指标,避免把“Canal 未暴露指标”误判成“采集端配置错误”。使用 curl 验证并筛选canal_instance前缀的指标:
curl -fsS http://127.0.0.1:11112/metrics | grep canal_instance | head-f(fail fast on HTTP error)、-s(静默)、-S(出错时显示错误信息)三个参数组合,保证只有 HTTP 状态码为 2xx 且输出非空时命令才成功退出。如果这里无输出或报错,请先排查 Canal 是否真正开启了指标拉取端口(例如端口被占用、canal.metrics.pull.port未生效等),而不是直接去改采集端。
Categraf 端配置:input.prometheus 采集
确认 Canal 端点正常后,在 Categraf 的conf/input.prometheus/目录下新建canal.toml:
interval = 15 [[instances]] urls = ["http://127.0.0.1:11112/metrics"] url_label_key = "canal_server" url_label_value = "{{.Host}}" labels = { job = "canal" }逐项说明:
| 配置项 | 取值 | 作用 |
|---|---|---|
interval | 15(秒) | 全局/插件级采集周期,即每 15 秒从 Canal 拉取一次/metrics |
urls | ["http://127.0.0.1:11112/metrics"] | 要抓取的 Prometheus 端点列表;多个 Canal Server 可写成多个 URL |
url_label_key | "canal_server" | 为每条时序追加的标签名,用于区分不同的 Canal 目标 |
url_label_value | "{{.Host}}" | 标签值模板,{{.Host}}会被替换为 URL 的主机部分(不含端口) |
labels | { job = "canal" } | 额外静态标签,统一标记数据来源为 canal,便于检索与过滤 |
参考同目录下 integrations/N9E/markdown/README.md 的 N9E 自监控配置可以看到同一模式:url_label_key/url_label_value组合用于把“来自哪台主机/哪个实例”写进标签,labels用于声明 job。多台 Canal Server 需要全部列入urls,并通过url_label_value的模板变量保证每台机器都有可区分的标识。
注意:N9E 示例用
instance作为 URL 标签键,而 Canal 示例刻意用canal_server——因为 Canal 原始指标里已经带有instance标签(表示 destination 的实例),Categraf 侧再用instance会与之冲突(详见下一节)。
为什么不能覆盖destination和instance标签
这是本集成包中最重要的一条约束,integrations/Canal/markdown/README.md 特别强调:
不要覆盖 Canal 原始指标中的
destination和instance标签,仪表盘变量依赖这些标签。
Canal 暴露的指标本身带有丰富的维度标签:
destination:Canal 的订阅目标名(对应canal.properties中配置的 destination,一个 Canal Server 可同时跑多个 destination);instance:destination 对应的实例标识;- 此外还有
parser、parallel、packetType、le等维度,分别标识解析器序号、并行模式、Client 包类型和直方图桶。
内置仪表盘正是靠这些标签做聚合和变量联动(下一节会看到所有面板的表达式都带destination=~"$destination"),如果在 Categraf 侧用labels强行覆盖destination或instance,会导致:
- 仪表盘顶部的
destination变量查不到值(变量定义是label_values(canal_instance, destination)); - 多个 destination 的曲线被混在一起,无法区分;
packetType、parser等原本用于分组的标签维度被破坏,部分面板(如 Client requests、并行解析器拆分)失去意义。
因此labels里只应放job这类“不冲突”的自定义标签,采集目标的标识交给url_label_key/url_label_value。
内置仪表盘深度解析:canal_by_categraf.json
集成包自带一个名为"Canal instances"的仪表盘模板,定义在 integrations/Canal/dashboards/canal_by_categraf.json,数据源类型为prometheus。它只有一个查询型变量destination,定义为:
label_values(canal_instance, destination)即从canal_instance系列的所有destination标签值动态生成下拉选项,所有面板再通过destination=~"$destination"与之联动。这就是上一节“禁止覆盖destination标签”的根源。
仪表盘按四个分组组织,从上到下依次为Instance status(实例状态)、Throughput(吞吐)、Client(客户端)、Store(存储),各组面板与核心 PromQL 如下。
Instance status:实例基本信息
| 面板 | 说明 | 核心表达式(节选) |
|---|---|---|
| Basic | Canal instance 基本信息 | canal_instance{destination=~"$destination"}、canal_instance_parser_mode(并行解析器)、canal_instance_store(批量模式 batchMode、缓冲区大小 size) |
| Network bandwidth | 网络带宽占用:inbound 为读取 MySQL binlog,outbound 为向 Client 传输格式化 binlog | rate(canal_instance_received_binlog_bytes{parser="0"}[2m]) / 1024、rate(canal_instance_client_bytes[2m]) / 1024,parser="1"/"2"分别展示并行解析器各序号的分量 |
| Delay | 延时:master 为 Canal Server 相对 MySQL master 的延时(master heartbeat 机制可在 idle 状态下刷新延时);put/get/ack 分别以 store put、client get、client ack 操作的时间点为基准 | canal_instance_traffic_delay / 1000、canal_instance_put_delay / 1000、canal_instance_get_delay / 1000、canal_instance_ack_delay / 1000(除以 1000 将原始值换算为秒级展示) |
| Blocking | sink 线程 blocking 占比;dump 线程 blocking 占比(仅 parallel mode) | clamp_max(rate(canal_instance_publish_blocking_time{parser="0"}[2m]), 1000) / 10、clamp_max(rate(canal_instance_sink_blocking_time[2m]), 1000) / 10 |
Throughput:吞吐
| 面板 | 说明 | 核心表达式 |
|---|---|---|
| TPS(table rows) | 以 master 变更行数(table rows)为基准计算的 binlog 处理 TPS,区分 put/get/ack 三类操作 | rate(canal_instance_put_rows[2m])、rate(canal_instance_get_rows[2m])、rate(canal_instance_ack_rows[2m]) |
| TPS(MySQL transaction) | 以 MySQL transaction 为单位的 binlog 处理 TPS | rate(canal_instance_transactions[2m]) |
这两个面板一个看行级吞吐、一个看事务级吞吐,配合使用能判断 Canal 的解析压力落在哪一层。
Client:客户端消费情况
| 面板 | 说明 | 核心表达式 |
|---|---|---|
| Client requests | Canal instance 接收到的请求统计,按 packet type 分类 | canal_instance_client_packets,图例{{packetType}} |
| Client QPS | client 请求的 GET 与 ACK 包 QPS | rate(canal_instance_client_packets{packetType="GET"}[2m])、rate(canal_instance_client_packets{packetType="CLIENTACK"}[2m]) |
| Empty packets | server 响应 GET 请求但返回空包的占比 | rate(canal_instance_client_empty_batches[2m])对比rate(canal_instance_client_packets{packetType="GET"}[2m]) |
| Response time | Canal client 请求响应时间概况(直方图) | rate(canal_instance_client_request_latency_bucket[2m]),图例{{le}}ms |
GET/ACK 的 QPS 直接反映下游 Client 的消费节奏;Empty packets 占比过高通常意味着 binlog 消费跟不上或下游长时间不拉取。
Store:内存队列水位
Canal 使用内存 ringbuffer(store)在解析线程与 Client 之间缓冲事件,水位过高往往预示下游消费阻塞:
| 面板 | 说明 | 核心表达式 |
|---|---|---|
| Store remain events | ringbuffer 内未释放的 events 数量 | canal_instance_store_produce_seq - canal_instance_store_consume_seq |
| Store remain mem | ringbuffer 内未释放 events 占用的内存(KB) | (canal_instance_store_produce_mem - canal_instance_store_consume_mem) / 1024 |
两个面板都用“生产序号/内存 − 消费序号/内存”的差值刻画积压量,是判断 Client 是否阻塞的最直接指标。各面板的中文描述词条可以在 integrations/Canal/i18n/en_US.json 中查到对应英文,便于在 Nightingale 界面切换语言时对照理解。
常见问题与排障
场景一:TPS、延迟和 Client 面板为空
只启动了 Canal Server、但没有创建 destination,或没有 binlog 读写流量时,TPS、延迟和 Client 面板为空是正常现象,不是采集故障。原因在于:
- 这些指标由 destination 的解析线程(parse/sink)产生,没有 destination 就没有对应序列;
- 没有 binlog 读写,
rate(...)对计数器求导的结果为 0 或不产生新样本,曲线自然不出现。
正确做法是先确认 Canal 侧确实有 destination 且主库有写入流量,再检查面板。而Instance status 和 Store 分组的面板基于canal_instance等基础序列,只要 Server 起来了通常就有数据,可用来反向确认采集链路是否打通。
场景二:destination变量下拉为空
检查是否在 Categraf 的labels或url_label_key里使用了destination或instance作为标签名,把原始标签覆盖掉了。按本文第三节的配置(url_label_key = "canal_server"、labels = { job = "canal" })即可规避。
场景三:curl 验证通过但仪表盘无数据
链路排查顺序:curl确认 Canal 端点 → 确认 Categraf 采集间隔interval与拉取日志无报错 → 在 Nightingale 时序库中按canal_instance检索是否存在数据 → 检查仪表盘datasource变量选中的是否是对应的 Prometheus 数据源。数据到达时序库后,可直接复用本集成包仪表盘模板(在 Nightingale 内置组件中搜索 Canal 导入)。
总结
Canal 集成是 Nightingale 生态中“零 exporter、纯 Prometheus 拉取”的典型代表:Canal 端只需打开canal.metrics.pull.port,Categraf 端只需一份十几行的input.prometheus配置,即可把 binlog 解析的 TPS、四类延时、Client 消费 QPS、Store 积压水位等关键指标汇入 Nightingale,配合随包发布的 "Canal instances" 仪表盘开箱即用。整个过程的配置模板与仪表盘定义都沉淀在 integrations/Canal 目录中,既是实操参考,也是 AI 检索链路(aiagent/tools/integrations_loader.go)中的配置权威来源——记住“不覆盖destination/instance标签”这一条红线,就能稳定复现这套方案。
【免费下载链接】nightingaleNightingale is to monitoring and alerting what Grafana is to visualization.项目地址: https://gitcode.com/GitHub_Trending/ni/nightingale
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考