我用Claude Code写了个服务器巡检工具,它真的理解了我的代码

作者:Koen
7年Java后端 → 全栈独立开发者


1. 为什么要写

我以前做Java后端的时候,服务器巡检这件事基本靠两种方式:一是登录机器手动看,二是等报警系统叫。

听起来也没什么问题,但真放到个人服务器、独立项目、小团队环境里,就会发现很难受。

比如我自己的服务器上跑了一堆东西:业务容器、MySQL、Redis、Nginx/OpenResty、各种自动化脚本、Agent服务、定时任务、Webhook推送。平时没事的时候它们都很安静,一旦出问题,通常不是一个点坏了,而是一串连锁反应。

磁盘快满了,日志开始刷爆;
某个容器挂了,反代还活着,但业务已经打不开;
定时任务失败了,没人知道;
接口偶发超时,等用户反馈已经晚了。

我最早的处理方式很原始:

df -h
free -h
docker ps
docker logs xxx --tail=100
systemctl status xxx

每次都要敲一遍,越敲越烦。

后来我想,与其每次人工排查,不如写一个巡检工具,每天定时跑一次,把服务器状态整理成一份报告。如果有异常,就直接推送给我;如果没异常,就安静别烦我。

需求很简单:

  1. 检查CPU、内存、磁盘
  2. 检查Docker容器状态
  3. 检查关键端口和服务
  4. 检查最近的错误日志
  5. 输出一份人能看懂的报告
  6. 异常时推送通知
  7. 尽量不要引入复杂依赖

如果按以前的方式,我大概会先建个Java项目,写配置文件,搞定时任务,接日志框架,再封装一堆检测类。不是不能写,但有点杀鸡用牛刀。

这类工具最适合shell + python。

shell负责拿系统信息,Python负责分析和生成报告。简单、直接、可维护。

于是我把这个需求丢给了Claude Code。

2. Claude Code怎么理解需求

我给Claude Code的第一版描述大概是这样的:

帮我写一个服务器巡检工具。

运行环境是Ubuntu服务器。
用shell收集系统状态,用python分析结果并生成报告。
需要检查:
- CPU负载
- 内存使用率
- 磁盘使用率
- Docker容器状态
- 关键端口监听情况
- 最近系统错误日志
- 最近Docker容器错误日志

输出Markdown报告。
如果发现异常,输出ALERTS数量和异常原因。
脚本要适合放到cron里每天执行。

这个提示词不算复杂,但我发现Claude Code处理这种任务有个特点:它不会只写一个脚本,而是会先理解"这个东西要怎么长期运行"。

它给我的第一版方案是三层:

check.sh              # 主入口,负责采集和调度
analyze.py            # 分析采集结果,生成报告
reports/              # 存放历史报告

我觉得这个拆分挺合理。

因为shell做系统采集很方便,比如:

df -h
free -m
uptime
docker ps -a
ss -lntp
journalctl -p err

但shell不适合做复杂判断和格式化报告。你当然可以用awk、sed硬写,但写到后面基本没人想维护。

Python刚好补上这部分。

Claude Code还主动考虑了几个点:

  • 报告文件按日期命名
  • 保留最近N份报告,避免无限增长
  • 检测项要有阈值
  • 异常数量要结构化输出
  • cron执行时不能依赖交互环境
  • 命令失败不能让整个脚本直接崩掉

这点让我挺满意。它不是简单给你一坨"能跑"的代码,而是会按一个小工具的形态去组织。

当然,第一版肯定不能直接上线。AI写运维脚本,必须人工过一遍。尤其是涉及磁盘、容器、日志、权限的地方,不能盲信。

3. 代码实现:shell + python巡检脚本

最后我保留了两个核心脚本。

第一个是check.sh,负责采集数据。

简化版大概长这样:

#!/usr/bin/env bash
set -u

BASE_DIR="$(cd "$(dirname "$0")" && pwd)"
REPORT_DIR="$BASE_DIR/reports"
TMP_DIR="$BASE_DIR/tmp"

mkdir -p "$REPORT_DIR" "$TMP_DIR"

SNAPSHOT="$TMP_DIR/snapshot.txt"

