一、故障背景与监控需求
这次接入监控的直接原因,是前端在短时间内出现过两次离线,而我都没有在第一时间感知到。
第一次问题来自部署流程。前端和反向代理相关的 Caddy 配置还在分支上,没有合并到主线;当时线上仍然使用旧的部署脚本,导致前端入口离线。这个问题不是服务代码本身异常,而是部署脚本和真实配置状态没有对齐。
第二次问题更直接。前端容器已经创建出来,但停留在 Created 状态,没有真正启动。后端、数据库、Redis、模型网关这些服务都还在,API 健康检查也能返回,但用户访问前端页面时仍然打不开。最后手动启动 web 容器后,服务才恢复。
这两次故障暴露的问题不是缺少复杂指标,而是缺少基础可用性告警。当前阶段,我需要及时知道服务是否仍在正常运行。
二、监控方案选型
Prometheus、Alertmanager 和 Grafana 是更完整的监控组合。它们适合采集时间序列指标,做告警路由,画长期趋势图,也适合后面分析“什么时候开始变慢”“哪类请求错误率更高”“资源是否接近瓶颈”。
但这次需求还没到那个阶段。当前服务规模不大,告警接收人也只有我自己,真正要解决的是“服务异常时能否及时感知”。如果为了这个目标再部署一整套 Prometheus 体系,反而会引入新的维护成本:Prometheus 自己要部署,Alertmanager 要配置,规则要维护,Grafana 也要管理。
所以这次选型很克制:用 systemd timer 每分钟触发一个 Python 脚本,脚本检查公网入口和 Docker 容器状态,连续失败达到阈值后通过 SMTP 发邮件。它不是完整可观测性平台,但足够回答当前最关键的问题:服务是否还可用。
三、健康检查范围
检查对象分成两层:用户入口是否可访问,以及内部组件是否处于健康运行状态。
公网入口只保留最关键的几个点:
| 检查项 | 目的 |
|---|---|
/ |
首页是否能正常返回 HTML |
/app |
主要业务页面入口是否能打开 |
/admin |
管理或次级业务页面入口是否能打开 |
/health |
后端 API 健康检查是否正常 |
这几项不是为了覆盖所有业务细节,而是为了判断用户入口和后端基础状态是否可用。前端离线这类问题,单看后端 health endpoint 是看不出来的,所以页面入口必须单独检查。
容器侧则只看关键组件:
| 容器 | 作用 |
|---|---|
project-web-1 |
前端静态站点 |
project-api-1 |
API 后端 |
project-worker-1 |
后台任务 worker |
project-postgres-1 |
PostgreSQL 数据库 |
project-redis-1 |
Redis |
project-gateway-1 |
内部网关或下游依赖 |
这里检查的是 Docker 状态里的 running 和 healthcheck 结果。它不能证明每条业务链路都完整可用,但能及时发现“容器未启动”“容器 unhealthy”这类基础故障。
四、实现与部署方式
实现上没有写成长驻进程,而是用 systemd timer 定时调起脚本。健康检查本身是短任务,执行完成后即可退出,不需要再维护一个额外的服务进程。
整体流程是:timer 每分钟触发一次 Python 脚本,脚本依次检查 URL 和容器状态,然后把结果写进本地状态文件。如果连续失败次数达到阈值,就发告警邮件;如果之前处于异常状态,后来恢复了,就发恢复邮件。
graph TD
A[systemd timer 每分钟触发] --> B[Python 健康检查脚本]
B --> C[检查公网 URL]
B --> D[检查 Docker 容器状态]
B --> E[更新本地状态文件]
E --> F{连续失败达到阈值?}
F -->|是| G[SMTP 发送告警邮件]
F -->|否| H[只记录状态]
E --> I{从失败恢复?}
I -->|是| J[SMTP 发送恢复邮件]
告警策略也尽量简单:每 60 秒检查一次,连续 2 次失败才发邮件。同一故障持续存在时,不每分钟重复发,而是隔一段时间再提醒;恢复后单独发一封恢复邮件。这样能过滤短暂网络抖动,也不会让真实故障沉默太久。
配置放在 root 只读的 env 文件里,SMTP 授权码不写进日志,也不写进文章。类似这样:
PROJECT_HEALTH_WATCHDOG_FAILURE_THRESHOLD=2
PROJECT_HEALTH_WATCHDOG_REPEAT_INTERVAL_SECONDS=10800
PROJECT_HEALTH_WATCHDOG_TIMEOUT_SECONDS=10
SMTP_HOST="smtp.example.com"
SMTP_PORT=465
SMTP_USER="sender@example.com"
SMTP_PASSWORD="<smtp-authorization-code>"
SMTP_USE_TLS=true
ALERT_TO="receiver@example.com"
部署时把脚本、env 文件、systemd service 和 timer 都收进 Ansible role。Ansible 负责创建目录、渲染配置、安装 unit,并启用 timer。这样以后调整检查项或者换机器,不需要再靠手工记忆去服务器上改文件。
上线后只看 active 还不够,还要确认 timer 真的在触发、日志里每个检查项都是预期结果,并且测试邮件能送达。健康检查脚本能跑通,不代表 SMTP 链路一定可用;邮件链路必须单独验证。
五、适用边界与后续演进
这套方案适合服务规模还不大、维护者较少、第一目标是“异常时能够收到通知”的阶段。它的价值在于低成本、低维护、可解释:出现问题后,可以直接查看 systemctl、journalctl、状态文件和脚本输出。
它不适合替代完整的可观测性平台。请求耗时趋势、错误率统计、资源曲线、下游服务调用成本、多级告警、长期历史指标,这些都不是它擅长的事情。等服务规模变大,或者开始需要分析“为什么慢”“什么时候开始变差”,Prometheus 这类体系仍然应该引入。
但在这次场景里,先做轻量监控是更实际的选择。前端曾经因为部署脚本和容器状态问题离线过,说明当前最缺的不是复杂指标,而是一套基础可用性告警机制。先把服务从“异常无人感知”变成“连续异常会触发邮件通知”,这一步已经足够有价值。