FEATURED · 精选文章

用Python打造轻量级ChatOps机器人:云服务器监控与自动化运维实践

发布时间 / 2026/9/14 4:19:48
来源 / 创域科博编辑部
栏目 / 资讯中心
用Python打造轻量级ChatOps机器人:云服务器监控与自动化运维实践 凌晨2点47分手机铃声把我从梦里拽出来。电话那头是机房值班同事的声音你那台跑着 Jenkins 的机器磁盘满了构建全挂了。我迷迷糊糊打开电脑SSH 登上去一看果不其然/var/lib/docker 占了 87%。问题是这台机器三天前我才手动清理过日志谁能想到镜像构建又悄悄吞了几十个 GB。那一刻我就在想我手头管着五台云服务器分布在三个不同的服务商平台为什么每次都是出了问题、客户投诉了、同事打电话了我才后知后觉那段时间我试过不少现成的监控方案Netdata、Cockpit、Prometheus Grafana 全家桶都折腾过要么太重要么只展示不处理要么安全策略上还得专门给监控系统开端口想想就头大。后来我换了个思路我要的根本不是一块大屏看板我要的是一个能在出问题时立刻通知我、同时允许我在手机上随手敲一条命令就把服务拉起来的工具。做完前期调研之后我决定自己写一个。这就是 CloddsBot 的由来——一个用 Python 写的、以聊天机器人作为交互界面的轻量云资源管理机器人。它没有 Web UI没有独立数据库服务整个项目压缩在两千行代码以内部署只需要一个 systemd 服务。这篇文章把完整的设计思路、核心代码和踩过的坑整理出来希望对同样被服务器运维折腾的朋友有帮助。1. 为什么我不买现成的监控工具偏要自己写一个机器人先说结论不是现成的工具不好而是监控和处置这两件事分开做效率太低了。我的需求很明确不希望在一个监控页面里看到告警之后还要再打开另一套工具去 SSH 登录、敲命令、看输出。我想要的是一句话能完成的操作闭环。1.1 我手头真实的服务器环境我在管理的资源一共五台实例情况如下实例配置主要用途所在平台node-012C4GJenkins 构建机服务商 Anode-024C8G生产 Web 集群服务商 Anode-032C2G数据库从库服务商 Bnode-041C2G日志采集器服务商 Bnode-052C4G测试环境服务商 C三套服务商的控制台各有各的监控页面账号密码都不一样。每次巡检我都要逐个登录控制台或者 SSH 到机器上敲df -h、free -m、uptime属于典型的重复劳动。更要命的是服务商平台的告警策略各不相同有的要额外付费开通有的延迟高到告警邮件到了业务其实已经挂了十分钟。这就是我当时最大的痛点不是没有监控而是监控数据散落在各个平台没有一个统一的出口。1.2 现成工具的试用体验我最早试的是 Netdata。安装确实方便一条命令就装好了页面也花哨实时图表看起来很专业。但它在我的场景里有两个硬伤第一Netdata 只负责展示和告警出问题时我还是得手动去处置第二每台机器都要装一套还要把它的 Web 端口暴露到外网或者走内网代理安全策略不好做。后来又试了 Prometheus Grafana 这套标准组合。这套组合能力确实强指标收集、存储、查询、可视化全链路都有但我也得诚实地说如果一台机器上跑的业务并不复杂这套组合的前期投入和运维成本是明显超配的。Grafana 面板需要调告警规则需要写Prometheus 本身也要占内存和磁盘部署在 2C2G 的小机器上光是它俩就吃掉了小一半资源。Cockpit 是最接近我需求的它的 Web 界面可以通过浏览器直接管理服务、看日志、操作终端。但它同样是被动等待用户登录的工具没有主动把异常推给我的能力。我总不能每台机器开着 Cockpit 页面随时看吧而且 Cockpit 的包在部分发行版上需要单独启用官方源才能装自动化部署时多一层麻烦。1.3 我想要的工具长什么样试用完这一圈我的需求清单反而越来越清晰了。我不需要仪表盘甚至不需要网页我需要的是以下四点统一的一个交互入口用一句话能查任意一台机器的状态。主动告警能力阈值触发后能立刻推到手机上。执行能力命令白名单内允许直接对服务做重启、清理、备份等操作。足够轻不挑机器配置2C2G 的小机器也能跑得动。顺着这条思路聊天机器人成了最自然的交互载体。它是一个长期运行的后台进程既负责定时采集数据又负责接收和响应消息天然就是一个管家而不是一块看板。我的选型逻辑也很简单Settings 里把需求定义清楚工具只是实现需求的手段。至于为什么最终用了 Python 全家桶后面我会单独说。2. CloddsBot 的功能定位与模块设计既然要做就得先把边界划清楚。我最怕的项目就是把需求越滚越大最后变成又一套 Prometheus。所以 CloddsBot 从第一版开始就锁定四个模块资源监控、定时任务、告警通知、权限控制。超出这四个模块的需求我一律不接进主程序留给插件机制去扩展。2.1 资源监控模块别做实时曲线做快照 趋势就够了我的方案是每 5 分钟采集一次 CPU 使用率、内存占用、磁盘占用、负载均值、带宽流量五类指标保留最近 24 小时的数据存到 SQLite 里。每次采集完成之后程序自动做一轮阈值比对触发告警条件的就立刻推送。这里我故意不做秒级采集也不做长期趋势存储。原因很简单云服务器上跑的是实际业务不是性能压测实验秒级数据对排障没有实质帮助只会白白增加磁盘写入。5 分钟一个采样点24 小时一共 288 条记录每台机器一天产生的监控数据不超过 50KBSQLite 毫无压力。2.2 定时任务模块让机器人替你执行重复操作定时任务分三大类每类对应一组独立函数缓存清理清除 Docker 悬空镜像、journald 三天前的日志、临时目录垃圾文件。自动备份把 MySQL 从库做 mysqldump压缩后传到对象存储或另一台机器的备份目录。健康检查HTTP 探测 Web 服务、检查关键进程是否存活、确认主从复制延迟在阈值以内。这部分本质上就是把原来写在 crontab 里的脚本收编进机器人内部好处是可以在每次执行前后都推送一条结果反馈而不再像以前那样脚本跑了没跑、成没成功全凭自觉。关于自动备份多说一句我最开始把备份脚本直接写在 crontab 里经常好几个月都没人注意到备份文件已经损坏。搬到 CloddsBot 之后每次备份完成会在聊天窗口推送文件大小和校验值失败有告警成功有回执这件事的可观测性一下子就不一样了。2.3 告警通知模块统一消息出口告警通知是我认为整个系统最核心的模块。一个不推消息的监控机器人等于没有监控所以通知渠道的稳定性比功能多少更重要。CloddsBot 的通知模块设计成了一个抽象接口每个消息平台实现一个适配器。目前内置了企业微信群机器人、飞书自定义机器人和钉钉机器人三个适配器全部走 Webhook 方式。每个适配器实现同一个send(title, content, level)接口上层逻辑不需要关心消息最后推送到哪里。这里要提醒一下做群机器人集成的朋友不同的消息平台对 Webhook 入站消息的格式要求差异很大比如企业微信要求 JSON 里用msgtype字段区分文本还是 Markdown飞书在 Markdown 语法上也和标准实现有差异。所以适配器层一定要做隔离否则上层代码里全是if platform wecom这种脏逻辑。2.4 权限与审计模块机器人有执行权必须有边界我一开始天真地以为机器人只在我的个人群里消息源没问题权限控制可以后面再说。直到有一次同事在项目群里艾特机器人执行了一条命令我才意识到只要消息平台允许群聊机器人任何在群里的人都能驱动机器人的能力。后来我做了三层校验消息来源校验只有配置中的群 ID 和发送者 ID 组合消息才会被处理。命令白名单机器人只能执行预设的命令集合不能执行任意系统命令。比如允许restart service nginx不允许直接透传 shell 命令。操作审计所有指令和操作结果落库方便事后追溯。3. 技术选型的逻辑与核心实现思路技术栈方面CloddsBot 使用的是 Python 3.10 psutil aiohttp APScheduler SQLite。这个栈够稳、够轻生态里各司其职。3.1 为什么用 Python 而不是 Go 或 Node.js这可能是读者最想问的问题。答案很直白因为整个系统里最复杂、最容易出错的不是并发和性能而是系统信息采集的兼容性。psutil 把 CPU、内存、磁盘、网络、进程这些系统明细都封装成了统一的 Python 接口在 Linux、macOS、Windows 上的行为几乎一致省去了一大堆调用sysinfo、/proc/stat、vmstat的底层细节处理。Go 当然能做到而且性能更好单二进制部署也方便。但当时我的首要目标是一周内跑通一版Python 的开发效率优势更明显。至于性能一个 5 分钟执行一次采集的机器人远远到不了需要考虑性能瓶颈的程度。Node.js 我也考虑过后来放弃了。不是说 Node.js 做不了而是它在前端生态之外的系统管理能力明显弱于 Python比如进程管理、信号处理、系统调用这类场景写起来远没有 Python 直接。选型这种事从来就没有最好的语言只有这个阶段最顺手的选择。3.2 数据采集逻辑psutil 一个库就够了数据采集的核心逻辑非常简单但有两个细节值得展开。第一是磁盘指标。psutil.disk_usage(/)返回的是根分区使用率但很多服务器会有多个挂载点比如/data、/var/lib/docker。所以我封装了一个disk_usage_all()函数遍历所有挂载点并过滤掉伪文件系统。import psutil IGNORE_FS {proc, sysfs, tmpfs, devtmpfs, overlay, cgroup, cgroup2} def disk_usage_all(): result [] for part in psutil.disk_partitions(allFalse): if part.fstype in IGNORE_FS: continue try: usage psutil.disk_usage(part.mountpoint) result.append({ mount: part.mountpoint, device: part.device, total_gb: round(usage.total / 1024**3, 2), used_gb: round(usage.used / 1024**3, 2), percent: usage.percent, }) except PermissionError: continue return result注意disk_partitions(allFalse)这个参数很关键。allFalse只返回真实磁盘分区不会把/proc、/sys这类虚拟文件系统扫进来避免误报警。第二是带宽流量。psutil 的net_io_counters()返回的是累计字节数需要做两次采样相减才得到增量。每次采集时我会把快照写入 SQLite下一次采集时跟前一次的值对比除以两次采样的时间差就得到平均网卡吞吐。这就是对比历史值的典型用法不理解这个原理直接读计数器会以为网卡每秒有几十 TB 流量。3.3 任务调度APScheduler 比 crontab 更合适定时任务这块原本最自然的方案是 crontab。为什么我最后还是选了 APScheduler原因主要有两个。第一crontab 的最小粒度是分钟但 CloddsBot 有些定时轮询动作需要精确到秒APScheduler 支持这种轻量级调度。第二crontab 的脚本和主程序是分离的脚本的日志、运行结果、异常捕获要想统合进机器人的消息推送逻辑里得额外做一层封装。而 APScheduler 直接作为进程内的调度库使用任务函数的返回值可以直接用于告警判断。from apscheduler.schedulers.asyncio import AsyncIOScheduler from apscheduler.triggers.cron import CronTrigger scheduler AsyncIOScheduler() scheduler.add_job( collect_metrics, triggerCronTrigger(minute*/5), idcollect_metrics, max_instances1, coalesceTrue, )scheduler配置中两个参数值得说说max_instances1保证同一个任务在上一次还没跑完时不会开启新实例这在服务器卡顿、采集任务耗时超过间隔时非常重要coalesceTrue的含义是如果任务因为某种原因错过了执行窗口恢复后不会补跑多次只补跑最近一次。这两个参数看起来不起眼但能挡住很多离奇的资源占用问题。3.4 命令解析别碰 shell用子命令结构CloddsBot 的命令解析我没有接入任何框架就是手写的参数解析。原因是我要控制代码量而且机器人命令的形态非常固定不需要支持灵活的交互式 shell。整体设计是子命令 参数的形式/status查看全部机器概要。/status node-01查看指定机器详情。/disk node-01查看磁盘分区详情。/backup db now手动触发数据库备份。/exec node-01 systemctl restart nginx执行白名单命令。所有命令都以/开头。收到消息后先检查是否以/开头再拆分为 command 和 args 两部分通过一层 if/elif 分发到对应处理函数。这里我刻意没有做自然语言理解之类的花活因为运维命令最怕歧义。比如用户说重启一下那台机器的服务是哪台机器哪个服务让用户按照既定格式精确表达反而更安全、更高效。3.5 消息适配层设计三行核心代码接入一个平台前面提过告警模块包含多个消息平台的适配器。适配器接口的核心是一个send方法。以企业微信群机器人为例import time import hmac import hashlib import base64 import requests import urllib.parse class WeComNotifier: def __init__(self, webhook_url, secretNone): self.webhook_url webhook_url self.secret secret def _sign(self, timestamp): if not self.secret: return {} string_to_sign f{timestamp}\n{self.secret} hmac_code hmac.new( self.secret.encode(utf-8), string_to_sign.encode(utf-8), digestmodhashlib.sha256, ).digest() sign urllib.parse.quote_plus(base64.b64encode(hmac_code)) return {timestamp: timestamp, sign: sign} def send(self, title, content, levelinfo): timestamp str(int(time.time())) params self._sign(timestamp) payload { msgtype: markdown, markdown: {content: f**{title}**\n{content}}, } resp requests.post(self.webhook_url, paramsparams, jsonpayload, timeout10) return resp.json()这段代码里_sign方法实现了企业微信加签机器人的签名算法。如果你的群机器人没有开启加签secret 传空字符串就行。我强烈建议生产环境开启加签否则只要 Webhook 地址泄露任何人都能往你的群里推送消息这也是一个容易被忽略的安全细节。4. 部署与核心功能跑通实录前面说的都是设计这一节直接上干货记录从零到一跑通 CloddsBot 的完整过程。4.1 环境准备与项目初始化我选择在一台 2C4G 的 Debian 12 云服务器上部署 CloddsBot这台机器同时充当家里的管理节点。系统要求很简单Python 3.9 以上即可。mkdir -p /opt/cloddsbot cd /opt/cloddsbot python3 -m venv .venv source .venv/bin/activate pip install psutil aiohttp APScheduler pyyaml项目结构我按照单一职责拆成这几个文件每个文件控制在 200 到 400 行左右cloddsbot/ ├── main.py # 入口初始化配置、启动调度器、启动消息监听 ├── config.yaml # 所有可配置项 ├── collect.py # 系统信息采集 ├── notifier.py # 消息平台适配器 ├── dispatcher.py # 命令注册与分发 ├── tasks.py # 定时任务备份、清理、健康检查 └── store.py # SQLite 读写封装一定要用虚拟环境venv不要图省事直接在系统 Python 里跑。后面踩坑记录里你会看到这一步帮我挡掉了一个大坑。4.2 配置文件与启动入口配置方面最核心的是 config.yaml我会把容易变的信息全部放进来而不是写死在代码里app: timezone: Asia/Shanghai data_dir: /var/lib/cloddsbot hosts: - name: node-01 host: 10.0.0.11 alias: jenkins - name: node-02 host: 10.0.0.12 alias: prod-web thresholds: cpu: 85 # CPU 使用率告警阈值 memory: 85 # 内存使用率告警阈值 disk: 80 # 磁盘使用率告警阈值 load: 4.0 # 1分钟负载阈值 notify: platform: wecom webhook_url: https://qyapi.weixin.qq.com/cgi-bin/webhook/send?keyYOUR_KEY secret: YOUR_SECRET chat: allowed_group_ids: - GROUP_ID_1 allowed_user_ids: - USER_ID_1 commands: restart: - nginx - jenkins入口文件 main.py 的逻辑很直白读取配置、初始化 SQLite、启动 APScheduler、启动消息循环。因为目前主要交互依赖消息平台的 Webhook 回调所以我在带外写了一个简单的长轮询适配器来拉取待处理指令。这里是去平台化的处理方式你可以根据自己的消息平台 SDK 替换成对应的 WebSocket 或回调接收方式。4.3 监控采集与告警推送跑通第一次跑通监控功能的时间点我记得很清楚。执行/status命令机器人依次返回五台机器的 CPU、内存、磁盘数据。测试时我故意把一台机器的 CPU 阈值临时调到 1%然后跑一个死循环脚本5 秒后手机收到告警推送标题是node-02 CPU 使用率超限正文包含当前值 96%、阈值 1%、持续时长等信息。这个核心链路由三部分组成采集函数定时执行、阈值判断、适配器推送。大家在复现时一定要先把这条链路完整打通再往下开发其他功能因为它决定了整个系统的基础。4.4 自动备份与清理任务跑通备份任务我以 MySQL 从库为例def backup_mysql(host): ts datetime.now().strftime(%Y%m%d_%H%M%S) backup_dir Path(/data/backup/mysql) backup_dir.mkdir(parentsTrue, exist_okTrue) dump_cmd ( fmysqldump f--single-transaction --routines --triggers f-h {host} -u backup_user -p*** --all-databases f| gzip {backup_dir}/{host}_{ts}.sql.gz ) proc subprocess.run(dump_cmd, shellTrue, capture_outputTrue, textTrue) if proc.returncode 0: size Path(f{backup_dir}/{host}_{ts}.sql.gz).stat().st_size return {success: True, file: f{host}_{ts}.sql.gz, size_mb: round(size / 1024 / 1024, 2)} return {success: False, error: proc.stderr}执行完成后机器人会把备份文件名、压缩包大小、耗时推送到群里失败则推送错误摘要。这里用 shellTrue 是为了管道符的支持但需要特别谨慎欠加大变量注入防护。4.5 权限校验落地权限校验的代码挂在消息处理的最前端def is_authorized(message: dict) - bool: group_id message.get(group_id) user_id message.get(user_id) if group_id not in config[chat][allowed_group_ids]: logger.warning(fUnauthorized group: {group_id}) return False if user_id not in config[chat][allowed_user_ids]: logger.warning(fUnauthorized user: {user_id} in allowed group) return False return True只有群 ID 和用户 ID 两个条件同时命中才继续处理后续逻辑。审计日志里会完整记录消息来源、时间、内容、处理结果。这样可以保证就算群里有人说话也无法驱动机器人执行任何操作。5. 运行半年踩过的坑从崩溃到稳定的完整排查记录这部分是本文最有价值的内容。CloddsBot 从第一版到现在稳定运行中间踩的坑不下七八个。我挑四个最典型、也最容易反复出现的记录下来。5.1 systemd 进程反复被杀的问题第一版部署用的是nohup python3 main.py 跑了两天就发现机器人失联了。登上服务器查看进程发现 Python 进程已经不在日志里也没有明显的错误信息。排查链路是这样走的先看/var/log/syslog没有 OOM Killer 记录说明不是内存不足被杀。再手动前台启动观察了一个多小时一切正常。一放回后台隔一段时间又消失。最后想到是不是系统对后台进程的信号管理问题。查了一圈发现 Debian 系统的 cgroup 在用户退出登录时会向进程组发送 SIGHUP如果父进程不是守护进程化session 会被回收。与其纠结这个信号细节不如直接上 systemd 管理一次性解决守护、自启、崩溃拉起的问题。配置如下[Unit] DescriptionCloddsBot Service Afternetwork-online.target [Service] Typesimple WorkingDirectory/opt/cloddsbot ExecStart/opt/cloddsbot/.venv/bin/python main.py Restartalways RestartSec10 EnvironmentPYTHONUNBUFFERED1 [Install] WantedBymulti-user.target这里面有三个关键点。Restartalways保证进程意外退出后 10 秒内自动拉起WorkingDirectory指定工作目录避免相对路径读取 config.yaml 失败PYTHONUNBUFFERED1强制 Python 不缓冲输出确保journalctl -u cloddsbot -f能实时看到日志万一崩了不至于日志也一起丢。5.2 定时采集任务假死协程阻塞导致调度器停摆项目跑了半个月我发现监控数据出现断档某一段时间 SQLite 里没有新记录但是进程还活着消息命令也还能响应。这就奇怪了调度器应该每 5 分钟触发一次采集为什么停了当时我怀疑是 APScheduler 的问题打日志发现调度器确实在触发但采集函数本身卡住了。进一步定位发现卡在psutil.net_io_counters()调用上——某一台机器因为内部路由异常读取/proc/net/dev时出现未知阻塞。这个问题的根因其实在于采集函数同步执行其中一个耗时的 I/O 操作阻塞了事件循环后续任务全部排队等待。解决方案有两步。第一步给所有网络请求和子进程调用都加超时。采集函数里凡是可能阻塞的操作统一用asyncio.wait_for包裹async def safe_collect(interval30): try: metrics await asyncio.wait_for(asyncio.to_thread(collect_metrics), timeoutinterval) return metrics except asyncio.TimeoutError: logger.error(collect_metrics timed out) return None第二步更重要不要把采集函数直接注册成异步任务而是用asyncio.to_thread把它丢到线程池里执行避免阻塞主事件循环。这之后即便采集异常消息响应也不会受影响。这个坑暴露出的教训是异步程序里任何同步阻塞调用都是定时炸弹尤其像 psutil、subprocess 这类底层系统调用必须显式控制超时。5.3 推送频率超限企业微信机器人每分钟 20 条上限一次磁盘告警惹出的问题。当时有一台机器磁盘频繁浮动在阈值附近导致机器人每隔 5 分钟就推送一条告警。结果转发到企业微信群时触发平台每分钟最多 20 条消息的频率限制Webhook 直接返回 1302 错误码后续消息全部被丢弃。这个问题在测试阶段完全暴露不出来因为测试时消息量很小。等真实业务跑到阈值震荡的场景才发现限流这么敏感。我的修复方案是在告警模块里加一层冷却窗口逻辑。同一台机器、同一个指标类型的告警10 分钟内只推送一次。后续如果指标恢复正常再推送一条已恢复消息并解除冷却。这个设计不仅解决了限流问题还大幅减少了消息噪音让群里的告警信息更容易被注意到。5.4 备份脚本把磁盘写满缺少校验与轮转前面提到过自动备份曾经出过一次比较大的事故。对象存储配置错误后备份文件没有上传成功但落盘的文件一直留在本地。连续跑了三个月一个 600MB 的数据库备份每天一个把 40GB 的数据分区写满了。这个问题本质上是备份策略缺少生命周期管理。后来我在备份函数里加了三层防护备份前检查磁盘剩余空间低于 20% 时强制停止备份任务。本地只保留最近 7 份备份超过的自动删除。上传对象存储成功后本地文件移动到临时目录再批量清理。from pathlib import Path BACKUP_KEEP 7 def rotate_backups(backup_dir: Path, keep: int BACKUP_KEEP): backups sorted(Path(backup_dir).glob(*.sql.gz), keylambda x: x.stat().st_mtime, reverseTrue) for old in backups[keep:]: old.unlink(missing_okTrue) logger.info(fRemoved old backup: {old})很多备份事故不是备份动作本身失败而是备份策略缺少退出机制。这一点做定时任务时一定要提前想清楚别等磁盘满了才着急。6. 运行大半年后的实际效果与下一步改进方向CloddsBot 在生产环境里稳定运行了八个月。期间经历过三次云服务商维护通知、两次数据库备份恢复演练、若干次偶发高负载这些场景下它都发挥了预期的作用。有一个细节让我印象很深一次构建机磁盘告警推送后我在手机上直接通过机器人的白名单命令执行了 Docker 悬空镜像清理从收到告警到完成处置整个过程不到两分钟完全不用开电脑。当前 CloddsBot 的定位始终没有变它不是一个把所有运维功能都包含进去的大平台而恰好是那个站在操作员和设备之间、能替你跑腿的小管家。支持的核心能力仍然集中在监控、备份、告警、基本处置这四块后续我计划补充几件小事把 SQLite 存储换成更直观的时间序列表方便做周/月维度的容量趋势推算。增加简单的值班轮换通知让团队里其他人也能收到定时巡检结果。在备份成功回执里附上校验和方便定期抽查备份完整性。如果你也打算给手头的服务器做一套轻量级的统一管理入口我的建议是别一上来就整大而全的监控平台先把一条命令查状态、一条告警推送、一条命令做清理这三件事跑通后面的功能都是锦上添花。工具永远是越贴合自己的使用习惯越值得留存CloddsBot 对我来说已经从一个 DIY 项目变成了每天离不开的基础设施希望你也能找到适合自己的那一款。
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