ServerSentinel 项目开发复盘:从服务器巡检到 AI 日志分析

6112 字
ServerSentinel 项目开发复盘:从服务器巡检到 AI 日志分析

这是一篇小工具开发的复盘,用来记录 ServerSentinel 从一个简单想法逐步变成可运行工具的过程。

此工具主要作用是把服务器巡检、日志分析、定时任务、报告生成、容器化运行、异常结构化提取、AI 辅助分析、数据脱敏、风险通知、Dashboard 展示、定时流水线和自动化验证串在一起。

ServerSentinel 整体处理流程
ServerSentinel 整体处理流程

项目目标#

ServerSentinel 的目标是做一个轻量级服务器巡检与日志分析工具。

最初设想的功能包括:

  • 定期检查网站是否正常访问;
  • 检查 Nginx 服务是否正常运行;
  • 检查磁盘和内存使用情况;
  • 分析 Nginx access.log
  • 识别常见扫描请求;
  • 生成每日 Markdown 报告;
  • 将异常日志整理为结构化 JSON;
  • 基于 DeepSeek API 生成可复核的 AI 分析报告;
  • 使用一键流水线串联日报、异常提取、AI 分析和 Markdown 报告;
  • 在发送 AI 前对敏感信息进行脱敏;
  • 按风险阈值发送 Webhook 通知;
  • 使用只读 Dashboard 展示巡检和日志分析结果;
  • 通过 cron 和外置环境文件支持 Linux 服务器定时运行;
  • 使用自动化测试确保脚本修改后不会破坏原有功能。

这个项目的重点不是做一个功能特别大的平台,而是把几个常见的服务器维护动作串成完整流程:看系统状态、读日志、写脚本、生成报告,再用自动化检查保证脚本没有被改坏。

ServerSentinel 项目目录与模块分工
ServerSentinel 项目目录与模块分工

第一阶段: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.gitwp-content 等敏感路径;
  • 请求来自哪个 Referer;
  • 使用了什么浏览器或客户端。

这一阶段实现了一个日志分析脚本,用来统计:

  • 总请求数;
  • HTTP 状态码分布;
  • 访问量最高的 IP;
  • 常见 404 路径;
  • 可疑扫描请求;
  • Referer 来源。

开发这个功能时,一个明显感受是:日志分析不是把日志全部看一遍,而是从海量文本里提取出“值得关注的信号”。

比如看到大量 .phpwp-admin.env 请求时,并不代表服务器一定被入侵了,但说明公网服务器正在被自动化扫描。这类请求在公开服务器上非常常见,关键是要能识别它,而不是看到陌生路径就慌。

第四阶段:固定样本与测试#

脚本写到一定程度后,遇到的问题是:每次修改逻辑,都不确定有没有把原来能工作的部分改坏。

所以项目加入了固定样本日志。

做法是准备一份测试用的 Nginx 日志文件,里面故意包含:

  • 正常访问;
  • 200404 等不同状态码;
  • WordPress 扫描路径;
  • .env.git 等敏感路径扫描;
  • 不同 IP 和 Referer。

然后让分析脚本对这份固定日志运行,并检查输出结果是否符合预期。

这个阶段慢慢体会到,测试不是为了让项目看起来复杂,而是为了让修改有安全感。

没有测试时,脚本越改越容易变成一团不敢碰的东西;有了固定样本和测试,后续新增功能时就能更快确认有没有破坏已有逻辑。

第五阶段:Python 生成 Markdown 报告#

在 Shell 和日志分析脚本完成后,项目继续加入 Python 报告生成。

Python 脚本负责读取巡检日志,并生成一份每日 Markdown 报告。

报告内容包括:

  • 当日检查概览;
  • OKWARNINGERROR 数量;
  • 异常记录列表;
  • 可能需要人工关注的问题;
  • 报告生成时间。

这一步的意义是把“日志”变成“报告”。

日志适合排查细节,但不适合快速阅读;报告适合快速了解当天整体情况。真实工作中也是类似的:底层有日志,上层需要报表、告警、看板或总结。

这个阶段也让我意识到,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:ro
reports:/app/reports

其中 logs 使用只读挂载,避免容器误改原始日志;reports 允许写入,用来输出生成的 Markdown 日报。

这个阶段还处理了一个容易忽略的问题:容器内生成的文件可能会变成 root 所有。为避免宿主机无法正常编辑报告,运行时通过 UID/GID 指定容器用户,让输出文件尽量保持和宿主机用户一致。

第八阶段:AI 接入前的结构化异常提取#

Docker 化完成后,项目进入 AI 日志分析前的准备阶段。

