如何从监控工具设置带签名的 Webhook 告警
Webhook 告警是连接正常运行时间监控和其他一切系统的集成纽带:PagerDuty、Opsgenie、内部事故管理系统、工单跟踪器、自定义 Slack 机器人。基本机制很简单(当事故触发时,监控工具向你的端点 POST JSON)。但细节(签名验证、重试行为、幂等性)才是实现中最有意思的部分。本指南涵盖实用的设置方法。
为什么选择 Webhook 而不是电子邮件或 Slack
电子邮件和 Slack 对人类来说运作良好。Webhook 则面向系统。有三类用例值得付出这些额外的设置成本。
路由到运行你的 On-Call 轮值的呼叫工具(PagerDuty、Opsgenie)。驱动一个内部事故管理系统,自动开工单、创建 Slack 讨论串、并运行运维手册。触发自动修复(重启 Worker、扩展集群规模、故障转移到备份区域)。这三类都需要结构化的负载、可重试的可靠投递以及身份验证。Webhook 三者兼备;电子邮件和 Slack 一项都没有。
预期的负载结构
大多数监控工具在事故事件上会发送类似结构的 JSON 负载。确切的字段名称有所不同,但四个部分是标准的。
- event:monitor.down 或 monitor.up,让你的端点可以路由事故的开启和关闭。
- monitor:id、名称、类型、目标、最新状态。足以识别出哪里出现了问题。
- check:状态码、响应时间、错误消息。诊断的细节信息。
- timestamp:ISO 8601 格式,UTC 时区。对于排序和去重至关重要。
验证 HMAC 签名
任何暴露在公共互联网上的 Webhook 端点都需要签名验证。如果没有签名验证,猜到 URL 的攻击者就能伪造一个 monitor down 事件,从而触发你的修复流程、呼叫流程和支持响应。有了 HMAC 验证,只有你的监控工具能签出有效负载。
模式如下:监控工具发送一个 X-Monitorah-Signature 头,其内容是使用共享密钥对原始请求体进行 HMAC-SHA256 计算的结果。你的端点使用相同的密钥重新计算请求体的 HMAC,以恒定时间方式与该头部值进行比较,不匹配则拒绝。请使用请求体的原始字节计算 HMAC,而不是已解析的 JSON:哪怕一个空白字符的差异都会产生不同的签名。
重试与幂等性
Webhook 投递是尽力而为的,并不保证一定送达。两种模式可以让你的端点对重试和部分失败具备健壮性。
- 优雅地处理重试。大多数监控工具会对 5xx 响应进行指数退避重试,最长可持续 24 小时。如果你无法处理事件,请返回 5xx;如果能处理,请返回 2xx。
- 将事件 id 视为幂等键。同一次事故的开启可能在网络抖动期间触发两次 Webhook。通过存储事件 id 并跳过已处理的事件来去重。
- 快速响应。即使下游处理需要更长时间,也要在 5 秒内返回 2xx。如有需要,请使用任务队列。
与 PagerDuty 集成
PagerDuty 通过其 Events API v2 接受通用 Webhook。流程如下:你的监控工具将自定义 JSON 负载 POST 到你自己的处理程序。你的处理程序验证 HMAC,将负载映射成 PagerDuty 所期望的结构,再用你的路由密钥 POST 到 PagerDuty 的事件端点。
这个转换层正是添加去重、升级规则以及按监控划分的路由密钥的合适位置。一旦事件被摄入,PagerDuty 的界面就会处理轮值。保持转换器的简洁性。最干净的版本不到一百行代码。
20 行代码的 Webhook 入门处理程序
模式如下:接收 POST 请求,提取 X-Monitorah-Signature 头,使用共享密钥对原始请求体进行 HMAC-SHA256 计算,并以恒定时间方式与该头部进行比较。如果匹配,则解析 JSON、按事件 id 去重并分发。如果不匹配,则直接返回 401,不做任何处理。这个处理程序是上层每一项集成的基础构建块:PagerDuty、Opsgenie、Slack 机器人、内部事故管理系统。一旦它就位,每一项新集成都只是几行分发逻辑而已,仅此而已。