14 10 月 2026

服务器不出事的时候,没人想得起它;出事的时候,才发现三个月没看过日志、磁盘早就满了、证书上周就过期了。

巡检是成本最低的保障。这篇给一份完整的巡检清单 + 可直接跑的自动化脚本,把「出事才发现」变成「出事前预警」。

需要云服务器可以看看财富云计算:一键开服面板,NAT 型无独立公网 IP,价格比独立 IP 机型省一半以上,主流游戏快速部署。

一、巡检清单:每天 5 分钟看这些

必看项(每天)

项命令正常标准
磁盘使用率df -h /< 80%
内存可用free -havailable > 20%
CPU 负载uptimeload < 核数
服务状态systemctl --failed无 failed
备份是否成功看备份日志今天有备份记录
带宽/流量iftop 抽查无异常峰值

每周看

项命令/位置关注点
安全日志lastb、fail2ban 状态暴力破解尝试
错误日志journalctl -p err --since "7 days ago"反复出现的错误
端口变化ss -tulnp 对比基线陌生监听
账号变化/etc/passwd 对比陌生账号
证书到期openssl x509 -enddate> 30 天
备份恢复演练抽一次解压验证备份可用

每月看

项关注点
系统更新apt update && apt list --upgradable,排期更新
大文件清理/var/log、/tmp、旧内核
用户/权限审计sudo 列表、应用账号权限
成本回顾带宽/流量/存储用量,该降配降配

二、自动化巡检脚本

把上面的清单写成一个脚本,每天定时跑,异常推送告警。

#!/bin/bash
#==================================================
# 服务器日常巡检脚本
# 输出巡检报告,异常项推送告警
# 用法: ./inspect.sh [--quiet 只输出异常]
#==================================================

WEBHOOK="https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=你的key"
REPORT="/tmp/inspect-$(date +%Y%m%d).txt"
WARN=""

log() { echo "$1" >> "$REPORT"; }
warn() { WARN="$WARN\n$1"; log "⚠️ $1"; }
alert() {
  [ -n "$WARN" ] && curl -s "$WEBHOOK" -H 'Content-Type: application/json' -d "{
    \"msgtype\": \"text\",
    \"text\": {\"content\": \"【巡检告警】$(hostname)$(echo -e $WARN)\"}
  }"
}

echo "======== $(hostname) 巡检报告 $(date '+%F %T') ========" > "$REPORT"

# ===== 1. 磁盘 =====
log "--- 磁盘使用率 ---"
df -h | grep -vE "tmpfs|overlay" | awk 'NR>1 {print $6, $5}' | while read fs pct; do
  p=${pct%\%}
  if [ "$p" -ge 90 ]; then warn "磁盘 $fs 使用率 ${p}%(危险)"; 
  elif [ "$p" -ge 80 ]; then warn "磁盘 $fs 使用率 ${p}%(关注)"; fi
done
df -h / >> "$REPORT" 2>&1

# ===== 2. 内存 =====
log "--- 内存 ---"
free -h >> "$REPORT"
AVAIL=$(free -m | awk 'NR==2 {print $7}')
TOTAL=$(free -m | awk 'NR==2 {print $2}')
PCT=$((AVAIL * 100 / TOTAL))
[ "$PCT" -lt 20 ] && warn "内存可用仅 ${PCT}%(${AVAIL}M/${TOTAL}M)"

SWAP_USED=$(free -m | awk 'NR==3 {print $3}')
[ "$SWAP_USED" -gt 512 ] && warn "Swap 已用 ${SWAP_USED}M,内存可能不足"

# ===== 3. 负载 =====
log "--- 负载 ---"
LOAD=$(uptime | awk -F'load average:' '{print $2}')
CORES=$(nproc)
log "负载:$LOAD (核数 $CORES)"
L1=$(echo "$LOAD" | awk -F, '{print $1}' | tr -d ' ')
awk "BEGIN{exit !($L1 > $CORES)}" && warn "1 分钟负载 $L1 超过核数 $CORES"

# ===== 4. 失败的服务 =====
log "--- 服务状态 ---"
FAILED=$(systemctl --failed --no-legend | wc -l)
if [ "$FAILED" -gt 0 ]; then
  warn "有 $FAILED 个服务处于 failed 状态"
  systemctl --failed --no-legend >> "$REPORT"
fi

# ===== 5. 端口基线对比 =====
log "--- 监听端口 ---"
CUR=$(ss -tuln | awk 'NR>1 {print $1, $5}' | sort)
echo "$CUR" >> "$REPORT"
BASE="/opt/scripts/port-baseline.txt"
if [ -f "$BASE" ]; then
  NEW=$(comm -13 <(sort "$BASE") <(echo "$CUR"))
  [ -n "$NEW" ] && warn "发现新监听端口:\n$NEW"
else
  echo "$CUR" > "$BASE"; log "端口基线已建立"
fi