这一阶段并没有直接调用外部 AI API,而是先做数据整理:从健康检查日志中提取 WARNINGERROR,再把异常转成稳定的 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 分析流程如下:

ServerSentinel AI 日志分析报告结构
ServerSentinel AI 日志分析报告结构

健康检查日志
-> 结构化异常 JSON
-> DeepSeek JSON Output
-> JSON Schema 验证
-> AI 分析 JSON
-> Markdown 人工复核报告

这一阶段新增了几个关键文件:

prompts/analyze-anomalies.md
config/ai-analysis-response.schema.json
src/validate_ai_response.py
src/analyze_with_ai.py
src/render_ai_report.py

prompts/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.py
scripts/run-daily-ai-analysis.sh
config/server-sentinel.env.example
docs/deployment.md

src/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:

Terminal window
docker compose --profile ai run --rm ai-pipeline

AI 服务不会在普通 docker compose run report-generator 时自动启动,避免一次普通报告生成误触发外部 API 请求。容器中日志目录仍然只读挂载,报告目录可写,API Key 只在运行时通过环境变量注入。

最后,docs/deployment.md 整理了 Ubuntu 服务器部署流程,包括依赖安装、仓库克隆、Python 虚拟环境、密钥文件权限、手动验证、cron 配置、Docker 运行方式和部署后的只读检查命令。

这一阶段的重点不是新增某一个单点功能,而是把已经完成的模块整理成可定时、可部署、可验证的运行链路。

第十一阶段:脱敏、多 Provider、通知与 Dashboard 展示#

完成 AI 巡检流水线后,项目继续补齐几个工程边界:外部 AI 调用前的数据脱敏、不同模型服务的配置抽象、风险通知,以及面向结果查看的只读 Dashboard。

新增的关键文件包括:

src/redact_sensitive_data.py
src/ai_providers.py
src/send_notification.py
src/analyze_nginx_log.py
src/serve_dashboard.py
dashboard/index.html
dashboard/app.js
dashboard/styles.css
tests/test_security_and_providers.py
tests/test_nginx_json_and_dashboard.py

src/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.pysrc/serve_dashboard.pydashboard/ 目录组成。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.1
GET /.env HTTP/1.1
GET /.git/config HTTP/1.1
GET /admin.php HTTP/1.1

这些路径和当前网站没有关系,最开始容易误以为网站出现了异常。

后面分析后可以判断:这通常是公网自动扫描器在尝试探测常见漏洞路径。只要服务器没有这些文件,并且返回 404,通常不代表已经被入侵。

需要记住的是:公网服务器被扫描是常态。重点不是完全不让别人请求,而是要能识别、记录、限制和告警。

2. 日志字段看起来很乱#

Nginx access.log 一行内容很长,包含 IP、时间、请求路径、状态码、Referer、User-Agent 等字段。

一开始直接看会觉得混乱,但拆开后就会清楚很多:

客户端 IP - - [时间] "请求方法 路径 协议" 状态码 响应大小 "Referer" "User-Agent"

理解日志格式以后,才能用 awkgrepsortuniq 等工具做统计。

这个问题的解决方式不是硬背命令,而是先理解每个字段的含义,再决定提取哪一列。

3. Shell 脚本容易越写越乱#

Shell 很适合快速完成任务,但如果没有结构,脚本会很快变得难维护。

比较好的做法是:

  • 把重复逻辑封装成函数;
  • 日志输出格式保持一致;
  • 关键变量放在脚本开头;
  • 异常情况明确返回;
  • 不要把所有逻辑堆在一长串命令里。

例如日志输出可以统一成:

2026-07-19 10:00:00 [OK] Website is reachable
2026-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 是排查访问行为的重要依据;
  • awkgrepsortuniqhead 是日志分析的基础工具;
  • 大量 .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 展示和定时部署串成了一个实际流程。它不是单个知识点的堆叠,而是围绕“服务器状态如何被检查、记录、分析、封装、验证、辅助解释、展示和周期性运行”这个问题逐步展开的。

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

ServerSentinel 项目开发复盘:从服务器巡检到 AI 日志分析
https://tiancheng-blog.com/posts/server-sentinel-project-review/
作者
TianCheng
发布于
2026-07-25
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
TianCheng
Hello, I'm TianCheng.
公告
欢迎来到我的博客!这是一则示例公告。
音乐
封面

音乐

暂未播放

0:000:00
暂无歌词
分类
标签
站点统计
文章
15
分类
6
标签
44
总字数
37,448
运行时长
0
最后活动
0 天前
站点信息
部署平台
Tencent Cloud Lighthouse
主题版本
Firefly v6.13.5
文章许可
CC BY-NC-SA 4.0

文章目录