每次开机手动检查 6 样东西?我写了个 2 秒自检脚本

背景

服务器上有几个容器、多个监听端口、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 秒钟给个心安。

相关推荐
wangqiaowq1 小时前
PostgresSQL server Docker 安装部署
运维·docker·容器
钱工的门窗技术笔记1 小时前
智能门窗的边界与前提:为什么“智能”不能替代本体性能
网络·物联网·智能家居
程序员阿明2 小时前
docker搭建视频流服务go2rtc,webRTC
docker·容器·webrtc
是真的小外套2 小时前
2021年软考中级信息安全工程师:选择题+案例分析真题全解析(含答案)
网络
盟接之桥2 小时前
盟接之桥谈线束数字化-按单入按单出:线束企业库存管理的破局之道
大数据·前端·网络·数据库·人工智能·制造
南棋网络安全2 小时前
ssrf跨站请求伪造(保姆级教程)
运维·服务器·网络·网络安全
帷幕落秋2 小时前
进程服务与文件描述符
linux·运维·面试
帷幕落秋2 小时前
信号、Ctrl 组合键与进程状态
linux·运维·面试
TTATTC2 小时前
让 16 个 AI 员工在飞书群自我协同:基于 OpenClaw 的多 Agent 敏捷 Squad 拓扑与一人公司实操
docker·开源