1. 问题现象描述
某工作日早晨,监控系统突然发出磁盘空间告警,显示服务器根分区使用率超过95%。登录服务器后确认情况:
bash
# 查看磁盘使用情况
df -h
# 输出结果:
Filesystem Size Used Avail Use% Mounted on
/dev/vda1 50G 47G 1.2G 98% /
/dev/vdb1 100G 30G 65G 32% /data
2. 初步排查步骤
2.1 检查大文件和目录
首先使用du命令查找占用空间较大的目录:
bash
# 从根目录开始,按目录大小排序,显示前20个
du -h --max-depth=1 / 2>/dev/null | sort -hr | head -20
# 输出示例:
47G /
30G /data
15G /var
8.2G /usr
3.1G /home
1.2G /opt
发现根目录占用异常,继续深入排查:
bash
# 检查/var目录的详细使用情况
du -h --max-depth=1 /var 2>/dev/null | sort -hr | head -10
# 输出示例:
15G /var
12G /var/log
2.1G /var/lib
800M /var/cache
2.2 检查日志文件
bash
# 检查/var/log目录下的大文件
find /var/log -type f -size +100M -exec ls -lh {} \; 2>/dev/null
# 输出结果:
-rw-r--r-- 1 root root 2.1G Mar 15 10:30 /var/log/syslog
-rw-r--r-- 1 root root 1.8G Mar 15 10:28 /var/log/kern.log
-rw-r--r-- 1 root root 890M Mar 15 10:25 /var/log/auth.log
3. 深入分析定位问题
3.1 检查日志轮转配置
bash
# 检查logrotate配置
ls -la /etc/logrotate.d/
cat /etc/logrotate.d/rsyslog
# 检查系统日志配置
cat /etc/rsyslog.conf
3.2 实时监控文件变化
bash
# 使用inotify-tools监控文件变化(需要安装:yum install inotify-tools)
inotifywait -m -r /var/log --format '%w%f %e' | grep -v ACCESS
# 或者使用watch命令持续观察
watch -n 5 'ls -lah /var/log/syslog /var/log/kern.log'
3.3 检查进程和打开的文件
bash
# 检查哪些进程正在写入大文件
lsof +L1 # 查看被删除但仍在使用的文件
lsof /var/log/syslog
# 使用fuser查看使用文件的进程
fuser -v /var/log/syslog
4. 问题根本原因分析
通过以上排查,发现问题是系统日志文件无限增长,主要原因是:
- 日志轮转配置错误 :
/etc/logrotate.d/rsyslog配置中的size参数设置过大 - 某个应用异常:持续产生大量错误日志
- 日志轮转未执行:cron任务异常导致logrotate未按时执行
5. 故障排查流程图
以下是完整的故障排查流程,可按照此图逐步排查:
graph TD
A[磁盘空间告警] --> B[df -h 确认情况]
B --> C[du 查找大目录]
C --> D{找到大文件/目录?}
D -->|是| E[分析具体文件]
D -->|否| F[检查被删除未释放文件]
E --> G[检查日志文件]
G --> H[分析日志内容]
H --> I[定位问题应用]
F --> J[lsof +L1 检查]
J --> K[重启相关进程]
I --> L[修复应用配置]
K --> M[清理临时文件]
L --> N[配置日志轮转]
M --> O[验证磁盘空间]
N --> O
O --> P[问题解决]
style A fill:#ff6b6b,stroke:#fff,stroke-width:2px,color:#fff
style P fill:#51cf66,stroke:#fff,stroke-width:2px,color:#fff
style B fill:#339af0,stroke:#fff,stroke-width:2px,color:#fff
style C fill:#339af0,stroke:#fff,stroke-width:2px,color:#fff
style E fill:#339af0,stroke:#fff,stroke-width:2px,color:#fff
style F fill:#339af0,stroke:#fff,stroke-width:2px,color:#fff
style G fill:#339af0,stroke:#fff,stroke-width:2px,color:#fff
style H fill:#339af0,stroke:#fff,stroke-width:2px,color:#fff
style I fill:#339af0,stroke:#fff,stroke-width:2px,color:#fff
style J fill:#339af0,stroke:#fff,stroke-width:2px,color:#fff
style K fill:#ffa94d,stroke:#fff,stroke-width:2px,color:#fff
style L fill:#ffa94d,stroke:#fff,stroke-width:2px,color:#fff
style M fill:#ffa94d,stroke:#fff,stroke-width:2px,color:#fff
style N fill:#ffa94d,stroke:#fff,stroke-width:2px,color:#fff
style O fill:#ffa94d,stroke:#fff,stroke-width:2px,color:#fff
6. 紧急处理措施
6.1 立即清理大日志文件
bash
# 方法1:清空日志文件(推荐,避免重启服务)
truncate -s 0 /var/log/syslog
truncate -s 0 /var/log/kern.log
truncate -s 0 /var/log/auth.log
# 方法2:备份后删除(如果需要保留部分日志)
cp /var/log/syslog /var/log/syslog.bak.$(date +%Y%m%d)
truncate -s 0 /var/log/syslog
# 方法3:使用logrotate强制轮转
logrotate -f /etc/logrotate.d/rsyslog
6.2 检查磁盘空间释放情况
bash
# 确认空间已释放
df -h
# 检查文件大小
ls -lh /var/log/syslog /var/log/kern.log /var/log/auth.log
7. 根本解决方案
7.1 修复logrotate配置
bash
# 备份原配置
cp /etc/logrotate.d/rsyslog /etc/logrotate.d/rsyslog.bak.$(date +%Y%m%d)
# 编辑配置文件
cat > /etc/logrotate.d/rsyslog << 'EOF'
/var/log/syslog
{
rotate 7
daily
missingok
notifempty
delaycompress
compress
postrotate
/usr/lib/rsyslog/rsyslog-rotate
endscript
}
/var/log/mail.info
/var/log/mail.warn
/var/log/mail.err
/var/log/mail.log
/var/log/daemon.log
/var/log/kern.log
/var/log/auth.log
/var/log/user.log
/var/log/lpr.log
/var/log/cron.log
/var/log/debug
/var/log/messages
{
rotate 4
weekly
missingok
notifempty
compress
delaycompress
sharedscripts
postrotate
/usr/lib/rsyslog/rsyslog-rotate
endscript
}
EOF
7.2 配置日志大小限制
bash
# 编辑rsyslog配置文件
cat >> /etc/rsyslog.conf << 'EOF'
# 限制单个日志文件大小为100MB,最多保留5个
$outchannel log_rotation,/var/log/syslog, 104857600, /var/log/syslog_rotation_script
*.* :omfile:$log_rotation
EOF
7.3 设置定时清理任务
bash
# 创建日志清理脚本
cat > /usr/local/bin/cleanup_logs.sh << 'EOF'
#!/bin/bash
# 日志清理脚本
# 清理超过7天的日志文件
LOG_DIRS=("/var/log" "/opt/application/logs")
for dir in "${LOG_DIRS[@]}"; do
if [ -d "$dir" ]; then
echo "Cleaning logs in $dir older than 7 days"
find "$dir" -type f -name "*.log.*" -mtime +7 -delete
find "$dir" -type f -name "*.gz" -mtime +7 -delete
fi
done
# 清理临时文件
echo "Cleaning temporary files"
find /tmp -type f -mtime +3 -delete
find /var/tmp -type f -mtime +7 -delete
# 清理包管理器缓存
echo "Cleaning package manager cache"
apt-get clean 2>/dev/null || yum clean all 2>/dev/null
echo "Cleanup completed at $(date)"
EOF
# 设置脚本权限
chmod +x /usr/local/bin/cleanup_logs.sh
# 添加定时任务
(crontab -l 2>/dev/null; echo "0 2 * * * /usr/local/bin/cleanup_logs.sh >> /var/log/cleanup.log 2>&1") | crontab -
8. 自动化清理脚本
创建完整的磁盘空间监控和清理脚本:
bash
cat > /usr/local/bin/disk_cleanup_manager.sh << 'EOF'
#!/bin/bash
# 磁盘清理管理脚本
# 配置参数
DISK_THRESHOLD=80
LOG_RETENTION_DAYS=7
TEMP_FILE_RETENTION_DAYS=3
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# 日志函数
log() {
echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1"
}
warn() {
echo -e "${YELLOW}[$(date +'%Y-%m-%d %H:%M:%S')] WARN:${NC} $1"
}
error() {
echo -e "${RED}[$(date +'%Y-%m-%d %H:%M:%S')] ERROR:${NC} $1"
}
# 检查磁盘使用率
check_disk_usage() {
local usage=$(df / | awk 'NR==2 {print $5}' | sed 's/%//')
echo $usage
}
# 查找大文件
find_large_files() {
local dir=${1:-/}
local size=${2:-100M}
log "查找目录 $dir 中大于 $size 的文件..."
find "$dir" -type f -size "+$size" -exec ls -lh {} \; 2>/dev/null | sort -k5 -hr
}
# 清理日志文件
cleanup_logs() {
log "开始清理日志文件..."
# 清理系统日志
local log_dirs=("/var/log" "/opt/*/logs" "/home/*/logs")
for pattern in "${log_dirs[@]}"; do
for dir in $pattern; do
if [ -d "$dir" ]; then
log "清理目录: $dir"
find "$dir" -type f \( -name "*.log.*" -o -name "*.gz" -o -name "*.bz2" \) -mtime "+$LOG_RETENTION_DAYS" -delete
# 清空过大日志文件(保留最近内容)
find "$dir" -type f -name "*.log" -size +1G -exec truncate -s 100M {} \;
fi
done
done
}
# 清理临时文件
cleanup_temp_files() {
log "开始清理临时文件..."
local temp_dirs=("/tmp" "/var/tmp" "/cache")
for dir in "${temp_dirs[@]}"; do
if [ -d "$dir" ]; then
find "$dir" -type f -mtime "+$TEMP_FILE_RETENTION_DAYS" -delete 2>/dev/null
find "$dir" -type d -empty -mtime "+$TEMP_FILE_RETENTION_DAYS" -delete 2>/dev/null
fi
done
}
# 清理包缓存
cleanup_package_cache() {
log "清理包管理器缓存..."
if command -v apt-get >/dev/null 2>&1; then
apt-get autoremove -y
apt-get clean
elif command -v yum >/dev/null 2>&1; then
yum clean all
fi
}
# 清理Docker资源(如果使用Docker)
cleanup_docker() {
if command -v docker >/dev/null 2>&1; then
log "清理Docker资源..."
docker system prune -f
fi
}
# 主函数
main() {
local action=${1:-"auto"}
case $action in
"auto")
local usage=$(check_disk_usage)
if [ "$usage" -ge "$DISK_THRESHOLD" ]; then
warn "磁盘使用率 ${usage}% 超过阈值 ${DISK_THRESHOLD}%,开始自动清理..."
cleanup_logs
cleanup_temp_files
cleanup_package_cache
cleanup_docker
local new_usage=$(check_disk_usage)
log "清理完成。磁盘使用率从 ${usage}% 降至 ${new_usage}%"
else
log "磁盘使用率 ${usage}% 正常,无需清理"
fi
;;
"manual")
log "执行手动清理..."
cleanup_logs
cleanup_temp_files
cleanup_package_cache
cleanup_docker
log "手动清理完成"
;;
"find-large-files")
find_large_files "/" "500M"
;;
"status")
local usage=$(check_disk_usage)
log "当前磁盘使用率: ${usage}%"
df -h
;;
*)
echo "用法: $0 {auto|manual|find-large-files|status}"
echo " auto: 自动检查并清理(阈值${DISK_THRESHOLD}%)"
echo " manual: 手动强制清理"
echo " find-large-files: 查找大文件"
echo " status: 查看磁盘状态"
exit 1
;;
esac
}
# 执行主函数
main "$@"
EOF
# 设置脚本权限
chmod +x /usr/local/bin/disk_cleanup_manager.sh
# 创建定时任务,每小时检查一次
(crontab -l 2>/dev/null; echo "0 * * * * /usr/local/bin/disk_cleanup_manager.sh auto >> /var/log/disk_cleanup.log 2>&1") | crontab -
9. 验证和监控
9.1 验证清理效果
bash
# 运行清理脚本
/usr/local/bin/disk_cleanup_manager.sh manual
# 检查磁盘空间
df -h
# 检查清理日志
tail -f /var/log/disk_cleanup.log
9.2 设置监控告警
bash
# 创建磁盘监控脚本
cat > /usr/local/bin/disk_monitor.sh << 'EOF'
#!/bin/bash
THRESHOLD=85
CURRENT=$(df / | awk 'NR==2 {print $5}' | sed 's/%//')
if [ "$CURRENT" -gt "$THRESHOLD" ]; then
# 发送告警(这里以日志为例,实际可集成邮件、微信等)
logger "CRITICAL: 磁盘使用率 ${CURRENT}% 超过阈值 ${THRESHOLD}%"
# 可添加邮件发送逻辑
# echo "磁盘告警" | mail -s "磁盘空间不足" admin@company.com
fi
EOF
chmod +x /usr/local/bin/disk_monitor.sh
# 每5分钟检查一次
(crontab -l 2>/dev/null; echo "*/5 * * * * /usr/local/bin/disk_monitor.sh") | crontab -
10. 预防措施总结
- 定期监控:设置磁盘使用率监控告警
- 日志管理:合理配置日志轮转策略和保留时间
- 定期维护:建立定期清理机制
- 容量规划:提前规划磁盘容量,及时扩容
- 应用优化:优化应用程序,避免产生过多日志
通过以上完整的排查和解决方案,可以有效预防和解决Linux磁盘空间被莫名占满的问题。建议将清理脚本部署到所有服务器,并建立完善的监控体系。