每次开机手动检查 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 秒钟给个心安。

相关推荐
木头左42 分钟前
【Nginx】内网服务源 IP 白名单实战:动态域名解析放行网段 + 端口接管零改造
运维·nginx·白名单·反向代理
Linux-lucky1 小时前
45-Linux学习之旅之防火墙与WEB加固
linux·前端·网络·学习·ubuntu
禁默1 小时前
启元 Q1 二次开发实践:Ubuntu 部署与仿真验证,从 PrimeConsole 到 ROS 2 Humble + RViz
linux·运维·ubuntu
skywalk81632 小时前
把 去也网站 从 Ubuntu 搬到 FreeBSD:一次 Service Jail + nginx 的部署实录
linux·运维·nginx·ubuntu·freebsd
翔云1234563 小时前
RocksDB中,客户端每写入一次数据,需要访问几次磁盘
运维·数据库·mysql
爱莉希雅&&&4 小时前
GitLab 完整学习笔记
linux·运维·服务器
java_logo5 小时前
Docker 部署 EmulatorJS:轻松搭建浏览器复古游戏平台
游戏·docker·容器·复古游戏·轩辕镜像·emulatorjs·红白机
博客zhu虎康5 小时前
实现局域网P2P通信:内网发送消息
网络·网络协议·p2p
小杨爱科普5 小时前
制造业上自动化改造,改造部分的专利到底归谁
运维·经验分享·自动化
吴声子夜歌5 小时前
Kubernetes权威指南——网络原理(一)
网络·容器·kubernetes