ServerSentinel 项目开发复盘:从服务器巡检到 AI 日志分析
这是一篇小工具开发的复盘,用来记录 ServerSentinel 从一个简单想法逐步变成可运行工具的过程。
此工具主要作用是把服务器巡检、日志分析、定时任务、报告生成、容器化运行、异常结构化提取、AI 辅助分析、数据脱敏、风险通知、Dashboard 展示、定时流水线和自动化验证串在一起。
项目目标
ServerSentinel 的目标是做一个轻量级服务器巡检与日志分析工具。
最初设想的功能包括:
- 定期检查网站是否正常访问;
- 检查 Nginx 服务是否正常运行;
- 检查磁盘和内存使用情况;
- 分析 Nginx
access.log; - 识别常见扫描请求;
- 生成每日 Markdown 报告;
- 将异常日志整理为结构化 JSON;
- 基于 DeepSeek API 生成可复核的 AI 分析报告;
- 使用一键流水线串联日报、异常提取、AI 分析和 Markdown 报告;
- 在发送 AI 前对敏感信息进行脱敏;
- 按风险阈值发送 Webhook 通知;
- 使用只读 Dashboard 展示巡检和日志分析结果;
- 通过 cron 和外置环境文件支持 Linux 服务器定时运行;
- 使用自动化测试确保脚本修改后不会破坏原有功能。
这个项目的重点不是做一个功能特别大的平台,而是把几个常见的服务器维护动作串成完整流程:看系统状态、读日志、写脚本、生成报告,再用自动化检查保证脚本没有被改坏。
第一阶段:Shell 健康检查脚本
项目第一步从 Shell 脚本开始。
这一阶段实现的是一个基础健康检查脚本,用来判断网站和服务器状态是否正常。脚本主要检查:
- 目标网站 HTTP 状态码;
- Nginx 服务状态;
- 根分区磁盘使用率;
- 内存使用率;
- 检查结果写入日志文件;
- 出现异常时返回非零退出码。
这里需要注意的是:脚本不应该只是“能输出一些文字”,而应该让后续工具也能判断它是否执行成功。
例如,健康检查脚本如果发现网站返回非 200 状态码,就应该明确记录异常,并通过退出码告诉外部系统:这次检查失败了。
这类设计在真实运维中很常见。因为脚本未来可能被 cron、GitHub Actions、监控平台或其他自动化系统调用,它的输出必须稳定、清晰、可判断。
第二阶段:接入定时任务
健康检查脚本完成后,下一步是让它定时执行。
这里使用的是 Linux 中常见的 crontab。
例如,每 5 分钟执行一次巡检脚本:
*/5 * * * * /home/ubuntu/health-check.sh这个阶段看起来只是加了一条定时任务,但它其实把“手动检查”变成了“自动巡检”。
手动执行一次脚本,只能说明当下是正常的;定时执行脚本,才能持续留下状态记录。等到后续排查问题时,这些日志就能还原当时的状态变化。
第三阶段:Nginx 日志分析
项目继续扩展到 Nginx access.log 分析。
Nginx 访问日志中包含了很多可以用于排查的信息,例如:
- 哪些 IP 访问了服务器;
- 请求了哪些路径;
- 返回了什么状态码;
- 是否出现大量
404; - 是否有人扫描
.php、.env、.git、wp-content等敏感路径; - 请求来自哪个 Referer;
- 使用了什么浏览器或客户端。
这一阶段实现了一个日志分析脚本,用来统计:
- 总请求数;
- HTTP 状态码分布;
- 访问量最高的 IP;
- 常见
404路径; - 可疑扫描请求;
- Referer 来源。
开发这个功能时,一个明显感受是:日志分析不是把日志全部看一遍,而是从海量文本里提取出“值得关注的信号”。
比如看到大量 .php、wp-admin、.env 请求时,并不代表服务器一定被入侵了,但说明公网服务器正在被自动化扫描。这类请求在公开服务器上非常常见,关键是要能识别它,而不是看到陌生路径就慌。
第四阶段:固定样本与测试
脚本写到一定程度后,遇到的问题是:每次修改逻辑,都不确定有没有把原来能工作的部分改坏。
所以项目加入了固定样本日志。
做法是准备一份测试用的 Nginx 日志文件,里面故意包含:
- 正常访问;
200、404等不同状态码;- WordPress 扫描路径;
.env、.git等敏感路径扫描;- 不同 IP 和 Referer。
然后让分析脚本对这份固定日志运行,并检查输出结果是否符合预期。
这个阶段慢慢体会到,测试不是为了让项目看起来复杂,而是为了让修改有安全感。
没有测试时,脚本越改越容易变成一团不敢碰的东西;有了固定样本和测试,后续新增功能时就能更快确认有没有破坏已有逻辑。
第五阶段:Python 生成 Markdown 报告
在 Shell 和日志分析脚本完成后,项目继续加入 Python 报告生成。
Python 脚本负责读取巡检日志,并生成一份每日 Markdown 报告。
报告内容包括:
- 当日检查概览;
OK、WARNING、ERROR数量;- 异常记录列表;
- 可能需要人工关注的问题;
- 报告生成时间。
这一步的意义是把“日志”变成“报告”。
日志适合排查细节,但不适合快速阅读;报告适合快速了解当天整体情况。真实工作中也是类似的:底层有日志,上层需要报表、告警、看板或总结。
这个阶段也让我意识到,Python 在运维开发里很适合做数据整理、文本处理和报告生成。Shell 适合快速调用系统命令,Python 更适合写结构清晰、后续容易扩展的逻辑。
第六阶段:GitHub Actions 自动化验证
完成 Shell、日志分析和 Python 报告生成之后,项目接入了 GitHub Actions。
每次代码提交后,自动执行检查,包括:
- Shell 脚本语法检查;
- Shell 测试脚本;
- Python 语法检查;
- Python 单元测试。
这一步把项目从“本地能跑”推进到“每次提交都能被自动验证”。
这样做之后,每次修改脚本都不需要完全依赖手动检查。代码一提交,自动化流程就会重新跑一遍基础验证,尽早暴露语法错误或测试失败。
第七阶段:Docker 化报告生成器
后续项目继续加入 Docker 化运行。
这一阶段没有把所有巡检逻辑都放进容器,而是只把 Python 日报生成器封装成容器任务。这样设计是因为健康检查脚本需要读取宿主机真实的 Nginx、systemd、磁盘和内存状态,直接运行在 Linux 主机上更合适;而日报生成器主要读取日志并输出 Markdown 文件,放进容器更容易控制运行环境。
当前 Docker 部分包含:
Dockerfile;.dockerignore;compose.yaml;tests/test-docker.sh;- GitHub Actions 中的 Docker Compose 配置验证和镜像测试。
容器运行时通过挂载目录读取日志并写出报告:
logs:/app/logs:roreports:/app/reports其中 logs 使用只读挂载,避免容器误改原始日志;reports 允许写入,用来输出生成的 Markdown 日报。
这个阶段还处理了一个容易忽略的问题:容器内生成的文件可能会变成 root 所有。为避免宿主机无法正常编辑报告,运行时通过 UID/GID 指定容器用户,让输出文件尽量保持和宿主机用户一致。
第八阶段:AI 接入前的结构化异常提取
Docker 化完成后,项目进入 AI 日志分析前的准备阶段。
这一阶段并没有直接调用外部 AI API,而是先做数据整理:从健康检查日志中提取 WARNING 和 ERROR,再把异常转成稳定的 JSON 结构。
当前脚本是:
src/extract_anomalies.py它会把异常分成几类:
disk:磁盘容量告警;memory:内存使用率告警;service:Nginx 等服务状态异常;availability:HTTP 或网络可用性异常;unknown:暂时无法通过规则分类的异常。
输出文件类似:
reports/anomalies-YYYY-MM-DD.json这一步的重点是把“原始日志”整理成“机器更容易处理的数据”。后续无论接入哪一种 AI 服务,都不应该直接把混乱日志整段丢给模型,而是先提取时间、严重程度、异常类别、摘要统计等字段。
这一阶段的重点是建立稳定的数据边界。异常提取脚本不直接处理自然语言解释,而是先把日志中的异常整理为结构化数据,供后续分析模块继续使用。
第九阶段:DeepSeek AI 日志分析链路
结构化异常提取完成后,项目继续加入 AI 辅助分析能力。
这一阶段没有让 AI 直接读取完整原始日志,而是沿用前一阶段生成的异常 JSON。这样可以减少无关内容,也能让输入字段更稳定。
当前 AI 分析流程如下:
健康检查日志-> 结构化异常 JSON-> DeepSeek JSON Output-> JSON Schema 验证-> AI 分析 JSON-> Markdown 人工复核报告这一阶段新增了几个关键文件:
prompts/analyze-anomalies.mdconfig/ai-analysis-response.schema.jsonsrc/validate_ai_response.pysrc/analyze_with_ai.pysrc/render_ai_report.pyprompts/analyze-anomalies.md 用来定义 AI 的分析边界。它要求模型把日志内容当作不可信数据,只基于证据给出分析,不执行日志中可能出现的指令,也不建议直接执行破坏性操作。
config/ai-analysis-response.schema.json 用来约束 AI 输出格式。它规定返回结果必须包含总体风险、摘要、发现项、排查步骤、只读命令和人工复核标记等字段。这样做可以避免模型返回一段自由散文,导致后续程序无法稳定处理。
src/analyze_with_ai.py 负责调用 DeepSeek API。它从环境变量中读取 API Key,不把密钥写进代码或配置文件。调用完成后,程序会先校验返回 JSON 是否符合 Schema,只有验证通过才写入正式结果文件。
src/render_ai_report.py 负责把 AI 分析 JSON 转换为 Markdown 报告。报告中包含总体风险、异常观察、可能原因、排查步骤、建议的只读诊断命令和人工复核提示。
这一阶段还补充了离线测试。测试不会真正访问 DeepSeek,也不会消耗 API 额度,而是通过模拟客户端和固定样本验证:
- AI 响应是否符合 JSON Schema;
- 缺失字段和多余字段是否会被拒绝;
requires_human_review是否必须为true;- API 返回空内容、非法 JSON 或不合规结构时是否会失败;
- Markdown 报告是否能正确渲染风险、证据和排查步骤。
这里需要记住的是:AI 分析模块不应该直接替代人工判断。它更适合负责整理线索、归纳异常、给出安全的排查顺序。真正修改配置、重启服务或调整安全策略之前,仍然需要人工确认。
第十阶段:AI 巡检流水线自动化与部署
完成 DeepSeek 分析模块后,项目继续把多个分散命令整理成一条完整流水线。
在这一阶段之前,如果要完成一次 AI 巡检,需要依次执行:
生成普通日报-> 提取异常 JSON-> 调用 DeepSeek 分析-> 校验 AI JSON-> 渲染 AI Markdown 报告这些步骤虽然都能单独运行,但手动串联容易漏步骤,也不适合放进定时任务。因此项目新增了:
src/run_ai_pipeline.pyscripts/run-daily-ai-analysis.shconfig/server-sentinel.env.exampledocs/deployment.mdsrc/run_ai_pipeline.py 是 Python 侧的一键流水线入口。它会读取健康检查日志,生成普通 Markdown 日报,提取异常 JSON,并在存在异常时继续调用 AI 分析模块。AI 返回结果通过 JSON Schema 验证后,再渲染成 Markdown 报告。
这一设计保留了一个重要边界:没有异常时不调用 AI API;有异常但没有配置 API Key 时,普通日报和异常 JSON 仍然会生成,程序再明确返回错误。这样可以避免因为外部 API 配置问题导致本地巡检结果完全丢失。
scripts/run-daily-ai-analysis.sh 是给 Linux cron 调用的 Shell 入口。它负责加载外部环境文件、检查 Python 解释器、检查健康检查日志是否存在,并使用 flock 防止上一次任务还没结束时重复启动。
密钥配置不放在仓库中,而是放在服务器的独立配置文件中:
/etc/server-sentinel/server-sentinel.env示例文件位于:
config/server-sentinel.env.example这种做法可以把代码和运行环境分开:仓库保存脚本、测试和示例配置,真实服务器保存 API Key、日志路径、报告目录和锁文件路径。
Docker Compose 也增加了独立的 AI profile:
docker compose --profile ai run --rm ai-pipelineAI 服务不会在普通 docker compose run report-generator 时自动启动,避免一次普通报告生成误触发外部 API 请求。容器中日志目录仍然只读挂载,报告目录可写,API Key 只在运行时通过环境变量注入。
最后,docs/deployment.md 整理了 Ubuntu 服务器部署流程,包括依赖安装、仓库克隆、Python 虚拟环境、密钥文件权限、手动验证、cron 配置、Docker 运行方式和部署后的只读检查命令。
这一阶段的重点不是新增某一个单点功能,而是把已经完成的模块整理成可定时、可部署、可验证的运行链路。
第十一阶段:脱敏、多 Provider、通知与 Dashboard 展示
完成 AI 巡检流水线后,项目继续补齐几个工程边界:外部 AI 调用前的数据脱敏、不同模型服务的配置抽象、风险通知,以及面向结果查看的只读 Dashboard。
新增的关键文件包括:
src/redact_sensitive_data.pysrc/ai_providers.pysrc/send_notification.pysrc/analyze_nginx_log.pysrc/serve_dashboard.pydashboard/index.htmldashboard/app.jsdashboard/styles.csstests/test_security_and_providers.pytests/test_nginx_json_and_dashboard.pysrc/redact_sensitive_data.py 负责在异常数据发送给外部 AI 前进行脱敏。当前会处理 URL、邮箱、IPv4、主机名和 Linux 绝对路径,并把它们替换为本次运行内稳定的占位符,例如:
<URL_1><IP_1><PATH_1>原始日志和本地异常 JSON 不会被改写,只有发送给 AI 的副本会被脱敏。这样既保留了本地排查所需的原始信息,也减少了向外部服务发送内部信息的范围。
src/ai_providers.py 把 AI 服务配置抽象成统一入口。当前支持:
- DeepSeek;
- OpenAI;
- 自定义 OpenAI-compatible 服务。
无论底层使用哪个 Provider,前面的异常 JSON、Prompt 和响应 JSON Schema 都保持一致。这样可以把“日志如何整理”和“调用哪个模型服务”分开,避免业务逻辑和厂商配置混在一起。
src/send_notification.py 负责按风险阈值发送通知。当前支持 generic、Slack 和 Discord Webhook 格式。通知内容只包含总体风险、摘要、Finding 数量和本地报告路径,不发送原始日志。
Dashboard 部分由 src/analyze_nginx_log.py、src/serve_dashboard.py 和 dashboard/ 目录组成。src/analyze_nginx_log.py 会生成前端可读取的 Nginx JSON 统计数据,Dashboard 页面再展示:
- 巡检概览;
- 异常列表;
- AI 风险等级;
- 排查步骤;
- HTTP 状态码分布;
- Top IP。
Dashboard 使用原生 HTML、CSS 和 JavaScript,不依赖数据库,也不需要前端构建工具。它是只读展示层,不负责修改服务器配置、不执行远程命令,也没有登录系统;如果后续部署到公网环境,需要额外增加认证或访问控制。
这一阶段补充了自动化测试,覆盖数据脱敏、Provider 配置、Webhook、Nginx JSON 统计和 Dashboard 数据加载。当前项目测试数量已经扩展到 37 个 Python 测试,测试过程使用本地假客户端或离线响应,不依赖真实 API Key,也不会访问外部网络。
这一阶段需要记住的边界是:脱敏可以降低信息泄露风险,但不能保证识别所有业务特有秘密;AI 输出适合作为辅助排查材料,不应直接替代人工确认;Dashboard 是展示层,不应直接暴露成无认证的公网管理入口。
开发过程中遇到的问题
1. 日志里出现大量陌生路径
查看 Nginx 日志时,会看到很多类似下面的请求:
GET /wp-content/plugins/... HTTP/1.1GET /.env HTTP/1.1GET /.git/config HTTP/1.1GET /admin.php HTTP/1.1这些路径和当前网站没有关系,最开始容易误以为网站出现了异常。
后面分析后可以判断:这通常是公网自动扫描器在尝试探测常见漏洞路径。只要服务器没有这些文件,并且返回 404,通常不代表已经被入侵。
需要记住的是:公网服务器被扫描是常态。重点不是完全不让别人请求,而是要能识别、记录、限制和告警。
2. 日志字段看起来很乱
Nginx access.log 一行内容很长,包含 IP、时间、请求路径、状态码、Referer、User-Agent 等字段。
一开始直接看会觉得混乱,但拆开后就会清楚很多:
客户端 IP - - [时间] "请求方法 路径 协议" 状态码 响应大小 "Referer" "User-Agent"理解日志格式以后,才能用 awk、grep、sort、uniq 等工具做统计。
这个问题的解决方式不是硬背命令,而是先理解每个字段的含义,再决定提取哪一列。
3. Shell 脚本容易越写越乱
Shell 很适合快速完成任务,但如果没有结构,脚本会很快变得难维护。
比较好的做法是:
- 把重复逻辑封装成函数;
- 日志输出格式保持一致;
- 关键变量放在脚本开头;
- 异常情况明确返回;
- 不要把所有逻辑堆在一长串命令里。
例如日志输出可以统一成:
2026-07-19 10:00:00 [OK] Website is reachable2026-07-19 10:05:00 [ERROR] Nginx is inactive这样后续 Python 脚本读取日志时也更容易解析。
4. 脚本能跑,不代表项目可靠
项目推进中很容易出现一种错觉:命令跑通了,就算完成了。
但实际不是这样。
更可靠的状态应该是:
- 有固定输入样本;
- 有明确预期输出;
- 有测试脚本;
- 有自动化检查;
- 修改后能快速验证。
所以后面加入固定样本日志和 GitHub Actions,是为了让项目从“练习脚本”变成“可持续维护的小工具”。
5. 容器不是为了替代宿主机检查
加入 Docker 时,最开始容易把所有功能都塞进容器。但服务器健康检查和普通应用容器不太一样。
健康检查脚本需要知道宿主机上的真实状态,例如:
- Nginx 服务是否运行;
- 根分区磁盘使用率;
- 内存使用情况;
- systemd 服务状态。
这些信息天然属于宿主机环境。把这部分强行放进容器,反而会让权限、挂载和系统状态判断变复杂。
因此当前设计是:
宿主机负责真实巡检容器负责读取日志并生成报告这样边界更清楚,也更容易测试。
6. AI 分析前要先整理输入
接入 AI 前,另一个需要注意的问题是:不要直接把完整日志全部丢给模型。
更稳妥的流程是:
原始日志-> 规则提取 WARNING / ERROR-> 分类异常类型-> 生成 JSON-> 再交给 AI 生成解释和建议这样做可以减少无关信息,也能让后续 AI 输出更稳定。
这次项目涉及的关键知识点
这次项目需要重点记住的知识点包括:
curl可以用于检查网站可用性和 HTTP 状态码;systemctl is-active可以判断 systemd 服务是否运行;df可以查看磁盘使用率;free可以查看内存使用情况;cron可以把手动脚本变成定时任务;- Nginx
access.log是排查访问行为的重要依据; awk、grep、sort、uniq、head是日志分析的基础工具;- 大量
.php、.env、.git请求通常是自动化扫描; - Python 适合做日志汇总、文本解析和 Markdown 报告生成;
- Docker 可以封装报告生成器运行环境,但宿主机状态检查仍适合直接运行在服务器上;
.dockerignore可以避免真实日志、报告和 Git 元数据进入镜像构建上下文;- Docker Compose 可以把挂载目录、环境变量和运行用户写成可复用配置;
- JSON 适合作为脚本和 AI 分析模块之间的数据交换格式;
- JSON Schema 可以约束 AI 返回结构,避免后续程序处理不稳定输出;
- Prompt 需要明确区分“可信指令”和“不可信日志数据”;
- API Key 应通过环境变量读取,不应写入代码、配置文件或 Git 仓库;
- 发送外部 AI 服务前应先进行敏感信息脱敏,减少 URL、IP、主机名、邮箱和路径等信息直接外发;
- Provider 抽象可以把 DeepSeek、OpenAI 和自定义 OpenAI-compatible 服务统一到同一套输入输出流程中;
- AI 分析结果应保留人工复核边界,避免把模型输出直接当作操作指令;
- 一键流水线适合把多个稳定步骤串成固定执行流程,减少手动漏步骤;
flock可以防止定时任务重叠执行,避免多个巡检任务同时写同一批报告;- 生产环境密钥应放在仓库外部,例如
/etc/server-sentinel/server-sentinel.env; - 配置文件权限应尽量收紧,例如只允许文件所有者读取和修改;
- Docker Compose profile 可以把普通任务和 AI API 任务分开,避免误触发外部服务调用;
- Webhook 通知适合传递风险摘要和报告路径,不适合直接发送完整原始日志;
- Dashboard 可以作为巡检结果的只读展示层,但没有认证时不应直接暴露到公网;
- 前端展示数据可以由 Python 生成 JSON,再由浏览器端 JavaScript 读取和渲染;
- 部署文档应记录依赖安装、密钥配置、定时任务、手动验证和只读排查命令;
- 测试样本可以让脚本修改更安全;
- GitHub Actions 可以在代码提交后自动执行 Shell、Python、Docker、AI 流水线和 Dashboard 数据加载检查;
- 运维开发的核心不是写一次命令,而是把流程沉淀成可复用工具。
项目阶段总结
目前 ServerSentinel 已经完成了八个主要能力:
Shell 健康检查Nginx 日志分析Python Markdown 报告生成Docker 化报告生成器结构化异常提取DeepSeek AI 日志分析与 Markdown 报告生成一键 AI 巡检流水线与 Linux 定时部署脱敏、多 Provider、Webhook 通知与只读 Dashboard同时,项目已经接入自动化测试流程,能够在提交代码后自动检查 Shell、Python、Docker、AI 分析、数据脱敏、Provider 配置、Webhook、Nginx JSON、Dashboard 数据加载和流水线编排相关功能。
后续可以继续往几个方向推进:
- 补充真实运行截图和示例报告;
- 增加部署环境的实际运行记录和异常案例复盘;
- 根据真实使用情况优化 Dashboard 交互、报告字段和异常分类规则;
- 视需要增加身份认证或内网访问控制,避免 Dashboard 暴露到公网;
- 接入更完整的监控指标;
回头看这个项目,它把 Linux 命令、Shell、Nginx 日志、Python、Docker、CI、AI 辅助分析、数据脱敏、风险通知、Dashboard 展示和定时部署串成了一个实际流程。它不是单个知识点的堆叠,而是围绕“服务器状态如何被检查、记录、分析、封装、验证、辅助解释、展示和周期性运行”这个问题逐步展开的。
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!












