记一次线上故障排查:Linux磁盘空间莫名占满,原来是它在"作妖"(附清理脚本)

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. 问题根本原因分析

通过以上排查,发现问题是系统日志文件无限增长,主要原因是:

  1. 日志轮转配置错误/etc/logrotate.d/rsyslog配置中的size参数设置过大
  2. 某个应用异常:持续产生大量错误日志
  3. 日志轮转未执行: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. 预防措施总结

  1. 定期监控:设置磁盘使用率监控告警
  2. 日志管理:合理配置日志轮转策略和保留时间
  3. 定期维护:建立定期清理机制
  4. 容量规划:提前规划磁盘容量,及时扩容
  5. 应用优化:优化应用程序,避免产生过多日志

通过以上完整的排查和解决方案,可以有效预防和解决Linux磁盘空间被莫名占满的问题。建议将清理脚本部署到所有服务器,并建立完善的监控体系。

相关推荐
code monkey.33 分钟前
【Linux之旅】深入 Linux Ext 系列文件系统:从磁盘物理结构到软硬链接的底层逻辑
linux·文件系统·ext2
RoboWizard2 小时前
高性能电脑热战寒冬 11月DIY配置推荐
linux·运维·服务器·电脑·金士顿
zl9798996 小时前
RabbitMQ-下载安装与Web页面
linux·分布式·rabbitmq
kitty_hi8 小时前
mysql主从配置升级,从mysql5.7升级到mysql8.4
linux·数据库·mysql·adb
moringlightyn9 小时前
Linux---进程状态
linux·运维·服务器·笔记·操作系统·c·进程状态
go_bai9 小时前
Linux-线程2
linux·c++·经验分享·笔记·学习方法
shizhan_cloud9 小时前
DNS 服务器
linux·运维
q***133410 小时前
Linux系统离线部署MySQL详细教程(带每步骤图文教程)
linux·mysql·adb
小雪_Snow10 小时前
Ubuntu 安装教程
linux·ubuntu
IT逆夜11 小时前
linux系统安全及应用
linux·运维