{
  echo "===== BASIC ====="
  date
  hostname
  uptime

  echo
  echo "===== DISK ====="
  df -h

  echo
  echo "===== MEMORY ====="
  free -m

  echo
  echo "===== DOCKER ====="
  if command -v docker >/dev/null 2>&1; then
    docker ps -a --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
  else
    echo "docker not installed"
  fi

  echo
  echo "===== PORTS ====="
  ss -lntp || true

  echo
  echo "===== SYSTEM ERRORS ====="
  journalctl -p err -n 80 --no-pager 2>/dev/null || true

} > "$SNAPSHOT"

python3 "$BASE_DIR/analyze.py" "$SNAPSHOT" "$REPORT_DIR"

这里我没有追求特别复杂。

运维脚本最重要的不是炫技,而是稳定。能在cron里跑,路径固定,失败可控,输出清楚,这就够了。

第二个是analyze.py,负责分析和生成报告。

核心逻辑类似这样:

#!/usr/bin/env python3
import sys
import re
from datetime import datetime
from pathlib import Path

DISK_WARN = 80
DISK_CRITICAL = 90
MEM_WARN = 80


def parse_disk(snapshot: str):
    alerts = []
    for line in snapshot.splitlines():
        parts = line.split()
        if len(parts) < 6:
            continue
        usage = parts[4]
        mount = parts[5]
        if not usage.endswith("%"):
            continue
        try:
            percent = int(usage.rstrip("%"))
        except ValueError:
            continue
        if percent >= DISK_CRITICAL:
            alerts.append(f"磁盘严重告警:{mount} 使用率 {percent}%")
        elif percent >= DISK_WARN:
            alerts.append(f"磁盘预警:{mount} 使用率 {percent}%")
    return alerts


def parse_docker(snapshot: str):
    alerts = []
    docker_section = (
        snapshot.split("===== DOCKER =====")[-1]
        .split("===== PORTS =====")[0]
    )
    for line in docker_section.splitlines():
        lower = line.lower()
        if "exited" in lower or "dead" in lower or "restarting" in lower:
            alerts.append(f"Docker容器异常:{line.strip()}")
    return alerts


def generate_report(snapshot: str, alerts: list[str]):
    now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    lines = [f"# 服务器巡检报告", "",
             f"- 巡检时间:{now}", f"- 异常数量:{len(alerts)}", ""]
    if alerts:
        lines.append("## 异常列表")
        lines.append("")
        for item in alerts:
            lines.append(f"- {item}")
        lines.append("")
    else:
        lines.append("## 巡检结果")
        lines.append("")
        lines.append("当前未发现明显异常。")
        lines.append("")
    return "\n".join(lines)

shell只负责"拿事实":

df -h
free -m
docker ps -a
ss -lntp
journalctl -p err

Python负责"做判断":

if percent >= DISK_CRITICAL:
    alerts.append(...)

最终输出也比较适合自动化系统读取:

REPORT=/path/to/reports/healthcheck-20260623-090000.md
ALERTS=2
ALERT: 磁盘预警:/ 使用率 83%
ALERT: Docker容器异常:api Exited (1) 2 hours ago

实际输出示例:这就是我SRE每日巡检脚本的格式。每天09:00自动跑,正常就静默,异常才推送。ALERTS=0时连报告都不发,只有异常了你才知道。

这样后面要接企业微信、Telegram、飞书、邮件都很方便。只要判断ALERTS > 0,再把报告推送出去就行。

我后来又加了一个报告清理逻辑,避免报告目录无限增长:

find "$REPORT_DIR" -name "healthcheck-*.md" -type f | sort | head -n -14 | xargs -r trash

这里我更推荐用trash而不是rm。巡检报告虽然不是核心数据,但能不硬删就别硬删。运维脚本里最怕一个路径变量为空,然后rm给你表演魔术。

4. 遇到的坑

这个工具看起来简单,但实际跑起来还是踩了几个坑。

第一个坑是cron环境变量太少。

你在终端里能跑,不代表cron里能跑。比如docker命令可能找不到,python3路径可能不一样,甚至当前目录都不是你以为的目录。

所以脚本里最好不要写相对路径:

BASE_DIR="$(cd "$(dirname "$0")" && pwd)"

命令也尽量做存在性检查:

if command -v docker >/dev/null 2>&1; then
  docker ps -a
else
  echo "docker not installed"
fi

第二个坑是权限。

