背景
服务器上有几个容器、多个监听端口、HTTPS 入口、SSL 证书、每日备份。每次服务器重启(或者隔几天登录一次),要检查的东西太多:
容器都起来了吗?
端口都在监听吗?
HTTPS 能访问吗?
证书还有几天到期?
磁盘内存还剩多少?
昨天备份跑了吗?
手动敲命令要 10 分钟,容易漏。写个脚本,2 秒扫完。
原因
不是不知道要检查什么,而是没有一个统一的"体检清单"。想到什么查什么,容易遗漏,也不便于对比。
这就好比去医院体检,医生不会每次临时想查什么,而是有一张固定的体检单,一项一项过。
解决:写 health_check.sh
结构设计:6 个检查节,每节独立,不互相影响。
bash
#!/bin/bash
set -uo pipefail
GREEN='\033[0;32m'; YELLOW='\033[0;33m'; RED='\033[0;31m'; NC='\033[0m'
ok() { echo -e " ${GREEN}✅${NC} $*"; }
warn() { echo -e " ${YELLOW}⚠️ ${NC} $*"; }
fail() { echo -e " ${RED}❌${NC} $*"; }
# ---------- 1. 容器状态 ----------
echo "[1/6] 容器状态"
for c in db flask-a flask-b monitor adminer; do
STATUS=$(docker inspect -f '{{.State.Status}}' "$c" 2>/dev/null || echo "missing")
HEALTH=$(docker inspect -f '{{if .State.Health}}{{.State.Health.Status}}{{else}}none{{end}}' "$c" 2>/dev/null)
if [ "$STATUS" = "running" ]; then
ok "$c ($STATUS, health=$HEALTH)"
else
fail "$c ($STATUS)"
fi
done
# ---------- 2. 端口监听 ----------
echo "[2/6] 端口监听"
for entry in "22:SSH" "80:nginx" "8443:HTTPS" "3001:Kuma"; do
port=${entry%%:*}; desc=${entry##*:}
if ss -tln | grep -qE "[:.]${port}\$"; then
ok "$port $desc"
else
fail "$port $desc (未监听)"
fi
done
# ---------- 3. HTTPS 入口 ----------
echo "[3/6] HTTPS 入口"
code=$(curl -sS -m 10 -o /dev/null -w '%{http_code}' https://example.com:8443)
[ "$code" = "200" ] && ok "dashboard HTTP 200" || fail "dashboard HTTP $code"
# ---------- 4. 证书有效期 ----------
echo "[4/6] 证书有效期"
END=$(echo | openssl s_client -servername example.com -connect example.com:8443 2>/dev/null \
| openssl x509 -noout -enddate | cut -d= -f2)
LEFT=$(( ($(date -d "$END" +%s) - $(date +%s)) / 86400 ))
[ "$LEFT" -ge 30 ] && ok "剩余 $LEFT 天" || warn "剩余 $LEFT 天,该续期"
# ---------- 5. 磁盘 / 内存 ----------
echo "[5/6] 磁盘 / 内存"
DISK=$(df / | awk 'NR==2 {gsub(/%/,"",$5); print $5}')
[ "$DISK" -lt 70 ] && ok "磁盘 ${DISK}%" || warn "磁盘 ${DISK}%"
MEM=$(free -m | awk '/^Mem:/ {print int(($2-$7)*100/$2)}')
[ "$MEM" -lt 70 ] && ok "内存 ${MEM}%" || warn "内存 ${MEM}%"
# ---------- 6. 最近备份 ----------
echo "[6/6] 最近备份"
LATEST=$(ls -t /home/user/backups/business_*.tar.gz 2>/dev/null | head -1)
if [ -n "$LATEST" ]; then
AGE=$(( ($(date +%s) - $(stat -c %Y "$LATEST")) / 3600 ))
[ "$AGE" -lt 26 ] && ok "最新备份 $AGE 小时前" || warn "最新备份 $AGE 小时前"
else
fail "没有任何备份"
fi
验证效果
bash
$ sudo ./health_check.sh
=== 环境自检 ===
[1/6] 容器状态
✅ db (running, health=healthy)
✅ flask-a (running, health=healthy)
...
[4/6] 证书有效期
✅ 剩余 87 天(Dec 22 08:36:48 2026 GMT)
[5/6] 磁盘 / 内存
✅ 磁盘 19%
✅ 内存 42%
[6/6] 最近备份
✅ 最新备份 0 小时前
2 秒出结果,绿黄红一眼看穿。
挂到 .bashrc 让每次登录都提示:
bash
echo '提示:跑 sudo /home/user/health_check.sh 自检环境' >> ~/.bashrc
三个坑
坑 1:阈值要分级
不要只有"好/坏"两态。磁盘 70% 是黄,85% 才是红;证书 30 天绿,7 天红。分级才能提前预警。如果全都用红色,大脑会脱敏,反而忽略真正的危险。
坑 2:date -d 的兼容性
date -d "$END" +%s 在 Ubuntu 上是 GNU date 的扩展,macOS 不认。部署脚本时注意目标系统,跨平台的话最好用 date -j -f 或转成 Python/Perl 处理。
坑 3:不要自动跑,要手动触发
自检脚本应该挂在登录提示里,但不要在登录时自动执行。因为有些检查会访问网络(比如 curl HTTPS、openssl),比较慢;而且人不在时跑也没意义。手动触发,2 秒钟给个心安。