摘要
手头上有几台服务器,经常要对服务器进行健康检查、安全检查等。这几天让ClaudeCode自己写了个Skill这样就一句话活就让AI给干了。本文详细介绍了如何使用Claude Code AI辅助开发工具,从零开始构建一个生产级运维健康检查的Skill。重点阐述了整体架构设计、核心功能实现细节以及实际应用效果。该系统采用模块化设计,提供三大检查模块(基础健康检查、深度安全审计、Docker容器监控),支持双格式输出(Markdown人工可读 + JSON机器可处理),具有良好的可扩展性和兼容性。
一、背景与设计目标
1.1 实际需求
在日常运维工作中,我们需要对多台服务器进行健康检查,涵盖系统资源、Docker容器状态、安全指标等多个维度。虽然市面上有很多成熟的监控方案(如Prometheus、Grafana、Zabbix等),但在以下场景中,我们需要一个轻量级、快速部署的检查工具:
- 日常巡检:每天对服务器进行快速健康扫描
- 安全审计:定期检查异常进程、可疑网络连接、文件系统安全
- 容器监控:检查Docker容器运行状态、资源使用情况
- 问题排查:当系统出现问题时,快速获取当前状态信息
1.2 设计目标
基于上述需求,我设定了以下设计目标交给了Claude Code:
核心特性:
- ✅ 轻量级部署:基于Bash脚本,无需额外依赖,远程执行无需安装
- ✅ 双格式输出:同时生成Markdown(人工阅读)和JSON(机器处理)格式
- ✅ 模块化架构:三大检查模块独立运行,可按需组合
- ✅ 状态可视化:使用emoji(✅正常/⚠️警告/❌严重)直观展示
- ✅ 广泛兼容性:支持bash 3.2+(macOS默认版本),适配主流Linux发行版
技术亮点:
- 统一的输出库设计,实现代码复用
- JSON三层结构(summary/details/metadata),满足不同使用场景
- 完善的错误处理和兼容性方案
- 清晰的扩展接口,便于添加新的检查类型
二、整体架构设计
2.1 系统架构
Claude Code自动帮我设计了整体架构 运维健康检查系统采用模块化设计,包含三个独立的检查模块:
| 模块名称 | 脚本文件 | 功能定位 | 核心检查项 |
|---|---|---|---|
| 基础健康检查 | health-check.sh | 日常巡检、快速评估 | 系统资源、内存、磁盘、网络、服务状态、基础安全 |
| 深度安全检查 | security-check.sh | 安全审计、威胁检测 | 异常进程、可疑连接、文件安全、账户安全、系统完整性 |
| Docker容器监控 | docker-check.sh | 容器环境管理 | 容器状态、资源使用、镜像管理、存储空间 |
设计原则:
- 独立性:每个脚本可单独运行,互不依赖
- 一致性:统一的输出格式和状态指示
- 可扩展性:易于添加新的检查项或模块
- 远程友好:支持SSH管道执行,无需远程安装
2.2 文件结构
host-manage/
├── skills/ops-health-check/
│ ├── SKILL.md # Skill定义和使用文档
│ └── scripts/
│ ├── health-check.sh # 基础健康检查脚本
│ ├── security-check.sh # 深度安全检查脚本
│ ├── docker-check.sh # Docker监控脚本
│ └── lib/
│ └── output.sh # 输出格式化库(核心)
├── docs/plans/
│ ├── 2025-01-17-ops-health-check-design.md # 整体设计文档
│ └── 2025-01-18-json-output-design.md # JSON输出设计
└── health-reports/ # 检查报告输出目录
├── health-check-*.md # Markdown格式报告
├── health-check-*.json # JSON格式数据
├── security-check-*.md
├── security-check-*.json
├── docker-check-*.md
└── docker-check-*.json
三、核心功能实现
Claude Code自动帮我实现了所有的功能
3.1 基础健康检查模块(health-check.sh)
这是日常运维使用最频繁的模块,专注于快速评估系统整体健康状况。
3.1.1 系统运行时间和负载检查
检查目的:了解系统运行时长和当前负载压力
实现逻辑:
# 获取系统运行时间
uptime_output=$(uptime)
uptime_clean=$(echo "$uptime_output" | sed 's/^ *//g')
# 兼容不同系统的uptime输出格式
uptime_str=$(uptime -p 2>/dev/null || \
echo "$uptime_clean" | awk -F'up ' '{print $2}' | awk -F',' '{print $1}')
# 提取负载平均值(1分钟、5分钟、15分钟)
load_str=$(echo "$uptime_clean" | awk -F'load average:' '{print $2}' | sed 's/^ *//g')
load_1min=$(echo $load_str | awk '{print $1}' | sed 's/,//')
load_5min=$(echo $load_str | awk '{print $2}' | sed 's/,//')
load_15min=$(echo $load_str | awk '{print $3}')
# 判断负载状态
load_status=$(check_load_status "$load_1min" "$CPU_WARNING" "$CPU_CRITICAL")
状态判断函数:
check_load_status() {
local load=$1
local warning=$2
local critical=$3
# 使用bc进行浮点数比较
if (( $(echo "$load >= $critical" | bc -l) )); then
echo "critical"
elif (( $(echo "$load >= $warning" | bc -l) )); then
echo "warning"
else
echo "ok"
fi
}
关键点:
- 使用
bc -l进行浮点数比较,兼容不同系统 - 兼容
uptime -p命令(human-readable格式)不可用的情况 - 同时收集三个时间维度的负载数据,用于趋势分析
3.1.2 内存使用检查
检查目的:监控内存和swap使用情况,防止内存耗尽
实现逻辑:
# 获取内存信息(单位:MB)
memory_info=$(free -m | grep Mem)
mem_total=$(echo $memory_info | awk '{print $2}')
mem_used=$(echo $memory_info | awk '{print $3}')
mem_avail=$(echo $memory_info | awk '{print $7}') # available列更准确
mem_percent=$(awk "BEGIN {printf \"%.1f\", $mem_used * 100 / $mem_total}")
# Swap检查
swap_info=$(free -m | grep Swap)
swap_total=$(echo $swap_info | awk '{print $2}')
swap_used=$(echo $swap_info | awk '{print $3}')
if [ "$swap_total" -gt 0 ]; then
swap_percent=$(awk "BEGIN {printf \"%.1f\", $swap_used * 100 / $swap_total}")
else
swap_percent=0
fi
# 状态判断
if (( $(echo "$mem_percent >= $MEMORY_CRITICAL" | bc -l) )); then
mem_status="critical"
elif (( $(echo "$mem_percent >= $MEMORY_WARNING" | bc -l) )); then
mem_status="warning"
else
mem_status="ok"
fi
输出示例:
### 内存使用
- **总内存**: 16384MB
- **已使用**: 12500MB (76.3%)
- **可用**: 3884MB
- **Swap**: 256MB / 2048MB (12.5%)
- **状态**: ⚠️ 警告
关键点:
- 使用
free命令的available列而非free列,更准确反映可用内存 - 同时检查swap使用率,高swap使用率通常意味着内存压力
- 支持自定义阈值配置(通过环境变量
MEMORY_WARNING、MEMORY_CRITICAL)
3.1.3 磁盘空间检查
检查目的:监控所有挂载点的磁盘使用情况,防止磁盘空间不足
实现逻辑:
echo "### 磁盘空间"
echo "| 挂载点 | 设备 | 容量 | 已用 | 可用 | 使用率 | 状态 |"
echo "|--------|------|------|------|------|--------|------|"
# 遍历所有挂载点(排除临时文件系统)
df -h | grep -vE '^Filesystem|tmpfs|overlay|none' | while read -r line; do
device=$(echo $line | awk '{print $1}')
size=$(echo $line | awk '{print $2}')
used=$(echo $line | awk '{print $3}')
avail=$(echo $line | awk '{print $4}')
use_percent=$(echo $line | awk '{print $5}' | sed 's/%//')
mount=$(echo $line | awk '{print $6}')
# 判断状态
if [ "$use_percent" -ge "$DISK_CRITICAL" ]; then
status="critical"
emoji="❌"
elif [ "$use_percent" -ge "$DISK_WARNING" ]; then
status="warning"
emoji="⚠️"
else
status="ok"
emoji="✅"
fi
# 输出Markdown表格行
echo "| $mount | $device | $size | $used | $avail | ${use_percent}% | $emoji |"
# 收集JSON数据
add_disk_data "$mount" "device" "$device"
add_disk_data "$mount" "total_gb" "$size"
add_disk_data "$mount" "used_gb" "$used"
add_disk_data "$mount" "available_gb" "$avail"
add_disk_data "$mount" "used_percent" "$use_percent"
add_disk_data "$mount" "status" "$status"
done
输出示例:
### 磁盘空间
| 挂载点 | 设备 | 容量 | 已用 | 可用 | 使用率 | 状态 |
|--------|------|------|------|------|--------|------|
| / | /dev/sda1 | 100G | 45G | 55G | 45.0% | ✅ 正常 |
| /data | /dev/sdb1 | 500G | 425G | 75G | 85.0% | ⚠️ 警告 |
| /boot | /dev/sda2 | 1G | 250M | 750M | 25.0% | ✅ 正常 |
关键点:
- 自动过滤
tmpfs、overlay等临时文件系统 - 支持多个挂载点,每个挂载点独立判断状态
- 使用整数比较(已去除百分号),提高性能
- 同时生成Markdown表格和JSON数组数据
3.1.4 网络连接统计
检查目的:了解当前网络连接数量,快速发现异常连接
实现逻辑:
# 使用ss命令替代netstat(更现代)
if command -v ss >/dev/null 2>&1; then
# 总连接数
total_connections=$(ss -tn | wc -l)
# 外部连接数(排除本地回环)
external_connections=$(ss -tn | awk '{print $5}' | \
grep -v '127.0.0.1' | \
grep -v '::1' | \
cut -d':' -f1 | \
sort -u | \
wc -l)
# 监听端口数
listening_ports=$(ss -tln | wc -l)
else
# 降级到netstat
total_connections=$(netstat -tn 2>/dev/null | wc -l)
external_connections=$(netstat -tn 2>/dev/null | awk '{print $5}' | \
grep -v '127.0.0.1' | \
grep -v '::1' | \
cut -d':' -f1 | \
sort -u | \
wc -l)
listening_ports=$(netstat -tln 2>/dev/null | wc -l)
fi
add_system_data "network_total_connections" "$total_connections"
add_system_data "network_external_connections" "$external_connections"
add_system_data "network_listening_ports" "$listening_ports"
关键点:
- 优先使用
ss命令(现代Linux推荐),降级到netstat - 统计外部独立IP连接数,而非连接总数(更准确反映异常)
- 包含监听端口数量,便于发现未授权监听
3.1.5 systemd服务状态检查
检查目的:检查系统服务运行状态,发现失败服务
实现逻辑:
# 检查systemd是否可用
if command -v systemctl >/dev/null 2>&1; then
# 统计服务状态
running_services=$(systemctl list-units --type=service --state=running 2>/dev/null | \
grep 'loaded loaded' | wc -l)
failed_services=$(systemctl list-units --type=service --state=failed 2>/dev/null | \
grep 'loaded loaded' | wc -l)
enabled_services=$(systemctl list-unit-files --type=service --state=enabled 2>/dev/null | \
grep 'enabled' | wc -l)
# 获取失败服务列表
if [ "$failed_services" -gt 0 ]; then
failed_list=$(systemctl list-units --type=service --state=failed 2>/dev/null | \
grep 'loaded loaded' | awk '{print $1}')
echo "### 失败的服务"
echo "$failed_list" | while read -r service; do
echo "- ❌ $service"
done
fi
# 常用关键服务检查
critical_services=("ssh" "docker" "cron" "rsyslog")
for svc in "${critical_services[@]}"; do
if systemctl is-active --quiet "${svc}.service" 2>/dev/null; then
echo "- ✅ ${svc}.service: 运行中"
else
echo "- ⚠️ ${svc}.service: 未运行或不存在"
fi
done
else
echo "⚠️ 系统不使用systemd"
fi
输出示例:
### 服务状态
- **运行中**: 23个
- **失败**: 2个
- **启用**: 15个
#### 关键服务状态
- ✅ ssh.service: 运行中
- ✅ docker.service: 运行中
- ✅ cron.service: 运行中
- ❌ postfix.service: 已停止
#### 失败的服务列表
- ❌ postfix.service
- ❌ ufw.service
关键点:
- 兼容非systemd系统(如SysV init)
- 区分"运行中"、"失败"、"启用"三种状态
- 单独检查关键服务(SSH、Docker等),便于快速定位问题
- 列出所有失败服务,方便管理员进一步排查
3.1.6 基础安全检查
虽然主要的安全检查在security-check.sh中,但基础健康检查也包含几个关键的安全指标:
echo "### 基础安全"
# 1. 检查挖矿进程
MINING_PROCESSES=("xmrig" "minerd" "cpuminer" "ccminer" "Claymore")
mining_found=0
for proc in "${MINING_PROCESSES[@]}"; do
if pgrep -x "$proc" >/dev/null; then
echo "- ❌ 发现挖矿进程: $proc"
mining_found=1
fi
done
if [ $mining_found -eq 0 ]; then
echo "- ✅ 未发现挖矿进程"
fi
# 2. 检查/tmp和/dev/shm中的可执行文件
tmp_execs=$(find /tmp /dev/shm -type f -executable 2>/dev/null | wc -l)
if [ "$tmp_execs" -gt 0 ]; then
echo "- ⚠️ 临时目录中发现 $tmp_execs 个可执行文件"
else
echo "- ✅ 临时目录无可执行文件"
fi
# 3. 检查失败登录次数(最近)
if [ -f /var/log/auth.log ]; then
failed_logins=$(grep "Failed password" /var/log/auth.log 2>/dev/null | \
tail -100 | wc -l)
echo "- 最近失败登录次数: $failed_logins"
fi
3.2 深度安全检查模块(security-check.sh)
这是最复杂的模块,涵盖了5大类、20+项安全检查,用于全面的安全审计。
3.2.1 异常进程检测
检查目的:发现挖矿程序、高资源占用进程、可疑可执行文件
1. 挖矿进程检测
# 定义已知挖矿程序名称列表
MINING_PROCESSES=(
"xmrig" "minerd" "cpuminer" "ccminer" "Claymore"
"cryptonight" "ethminer" "nbminer" "bminer"
)
suspicious_procs=""
mining_detected="false"
# 遍历检查
for proc in "${MINING_PROCESSES[@]}"; do
# 使用pgrep查找进程(比ps aux更快)
found=$(pgrep -ix "$proc" 2>/dev/null)
if [ -n "$found" ]; then
# 获取进程详细信息
proc_info=$(ps -p "$found" -o pid,user,cmd --no-headers 2>/dev/null)
suspicious_procs="$suspicious_procs\n$proc_info"
mining_detected="true"
# 记录到安全数据
add_security_data "mining_process" "$proc"
add_security_data "mining_pid" "$found"
fi
done
if [ "$mining_detected" = "true" ]; then
echo "❌ 发现挖矿进程:"
echo -e "$suspicious_procs"
else
echo "✅ 未发现挖矿进程"
fi
2. 高资源占用进程检测
echo "### 高资源占用进程(Top 10)"
echo "| PID | 用户 | CPU% | 内存% | 命令 |"
echo "|-----|------|------|--------|------|"
# CPU占用最高的进程
ps aux --sort=-%cpu | head -n 11 | tail -n 10 | while read -r line; do
pid=$(echo "$line" | awk '{print $2}')
user=$(echo "$line" | awk '{print $1}')
cpu=$(echo "$line" | awk '{print $3}')
mem=$(echo "$line" | awk '{print $4}')
cmd=$(echo "$line" | awk '{for(i=11;i<=NF;i++)printf $i" "}' | cut -c1-50)
# 标记异常高占用
if (( $(echo "$cpu > 80" | bc -l) )); then
echo "| $pid | $user | **$cpu** | $mem | $cmd |"
else
echo "| $pid | $user | $cpu | $mem | $cmd |"
fi
done
3. 临时目录可执行文件检查
# 检查/tmp和/dev/shm中的可执行文件
tmp_dirs=("/tmp" "/dev/shm" "/var/tmp")
for dir in "${tmp_dirs[@]}"; do
if [ -d "$dir" ]; then
exec_files=$(find "$dir" -type f -executable 2>/dev/null)
exec_count=$(echo "$exec_files" | grep -v '^$' | wc -l)
if [ "$exec_count" -gt 0 ]; then
echo "⚠️ $dir 中发现 $exec_count 个可执行文件:"
echo "$exec_files" | head -n 10 | while read -r file; do
perms=$(stat -c "%a" "$file" 2>/dev/null)
size=$(stat -c "%s" "$file" 2>/dev/null)
mtime=$(stat -c "%y" "$file" 2>/dev/null | cut -d'.' -f1)
echo " - $file (权限:$perms, 大小:$size字节, 修改:$mtime)"
done
fi
fi
done
关键点:
- 使用
pgrep而非ps aux | grep,性能更高且避免误匹配 - 记录进程的PID、用户、完整命令行,便于追踪
- 同时检查/tmp、/dev/shm、/var/tmp三个常见临时目录
- 显示文件的权限、大小、修改时间,帮助判断是否可疑
3.2.2 网络安全检查
检查目的:发现反向shell、可疑端口监听、异常外部连接
1. 反向shell检测
# 反向shell典型特征:外部IP连接本地高端口(非标准服务端口)
echo "### 反向shell检测"
# 获取所有TCP连接
if command -v ss >/dev/null 2>&1; then
connections=$(ss -tnp 2>/dev/null)
else
connections=$(netstat -tnp 2>/dev/null)
fi
# 检测可疑模式:外部IP连接到本地50000-65535端口
reverse_shell_detected="false"
echo "$connections" | while read -r line; do
# 解析本地地址和远程地址
local_addr=$(echo "$line" | awk '{print $4}')
remote_addr=$(echo "$line" | awk '{print $5}')
# 提取端口号
local_port=$(echo "$local_addr" | cut -d':' -f2 | cut -d':' -f1)
remote_ip=$(echo "$remote_addr" | cut -d':' -f1)
# 排除本地回环和标准端口
if [[ ! "$remote_ip" =~ ^(127\.|::1) ]]; then
# 检查本地端口是否为高端口(非标准服务)
if [ "$local_port" -ge 50000 ] 2>/dev/null; then
# 排除部分合法的高端口号
if [[ ! "$local_port" =~ ^(50000|50001|50002) ]]; then
echo "⚠️ 可疑连接:远程 $remote_addr -> 本地端口 $local_port"
reverse_shell_detected="true"
fi
fi
fi
done
if [ "$reverse_shell_detected" = "false" ]; then
echo "✅ 未发现反向shell特征"
fi
2. 监听端口分析
echo "### 监听端口分析"
# 获取所有监听端口
if command -v ss >/dev/null 2>&1; then
listening=$(ss -tulpn 2>/dev/null)
else
listening=$(netstat -tulpn 2>/dev/null)
fi
echo "| 端口 | 协议 | 进程 | 用户 |"
echo "|------|------|------|------|"
echo "$listening" | grep LISTEN | while read -r line; do
port=$(echo "$line" | awk '{print $5}' | rev | cut -d':' -f1 | rev)
protocol=$(echo "$line" | awk '{print $1}')
process=$(echo "$line" | awk '{print $7}' | cut -d'/' -f2 | cut -d',' -f1)
user=$(echo "$line" | awk '{print $7}' | cut -d'"' -f2)
# 标记非标准高端口
if [ "$port" -ge 10000 ]; then
echo "| **$port** | $protocol | $process | $user |"
else
echo "| $port | $protocol | $process | $user |"
fi
done
3. 外部连接统计
# 统计外部连接的独立IP数量
external_ips=$(ss -tn 2>/dev/null | \
awk '{print $5}' | \
cut -d':' -f1 | \
grep -vE '^(127\.|::1|0\.0\.0\.0)' | \
sort -u | \
wc -l)
echo "### 外部连接统计"
echo "- 外部连接的独立IP数:$external_ips"
# 列出Top 10外部IP
echo "- 连接最多的外部IP:"
ss -tn 2>/dev/null | \
awk '{print $5}' | \
cut -d':' -f1 | \
grep -vE '^(127\.|::1|0\.0\.0\.0)' | \
sort | uniq -c | sort -rn | head -n 10 | \
while read -r line; do
count=$(echo "$line" | awk '{print $1}')
ip=$(echo "$line" | awk '{print $2}')
echo " - $ip: $count 次连接"
done
关键点:
- 反向shell检测基于行为特征:外部IP连接本地高端口
- 排除已知的合法高端口(如某些应用服务)
- 统计外部连接IP数量和连接频率,异常高值需要关注
- 显示监听端口对应的进程和用户,便于排查
3.2.3 文件系统安全检查
检查目的:发现最近修改的敏感文件、勒索病毒特征、SUID/SGID文件
1. 关键目录变更检测
echo "### 关键目录最近变更(7天内)"
# 定义需要监控的关键目录
critical_dirs=("/etc" "/home" "/root" "/var/www" "/opt")
for dir in "${critical_dirs[@]}"; do
if [ -d "$dir" ]; then
# 查找7天内修改的文件
recent_files=$(find "$dir" -type f -mtime -7 2>/dev/null)
if [ -n "$recent_files" ]; then
file_count=$(echo "$recent_files" | grep -v '^$' | wc -l)
echo "⚠️ $dir: $file_count 个文件在最近7天被修改"
# 显示部分关键文件
echo "$recent_files" | head -n 5 | while read -r file; do
perms=$(stat -c "%a" "$file" 2>/dev/null)
echo " - $file (权限:$perms)"
done
fi
fi
done
2. 勒索病毒特征检测
echo "### 勒索病毒特征检测"
# 定义勒索病毒常用的文件扩展名
ransomware_patterns=(
"*.encrypted"
"*.locked"
"*.crypto"
"*.crypt"
"*.crypted"
"*_README.txt"
"*_DECRYPT.txt"
"*_HELP.txt"
"*_RESTORE.txt"
"how_to_decrypt.*"
"recover_files.*"
)
ransomware_found="false"
for pattern in "${ransomware_patterns[@]}"; do
# 在用户目录中查找
found_files=$(find /home /root -name "$pattern" 2>/dev/null)
if [ -n "$found_files" ]; then
echo "❌ 发现勒索病毒特征文件:"
echo "$found_files" | head -n 10
ransomware_found="true"
fi
done
if [ "$ransomware_found" = "false" ]; then
echo "✅ 未发现勒索病毒特征"
fi
3. SUID/SGID文件检查
echo "### SUID/SGID文件检查"
# 查找所有SUID文件
suid_files=$(find / -type f -perm -4000 2>/dev/null | \
grep -v -E '^/proc|^/sys|^/dev' | \
head -n 30)
suid_count=$(echo "$suid_files" | grep -v '^$' | wc -l)
if [ "$suid_count" -gt 0 ]; then
echo "发现 $suid_count 个SUID文件(部分列表):"
echo "$suid_files" | while read -r file; do
perms=$(stat -c "%a" "$file" 2>/dev/null)
owner=$(stat -c "%U" "$file" 2>/dev/null)
echo " - $file (权限:$perms, 所有者:$owner)"
done
fi
# 查找所有SGID文件
sgid_files=$(find / -type f -perm -2000 2>/dev/null | \
grep -v -E '^/proc|^/sys|^/dev' | \
head -n 20)
sgid_count=$(echo "$sgid_files" | grep -v '^$' | wc -l)
if [ "$sgid_count" -gt 0 ]; then
echo "发现 $sgid_count 个SGID文件(部分列表):"
echo "$sgid_files" | while read -r file; do
perms=$(stat -c "%a" "$file" 2>/dev/null)
owner=$(stat -c "%U" "$file" 2>/dev/null)
echo " - $file (权限:$perms, 所有者:$owner)"
done
fi
关键点:
- 重点监控/etc、/home、/root等敏感目录
- 勒索病毒检测基于文件扩展名特征(实际环境中建议结合文件内容分析)
- SUID/SGID文件是提权漏洞的常见切入点,需要重点关注
- 限制输出数量,避免报告过长
3.2.4 账户和登录安全检查
检查目的:检测异常登录、新增用户、sudo使用情况
1. 最近登录记录
echo "### 最近登录记录(最近10次)"
# 使用last命令获取登录记录
recent_logins=$(last -n 10 -a 2>/dev/null | head -n -2)
if [ -n "$recent_logins" ]; then
echo "| 用户 | 终端 | 来源IP | 登录时间 |"
echo "|------|------|--------|----------|"
echo "$recent_logins" | while read -r line; do
user=$(echo "$line" | awk '{print $1}')
terminal=$(echo "$line" | awk '{print $2}')
# IP在最后,主机名倒数第二
ip=$(echo "$line" | awk '{print $(NF-1)}' | sed 's/(//g')
# 时间范围在中间部分
time_range=$(echo "$line" | awk '{for(i=3;i<=NF-2;i++)printf $i" "}')
echo "| $user | $terminal | $ip | $time_range |"
done
fi
2. 失败登录统计
echo "### 失败登录统计"
# 统计最近失败的登录次数
if [ -f /var/log/auth.log ]; then
# Debian/Ubuntu系统
failed_count=$(grep "Failed password" /var/log/auth.log 2>/dev/null | \
tail -1000 | wc -l)
echo "- 最近失败登录次数(最近1000条日志):$failed_count"
# 统计失败登录来源IP
echo "- 失败登录最多的IP:"
grep "Failed password" /var/log/auth.log 2>/dev/null | \
awk '{print $13}' | \
sort | uniq -c | sort -rn | head -n 5 | \
while read -r line; do
count=$(echo "$line" | awk '{print $1}')
ip=$(echo "$line" | awk '{print $2}')
echo " - $ip: $count 次"
done
elif [ -f /var/log/secure ]; then
# CentOS/RHEL系统
failed_count=$(grep "Failed password" /var/log/secure 2>/dev/null | \
tail -1000 | wc -l)
echo "- 最近失败登录次数(最近1000条日志):$failed_count"
fi
3. 新增用户检测
echo "### 新增用户检测(最近30天)"
# 检查shadow文件中最近修改的用户
if [ -f /etc/shadow ]; then
# 第4字段是最后修改时间(天数,从1970-01-01起)
thirty_days_ago=$(($(date +%s) / 86400 - 30))
new_users=$(awk -F: -v date="$thirty_days_ago" \
'$4 >= date {print $1}' /etc/shadow 2>/dev/null)
if [ -n "$new_users" ]; then
echo "⚠️ 发现最近30天新增的用户:"
echo "$new_users" | while read -r user; do
# 获取用户详细信息
user_info=$(grep "^$user:" /etc/passwd)
uid=$(echo "$user_info" | cut -d':' -f3)
home=$(echo "$user_info" | cut -d':' -f6)
shell=$(echo "$user_info" | cut -d':' -f7)
echo " - $user (UID:$uid, HOME:$home, SHELL:$shell)"
done
else
echo "✅ 未发现最近30天的新增用户"
fi
fi
4. Sudo使用审计
echo "### Sudo使用审计(最近20条)"
# 检查sudo日志
if command -v journalctl >/dev/null 2>&1; then
# 使用journalctl读取sudo日志
sudo_logs=$(journalctl -u sudo 2>/dev/null | tail -n 20)
if [ -n "$sudo_logs" ]; then
echo "$sudo_logs" | while read -r line; do
# 提取关键信息:时间、用户、命令
echo " - $line"
done
fi
elif [ -f /var/log/auth.log ]; then
# 从auth.log中提取sudo记录
sudo_logs=$(grep sudo /var/log/auth.log 2>/dev/null | tail -n 20)
if [ -n "$sudo_logs" ]; then
echo "$sudo_logs"
fi
fi
关键点:
- 兼容不同Linux发行版的日志文件路径
- 统计失败登录次数和来源IP,高值可能意味着暴力破解
- 检测新增用户,特别是UID为0(root权限)的用户
- 审计sudo使用情况,发现权限滥用
3.2.5 系统完整性检查
检查目的:检查关键文件权限、系统二进制文件完整性
echo "### 系统完整性检查"
# 定义关键文件列表
critical_files=(
"/etc/passwd"
"/etc/shadow"
"/etc/sudoers"
"/etc/ssh/sshd_config"
"/etc/ssh/ssh_host_*_key"
)
echo "| 文件 | 权限 | 所有者 | 组 | 状态 |"
echo "|------|------|--------|-----|------|"
for file in "${critical_files[@]}"; do
# 使用通配符展开
for expanded_file in $file; do
if [ -e "$expanded_file" ]; then
perms=$(stat -c "%a" "$expanded_file" 2>/dev/null || stat -f "%OLp" "$expanded_file" 2>/dev/null)
owner=$(stat -c "%U" "$expanded_file" 2>/dev/null || stat -f "%Su" "$expanded_file" 2>/dev/null)
group=$(stat -c "%G" "$expanded_file" 2>/dev/null || stat -f "%Sg" "$expanded_file" 2>/dev/null)
# 检查权限是否合适(简化判断)
status="✅"
case "$(basename "$expanded_file")" in
shadow)
if [ "$perms" != "000" ] && [ "$perms" != "640" ]; then
status="⚠️"
fi
;;
sudoers)
if [ "$perms" != "440" ] && [ "$perms" != "400" ]; then
status="⚠️"
fi
;;
esac
echo "| $expanded_file | $perms | $owner | $group | $status |"
fi
done
done
关键点:
- 重点检查/etc/passwd、/etc/shadow等认证相关文件
- /etc/shadow应该是000或640权限
- /etc/sudoers应该是440或400权限
- SSH私钥文件应该是600或400权限
3.3 Docker容器监控模块(docker-check.sh)
专注于Docker环境的健康检查和资源监控。
3.3.1 Docker服务状态检查
检查目的:确认Docker服务运行正常,获取版本信息
echo "## 🐳 Docker服务状态"
# 检查Docker服务是否运行
if command -v docker >/dev/null 2>&1; then
docker_status=$(systemctl is-active docker 2>/dev/null)
if [ "$docker_status" = "active" ]; then
echo "Docker服务: ✅ 运行中"
# 获取Docker版本
docker_version=$(docker --version 2>/dev/null | awk '{print $3}' | sed 's/,//')
echo "Docker版本: $docker_version"
add_docker_data "service_status" "running"
add_docker_data "version" "$docker_version"
else
echo "Docker服务: ⚠️ 未运行 (状态: $docker_status)"
add_docker_data "service_status" "$docker_status"
fi
else
echo "Docker: ❌ 未安装"
add_docker_data "service_status" "not_installed"
return
fi
3.3.2 容器统计与状态
检查目的:了解容器总数、运行状态、停止状态
echo "## 📦 容器状态概览"
# 统计容器数量
total_containers=$(docker ps -a -q 2>/dev/null | wc -l)
running_containers=$(docker ps -q 2>/dev/null | wc -l)
paused_containers=$(docker ps -q -f status=paused 2>/dev/null | wc -l)
stopped_containers=$((total_containers - running_containers - paused_containers))
echo "- 总容器数: $total_containers"
echo "- 运行中: $running_containers"
echo "- 已暂停: $paused_containers"
echo "- 已停止: $stopped_containers"
# 判断整体状态
if [ "$stopped_containers" -gt 0 ]; then
echo "- 状态: ⚠️ 有 $stopped_containers 个容器已停止"
overall_status="warning"
elif [ "$running_containers" -gt 0 ]; then
echo "- 状态: ✅ 正常"
overall_status="ok"
else
echo "- 状态: ⚠️ 无运行中的容器"
overall_status="warning"
fi
# 记录到JSON
add_docker_data "containers_total" "$total_containers"
add_docker_data "containers_running" "$running_containers"
add_docker_data "containers_paused" "$paused_containers"
add_docker_data "containers_stopped" "$stopped_containers"
add_docker_data "overall_status" "$overall_status"
# 列出停止的容器
if [ "$stopped_containers" -gt 0 ]; then
echo ""
echo "### 停止的容器:"
docker ps -a -f status=exited --format "table {{.Names}}\t{{.Status}}\t{{.CreatedAt}}" 2>/dev/null
fi
输出示例:
## 📦 容器状态概览
- 总容器数: 25
- 运行中: 23
- 已暂停: 0
- 已停止: 2
- 状态: ⚠️ 有 2 个容器已停止
### 停止的容器:
NAMES STATUS CREATEDAT
old-postgres Exited (0) 2 days ago 2026-01-16 10:30:00
test-mysql Exited (1) 5 days ago 2026-01-13 14:20:00
3.3.3 容器资源使用分析
检查目的:发现资源占用异常的容器,进行容量规划
echo "## 📊 容器资源使用(Top 10)"
echo "| 容器名 | 状态 | CPU% | 内存使用 | 内存% | 网络接收 | 网络发送 |"
echo "|--------|------|------|----------|--------|----------|----------|"
# 获取容器实时资源使用(--no-stream避免持续输出)
docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}\t{{.NetIO}}" 2>/dev/null | \
tail -n +2 | \
while read -r line; do
name=$(echo "$line" | awk '{print $1}')
cpu=$(echo "$line" | awk '{print $2}' | sed 's/%//')
mem_usage=$(echo "$line" | awk '{print $3}')
mem_percent=$(echo "$line" | awk '{print $4}' | sed 's/%//')
net_rx=$(echo "$line" | awk '{print $5}')
net_tx=$(echo "$line" | awk '{print $6}')
# 判断容器状态
if docker ps --format "{{.Names}}" | grep -q "^${name}$"; then
status="✅"
else
status="❌"
fi
# 标记高资源占用
if (( $(echo "$cpu > 50" | bc -l 2>/dev/null || echo "0") )); then
cpu="**$cpu%**"
else
cpu="${cpu}%"
fi
if [ "$mem_percent" -gt 80 ] 2>/dev/null; then
mem_usage="**$mem_usage**"
fi
echo "| $name | $status | $cpu | $mem_usage | ${mem_percent}% | $net_rx | $net_tx |"
done | head -n 11 # 限制Top 10
关键点:
- 使用
--no-stream参数获取当前瞬时数据,而非持续输出 - 标记CPU>50%和内存>80%的容器,便于快速发现问题
- 区分运行中和停止的容器
- 限制输出数量,避免报告过长
3.3.4 镜像管理检查
检查目的:发现悬空镜像,提供清理建议
echo "## 🖼️ 镜像管理"
# 统计镜像数量
total_images=$(docker images -q 2>/dev/null | wc -l)
dangling_images=$(docker images -f "dangling=true" -q 2>/dev/null | wc -l)
echo "- 总镜像数: $total_images"
echo "- 悬空镜像数: $dangling_images"
# 列出镜像占用空间
if [ "$total_images" -gt 0 ]; then
echo ""
echo "### 镜像占用空间(Top 10):"
echo "| 镜像名 | 标签 | 大小 | 创建时间 |"
echo "|--------|------|------|----------|"
docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}\t{{.CreatedAt}}" 2>/dev/null | \
tail -n +2 | \
head -n 10 | \
while read -r line; do
echo "| $line |"
done
fi
# 清理建议
if [ "$dangling_images" -gt 0 ]; then
echo ""
echo "### 清理建议"
echo "⚠️ 发现 $dangling_images 个悬空镜像,建议清理:"
echo "\`\`\`bash"
echo "docker image prune -f"
echo "\`\`\`"
# 计算可回收空间
dangling_size=$(docker images -f "dangling=true" -q 2>/dev/null | \
xargs docker inspect --format='{{.Size}}' 2>/dev/null | \
awk '{sum+=$1} END {printf "%.0f", sum/1024/1024}')
echo "预计可回收空间: ${dangling_size}MB"
fi
3.3.5 存储空间使用
检查目的:了解Docker整体存储占用,发现异常占用
echo "## 💾 存储空间使用"
docker_info=$(docker system df 2>/dev/null)
if [ -n "$docker_info" ]; then
echo "$docker_info"
# 解析存储使用数据
images_size=$(echo "$docker_info" | grep 'Images' | awk '{print $3}')
containers_size=$(echo "$docker_info" | grep 'Containers' | awk '{print $3}')
volumes_size=$(echo "$docker_info" | grep 'Local Volumes' | awk '{print $3}')
build_cache_size=$(echo "$docker_info" | grep 'Build Cache' | awk '{print $3}')
add_docker_data "storage_images" "$images_size"
add_docker_data "storage_containers" "$containers_size"
add_docker_data "storage_volumes" "$volumes_size"
add_docker_data "storage_build_cache" "$build_cache_size"
# 判断是否需要清理
if [ "$build_cache_size" != "0B" ] && [ "$build_cache_size" != "" ]; then
echo ""
echo "### 清理建议"
echo "⚠️ 构建缓存占用 ${build_cache_size},建议清理:"
echo "\`\`\`bash"
echo "docker builder prune -f"
echo "\`\`\`"
fi
else
echo "⚠️ 无法获取存储使用信息"
fi
输出示例:
## 💾 存储空间使用
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 15 10 3.2GB 1.1GB (34%)
Containers 25 23 1.8GB 256MB (14%)
Local Volumes 8 6 2.5GB 512MB (20%)
Build Cache 0 0 856MB 856MB
### 清理建议
⚠️ 构建缓存占用 856MB,建议清理:
```bash
docker builder prune -f
#### 3.3.6 网络和卷统计
```bash
echo "## 🔌 网络和卷"
# 统计网络
total_networks=$(docker network ls -q 2>/dev/null | wc -l)
echo "- 网络总数: $total_networks"
# 列出网络
if [ "$total_networks" -gt 0 ]; then
echo ""
echo "### 网络列表:"
docker network ls --format "table {{.Name}}\t{{.Driver}}\t{{.Scope}}" 2>/dev/null
fi
# 统计卷
total_volumes=$(docker volume ls -q 2>/dev/null | wc -l)
echo ""
echo "- 卷总数: $total_volumes"
# 列出卷
if [ "$total_volumes" -gt 0 ]; then
echo ""
echo "### 卷列表:"
docker volume ls --format "table {{.Name}}\t{{.Driver}}\t{{.Scope}}" 2>/dev/null
fi
add_docker_data "networks_total" "$total_networks"
add_docker_data "volumes_total" "$total_volumes"
关键点:
- 提供清晰的清理建议和命令
- 统计并显示可回收空间,帮助决策是否清理
- 列出网络和卷,便于了解整体架构
- 所有数据同时记录到JSON,便于自动化分析
四、实际应用效果展示
4.1 基础健康检查报告
Markdown报告(人工阅读)
图:健康检查报告