journalctl和docker ps在某些机器上需要权限。你用自己的用户手动跑没问题,换成cron用户就失败了。

解决方式有两个:

一种是把脚本放到有权限的用户下跑。
另一种是给固定命令配置sudo免密。

但我不建议一上来就给整个脚本sudo权限。巡检脚本权限越大,出错时伤害也越大。

第三个坑是日志太多。

第一次我让脚本抓最近几百行错误日志,结果报告巨大,推送消息直接爆了。

后来改成两层:

  • 报告文件里保留较完整内容
  • 推送消息里只放摘要

比如推送内容只放:

服务器巡检发现 2 个异常:
1. 磁盘预警:/ 使用率 83%
2. Docker容器异常:api Exited (1) 2 hours ago

完整报告:/home/xxx/reports/healthcheck-xxx.md

第四个坑是AI喜欢写得太"完整"。

Claude Code很容易主动加配置文件、日志模块、通知模块、彩色输出、命令行参数。不是不好,但这个工具的核心目标是稳定巡检,不是做一个平台。

所以我会明确告诉它:

不要引入复杂框架。
不要做Web界面。
不要使用数据库。
脚本必须能直接在Ubuntu服务器运行。
优先稳定和可维护。

AI写代码时,边界越清楚,结果越靠谱。

第五个坑是阈值不能太敏感。

比如内存使用率在Linux上本来就容易看起来很高,因为系统会用空闲内存做缓存。如果简单按used / total判断,很容易误报。

更合理的方式是看available,或者用 (total - available) / total 来算。

5. 效果对比

写这个工具前,我的服务器巡检基本靠想起来。

有时候几天不看,一看发现某个容器已经重启几十次;有时候磁盘快满了,还是业务异常后才发现;还有时候定时任务失败了,但因为没通知,等于失败了个寂寞。

写完之后,流程变成这样:

每天定时执行 check.sh
        ↓
采集系统状态
        ↓
Python分析异常
        ↓
生成Markdown报告
        ↓
ALERTS > 0 时推送通知
        ↓
无异常就静默

最明显的变化是:我不用主动想"今天要不要看看服务器"。

它每天自己看。

如果没问题,我不需要知道。
如果有问题,它会把异常点列出来。

这比传统监控系统轻很多,也比纯手工靠谱很多。

当然,它替代不了Prometheus、Grafana、Zabbix这类完整监控方案。它没有时序数据,没有复杂图表,也没有多维度指标聚合。

但对独立开发者、小服务器、个人项目来说,这种轻量巡检工具反而刚好。

我不需要一个很重的平台来告诉我"磁盘80%了"。
我只需要每天有个脚本认真扫一遍,然后在该提醒我的时候提醒我。

Claude Code在这个过程里最大的价值,不是"帮我写了几行脚本"。

它真正省时间的地方在于:把一个模糊想法快速变成了可运行的工具骨架。

我不用从空文件开始想目录结构,不用纠结shell和Python怎么分工,也不用一点点补边界处理。它先给出一个可用版本,我再按自己的服务器环境调整。

这个协作方式很像带一个初级但手速很快的同事。

你不能完全放手,但你可以让它先干起来。你负责判断方向、补安全边界、做最终验收。

6. 总结

这次用Claude Code写服务器巡检工具,我最大的感受是:AI很适合写这种"需求明确、边界清楚、反馈快"的运维小工具。

它不需要特别复杂的架构,也不需要上来就工程化到极致。先让工具跑起来,先解决每天巡检的问题,再慢慢补通知、报告清理、异常分类、服务白名单,这个节奏更舒服。

如果你也有自己的服务器,我建议从一个最简单的巡检脚本开始:

df -h
free -m
docker ps -a
journalctl -p err -n 50

然后让AI帮你把这些输出整理成报告。

不要一开始就做大而全的监控系统。很多时候,一个每天自动运行、异常才提醒你的脚本,就已经能解决80%的问题。

最后我也给自己定了个原则:服务器上凡是我重复手动检查超过三次的东西,都应该脚本化;凡是脚本化后还需要我判断的东西,都可以让AI先分析一遍。

Koen的一句话总结: Claude Code没有替我"运维服务器",但它帮我把那些重复、琐碎、容易忘的巡检动作,变成了一个每天自动上班的小工具。

更多推荐