记一次线上故障排查: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磁盘空间被莫名占满的问题。建议将清理脚本部署到所有服务器,并建立完善的监控体系。

相关推荐
HLC++8 小时前
Linux的进程间通信
android·linux·服务器
华清远见IT开放实验室9 小时前
实验室建设案例 | 石家庄科技信息职业学院嵌入式实验室——从底层硬件到系统应用,一所应用型高校的嵌入式人才培养这样落地
linux·arm开发·stm32·嵌入式硬件·高校·实验室建设
groundhappy12 小时前
idalib安装和codex ida-mcp配置
linux·开发语言·python
通信小小昕12 小时前
Ubuntu 26.04 中文输入法安装
linux·运维·ubuntu
张小姐的猫13 小时前
【Linux】网络编程 —— HTTP协议(上)
linux·运维·服务器·网络·http·单例模式·策略模式
栩栩云生14 小时前
AI 写代码犯的错,早被写进了错题集
linux·安全·ai编程
imc.1115 小时前
linux基础IO
linux·运维·服务器
BelongPanda17 小时前
Linux Nginx 纯手动 Let‘s Encrypt 泛域名证书配置教程
linux·nginx
酷可达拉斯17 小时前
Linux操作系统-shell编程(0)
linux·运维·服务器·python·云计算
2301_7779983417 小时前
Linux中断机制:操作系统如何高效运行
linux·运维·服务器