JSON报告(机器处理)
{
"summary": {
"host": {
"hostname": "pve-ubuntu-pandawiki",
"ip": "192.168.0.55",
"check_time": "2026-01-18T08:40:43+0000"
},
"overall_status": "warning",
"status_counts": {
"ok": 8,
"warning": 3,
"critical": 0
},
"check_types": ["health"]
},
"details": {
"system": {
"uptime": "15 days, 3:24",
"load_1min": 0.15,
"load_5min": 0.30,
"load_15min": 0.35,
"network_total_connections": 45,
"network_external_connections": 8,
"network_listening_ports": 12
},
"memory": {
"total_mb": 16384,
"used_mb": 12500,
"available_mb": 3884,
"used_percent": 76.3,
"swap_total_mb": 2048,
"swap_used_mb": 256,
"swap_percent": 12.5,
"status": "warning"
},
"disk": [
{
"mount": "/",
"device": "/dev/sda1",
"total_gb": "100G",
"used_gb": "45G",
"available_gb": "55G",
"used_percent": 45.0,
"status": "ok"
},
{
"mount": "/data",
"device": "/dev/sdb1",
"total_gb": "500G",
"used_gb": "425G",
"available_gb": "75G",
"used_percent": 85.0,
"status": "warning"
}
],
"services": {
"systemd_running": 23,
"systemd_failed": 0
},
"security": {
"mining_detected": false,
"tmp_executables": 0,
"failed_logins": 3
}
},
"metadata": {
"check_version": "1.0",
"tool_version": "ops-health-check v1.0",
"check_script": "health-check.sh",
"thresholds": {
"disk_warning": 50,
"disk_critical": 80,
"memory_warning": 70,
"memory_critical": 90
}
}
}
实际价值:
- Markdown报告便于运维人员快速了解系统状态
- JSON数据可被监控系统自动采集和分析
- 清晰的emoji状态指示,一眼就能看出问题所在
- 详细的阈值配置,便于追溯和调整
五、总结与展望
5.1 实现成果
本次开发成功构建了一个生产级运维健康检查系统:
✅ 三大核心模块
- 基础健康检查:涵盖系统资源、服务状态、基础安全
- 深度安全检查:5大类20+项安全检查
- Docker监控:容器、镜像、存储全方位监控
✅ 双格式输出
- Markdown:人工可读,便于快速决策
- JSON:机器可处理,易于自动化集成
✅ 广泛兼容性
- bash 3.2+兼容(macOS默认版本)
- 支持主流Linux发行版(Ubuntu、CentOS、Debian)
- 适配systemd和SysV init系统
✅ 完整文档
- SKILL.md:使用指南
- 设计文档:架构和实现思路
- 博客文章:实战经验分享
5.2 技术亮点
- 模块化设计:三个检查模块独立运行,互不依赖
- 统一输出库:封装数据收集和格式化逻辑
- 阈值可配置:支持通过环境变量自定义阈值
- 兼容性处理:优雅降级,兼容不同版本工具
- 清理建议:不仅发现问题,还提供解决建议
5.3 实际应用价值
效率提升:
- 批量检查10台主机仅需2分钟
- 自动生成双格式报告,无需手工整理
- JSON数据可直接导入监控系统
安全增强:
- 发现并阻止挖矿程序
- 检测异常登录和未授权访问
- 及时发现文件系统异常变更
成本节约:
- 无需部署复杂监控系统
- 基于现有SSH连接,无需额外代理
- 轻量级脚本,资源占用极低
5.4 开发体会
通过Claude Code的AI辅助开发,整个过程非常高效:
- 快速迭代:从需求到实现不到4小时
- 智能提示:Claude主动发现兼容性问题并给出解决方案
- 代码质量:生成的代码结构清晰,符合最佳实践
- 文档完善:自动生成详细的使用文档和示例
AI辅助开发不仅提高了效率,更重要的是让开发者可以专注于业务逻辑和架构设计,而将具体的编码工作交给AI完成。这种人机协作模式,是未来软件开发的重要趋势。
项目地址:https://github.com/xiejava/ops-health-check
博客地址:http://xiejava.ishareread.com
🤖 本文由Claude Code辅助编写
关注:微信公众号,一起学习成长!