生产巡检
本文范围
生产巡检不是打开服务器随便看两眼,也不是把一堆命令丢进脚本里等它输出一大屏。巡检更像一次有顺序的上机检查:先确认自己在哪台机器上,再看机器有没有明显资源风险,然后看服务、端口、日志、备份、告警,最后把异常记录下来。
这里先记住一个原则:巡检脚本只负责发现和记录,不要默认自动修复。比如自动删日志、自动杀进程、自动重启 MySQL、自动清空 Redis 或 MQ 队列,这些操作都可能把小问题变成事故。
本文不做自动化修复平台设计,不替代 Prometheus 告警、日志平台、链路追踪和应急预案,也不深入数据库调优、JVM 调优或网络架构优化。这里聚焦运维巡检现场:看什么、怎么查、怎么记录、怎么把风险推进到闭环。
每次巡检开始前,先执行下面这组命令:
hostnamectl
date
uptime
who
id
sudo -l这几条命令解决的是“我是谁、我在哪、这台机器现在大概什么状态”的问题。
hostnamectl看主机名、系统版本和内核版本,避免登错机器。date看系统时间,时间不准会影响证书、日志、Token、数据库复制和 MQ 排障。uptime看运行时长和负载,能快速发现机器是否刚重启过、压力是否异常。who看当前还有谁在线,生产机上有人同时操作时要先沟通。id和sudo -l看当前账号权限,避免巡检脚本以不该有的权限运行。
执行完以后,一般应该看到主机信息、当前时间、负载和当前登录用户。如果 sudo -l 提示没有权限,不代表巡检做不了,只是后面涉及系统日志、服务状态、端口进程归属时可能看不全,需要换只读运维账号或让有权限的人补充确认。
前置条件
巡检前先准备这些东西,不然现场很容易变成“边查边猜”:
- 只读巡检账号或受控运维账号已准备好,权限范围、提权方式和操作审计都明确。
- 机器、服务、域名、证书、数据库、中间件、备份任务和监控目标有最新清单。
- 已确定巡检窗口和沟通群。高峰期上生产机巡检,要避免和发布、变更、压测、数据修复撞在一起。
- 基线阈值已准备,例如磁盘增长速度、CPU/Load 正常区间、Redis 内存水位、MQ 积压阈值、慢查询阈值。
- 离线或内网环境已准备巡检工具包、校验文件和安装来源,不在生产机上临时从公网下载工具。
- 巡检报告模板、异常记录模板、证据目录和截图/日志留存方式已经统一。
- 异常升级路径明确:P0/P1 找谁,备份失败找谁,证书风险找谁,安全暴露找谁。
- 巡检只做发现、确认和记录;任何删除、重启、扩容、切流、回滚,都要进入变更或应急流程。
先分清楚环境
生产环境大概分三类:能访问公网的环境、企业内网环境、完全离线环境。巡检命令大部分一样,但工具准备方式不一样。
公网环境里,很多人习惯临时安装工具;内网和离线环境里,这种习惯很容易失效。生产巡检前,先检查常用工具有没有:
command -v top
command -v free
command -v df
command -v ss
command -v systemctl
command -v journalctl
command -v curl
command -v openssl
command -v docker如果某个命令没有,不要在生产机上随手去公网下载安装。一般需要走企业软件源、制品库、离线工具包或云厂商批准的安装通道。离线环境建议提前准备一个巡检工具包,并带上校验文件:
sha256sum ops-tools-linux-x86_64.tar.gz > ops-tools-linux-x86_64.tar.gz.sha256
sha256sum -c ops-tools-linux-x86_64.tar.gz.sha256
tar -xzf ops-tools-linux-x86_64.tar.gz -C /opt/ops-tools这里要注意,脚本能复制到离线服务器,不代表脚本就能跑。脚本里调用的 curl、nc、dig、redis-cli、mysqladmin、kafka-consumer-groups.sh 都是运行依赖,必须提前准备。
中国大陆公网环境还要多看两件事:DNS 和证书。服务明明在机器上正常,用户访问却不正常,问题可能在安全组、防火墙、CDN、DNS 解析或证书链上。巡检时要保留两套验证方式:
curl -I http://127.0.0.1:8080
curl -Iv https://example.com第一条看本机服务,第二条看外部入口。两条结果不一致时,不要急着重启应用,先查 Nginx、网络、安全组、DNS 和证书。
巡检频率怎么定
巡检频率不用搞得很复杂,先按这四类来:
| 频率 | 主要看什么 | 结果怎么落 |
|---|---|---|
| 每日 | CPU、内存、磁盘、inode、负载、进程、端口、容器、Nginx、MySQL、Redis、MQ、备份、告警 | 日巡检记录 |
| 每周 | 证书有效期、日志增长、大 Key、慢日志、定时任务、安全风险、容量趋势 | 周风险清单 |
| 每月 | 恢复演练、权限复核、告警规则复核、容量预测、证书策略复核 | 月度巡检报告 |
| 变更前后 | 发布、扩容、迁移、配置修改、证书替换、网络策略调整 | 变更验证记录 |
这里要注意,备份检查最好每天固定时间做。比如每天 10:00 前看前一天夜里的备份是否成功。不要只看“目录里有文件”,还要看备份日志、文件大小、校验值和远端同步结果。
生产巡检分层地图
巡检不是从某一条命令开始,而是从“先确认边界,再逐层收集证据”开始。下面这张图可以当成巡检顺序:先看环境和主机,再看进程、入口、日志、数据组件,最后才进入备份、监控和异常闭环。
这张图的用法很简单:每一层都要留下能复查的证据,不要只写“已检查”。如果主机资源层已经发现磁盘、inode 或 load 明显异常,就先停在这一层处理,不要继续把 Nginx、数据库和 MQ 全部扫一遍,最后才回头找根因。
最小验证命令可以这样跑一轮:
hostnamectl
date
uptime
free -h
df -hT
df -ih
ss -lntup
systemctl --failed --no-pager
journalctl -p err --since "24 hours ago" --no-pager | tail -n 100常见坑是把巡检做成“命令输出合集”:命令很多,但没有环境、时间窗口、负责人和异常判断。生产约束是:巡检账号尽量只读;发现高风险项要先记录现场再处理;涉及清日志、重启、扩容、切流量、回滚的动作,必须进入变更或应急流程,不能藏在日常巡检里悄悄做。
一上来先看机器总览
登录生产机以后,不要第一时间打开应用日志。先看机器总览:
uptime
free -h
df -hT
df -ih
top -b -n 1 | head -n 30
ss -lntup
systemctl --failed --no-pager这组命令解决的是“机器有没有明显要爆的地方”。
free -h看内存和 swap。df -hT看磁盘空间和文件系统类型。df -ih看 inode,小文件太多时磁盘没满也会写不进去。top看 CPU、内存、负载和最吃资源的进程。ss -lntup看监听端口和进程。systemctl --failed看 systemd 有没有失败服务。
正常情况下,磁盘和 inode 不应该接近 100%,systemctl --failed 不应该有核心服务失败,端口应该和服务清单一致。如果这里已经看到 /data 96%、inode 99%、mysqld failed,后面就不要按普通巡检继续往下走了,先升级为异常处理。
记录时不要只写“机器正常”。至少写成这样:
主机:app-01
时间:2026-07-09 10:00
CPU:正常
Load:0.82/0.76/0.71,CPU 8 核
内存:available 11G,无 swap in/out
磁盘:/data 78%,inode 12%
失败服务:无
异常:无有了这种记录,后面做趋势分析才有依据。
CPU 和负载
CPU 高不一定就是故障。比如白天流量高、批处理在跑、压缩备份正在执行,CPU 短时间升高是正常现象。巡检要看的是“是否持续高、是否偏离基线、是否影响业务”。
先看整体:
uptime
top -b -n 1 | head -n 25
nprocuptime 里的三个数字是 1 分钟、5 分钟、15 分钟平均负载。一般先用 CPU 核数做参考。如果机器是 8 核,load 长时间在 20 以上,就要认真看了。
接着看 CPU 分布:
mpstat 1 3
pidstat -u 1 3
ps -eo pid,ppid,user,stat,pcpu,pmem,comm,args --sort=-pcpu | head -n 15如果没有 mpstat 和 pidstat,说明 sysstat 没装。内网或离线环境需要提前准备包,不要现场临时找源。
看结果时重点看这几个位置:
%us高,通常是业务代码、计算、加解密、压缩等消耗。%sy高,可能是系统调用、网络、中断、文件系统压力。%wa高,说明 CPU 在等 IO,真正的问题可能在磁盘、数据库或远端存储。- 某个进程
pcpu长时间很高,要继续看它的线程。
看某个进程:
PID=1234
ps -fp "$PID"
top -H -p "$PID"
pidstat -p "$PID" -u -r -d 1 5如果是 Java 服务,还可以看线程和堆:
jcmd "$PID" Thread.print | head -n 80
jcmd "$PID" GC.heap_info这里要注意,load 高但 CPU 不高时,不要只盯应用代码。先查有没有 D 状态进程:
ps -eo pid,ppid,user,stat,wchan:30,comm,args | awk '$4 ~ /D/ {print}'如果有大量 D 状态,常见原因是磁盘、网络存储、NFS、数据库文件系统或块设备卡住。这个时候重启应用可能没用,甚至会让恢复更慢。
内存和 OOM
内存巡检先看 available,不要看到 buff/cache 高就判断内存不够。Linux 会用空闲内存做缓存,这部分在需要时可以回收。
free -h
vmstat 1 5
ps -eo pid,user,stat,rss,vsz,pmem,comm,args --sort=-rss | head -n 15free -h 里重点看 available 和 swap。vmstat 里重点看 si、so,也就是 swap in 和 swap out。正常巡检里,这两个值一般应该长期为 0 或偶发很小。如果持续有 swap 进出,应用延迟通常会变差。
再看有没有 OOM:
dmesg -T | grep -i -E 'out of memory|oom|killed process' | tail -n 20
journalctl -k --since "24 hours ago" | grep -i -E 'out of memory|oom|killed process'如果看到 Killed process,要记录被杀的进程、时间和内存占用。不要只写“服务重启了”,要说明是不是被内核杀掉。
容器环境还要看容器限制:
docker stats --no-stream
docker inspect CONTAINER_NAME --format 'Memory={{.HostConfig.Memory}} MemorySwap={{.HostConfig.MemorySwap}} OOMKilled={{.State.OOMKilled}}'这里有个常见坑:宿主机内存看起来还有很多,但容器被 OOM kill 了。这是因为容器有自己的内存限制,超过限制就可能被杀。巡检报告里要把宿主机内存和容器内存限制分开写。
磁盘和 inode
磁盘巡检不是只看 df -h。生产上真正麻烦的是“哪个目录在增长、为什么增长、能不能清、清了是否释放空间”。
先看分区:
df -hT
df -ih
lsblk -f
findmntdf -hT 看空间,df -ih 看 inode,lsblk -f 看磁盘和文件系统,findmnt 看挂载关系。一般生产上 /、/var、/data、数据库数据盘、备份盘都要单独看。
再找大目录:
du -xh --max-depth=1 /var 2>/dev/null | sort -h
du -xh --max-depth=1 /data 2>/dev/null | sort -h
du -xh --max-depth=1 /opt 2>/dev/null | sort -h找大文件:
find /var /data /opt -xdev -type f -size +1G -printf '%s %p\n' 2>/dev/null | sort -n | tail -n 20这里要注意,du 可能比较慢,特别是小文件很多的时候。巡检时不要在业务高峰对超大目录反复全量扫。先扫常见目录,比如日志目录、备份目录、Docker 目录、数据库目录。
如果空间满了,但你已经删除大文件,df 还是不降,通常是进程还占着已删除文件:
lsof | grep deleted | head -n 30这时候要让对应进程重新打开日志文件,或者在变更窗口重启相关服务。不要为了释放空间随手重启数据库。
inode 也要单独看:
df -ih
find /var -xdev -type f 2>/dev/null | awk -F/ '{print "/"$2"/"$3}' | sort | uniq -c | sort -n | tail如果 inode 用完,通常是大量小文件,比如临时文件、日志切片、缓存文件、邮件队列、任务队列。空间可能还剩很多,但系统已经无法创建新文件。
Docker 磁盘为什么容易涨
容器巡检里经常会遇到一个问题:docker system df 显示 Docker 占了很多空间,但应用同学说自己没写多少数据。这里就要理解一点 Docker 的存储层。
Docker 使用镜像层和容器可写层。镜像层是只读的,多个容器可以共享;容器运行后新增或修改的文件,会写到自己的可写层。Overlay2 里可以简单理解成下面这样:
lowerdir:镜像只读层
upperdir:容器可写层
merged:容器看到的统一文件系统写时复制,也就是 CoW,解决的是“多个容器共享镜像时,不要每个容器都复制一份完整文件系统”。只有当容器要修改某个文件时,才会把这个文件从只读层复制到可写层再修改。
这个原理和巡检有什么关系?关系很直接:
- 容器里写日志,如果没有挂载到宿主机日志目录,就会写进容器可写层。
- 容器里下载临时文件、生成缓存,也会让可写层变大。
- 删除镜像不一定释放容器可写层。
- 删除容器不一定删除挂载卷。
查看 Docker 占用:
docker system df
docker ps --size
docker stats --no-stream如果要看某个容器日志文件位置:
docker inspect CONTAINER_NAME --format '{{.LogPath}}'
sudo ls -lh "$(docker inspect CONTAINER_NAME --format '{{.LogPath}}')"生产建议给 Docker 配置日志轮转:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
}
}配置后要重启 Docker 才会对新容器生效。这里要注意,重启 Docker 可能影响容器,不能在生产高峰随手执行。
进程和端口
服务是否正常,不能只看“进程在不在”。进程在,只说明程序没退出;端口监听了,也不代表业务可用。
先看核心进程:
ps -eo pid,ppid,user,stat,etime,pcpu,pmem,comm,args --sort=-pcpu | head -n 20
pgrep -af nginx
pgrep -af mysqld
pgrep -af redis-server
pgrep -af java重点看运行用户、启动时间、进程数量。比如 Nginx 应该有 master 和 worker;MySQL 不应该突然换成 root 手工启动;Java 服务如果启动时间是今天凌晨,但没有发布记录,就要查是不是异常重启。
查端口:
ss -lntup
sudo ss -lntup | grep ':8080'
sudo lsof -iTCP:8080 -sTCP:LISTEN -n -Pss -lntup 里要看监听地址:
127.0.0.1:3306只允许本机访问,数据库常见。0.0.0.0:3306表示所有网卡监听,要确认是否真的允许这样暴露。- 内网 IP 监听适合组件间访问。
- 公网 IP 或
0.0.0.0暴露管理端口时要非常谨慎。
这里要注意,数据库、Redis、内部 API、管理后台、Prometheus、RabbitMQ 管理端口不要默认对公网开放。看到 0.0.0.0:6379、0.0.0.0:3306、0.0.0.0:15672,先记录并确认安全组、防火墙、认证和白名单。
验证端口不能只看监听,还要访问:
curl -I http://127.0.0.1:8080
nc -vz 127.0.0.1 8080
nc -vz example.com 443如果本机通、远端不通,一般查安全组、防火墙、Nginx、路由和 DNS。如果本机都不通,先查服务进程和应用日志。
系统防火墙也要看一眼。CentOS、Rocky Linux、AlmaLinux 这类机器常见是 firewalld:
sudo firewall-cmd --state
sudo firewall-cmd --list-ports
sudo firewall-cmd --list-servicesUbuntu 常见是 ufw:
sudo ufw status verbose这里要注意,云安全组放通了,不代表系统防火墙也放通;系统防火墙放通了,也不代表云安全组放通。巡检记录里最好把“云安全组”和“系统防火墙”分开写。
网络和 DNS
网络问题最容易表现成“偶发慢”。巡检时先看网卡、路由和连接状态:
ip addr
ip route
ip -s link
ss -s再看 TCP 状态分布:
ss -ant | awk 'NR>1 {count[$1]++} END {for (s in count) print s, count[s]}' | sort如果 SYN-SENT 很多,通常是连接远端失败或被防火墙丢弃。如果 TIME-WAIT 很多,不一定是故障,要结合短连接数量、端口范围和应用延迟判断。
DNS 单独查:
cat /etc/resolv.conf
getent hosts example.com
dig example.com
time getent hosts example.com如果没有 dig,用:
nslookup example.com
getent hosts example.com容器内也要查一遍:
docker exec CONTAINER_NAME cat /etc/resolv.conf
docker exec CONTAINER_NAME getent hosts example.com这里经常会遇到一种情况:宿主机解析正常,容器里解析失败。这个时候不要改应用配置,先查 Docker DNS、容器网络、内网 DNS 和 /etc/resolv.conf。
时间同步
时间同步很容易被忽略,但它会影响很多东西:证书校验、登录 Token、分布式日志排序、链路追踪、数据库复制、MQ 消费时间线。
先查:
date
timedatectl如果使用 chrony:
chronyc tracking
chronyc sources -v正常情况下,系统应该显示已同步,并且 NTP 源可达。如果 System clock synchronized 是 no,或者 offset 很大,要查 NTP 服务、内网时间源、防火墙和 DNS。
内网和离线环境一般需要内部 NTP 源:
grep -E '^(server|pool)' /etc/chrony.conf /etc/chrony/chrony.conf 2>/dev/null这里要注意,多台机器时间不一致时,排障会非常痛苦。日志看起来像“先报错再请求”,其实可能只是机器时间不同。
日志
日志巡检不是把日志从头翻到尾。先看最近 24 小时有没有系统级错误,再看应用错误,再看日志增长。
系统日志:
journalctl -p warning --since "24 hours ago" --no-pager | tail -n 100
journalctl -p err --since "24 hours ago" --no-pager指定服务:
journalctl -u nginx --since "24 hours ago" --no-pager | tail -n 100
journalctl -u docker --since "24 hours ago" --no-pager | tail -n 100应用日志目录:
APP_LOG_DIR=/opt/app/logs
find "$APP_LOG_DIR" -type f -mtime -1 -printf '%TY-%Tm-%Td %TH:%TM %s %p\n' 2>/dev/null | sort | tail -n 30
grep -RniE 'ERROR|Exception|timeout|refused|denied|OutOfMemory|Too many connections' "$APP_LOG_DIR" 2>/dev/null | tail -n 100这几条命令分别解决三个问题:最近有没有错误、错误集中在哪些服务、日志文件有没有异常增长。
如果日志文件越来越大,还要看切割策略:
ls -l /etc/logrotate.d/
sudo logrotate -d /etc/logrotate.conf | head -n 80
journalctl --disk-usage这里要注意,日志切割后应用还可能继续写旧文件。看到磁盘不释放时,记得结合 lsof | grep deleted 一起看。
证书
证书巡检至少看四件事:是否过期、域名是否匹配、证书链是否完整、线上服务是否加载了新证书。
看本地证书文件:
openssl x509 -in /etc/nginx/ssl/fullchain.pem -noout -subject -issuer -dates
openssl x509 -in /etc/nginx/ssl/fullchain.pem -noout -text | grep -A1 "Subject Alternative Name"看远端实际证书:
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -subject -issuer -dates
curl -Iv https://example.com检查 30 天内是否到期:
openssl x509 -checkend $((30 * 24 * 3600)) -noout -in /etc/nginx/ssl/fullchain.pem \
&& echo "certificate ok for 30 days" \
|| echo "certificate expires within 30 days"如果本地证书已经更新,但浏览器看到的还是旧证书,先查 Nginx 是否 reload,再查 CDN、负载均衡、网关和源站是不是同一份证书。
sudo nginx -t
sudo systemctl reload nginx这里要注意,内网 IP 访问 HTTPS 时,证书 SAN 里必须包含对应 IP,并且客户端要信任签发它的内部 CA。否则服务没问题,浏览器照样报证书错误。
Docker 容器
容器巡检第一步看运行状态:
docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}'
docker ps -a --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}'STATUS 里如果出现 Restarting、Exited、unhealthy,就要继续查。容器 Up 只能说明进程没退出,不代表接口可用。
看资源:
docker stats --no-stream
docker system df看健康检查:
docker inspect CONTAINER_NAME --format '{{json .State.Health}}'看退出原因:
docker inspect CONTAINER_NAME --format 'Status={{.State.Status}} ExitCode={{.State.ExitCode}} OOMKilled={{.State.OOMKilled}} RestartCount={{.RestartCount}} StartedAt={{.State.StartedAt}} FinishedAt={{.State.FinishedAt}}'看日志:
docker logs --tail 200 --timestamps CONTAINER_NAME
docker logs --since 24h --tail 500 CONTAINER_NAME常见判断:
RestartCount增加,说明容器反复重启,要看应用日志和退出码。OOMKilled=true,先看容器内存限制和应用内存。unhealthy,说明健康检查失败,要看 healthcheck 输出和应用端口。- Docker 日志文件巨大,先查日志轮转,不要直接删容器目录。
如果容器里没有 bash,进入容器时换成 sh:
docker exec -it CONTAINER_NAME sh生产建议是:能在宿主机查的尽量在宿主机查,进入容器只作为补充。不要在生产容器里临时安装一堆调试工具,容易污染环境,也不一定符合安全规范。
Nginx
Nginx 是入口层,巡检时先看配置是否正确,再看端口和日志。
nginx -v
sudo nginx -t
sudo systemctl status nginx --no-pager
ss -lntp | grep -E ':80|:443'nginx -t 成功,说明配置语法没问题;不代表后端服务一定可用。继续看配置展开:
sudo nginx -T | grep -nE 'server_name|listen|proxy_pass|root|alias|ssl_certificate|access_log|error_log'访问验证:
curl -I http://127.0.0.1
curl -Ik https://example.com看日志:
tail -n 100 /var/log/nginx/error.log
tail -n 100 /var/log/nginx/access.log
awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -n | tail如果开启了状态页:
curl -sS http://127.0.0.1/nginx_status访问后应该看到 Active connections、server accepts handled requests、Reading、Writing、Waiting 等信息。
常见问题这样判断:
- 403:先查文件权限、目录权限、
root/alias配置。 - 404:先查 location 是否匹配、静态文件是否存在。
- 502:先查上游服务是否活着、端口是否对、Nginx 到上游是否通。
- 504:多半是上游慢、超时、数据库慢或线程池耗尽。
- HTTPS 异常:先查证书链、SAN、私钥和实际加载的证书。
生产 reload 前先跑:
sudo nginx -t通过以后再 reload:
sudo systemctl reload nginx这里要注意,Nginx reload 失败时通常会继续使用旧配置,但巡检报告里要保留失败输出,不能只写“已 reload”。
MySQL
MySQL 巡检不要一上来就看慢 SQL。先确认服务能连接,再看连接数、运行线程、慢查询、复制、容量和备份。
基础检查:
mysqladmin -uroot -p ping
mysqladmin -uroot -p status
mysqladmin -uroot -p processlistmysqladmin ping 返回 alive,说明服务可用。这里要注意,即使账号被拒绝,有时也说明 mysqld 是活着的,只是认证失败。生产巡检最好使用专门的只读巡检账号。
进入 MySQL 后看状态:
SHOW GLOBAL STATUS LIKE 'Threads_connected';
SHOW GLOBAL STATUS LIKE 'Threads_running';
SHOW GLOBAL STATUS LIKE 'Aborted_connects';
SHOW GLOBAL STATUS LIKE 'Slow_queries';
SHOW VARIABLES LIKE 'max_connections';
SHOW VARIABLES LIKE 'slow_query_log';
SHOW VARIABLES LIKE 'long_query_time';
SHOW VARIABLES LIKE 'log_bin';看进程:
SHOW FULL PROCESSLIST;
SHOW ENGINE INNODB STATUS\G看容量:
SELECT table_schema,
ROUND(SUM(data_length + index_length) / 1024 / 1024 / 1024, 2) AS size_gb
FROM information_schema.tables
GROUP BY table_schema
ORDER BY size_gb DESC;主从复制看:
SHOW REPLICA STATUS\G重点看:
Replica_IO_Running
Replica_SQL_Running
Seconds_Behind_Source
Last_IO_Error
Last_SQL_Error老环境可能还是:
SHOW SLAVE STATUS\G这里要注意,新版本文档里推荐使用 replica/source 术语,但存量环境不一定已经改完,巡检脚本要兼容实际版本。
常见异常这样看:
- 连接数接近
max_connections:查连接池、慢 SQL、连接泄漏。 Threads_running高:查慢 SQL、锁等待、CPU 和 IO。- 慢查询突然增多:查发布、索引、执行计划和数据量变化。
- 复制延迟:查主库写入量、从库 SQL 线程、网络和磁盘。
- 磁盘增长快:查 binlog、slow log、临时表和数据膨胀。
文件层也要看:
df -hT
du -sh /var/lib/mysql 2>/dev/null
ls -lh /var/log/mysqld.log /var/log/mysql/error.log 2>/dev/null生产建议:MySQL 巡检结论不要只写“数据库正常”,至少写连接数、运行线程、慢查询数量、复制状态、容量和备份状态。
Redis
Redis 巡检先看 PING,再看内存、连接、慢日志、持久化和复制。
redis-cli -h 127.0.0.1 -p 6379 PING
redis-cli -h 127.0.0.1 -p 6379 INFO server
redis-cli -h 127.0.0.1 -p 6379 INFO clients
redis-cli -h 127.0.0.1 -p 6379 INFO memory
redis-cli -h 127.0.0.1 -p 6379 INFO persistence
redis-cli -h 127.0.0.1 -p 6379 INFO replication如果设置了密码,要使用受控凭据,不要把密码写进脚本正文或 Git。
看内存:
redis-cli INFO memory | egrep 'used_memory_human|maxmemory_human|maxmemory_policy|mem_fragmentation_ratio'
redis-cli INFO stats | egrep 'evicted_keys|expired_keys|keyspace_hits|keyspace_misses'看慢日志:
redis-cli SLOWLOG LEN
redis-cli SLOWLOG GET 10
redis-cli CONFIG GET slowlog-log-slower-than看延迟:
redis-cli --latency -h 127.0.0.1 -p 6379
redis-cli --latency-history -h 127.0.0.1 -p 6379大 Key 巡检要谨慎。不要在生产高峰执行 KEYS *。可以低峰使用扫描方式:
redis-cli --bigkeys -h 127.0.0.1 -p 6379抽样看某个 key:
redis-cli TYPE some:key
redis-cli TTL some:key
redis-cli MEMORY USAGE some:key常见异常:
used_memory接近maxmemory:要看淘汰策略和业务容量。evicted_keys增长:说明开始淘汰,可能已经影响业务。- 慢日志增加:常见是大 Key、复杂命令、阻塞命令。
connected_clients很高:可能是连接池泄漏或短连接太多。- RDB/AOF 失败:查磁盘、权限、fork、内存和日志。
这里要注意,Redis 的 maxmemory_policy 很关键。同样是内存 90%,noeviction 和 allkeys-lru 的风险完全不同。
MQ
MQ 巡检不是只看进程。核心是看队列有没有积压、消费者是否在线、消息是否越积越多。
RabbitMQ
先看节点:
rabbitmq-diagnostics status
rabbitmq-diagnostics cluster_status
rabbitmq-diagnostics check_running
rabbitmq-diagnostics check_local_alarms再看队列:
rabbitmqctl list_queues name messages_ready messages_unacknowledged consumers durable state怎么看结果:
messages_ready持续增长,说明消息在等消费。messages_unacknowledged很高,说明消费者拿走了但还没 ack。consumers是 0,说明没有消费者在线。- local alarm 触发,通常和磁盘或内存水位有关。
Kafka
先看 Topic:
kafka-topics.sh --bootstrap-server broker:9092 --describe看消费者组:
kafka-consumer-groups.sh --bootstrap-server broker:9092 --describe --group group_name重点看 LAG。如果 LAG 持续增长,说明消费速度跟不上生产速度。这里要记录 topic、partition、group、当前 lag、增长速度和消费者实例。
Kafka 生产环境一般要把 Broker、Topic、Consumer Lag 接入 JMX Exporter 或等价监控,只靠命令巡检不够。
RocketMQ
看集群:
mqadmin clusterList -n namesrv:9876
mqadmin brokerStatus -n namesrv:9876 -b broker-a:10911看消费进度:
mqadmin consumerProgress -n namesrv:9876 -g group_name看 Topic 路由:
mqadmin topicRoute -n namesrv:9876 -t TopicNameMQ 异常记录不要只写“有积压”。要写清楚 topic/queue、consumer group、积压量、最早消息时间、消费者数量和最近发布记录。
备份
备份巡检最重要的一句话:有备份文件不等于备份可用。至少要看生成、日志、大小、校验、远端同步和恢复演练。
先看最近文件:
find /backup -type f -mtime -2 -printf '%TY-%Tm-%Td %TH:%TM %s %p\n' 2>/dev/null | sort
du -sh /backup/* 2>/dev/null | sort -h再看备份日志:
find /backup -type f -name '*.log' -mtime -2 -print -exec tail -n 30 {} \;
grep -RniE 'error|fail|failed|warning|no space|denied' /backup 2>/dev/null | tail -n 100校验文件:
sha256sum /backup/mysql/latest.sql.gz > /backup/mysql/latest.sql.gz.sha256
sha256sum -c /backup/mysql/latest.sql.gz.sha256如果备份还要同步到远端,至少做一次只读列表验证:
rsync -n --list-only backup@backup-server:/data/mysql/ | tail如果使用对象存储,就用对应 CLI 查看最近对象、大小和时间。这里要注意,不要把 AK/SK 直接写进巡检脚本,凭据应该走环境变量、实例角色、密钥管理系统或受控配置文件。
压缩包基础校验:
gunzip -t /backup/mysql/latest.sql.gz这里要注意,备份文件突然变小,比没有备份更值得警惕。可能是备份中断、权限变化、库表漏备、脚本参数错了。巡检记录里一定要写文件大小和上一周期对比。
每月至少做一次恢复演练。恢复演练不要在生产库原地执行,要用隔离环境或临时实例。
监控告警
监控告警巡检要看四段链路:采集是否正常、规则是否有效、通知是否能到人、处理是否闭环。
Node Exporter:
curl -sS http://127.0.0.1:9100/metrics | head
curl -sS http://127.0.0.1:9100/metrics | grep -E 'node_cpu_seconds_total|node_filesystem_avail_bytes|node_memory_MemAvailable_bytes' | headPrometheus:
curl -sS http://prometheus:9090/-/healthy
curl -sS http://prometheus:9090/-/ready
curl -sS 'http://prometheus:9090/api/v1/targets?state=active' | headAlertmanager:
curl -sS http://alertmanager:9093/-/healthyGrafana:
curl -sS http://grafana:3000/api/health看结果时重点确认:
- targets 是否 up。
- 是否有抓取超时。
- 告警规则是否有
for,避免瞬时抖动就报警。 - 告警是否有通知渠道和负责人。
- 是否有过期未取消的 silence。
- 最近一次测试告警是否能到人。
这里要注意,人工巡检发现的问题,如果能规则化,就应该补到监控告警里。比如证书 30 天到期、磁盘超过 85%、备份 24 小时未生成、MQ lag 持续增长,都不应该长期靠人肉巡检。
系统服务和定时任务
systemd 先看失败服务:
systemctl --failed --no-pager
systemctl list-units --type=service --state=running --no-pager | head看指定服务:
systemctl status nginx --no-pager
systemctl is-active nginx
systemctl is-enabled nginx
journalctl -u nginx --since "24 hours ago" --no-pager | tail -n 100is-active 看当前是否运行,is-enabled 看是否开机自启。这里要注意,有些服务现在是 active,但 disabled,机器重启后就不会自动起来。
定时任务看三类:用户 crontab、系统 cron、systemd timer。
crontab -l
sudo crontab -l
ls -l /etc/cron.d /etc/cron.daily /etc/cron.hourly /etc/cron.weekly 2>/dev/null
systemctl list-timers --all --no-pager看执行日志:
journalctl -u cron --since "24 hours ago" --no-pager 2>/dev/null | tail -n 100
journalctl -u crond --since "24 hours ago" --no-pager 2>/dev/null | tail -n 100
grep CRON /var/log/syslog 2>/dev/null | tail -n 100
grep CRON /var/log/cron 2>/dev/null | tail -n 100生产脚本一般需要加锁,避免上一次没跑完下一次又启动:
flock -n /var/lock/daily-inspection.lock /opt/ops/daily-inspection.shcron 里最好显式写 PATH:
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
10 10 * * * flock -n /var/lock/daily-inspection.lock /opt/ops/daily-inspection.sh >> /var/log/ops/daily-inspection-cron.log 2>&1这里要注意,手工执行成功,不代表 cron 执行成功。cron 的环境变量更少,工作目录也不一定是你想的目录。
安全风险
安全巡检先看登录、账号、端口、SSH 配置、敏感文件和补丁。
登录记录:
who
last -n 20
lastb -n 20 2>/dev/null || true
journalctl -u ssh --since "24 hours ago" --no-pager 2>/dev/null | tail -n 100
journalctl -u sshd --since "24 hours ago" --no-pager 2>/dev/null | tail -n 100账号:
awk -F: '$3 == 0 {print}' /etc/passwd
awk -F: '$7 !~ /(nologin|false)$/ {print $1,$7}' /etc/passwd
getent group sudo
getent group wheelSSH 配置:
sudo sshd -T | egrep 'permitrootlogin|passwordauthentication|pubkeyauthentication|port|allowusers|allowgroups'敏感文件权限:
ls -l ~/.ssh
find /opt /data -type f \( -name '*.key' -o -name '*.pem' -o -name '.env' \) -printf '%m %u %g %p\n' 2>/dev/null | head -n 50开放端口再看一遍:
ss -lntup如果发现 root 可密码登录、数据库公网暴露、未知账号可登录、私钥权限过宽、.env 文件权限过宽,要升级为安全风险处理。
记录安全问题时,不要把完整密码、Token、私钥贴到报告里。只记录文件、权限、发现方式、影响范围和处理动作。
巡检脚本模板
下面这个脚本只做基础采集和初步判断,适合作为第一版日巡检脚本。它不会自动删文件、杀进程或重启服务。
#!/usr/bin/env bash
set -Eeuo pipefail
IFS=$'\n\t'
HOST="$(hostname -f 2>/dev/null || hostname)"
NOW="$(date '+%F %T')"
REPORT_DIR="${REPORT_DIR:-/var/log/ops}"
REPORT_FILE="${REPORT_DIR}/inspection-$(date '+%F').log"
mkdir -p "$REPORT_DIR"
log() {
local level="$1"
shift
printf '%s [%s] [%s] %s\n' "$NOW" "$level" "$HOST" "$*" | tee -a "$REPORT_FILE"
}
need_cmd() {
command -v "$1" >/dev/null 2>&1 || log WARN "missing command: $1"
}
check_disk() {
log INFO "check disk usage"
df -P -h | tee -a "$REPORT_FILE"
df -P | awk 'NR>1 {gsub("%","",$5); if ($5 >= 90) print $6, $5}' | while read -r mount usage; do
log ERROR "disk usage high: mount=${mount}, usage=${usage}%"
done
}
check_inode() {
log INFO "check inode usage"
df -P -i | tee -a "$REPORT_FILE"
df -P -i | awk 'NR>1 {gsub("%","",$5); if ($5 >= 90) print $6, $5}' | while read -r mount usage; do
log ERROR "inode usage high: mount=${mount}, usage=${usage}%"
done
}
check_memory() {
log INFO "check memory"
free -h | tee -a "$REPORT_FILE"
awk '
/MemAvailable/ {available=$2}
/MemTotal/ {total=$2}
END {
if (total > 0) {
ratio=available*100/total
if (ratio < 10) {
printf "memory available low: %.2f%%\n", ratio
}
}
}
' /proc/meminfo | while read -r line; do
log WARN "$line"
done
}
check_load() {
log INFO "check load"
uptime | tee -a "$REPORT_FILE"
local cores
cores="$(nproc)"
awk -v cores="$cores" '{if ($1 > cores * 2) printf "load high: load1=%s cores=%s\n", $1, cores}' /proc/loadavg | while read -r line; do
log WARN "$line"
done
}
check_services() {
log INFO "check failed systemd services"
systemctl --failed --no-pager | tee -a "$REPORT_FILE" || true
}
check_ports() {
log INFO "check listening ports"
ss -lntup | tee -a "$REPORT_FILE" || true
}
check_docker() {
if command -v docker >/dev/null 2>&1; then
log INFO "check docker"
docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}' | tee -a "$REPORT_FILE" || true
docker stats --no-stream | tee -a "$REPORT_FILE" || true
docker system df | tee -a "$REPORT_FILE" || true
fi
}
main() {
need_cmd df
need_cmd free
need_cmd ss
need_cmd systemctl
log INFO "inspection start"
check_disk
check_inode
check_memory
check_load
check_services
check_ports
check_docker
log INFO "inspection done, report=${REPORT_FILE}"
}
main "$@"安装脚本:
sudo install -d -m 0755 /opt/ops /var/log/ops
sudo install -m 0755 daily-inspection.sh /opt/ops/daily-inspection.sh
/opt/ops/daily-inspection.sh执行后应该在 /var/log/ops 看到当天报告。如果脚本没有输出,先查执行权限、解释器路径、PATH 和日志目录权限。
后续可以继续加证书检查、备份检查、Docker unhealthy 检查、MySQL/Redis/MQ 检查,但每加一项都要考虑超时和权限。巡检脚本不能因为一个 Redis 卡住,就把整台机器巡检卡死。
异常记录和问题闭环
巡检发现异常以后,先记录,再处理。记录不是走形式,是为了让后面的人能接着查。
## 异常记录
- 发现时间:
- 发现人:
- 环境:
- 主机:
- 服务:
- 异常等级:P0 / P1 / P2 / P3
- 异常现象:
- 影响范围:
- 证据命令:
- 证据摘要:
- 相关日志:
- 相关监控:
- 最近变更:
- 临时处理:
- 根因判断:
- 恢复时间:
- 验证方式:
- 后续动作:
- 负责人:
- 截止时间:异常等级可以这样定:
| 等级 | 判断 |
|---|---|
| P0 | 核心业务不可用、数据风险、不可恢复风险 |
| P1 | 核心链路明显受影响,但有降级或部分可用 |
| P2 | 有风险或局部异常,需要当天处理 |
| P3 | 趋势问题或低风险问题,进入计划 |
闭环不是“提醒大家注意”。真正闭环至少要落到一个动作:补监控、改脚本、扩容、改配置、修复备份、收敛权限、补文档、做恢复演练。
巡检报告模板
日报可以简单,但格式要固定:
# 生产巡检日报
- 日期:
- 巡检人:
- 环境:
- 主机范围:
- 总体结论:正常 / 有风险 / 异常
## 资源状态
| 主机 | CPU | Load | 内存 | 磁盘 | inode | 结论 |
| --- | --- | --- | --- | --- | --- | --- |
| app-01 | 正常 | 正常 | 正常 | /data 78% | 正常 | 关注 |
## 服务状态
| 服务 | 状态 | 端口 | 日志 | 结论 |
| --- | --- | --- | --- | --- |
| Nginx | active | 80/443 | 无新增 error | 正常 |
| MySQL | active | 3306 | 慢查询 12 条 | 关注 |
| Redis | active | 6379 | 慢日志 0 | 正常 |
| MQ | active | 5672/9092 | 无异常 | 正常 |
## 备份状态
| 对象 | 最近备份 | 文件大小 | 校验 | 远端同步 | 结论 |
| --- | --- | --- | --- | --- | --- |
| MySQL | 2026-07-09 02:00 | 12G | 通过 | 通过 | 正常 |
## 风险和待办
| 风险 | 等级 | 负责人 | 截止时间 | 状态 |
| --- | --- | --- | --- | --- |
| app-02 /data 预计两周后到 85% | P2 | 李四 | 2026-07-12 | 待扩容评估 |周报和月报要加趋势,比如磁盘增长、慢查询、Redis 内存、MQ 积压、告警数量、误报数量、恢复演练结果和未闭环问题。
落地工程深水区
生产巡检真正要治理的是系统退化,而不是某一天命令输出是否好看:
- 基线比绝对阈值更重要。CPU 30% 不一定安全,磁盘 70% 也不一定危险,要结合历史增长、业务周期和扩容周期判断。
- 自动修复要有边界。自动清日志、重启服务、清队列、杀进程都可能破坏现场,默认只能自动采集和告警,修复动作要有审批和回滚。
- 证据要可追溯。巡检时间、主机、命令、输出摘要、异常截图、日志片段、负责人和截止时间要能追到同一条记录。
- 巡检结果要和监控对齐。巡检发现的风险如果长期存在,就应该沉淀成监控规则、容量看板或发布检查项。
- 告警噪声也要巡检。长期误报、无人认领、被静默过久的告警,本身就是稳定性风险。
- 权限漂移要定期看。临时账号、过期公钥、共享 sudo 权限、未下线的跳板机权限,往往比 CPU 高一点更危险。
- 趋势报告要能推动预算和排期。磁盘增长、备份耗时、慢查询数量、证书剩余天数、MQ 积压峰值,都应该能支持扩容或治理决策。
- 每次重大事故后都要更新巡检项。事故暴露出的盲点没有进入巡检表,就等于等下一次再踩。
最后一张巡检检查清单
真正到生产现场,可以按这张表从上往下过:
- [ ] 时间同步正常:`timedatectl` / `chronyc sources -v`
- [ ] CPU 无持续异常:`top` / `mpstat` / `pidstat`
- [ ] Load 符合基线:`uptime` / `/proc/loadavg`
- [ ] 内存无 OOM 和持续 swap:`free -h` / `dmesg`
- [ ] 磁盘空间安全:`df -hT`
- [ ] inode 空间安全:`df -ih`
- [ ] 核心进程存在且启动时间符合预期:`ps` / `systemctl status`
- [ ] 端口监听正确,无意外公网暴露:`ss -lntup`
- [ ] 网络连通正常:`curl` / `nc` / `ping`
- [ ] DNS 解析正确且耗时正常:`getent hosts` / `dig`
- [ ] 日志无异常增长和高频错误:`journalctl` / `grep`
- [ ] 证书 30 天内不过期,链路正确:`openssl` / `curl -Iv`
- [ ] Docker 容器无 unhealthy、重启和 OOM:`docker ps` / `docker inspect`
- [ ] Nginx 配置正确,error log 无新增核心错误:`nginx -t` / `tail error.log`
- [ ] MySQL 连接、慢日志、复制、容量正常:`mysqladmin` / SQL 状态
- [ ] Redis 内存、慢日志、持久化、复制正常:`redis-cli INFO`
- [ ] MQ 无异常积压,消费者在线:RabbitMQ / Kafka / RocketMQ 对应命令
- [ ] 备份已生成、大小正常、校验通过、远端同步正常
- [ ] 监控 targets 正常,告警通知链路有效
- [ ] systemd 无 failed 服务:`systemctl --failed`
- [ ] 定时任务按时执行且有日志:`crontab` / `systemctl list-timers`
- [ ] 安全风险无新增:登录、端口、账号、私钥、补丁
- [ ] 异常已记录负责人、截止时间和闭环动作最后再强调一次:巡检不是为了证明今天没事,而是为了更早发现系统正在变差。命令只是入口,真正有用的是基线、记录、趋势和闭环。
官方文档入口
巡检手册会随着系统和版本演进,下面这些入口建议定期回看:
- Dockerfile HEALTHCHECK
- docker logs
- docker stats
- Docker logging drivers
- Nginx control signals
- Nginx stub_status module
- Nginx SSL module
- MySQL mysqladmin
- MySQL Backup and Recovery
- MySQL Slow Query Log
- Redis INFO
- Redis SLOWLOG GET
- Redis latency diagnosis
- RabbitMQ Monitoring
- Kafka Monitoring
- RocketMQ Metrics
- Prometheus Node Exporter guide
- Prometheus alerting rules
- Grafana Alerting