# ===== 6. 备份检查 =====
log "--- 备份检查 ---"
BK="/backup/mysql"
if [ -d "$BK" ]; then
  LATEST=$(ls -1t "$BK"/*.sql.gz 2>/dev/null | head -1)
  if [ -n "$LATEST" ]; then
    AGE=$(( ($(date +%s) - $(stat -c %Y "$LATEST")) / 3600 ))
    [ "$AGE" -gt 26 ] && warn "最新备份已 ${AGE} 小时前,备份可能没在跑"
    log "最新备份: $LATEST (${AGE} 小时前)"
  else
    warn "备份目录为空!"
  fi
fi

# ===== 7. 证书到期 =====
log "--- SSL 证书 ---"
for cert in /etc/letsencrypt/live/*/fullchain.pem; do
  [ -f "$cert" ] || continue
  END=$(openssl x64 -in "$cert" -noout -enddate 2>/dev/null || openssl x509 -in "$cert" -noout -enddate 2>/dev/null | cut -d= -f2)
  DAYS=$(( ($(date -d "$END" +%s) - $(date +%s)) / 86400 ))
  [ "$DAYS" -lt 14 ] && warn "证书 $cert ${DAYS} 天后过期"
done

# ===== 8. 安全: 暴力破解尝试 =====
log "--- SSH 登录失败 ---"
FAILS=$(grep -c "Failed password" /var/log/secure /var/log/auth.log 2>/dev/null | awk -F: '{s+=$2} END {print s}')
[ "${FAILS:-0}" -gt 100 ] && warn "SSH 失败登录 ${FAILS} 次,检查 fail2ban"

# ===== 9. 游戏服进程抽查 =====
log "--- 游戏服进程 ---"
for svc in palworld ark terraria; do
  if systemctl list-unit-files | grep -q "$svc.service"; then
    if ! systemctl is-active --quiet "$svc"; then
      warn "游戏服 $svc 未运行!"
    fi
  fi
done

# ===== 输出 =====
log ""
log "======== 巡检结束 ========"
[ -n "$WARN" ] && alert

cat "$REPORT"

挂定时任务

chmod +x /opt/scripts/inspect.sh

# 每天早上 9 点巡检,有异常才推送
crontab -e
0 9 * * * /opt/scripts/inspect.sh --quiet >/dev/null 2>&1

三、告警渠道选择

渠道延迟成本适合
企业微信/钉钉机器人秒级免费团队协作,推荐
邮件分钟级免费非紧急、日报
短信/电话秒级几分钱/条致命告警(磁盘满、服务挂)
Server酱/Telegram秒级免费个人

告警分级建议:

  • P1 电话:服务全挂、磁盘 95%+
  • P2 企业微信:备份失败、服务重启、异常登录
  • P3 日报:汇总巡检报告,早上看

别所有告警都推送——告警太多等于没有告警。分级 + 只推异常。

四、游戏服巡检专项

游戏服除了常规项,还要看:

# 1. 服务端进程存活(被 OOM 杀了最常见)
systemctl is-active palworld

# 2. 端口在监听
ss -ulnp | grep 8211

# 3. 存档最近更新时间(判断服务端是否真的在跑)
ls -la /opt/games/palworld/server/Pal/Saved/SaveGames/

# 4. 内存趋势(泄漏)
ps -o rss= -p $(pgrep -f PalServer) 

# 5. SteamCMD 更新是否失败(看日志)
tail -20 /opt/games/palworld/server/Steam/logs/

# 6. 在线人数(从查询端口或 RCON)

把这些加进巡检脚本的「游戏服进程抽查」部分。

五、巡检报告的可视化

纯文本报告看久了累,两个轻量方案:

# 方案1: 报告转 HTML(邮件发)
# 用 bash + 简单模板生成,或用 markdown 转

# 方案2: 历史数据存起来,用 Netdata/Grafana 看趋势
# 把 df/free/load 指标定时采集,存成文件
echo "$(date +%s) $(df / | awk 'NR==2 {print $5}') $(free -m | awk 'NR==2 {print $3}')" >> /var/log/metrics.log

趋势比快照重要:磁盘从 60% 涨到 85% 用了两周,比某天突然 90% 更有预警价值。

六、常见问题

Q:crontab 里的脚本不执行?
环境变量问题。脚本用绝对路径、开头 export PATH=...、检查 /var/log/cron 或 journalctl -u cron。

Q:告警太频繁,烦了?
调高阈值(磁盘 80% 再报)、加防抖(连续两次异常才报)、分级推送。别把「关注级」和「危险级」混在一起。

Q:巡检发现端口变化但不知道是谁?
ss -tulnp 看进程名和 PID,ls -l /proc/PID/exe 看可执行文件路径。陌生进程按应急流程处理。

Q:备份检查怎么知道备份成功?
看两件事:文件存在 + 时间戳是新的。更严的做法:备份脚本跑完写个状态文件,巡检读状态。

Q:脚本能同时管多台吗?
单机巡检脚本每台跑一个;想汇总多台,用 Ansible 批量跑(见Ansible 批量运维),或者部署 Prometheus 统一采集。

小结

巡检的核心是「自动化 + 分级告警」:每天脚本自动跑一遍清单,异常按级别推送,正常只进日报。配合每周的人工项(恢复演练、权限审计),服务器的可用性就有基本盘了。这套东西一两个小时搭好,之后每天省下来的心和避开的坑,远超投入。

更多运维内容见财富云计算知识库:crontab、监控体系、备份策略、Ansible 全系列。需要云服务器可以看看财富云计算:一键开服面板,NAT 型无独立公网 IP,价格比独立 IP 机型省一半以上。

延伸阅读
  1. 网站环境怎么选:LNMP 手动 / 宝塔面板 / Docker / 云开发
  2. 问卷表单系统搭建:SurveyKing / Formbricks,数据自己收
  3. 图床搭建:Lsky Pro / Lychee,自托管图片托管方案
  4. 邮件列表搭建:listmonk 自托管 Newsletter,把读者握在自己手里
  5. 文档站点搭建:MkDocs / VuePress / Docsify,产品文档与技术手册方案
  6. RSS 阅读器搭建:FreshRSS 自托管,把信息流拿回自己手里
  7. 短链接服务搭建:Shlink 自托管,链接数据自己掌握
  8. 网站状态监控页搭建:Uptime Kuma,服务器挂了第一时间知道
更多阅读
  1. 上一篇:Ansible 批量运维入门:管十台像管一台
  2. 下一篇:WordPress 搭建与性能优化完整指南:从安装到提速
发表评论 6