作为运维工程师或后端开发者,日常工作中最耗时的往往不是写代码,而是排查服务器问题。CPU飙高、内存泄漏、磁盘满了、网络不通、进程僵死…这些问题反复出现,每次都手动敲命令排查效率太低。
本文分享10个经过实战检验的一键排查脚本,覆盖CPU、内存、磁盘、网络、进程、日志等常见场景。每个脚本都可以直接保存使用。
#!/bin/bash
# 文件名: health-check.sh
# 用途: 一键检查服务器整体健康状况
echo "========== 系统健康检查 =========="
echo "检查时间: $(date '+%Y-%m-%d %H:%M:%S')"
echo "主机名: $(hostname)"
echo ""
# 系统运行时间
echo "--- 运行时间 ---"
uptime
echo ""
# CPU使用率
echo "--- CPU使用率 (Top 5进程) ---"
ps aux --sort=-%cpu | head -6
echo ""
# 内存使用
echo "--- 内存使用 ---"
free -h
echo ""
# 磁盘使用
echo "--- 磁盘使用 ---"
df -h | grep -E '(Filesystem|/dev/)'
echo ""
# 系统负载
echo "--- 系统负载 ---"
cat /proc/loadavg
echo ""
# 网络连接数
echo "--- 网络连接统计 ---"
ss -s
echo ""
# 最近登录
echo "--- 最近5次登录 ---"
last -5
#!/bin/bash
# 文件名: check-port.sh
# 用途: 检查指定端口是否被占用,以及被谁占用
# 用法: ./check-port.sh 8080
PORT=${1:?"用法: $0 <端口号>"}
echo "========== 端口 $PORT 排查 =========="
# 检查端口是否在监听
echo "--- 监听状态 ---"
ss -tlnp | grep ":$PORT " || echo "端口 $PORT 未被监听"
# 检查占用进程
echo "--- 占用进程 ---"
lsof -i :$PORT 2>/dev/null || fuser $PORT/tcp 2>/dev/null
# 检查防火墙规则
echo "--- 防火墙状态 ---"
if command -v ufw &> /dev/null; then
ufw status | grep $PORT || echo "UFW: 未找到端口 $PORT 的规则"
elif command -v firewall-cmd &> /dev/null; then
firewall-cmd --list-ports | grep $PORT || echo "firewalld: 未找到端口 $PORT 的规则"
fi
# 测试端口连通性
echo "--- 连通性测试 ---"
timeout 3 bash -c "echo > /dev/tcp/127.0.0.1/$PORT" 2>/dev/null && \
echo "本地端口 $PORT 可连接" || echo "本地端口 $PORT 不可连接"
#!/bin/bash
# 文件名: mem-analysis.sh
# 用途: 深度分析内存使用情况
echo "========== 内存分析 =========="
# 基本内存信息
echo "--- 内存概况 ---"
free -h
echo ""
# 内存占用Top 10进程
echo "--- 内存占用Top 10 ---"
ps aux --sort=-%mem | head -11 | awk '{printf "%-12s %-8s %-8s %s\n", $1, $4"%", $6/1024"MB", $11}'
echo ""
# 共享内存
echo "--- 共享内存段 ---"
ipcs -m 2>/dev/null || echo "ipcs命令不可用"
echo ""
# 内存碎片
echo "--- 内存碎片信息 ---"
cat /proc/buddyinfo 2>/dev/null | head -3
echo ""
# Swap使用情况
echo "--- Swap使用Top 10 ---"
for file in /proc/*/status; do
awk '/VmSwap|Name/{printf $2 " " $3}END{print ""}' "$file"
done 2>/dev/null | sort -k 2 -n -r | head -10
echo ""
# 大页内存
echo "--- 大页内存 ---"
cat /proc/meminfo | grep -i huge
#!/bin/bash
# 文件名: disk-analysis.sh
# 用途: 分析磁盘空间占用,找出大文件和目录
echo "========== 磁盘分析 =========="
# 总体使用情况
echo "--- 文件系统使用率 ---"
df -h | grep -E '(Filesystem|/dev/)'
echo ""
# inode使用情况
echo "--- Inode使用率 ---"
df -i | grep -E '(Filesystem|/dev/)'
echo ""
# 当前目录下最大的20个文件
echo "--- 当前目录最大20个文件 ---"
find . -type f -exec du -h {} + 2>/dev/null | sort -rh | head -20
echo ""
# 当前目录下最大的10个目录
echo "--- 当前目录最大10个子目录 ---"
du -sh */ 2>/dev/null | sort -rh | head -10
echo ""
# 已删除但仍占用空间的文件
echo "--- 已删除但仍占用空间的文件 ---"
lsof +L1 2>/dev/null | awk '{printf "%s %s %s %s\n", $1, $2, $7, $NF}' | head -10 || \
echo "无已删除文件占用空间"
echo ""
# 临时文件大小
echo "--- 临时文件目录大小 ---"
du -sh /tmp /var/tmp 2>/dev/null
echo ""
# 日志文件大小
echo "--- 日志文件大小 ---"
du -sh /var/log/* 2>/dev/null | sort -rh | head -10
#!/bin/bash
# 文件名: net-diagnose.sh
# 用途: 诊断网络连通性问题
# 用法: ./net-diagnose.sh example.com
TARGET=${1:-"8.8.8.8"}
echo "========== 网络诊断: $TARGET =========="
# DNS解析
echo "--- DNS解析 ---"
dig +short $TARGET 2>/dev/null || nslookup $TARGET 2>/dev/null | grep Address
echo ""
# Ping测试
echo "--- Ping测试 (5次) ---"
ping -c 5 -W 2 $TARGET 2>&1
echo ""
# 路由追踪
echo "--- 路由追踪 (前10跳) ---"
traceroute -m 10 -w 2 $TARGET 2>/dev/null || tracepath -m 10 $TARGET 2>/dev/null
echo ""
# 端口扫描(常用端口)
echo "--- 常用端口检测 ---"
for port in 22 80 443 3306 6379 8080; do
timeout 2 bash -c "echo > /dev/tcp/$TARGET/$port" 2>/dev/null && \
echo " 端口 $port: 开放" || echo " 端口 $port: 关闭/超时"
done
echo ""
# 网络接口状态
echo "--- 网络接口状态 ---"
ip -brief addr show 2>/dev/null || ifconfig 2>/dev/null
echo ""
# 路由表
echo "--- 路由表 ---"
ip route 2>/dev/null || route -n 2>/dev/null
#!/bin/bash
# 文件名: process-monitor.sh
# 用途: 检测异常进程(僵尸进程、CPU/内存异常等)
echo "========== 进程异常检测 =========="
# 僵尸进程
echo "--- 僵尸进程 ---"
ZOMBIE_COUNT=$(ps aux | awk '{if($8=="Z") print}' | wc -l)
if [ "$ZOMBIE_COUNT" -gt 0 ]; then
echo "发现 $ZOMBIE_COUNT 个僵尸进程:"
ps aux | awk '{if($8=="Z") print}'
else
echo "未发现僵尸进程"
fi
echo ""
# CPU占用超过80%的进程
echo "--- CPU占用>80%的进程 ---"
ps aux --sort=-%cpu | awk 'NR>1 && $3>80 {printf "PID: %s, CPU: %s%%, CMD: %s\n", $2, $3, $11}'
echo ""
# 内存占用超过50%的进程
echo "--- 内存占用>50%的进程 ---"
ps aux --sort=-%mem | awk 'NR>1 && $4>50 {printf "PID: %s, MEM: %s%%, CMD: %s\n", $2, $4, $11}'
echo ""
# 运行时间超过7天的进程
echo "--- 运行超过7天的进程 ---"
ps -eo pid,etime,cmd | awk '$2 ~ /^[0-9]{4,}/ {print "PID:", $1, "运行时间:", $2, "CMD:", $3}'
echo ""
# 重复进程
echo "--- 可能的重复进程 ---"
ps aux | awk '{print $11}' | sort | uniq -c | sort -rn | head -10 | awk '$1>3 {print "进程:", $2, "数量:", $1}'
#!/bin/bash
# 文件名: log-analysis.sh
# 用途: 快速分析Web服务器访问日志
# 用法: ./log-analysis.sh /var/log/nginx/access.log
LOG_FILE=${1:-"/var/log/nginx/access.log"}
if [ ! -f "$LOG_FILE" ]; then
echo "日志文件不存在: $LOG_FILE"
exit 1
fi
echo "========== 日志分析: $LOG_FILE =========="
echo "日志总行数: $(wc -l < "$LOG_FILE")"
echo ""
# Top 10 访问IP
echo "--- Top 10 访问IP ---"
awk '{print $1}' "$LOG_FILE" | sort | uniq -c | sort -rn | head -10
echo ""
# Top 10 访问URL
echo "--- Top 10 访问URL ---"
awk '{print $7}' "$LOG_FILE" | sort | uniq -c | sort -rn | head -10
echo ""
# HTTP状态码分布
echo "--- HTTP状态码分布 ---"
awk '{print $9}' "$LOG_FILE" | sort | uniq -c | sort -rn
echo ""
# 5xx错误
echo "--- 5xx错误 (最近20条) ---"
grep ' 5[0-9][0-9] ' "$LOG_FILE" | tail -20
echo ""
# 每小时请求量
echo "--- 每小时请求量 ---"
awk '{print substr($4,14,2)}' "$LOG_FILE" | sort | uniq -c | sort -k2n
#!/bin/bash
# 文件名: ssl-check.sh
# 用途: 检查SSL证书有效期
# 用法: ./ssl-check.sh example.com
DOMAIN=${1:?"用法: $0 <域名>"}
echo "========== SSL证书检查: $DOMAIN =========="
# 获取证书信息
echo "--- 证书详情 ---"
echo | openssl s_client -servername "$DOMAIN" -connect "$DOMAIN":443 2>/dev/null | \
openssl x509 -noout -subject -issuer -dates -ext subjectAltName 2>/dev/null
echo ""
# 计算剩余天数
EXPIRY_DATE=$(echo | openssl s_client -servername "$DOMAIN" -connect "$DOMAIN":443 2>/dev/null | \
openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)
if [ -n "$EXPIRY_DATE" ]; then
EXPIRY_EPOCH=$(date -d "$EXPIRY_DATE" +%s 2>/dev/null)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( (EXPIRY_EPOCH - NOW_EPOCH) / 86400 ))
echo "--- 剩余天数: $DAYS_LEFT 天 ---"
if [ "$DAYS_LEFT" -lt 0 ]; then
echo "!!! 证书已过期 !!!"
elif [ "$DAYS_LEFT" -lt 7 ]; then
echo "!!! 警告: 证书将在7天内过期 !!!"
elif [ "$DAYS_LEFT" -lt 30 ]; then
echo "注意: 证书将在30天内过期"
else
echo "证书状态正常"
fi
else
echo "无法获取证书信息"
fi
#!/bin/bash
# 文件名: security-check.sh
# 用途: 基础安全检查
echo "========== 安全检查 =========="
# SSH配置检查
echo "--- SSH安全配置 ---"
grep -E "^(PermitRootLogin|PasswordAuthentication|Port|PubkeyAuthentication)" /etc/ssh/sshd_config 2>/dev/null
echo ""
# 防火墙状态
echo "--- 防火墙状态 ---"
if command -v ufw &> /dev/null; then
ufw status
elif command -v firewall-cmd &> /dev/null; then
firewall-cmd --state
firewall-cmd --list-all
fi
echo ""
# 失败登录尝试
echo "--- 最近失败登录 (Top 10 IP) ---"
grep "Failed password" /var/log/auth.log 2>/dev/null | \
awk '{print $(NF-3)}' | sort | uniq -c | sort -rn | head -10 || \
journalctl -u sshd --no-pager | grep "Failed" | tail -10
echo ""
# SUID文件检查
echo "--- 可疑SUID文件 ---"
find / -perm -4000 -type f 2>/dev/null | head -20
echo ""
# 开放端口
echo "--- 监听端口 ---"
ss -tlnp | awk 'NR>1 {print $4, $6}' | sort
echo ""
# 当前登录用户
echo "--- 当前登录用户 ---"
who
echo ""
# 计划任务
echo "--- 当前用户的计划任务 ---"
crontab -l 2>/dev/null || echo "无计划任务"
#!/bin/bash
# 文件名: quick-backup.sh
# 用途: 快速备份指定目录,支持增量备份
# 用法: ./quick-backup.sh /var/www/html
SOURCE_DIR=${1:?"用法: $0 <源目录>"}
BACKUP_DIR="/backup"
TIMESTAMP=$(date '+%Y%m%d_%H%M%S')
BACKUP_NAME=$(basename "$SOURCE_DIR")_${TIMESTAMP}.tar.gz
MAX_BACKUPS=7
echo "========== 快速备份 =========="
echo "源目录: $SOURCE_DIR"
echo "备份目录: $BACKUP_DIR"
echo "备份文件: $BACKUP_NAME"
# 创建备份目录
mkdir -p "$BACKUP_DIR"
# 执行备份(排除日志和临时文件)
echo "正在备份..."
tar -czf "${BACKUP_DIR}/${BACKUP_NAME}" \
--exclude="*.log" \
--exclude="*.tmp" \
--exclude="node_modules" \
--exclude=".git" \
--exclude="__pycache__" \
-C "$(dirname "$SOURCE_DIR")" \
"$(basename "$SOURCE_DIR")" 2>/dev/null
if [ $? -eq 0 ]; then
BACKUP_SIZE=$(du -sh "${BACKUP_DIR}/${BACKUP_NAME}" | cut -f1)
echo "备份成功: ${BACKUP_DIR}/${BACKUP_NAME} ($BACKUP_SIZE)"
else
echo "备份失败!"
exit 1
fi
# 清理旧备份(保留最近N个)
BACKUP_COUNT=$(ls -1 "${BACKUP_DIR}/"*.tar.gz 2>/dev/null | wc -l)
if [ "$BACKUP_COUNT" -gt "$MAX_BACKUPS" ]; then
echo "清理旧备份 (保留最近${MAX_BACKUPS}个)..."
ls -1t "${BACKUP_DIR}/"*.tar.gz | tail -n +$((MAX_BACKUPS + 1)) | xargs rm -f
echo "清理完成"
fi
echo "当前备份列表:"
ls -lh "${BACKUP_DIR}/"*.tar.gz 2>/dev/null
/usr/local/bin/ 或 ~/scripts/,添加到PATH中# 设置crontab定时执行
# 每天早上8点执行健康检查
0 8 * * * /root/scripts/health-check.sh > /var/log/health-check.log 2>&1
# 每周一早上9点执行安全检查
0 9 * * 1 /root/scripts/security-check.sh > /var/log/security-check.log 2>&1
# 每天凌晨2点自动备份
0 2 * * * /root/scripts/quick-backup.sh /var/www/html
你日常运维中还有哪些常用的脚本?欢迎在评论区分享。