太卷了,史上最简单的监控系统 catpaw 简介
指标监控的痛点
当下比较流行的监控系统,比如 Prometheus、Nightingale、VictoriaMetrics,都是基于数值型指标的监控系统,这类监控系统的痛点在于:告警的时候只能拿到异常值,以及有限的几个标签,难以拿到更详细的信息。比如 HTTP 探测监控,通常用监控值表示不同的错误:
Success = 0
ConnectionFailed = 1
Timeout = 2
DNSError = 3
AddressError = 4
BodyMismatch = 5
CodeMismatch = 6
告警的时候,比如你收到一个异常值,说访问 http://x.com 异常了,异常值是 3,需要比较资深的人才能知道这个异常是 DNS 解析失败,对于普通研发,就会很懵。当然了,我们可以在告警规则的备注里把这个异常值和错误消息的对应关系写上,但总觉得这样不太优雅。如果我们能在告警的时候,把详细错误信息也一并告诉接收者,那就更好了。比如,我们更希望的是收到类似下面的信息:
本文看完,就可以做到上图的效果了,我们继续。
当然了,也可以用日志系统来做这类监控,比如 ElasticSearch、Loki,然后辅以一个周期性查询的告警引擎就可以了。但是整个架构就复杂了,另外,如果还要考虑告警接收人管理、告警媒介对接管理、告警触达策略、降噪规则、静默规则、抑制规则、排班、认领、升级等等,那就更复杂了,有没有轻量的工具可用?
思路
最轻量的工具,显然是 SaaS 类工具,无需部署、维护,即开即用,快猫星云提供了 FlashDuty,用于聚合接收各个监控系统的告警事件,比如 Prometheus、Zabbix、Nightingale、公有云云监控,提供告警事件的统一纳管、灵活派发、静默、排班认领升级等功能,这里不再赘述。基本上,可以认为 FlashDuty 已经完成了事件后续处理的所有功能。如果我们能有一个工具,可以产出告警事件,然后把告警事件推送到 FlashDuty,那就可以实现非常完备的功能了。而且 FlashDuty 是 SaaS 化的,无需维护,我们只需要搞一个轻量的小工具跑在自己的环境下采集异常事件即可,这个小工具就是 catpaw。
适合的场景
catpaw 不能取代指标监控和日志系统,它只是一个轻量的 check 工具,一些场景举例:
- 探测某个 HTTP 地址,如果不可用,把不可用的原因发出来
- 探测某个 TCP 地址,如果不可用,把不可用的原因发出来
- 探测某个文件,如果文件不存在,或者 MD5 发生变化,发出告警事件
- 探测 ulimit 配置,如果发现不合理,发出告警事件
- 探测目录是否可读可写,如果异常发出告警事件
- 探测是否有目录已写满,如果写满的目录,发出告警事件,把
df -h
的结果一并发出来 - 探测某个进程是否存在,如果不存在,发出告警事件
- 探测 MySQL 慢查询,如果发现慢SQL,发出告警事件,事件中带上SQL
- 探测 MySQL 主从延迟,如果延迟超过阈值,发出告警事件,事件中带上延迟时间以及两个 Thread 的情况
- 接收 SNMP Trap 消息,如果消息中包含某个关键字,发出告警事件
- 检查系统日志,如果出现某个关键字,告警,把日志详情一并发出来
- ….
更多场景不再赘述,其特点基本上可以概括为:
- check 的时候已经可以知道是否正常
- check 的时候大概率可以拿到异常原因或者现场值,把这些信息一并发出来
其实吧,这就是 nagios 的逻辑,只不过 nagios 安装复杂、组件散乱、缺少 FlashDuty 这样的事件后续处理能力。我更想要的是一个轻量的事件采集器,配置 FlashDuty,完美。
catpaw 的安装
catpaw 刚刚开始,目前只提供了 Linux 版本,下载地址:https://download.flashcat.cloud/catpaw-v0.1.1-linux-amd64.tar.gz
下载解压缩,可以看到如下文件:
catpaw-v0.1.1-linux-amd64
├── catpaw
├── conf.d
│ ├── config.toml
│ ├── p.exec
│ │ └── exec.toml
│ └── p.http
│ └── http.toml
└── scripts
├── demo.sh
└── ulimit.sh
5 directories, 6 files
其中:
- catpaw:二进制文件
- conf.d:配置文件目录,下面的 config.toml 是主配置,p.exec 和 p.http 是插件配置,目前只提供了这两个插件,exec 插件可以自定义脚本,提供了无限可能
- scripts:一些示例脚本
主配置
config.toml 的内容:
[global]
# 全局采集频率,如果插件没有配置采集频率,就使用全局的
interval = "30s"
# 全局附加标签,这些标签会附加到所有的事件上
# $hostname $ip 是特殊变量,会被替换为当前主机的 hostname 和 ip
[global.labels]
from_agent = "catpaw"
from_hostname = "$hostname"
from_hostip = "$ip"
# 日志配置,默认是 json 格式的日志,打印到标准输出
# 一般用 systemd 托管,日志写在 /var/log/messages,自动切分
[log]
level = "info"
# format = "json"
# output = "stdout"
# fields = {}
# 事件通知地址,目前只支持 flashduty,当然,你也可以模仿 flashduty 的接口,自己实现一个事件接收服务
[flashduty]
url = "https://api.flashcat.cloud/event/push/alert/standard?integration_key=x"
timeout = "10s"
flashduty.url 需要去 FlashDuty 获取,地址在这里:https://console.flashcat.cloud/,可以免费注册,注册之后,系统会自动引导你创建一个协作空间,然后在协作空间下,添加告警接入专属集成就可以了:
点击刚才创建的自定义集成,我的自定义集成取名为 catpaw,点击之后出现右侧抽屉侧拉板。
FlashDuty 如果使用过程有问题,可以联系我,我会帮助你解决。注册进来默认有两周的全功能免费试用期,试用期过后,如果你觉得不错,可以继续使用(有免费版、标准版、专业版三个版本),如果你觉得不好用,扔一边就好,无需有压力。
插件配置
当前是 v0.1.1 版本,只提供了两个插件,http 插件和 exec 插件,exec 插件可以自定义脚本,所以,提供了无限扩展可能,下面我们分别介绍。
http 插件
http 插件的配置文件是 conf.d/p.http/http.toml,内容如下:
[[instances]]
targets = [
"https://baidu.com",
]
# # Concurrent requests to make per instance
# concurrency = 10
# # gather interval
# interval = "30s"
# # Optional append labels
# labels = { env="production", team="devops" }
## Set http_proxy (catpaw uses the system wide proxy settings if it's is not set)
# http_proxy = "http://localhost:8888"
## Interface to use when dialing an address
# interface = "eth0"
## HTTP Request Method
# method = "GET"
## Set timeout (default 5 seconds)
# timeout = "5s"
## Whether to follow redirects from the server (defaults to false)
# follow_redirects = false
## Optional HTTP Basic Auth Credentials
# basic_auth_user = "username"
# basic_auth_pass = "pa$$word"
## Optional headers
# headers = ["Header-Key-1", "Header-Value-1", "Header-Key-2", "Header-Value-2"]
## Optional HTTP Request Body
# payload = '''
# {'fake':'data'}
# '''
## Optional TLS Config
# use_tls = false
# tls_ca = "/etc/catpaw/ca.pem"
# tls_cert = "/etc/catpaw/cert.pem"
# tls_key = "/etc/catpaw/key.pem"
## Use TLS but skip chain & host verification
# insecure_skip_verify = false
[instances.expect]
## Optional expected response status code.
response_status_code = ["20*", "30*"]
## Optional substring match in body of the response (case sensitive)
response_substring = "html"
## Optional alert when cert will expire in x hours
cert_expire_threshold = "72h"
[instances.alerting]
## Enable alerting or not
enabled = true
## Same functionality as Prometheus keyword 'for'
for_duration = 0
## Minimum interval duration between notifications
repeat_interval = "5m"
## Maximum number of notifications
repeat_number = 3
## Whether notify recovery event
recovery_notification = true
## Choice: Critical, Warning, Info
severity = "Warning"
看起来配置很多,实际非常清晰,我做一个简单讲解。要探测 HTTP 地址,只需要在 targets 里面填写即可,可以填写多个,每个地址用双引号包裹,多个地址用逗号分隔,如下:
[[instances]]
targets = [
"https://baidu.com",
"http://a.cn",
]
比如我们把公司依赖的所有第三方接口都配置在这里,那可能有好几百个,探测的时候最好要控制一下并发度,所以提供了 concurrency 配置,可以控制并发度。interval 是探测频率,如果不配置,就使用全局的探测频率。labels 是附加标签,这些标签会附加到这个 [[instances]]
产生的所有事件上。
下面的 http_proxy、interface、method、timeout、follow_redirects、basic_auth_user、basic_auth_pass、headers、payload、use_tls、tls_ca、tls_cert、tls_key、insecure_skip_verify 这些都是 http 客户端的配置,catpaw 作为一个 http 客户端,向 targets 中的地址发探测请求,需要有一些基本的配置,看起来配置项挺多,都可以维持默认值,不需要修改。
其中,有些公司会使用自签证书,此时需要配置:
use_tls = true
insecure_skip_verify = true
接下来是 [instances.expect]
部分,配置了一些期望,比如期望返回状态码是 20x 或者 30x,期望返回的 body 中包含 html 字符串,期望证书过期时间大于 72 小时,如果不满足期望,就会触发告警。
接下来是 [instances.alerting]
部分,配置了告警的一些参数,比如告警是否启用、告警触发要求的持续时长(类似 Prometheus 的 for 关键字)、告警的级别、告警的重复次数、告警的重复间隔、是否通知恢复等等。
targets 中可以配置一个假地址,比如 http://a.cn
,这样待会启动 catpaw 之后立马就可以看到效果。
exec 插件
exec 插件核心是指定要执行的脚本路径,比如我的一个配置样例:
[[instances]]
commands = [
"/root/works/catpaw/dist/catpaw-v0.1.1-linux-amd64/scripts/*.sh"
]
其他都可以维持默认,exec 插件的 [instances.alerting]
没有指定 severity,因为 exec 插件的脚本是自行控制 severity(通过脚本的 stdout),大家看一下 scripts 目录下的样例脚本就知道了。
比如 ulimit.sh :
#!/bin/sh
if [ "$1" ]; then
threshhold=$1
else
threshhold=2048
fi
count=$(ulimit -n)
status="Ok"
if [ $count -lt $threshhold ]; then
status="Warning"
fi
echo '[
{
"event_status": "'${status}'",
"labels": {
"check": "ulimit check"
},
"title_rule": "$check",
"description": "ulimit -n: '${count}', too low, should be greater than '${threshhold}'"
}
]'
这个脚本的作用是 check 系统的 ulimit 配置,默认 Linux 句柄限制是 1024,生产环境显然是不够用的,所以我们可以通过这个脚本来检查 ulimit 配置,如果低于 2048,就触发告警。
脚本的输出是一个 json 数组,每个元素是一个事件,事件的字段有:
- event_status:事件状态,可选值:Ok、Info、Warning、Critical
- labels:附加标签,这些标签会附加到这个事件上,不同的事件就是通过 labels 来区分的
- title_rule:事件标题,支持模板,模板中的变量是 labels 中的字段(具体可以查阅刚才提到的 FlashDuty 的文档)
- description:事件描述,可以使用 markdown 格式
启动测试
我们只是修改了 config.toml 中的 flashduty.url,exec.toml 中的 scripts 路径,其他都维持不变,启动测试:
./catpaw --configs conf.d
启动之后,可以看到控制台输出了一些信息,产生了 3 条告警事件,推给了 FlashDuty,FlashDuty 也收到了告警,如下图:
这里我点击 HTTP check failed 这个告警,可以看到详情:
描述信息是 markdown 的,可以明显看到是 DNS 的问题(lookup x on x:53: no such host 是典型的 DNS 报错),非常清晰。
测试恢复消息
这里我手工修改 scripts 下的 demo.sh,把 event_status 返回 Ok:
#!/bin/sh
echo '[
{
"event_status": "Ok",
"labels": {
"check": "script demo"
},
"title_rule": "$check",
"description": "this is description, support markdown"
}
]'
稍等一下下,FlashDuty 就会收到恢复消息:
点击告警详情,点开关联事件,可以看到 Ok 的事件:
总结
Catpaw 是一个非常简单的告警工具,它的核心是插件机制,插件机制使得 Catpaw 可以很容易地扩展,比如我想要一个插件,可以检查 MySQL 的连接数,那么我只需要写一个脚本,输出事件 json 数组,然后配置到 exec 插件中,就可以了。
当然,后面我们也会把更多常见的场景直接内置到 catpaw 的二进制中,开箱即用。各位老铁,有好的脚本,欢迎提交到 https://github.com/flashcatcloud/catpaw-scripts,群策群力,争取搞个牛逼的脚本库,我们会把好的脚本合并到 catpaw 的二进制中,方便大家使用,一起来吧!
更新
本文发布的时候,catpaw 已经更新到了 v0.2.0 版本,下载地址:https://download.flashcat.cloud/catpaw-v0.2.0-linux-amd64.tar.gz,增加了两个插件:
net 插件
用于 tcp、udp 连接检查,比如探测网络连通性、端口是否在监听等等,如果有异常,catpaw 会把详细错误信息推送到 FlashDuty。
journaltail 插件
用于过滤系统日志中的异常关键字,比如下面的一些场景:
- 系统发生 OOM,某个进程被 Kill 了
- 某个硬盘、RAID 故障
- Conntrack Table 满了
- 等等
这些异常都会在系统日志打印异常信息,可以使用 journaltail 插件,把这些异常信息过滤出来,推送到 FlashDuty。关键配置如下:
[[instances]]
# journalctl -S -${time_span}
time_span = "1m"
# relationship: or
keywords = ["Out of memory"]
# check rule name
check = "oom killed"
# # gather interval
interval = "30s"
实际插件底层就是调用了 journalctl 命令:journalctl --since -${time_span} --no-pager --no-tail
如果有日志,就开始匹配 keywords,任何一个 keyword 匹配上,就触发告警。
check 字段姑且可以理解为探测规则的名称,可以自定义,比如上面的 oom killed,这个名称会显示在 FlashDuty 的告警标题中,方便区分不同的告警。
enjoy :)