Linux磁盘空间满了怎么办,磁盘清理

原文链接:https://mp.weixin.qq.com/s/YWhfnhUTbZ2HctGg87UtYA

一、概述

1.1 背景介绍

磁盘空间问题是Linux服务器运维中最常见的问题之一,没有之一。根据我这些年的统计,在所有生产环境告警中,磁盘相关的告警占比大概在15%-20%左右。而且这类问题往往来得很突然——前一天还好好的,第二天早上磁盘就满了。

为什么磁盘问题这么常见?原因很简单:

  1. 日志文件在不停地写入
  2. 应用程序可能存在内存泄漏,产生大量core dump
  3. 临时文件没有及时清理
  4. Docker镜像和容器数据堆积
  5. 数据库binlog或WAL日志疯狂增长

更要命的是,磁盘空间不足会引发连锁反应。数据库写不进去数据,应用程序抛异常,缓存服务挂掉,最后整个业务系统瘫痪。我见过太多P0级别的故障,根因追溯下来就是磁盘满了。

1.2 技术特点

磁盘空间排查看似简单,实际上涉及到Linux文件系统的很多底层知识:

存储空间 vs inode数量

这是很多新手容易忽略的点。磁盘空间有两个维度:一个是存储空间(blocks),另一个是inode数量。即使存储空间还有剩余,如果inode用完了,也无法创建新文件。我就遇到过这种情况:df -h显示还有30%的空间,但系统报"No space left on device"。查了半天才发现是inode耗尽了。

已删除但未释放的文件

这是另一个经典坑。用rm删除了一个大文件,但df显示空间没有释放。原因是还有进程在持有这个文件的文件描述符。文件虽然从目录项中删除了,但数据块并没有真正释放。

预留空间机制

ext4文件系统默认会预留5%的空间给root用户。这个设计是为了防止普通用户把磁盘写满后,root用户连登录都登不了。但在某些场景下,这5%的空间也是可以利用的。

1.3 适用场景

这套排查流程适用于以下场景:

  • 生产环境收到磁盘使用率告警
  • 应用程序报"No space left on device"错误
  • 系统响应变慢,怀疑是磁盘IO问题
  • 定期巡检发现磁盘使用率偏高
  • 需要制定磁盘空间清理策略

1.4 环境要求

本文的命令和配置基于以下环境测试:

操作系统: Ubuntu 22.04 LTS / CentOS 7.9 / Rocky Linux 9
文件系统: ext4 / xfs
内核版本: 5.15+
容器运行时: Docker 24.x / containerd 1.7.x
监控系统: Prometheus 2.47+ / Grafana 10.x

大部分命令在主流Linux发行版上都能正常使用,个别工具可能需要额外安装。

二、详细步骤

2.1 第一步:快速确认磁盘状态

收到告警后,第一件事是登录服务器,用df命令确认磁盘的整体使用情况:

df -h

输出示例:

Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1       100G   95G  5.0G  95% /
/dev/sdb1       500G  120G  380G  24% /data
tmpfs           7.8G  1.2M  7.8G   1% /run
/dev/sdc1       200G  200G     0 100% /var/log

看到这个输出,问题就很明显了:根分区使用了95%,/var/log分区已经100%满了。

但光看存储空间还不够,还要检查inode的使用情况:

df -i

输出示例:

Filesystem      Inodes   IUsed   IFree IUse% Mounted on
/dev/sda1      6553600 6553598       2  100% /
/dev/sdb1     32768000  125000 32643000    1% /data

这个输出就更严重了——根分区的inode几乎用完了,只剩2个。这种情况下,即使还有5G的存储空间,也无法创建新文件。

2.2 第二步:定位大文件和目录

确认磁盘状态后,接下来要找出是什么东西占用了这么多空间。这里有几个工具可以用,我按实用程度排序介绍。

2.2.1 du命令(基础款)

du是Linux自带的命令,不需要额外安装。最常用的用法:

# 查看当前目录下各子目录的大小,按大小排序
du -sh /* 2>/dev/null | sort -rh | head -20

输出示例:

45G     /var
32G     /home
15G     /opt
8.5G    /usr
2.1G    /root

发现/var目录占用最多,继续往下钻:

du -sh /var/* 2>/dev/null | sort -rh | head -10

输出:

38G     /var/log
4.2G    /var/lib
1.8G    /var/cache

再往下:

du -sh /var/log/* 2>/dev/null | sort -rh | head -10

输出:

25G     /var/log/nginx
8.5G    /var/log/app
3.2G    /var/log/syslog
1.1G    /var/log/auth.log

找到了!nginx的日志占了25G。

du命令的问题

du命令有个很大的缺点:慢。在大目录下执行du,可能要等几分钟才能出结果。而且它没有交互式界面,每次都要重新敲命令,效率很低。

2.2.2 ncdu(进阶款,强烈推荐)

ncdu是我日常用得最多的磁盘分析工具,全称是NCurses Disk Usage。它有一个交互式的终端界面,可以快速浏览目录结构,支持排序和删除操作。

安装方式:

# Ubuntu/Debian
apt install ncdu

# CentOS/RHEL
yum install ncdu

# Rocky/Alma Linux
dnf install ncdu

使用方法:

# 分析根目录
ncdu /

# 分析指定目录
ncdu /var/log

# 排除某些目录(比如挂载的网络文件系统)
ncdu / --exclude /mnt --exclude /media

执行后会进入一个交互式界面:

ncdu 1.18 ~ Use the arrow keys to navigate, press ? for help
--- /var/log -------------------------------------------------------
   25.1 GiB [##########] /nginx
    8.5 GiB [###       ] /app
    3.2 GiB [#         ]  syslog
    1.1 GiB [          ]  auth.log
  512.0 MiB [          ] /journal
  128.0 MiB [          ]  kern.log

 Total disk usage:  38.4 GiB  Apparent size:  38.2 GiB  Items: 15234

在这个界面里,你可以用方向键上下移动,按回车进入子目录,按d删除文件或目录(会有确认提示),按q退出。

ncdu还有个很实用的功能:可以先扫描再查看。在生产环境,我一般会先把扫描结果导出到文件,然后慢慢分析:

# 扫描并导出结果
ncdu -o /tmp/ncdu-root.json /

# 后续直接加载结果,秒开
ncdu -f /tmp/ncdu-root.json

2.2.3 dust(现代款)

dust是用Rust写的du替代品,速度快,输出美观。它会用树状图直观显示目录大小占比。

安装方式:

# Ubuntu 22.04+
apt install dust

# 其他系统用cargo安装
cargo install du-dust

# 或者下载预编译二进制
wget https://github.com/bootandy/dust/releases/download/v1.0.0/dust-v1.0.0-x86_64-unknown-linux-gnu.tar.gz
tar xzf dust-v1.0.0-x86_64-unknown-linux-gnu.tar.gz
mv dust /usr/local/bin/

使用示例:

dust -d 2 /var

输出:

 38G   ┌── log                     │                              ████████████ │  82%
 4.2G  │ ┌── lib                   │                              ██           │   9%
 1.8G  │ ├── cache                 │                              █            │   4%
 2.1G  ├─┴ var                     │                              ██           │   5%
 46G   ┴ .                         │████████████████████████████████████████████│ 100%

dust的优点是输出很直观,一眼就能看出哪个目录占比最大。但它不支持交互式操作,所以我一般是用dust快速定位,然后用ncdu深入分析。

2.2.4 三款工具对比总结

特性duncdudust
系统自带
扫描速度
交互界面
可视化 简单 树状图
删除功能
导出结果
推荐场景 简单查询 深度分析 快速定位

我的使用习惯是:紧急情况用du快速定位,日常巡检用ncdu,给领导汇报用dust(因为图好看)。

2.3 第三步:检查已删除但未释放的文件

这是一个非常容易被忽略的问题。有时候你明明删了很多文件,但df显示空间没变化。这时候就要检查是否有进程还在持有已删除文件的句柄。

用lsof命令查找:

lsof +L1 2>/dev/null | head -20

输出示例:

COMMAND     PID   USER   FD   TYPE DEVICE    SIZE/OFF NLINK  NODE NAME
java      12345   app   25w   REG  253,1  21474836480     0 12345 /var/log/app/debug.log (deleted)
nginx     23456   www   12w   REG  253,1   5368709120     0 23456 /var/log/nginx/access.log (deleted)

看到了吧,有两个进程还在写已删除的文件。java进程持有一个20G的debug.log,nginx持有一个5G的access.log。这25G的空间虽然文件被删了,但实际并没有释放。

解决方法有两种:

方法一:重启进程(推荐)

# 重启应用
systemctl restart app

# 重载nginx(不需要完全重启)
nginx -s reopen

方法二:清空文件而不是删除

如果不能重启进程,可以用truncate清空文件内容:

# 进程还在运行时,用这种方式清空日志
truncate -s 0 /var/log/app/debug.log

# 或者用这种方式
> /var/log/app/debug.log

# 或者
cat /dev/null > /var/log/app/debug.log

这三种写法效果一样,都是把文件内容清空但保留文件本身。这样进程的文件句柄依然有效,空间立即释放。

方法三:强制释放(黑科技,慎用)

如果实在不能重启进程,还有一个黑科技——通过/proc文件系统直接truncate文件描述符:

# 假设进程PID是12345,文件描述符是25
: > /proc/12345/fd/25

这个方法我只在紧急情况下用过几次。原理是直接操作进程的文件描述符,把对应的文件内容清空。但这种操作有风险,可能导致进程行为异常,所以要谨慎。

2.4 第四步:处理inode耗尽问题

如果df -i显示inode使用率很高,说明系统中存在大量小文件。这种情况通常是以下原因导致的:

  1. 邮件队列堆积(/var/spool/postfix)
  2. Session文件堆积(/var/lib/php/sessions)
  3. 缓存文件碎片化(/tmp, /var/cache)
  4. 容器层文件碎片(/var/lib/docker)

找出哪个目录的文件数量最多:

# 统计各目录的文件数量
for dir in /*; do
    echo -n "$dir: "
    find "$dir" -xdev -type f 2>/dev/null | wc -l
done | sort -t: -k2 -rn | head -10

输出示例:

/var: 1523456
/home: 234567
/usr: 123456
/opt: 45678

继续细查/var目录:

for dir in /var/*; do
    echo -n "$dir: "
    find "$dir" -xdev -type f 2>/dev/null | wc -l
done | sort -t: -k2 -rn | head -10

输出:

/var/spool: 1234567
/var/log: 156789
/var/lib: 89012

找到了,/var/spool目录下有120多万个文件。再往下查:

ls -la /var/spool/postfix/deferred/ | head -20

果然是邮件队列堆积了。清理方法:

# 查看邮件队列状态
postqueue -p

# 清空所有队列
postsuper -d ALL

# 如果是Sendmail
rm -rf /var/spool/mqueue/*

另一个常见的inode杀手是PHP session文件:

# 统计session文件数量
ls /var/lib/php/sessions | wc -l

# 清理过期的session
find /var/lib/php/sessions -type f -mtime +7 -delete

2.5 第五步:释放预留空间(救急用)

ext4文件系统默认预留5%的空间给root用户。对于大容量磁盘,这个比例可能太高了。比如1TB的磁盘,5%就是50GB,这个空间平时用不到有点浪费。

查看当前预留比例:

tune2fs -l /dev/sda1 | grep "Reserved block count"

修改预留比例:

# 将预留空间改为1%
tune2fs -m 1 /dev/sda1

# 或者直接设置预留的块数量
tune2fs -r 100000 /dev/sda1

注意:这个操作可以在文件系统挂载状态下执行,不需要卸载。但建议在业务低峰期操作。

对于XFS文件系统,预留空间的机制不太一样,一般不需要调整。

三、示例代码和配置

3.1 常见磁盘杀手处理

3.1.1 日志文件清理

日志文件是最常见的磁盘空间杀手。在清理之前,先确认哪些日志可以安全删除:

# 查找大于100MB的日志文件
find /var/log -type f -size +100M -exec ls -lh {} \; 2>/dev/null

# 查找超过7天的日志文件
find /var/log -type f -mtime +7 -name "*.log" -exec ls -lh {} \;

# 查找并删除超过30天的压缩日志
find /var/log -type f -mtime +30 -name "*.gz" -delete

对于正在被进程写入的日志,不要直接删除,用truncate清空:

# 清空nginx日志
truncate -s 0 /var/log/nginx/access.log
truncate -s 0 /var/log/nginx/error.log

# 通知nginx重新打开日志文件
nginx -s reopen

对于systemd journal日志:

# 查看journal占用空间
journalctl --disk-usage

# 只保留最近7天的日志
journalctl --vacuum-time=7d

# 只保留最近500MB的日志
journalctl --vacuum-size=500M

3.1.2 Docker镜像和容器清理

Docker是另一个磁盘空间大户。我见过很多服务器,/var/lib/docker目录占用上百GB。

# 查看Docker占用的磁盘空间
docker system df

# 输出示例:
TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          45        12        12.5GB    8.2GB (65%)
Containers      28        8         2.3GB     1.8GB (78%)
Local Volumes   15        8         45.6GB    23.4GB (51%)
Build Cache     0         0         0B        0B

清理命令:

# 删除所有停止的容器
docker container prune -f

# 删除所有未使用的镜像
docker image prune -a -f

# 删除所有未使用的volume(危险,可能丢数据)
docker volume prune -f

# 一键清理所有未使用的资源
docker system prune -a -f --volumes

# 删除指定时间之前创建的镜像
docker image prune -a -f --filter "until=720h"

对于Kubernetes环境,还要清理containerd的数据:

# 查看crictl占用空间(containerd)
crictl images
crictl ps -a

# 清理未使用的镜像
crictl rmi --prune

# 清理停止的容器
crictl rm $(crictl ps -a -q --state exited)

3.1.3 临时文件清理

临时目录如果长期不清理,也会堆积大量垃圾:

# 查看/tmp目录大小
du -sh /tmp

# 删除超过7天的临时文件
find /tmp -type f -atime +7 -delete

# 删除超过7天的空目录
find /tmp -type d -empty -mtime +7 -delete

# 清理pip缓存
pip cache purge
rm -rf ~/.cache/pip

# 清理npm缓存
npm cache clean --force

# 清理apt缓存
apt clean
apt autoclean

# 清理yum缓存
yum clean all
rm -rf /var/cache/yum

3.1.4 旧内核清理

升级系统后,旧内核文件会保留在/boot分区。如果/boot分区比较小,很容易被撑满:

# 查看已安装的内核
dpkg --list | grep linux-image   # Debian/Ubuntu
rpm -qa | grep kernel            # RHEL/CentOS

# 查看当前使用的内核
uname -r

# Ubuntu自动清理旧内核
apt autoremove --purge

# CentOS保留最近2个内核,删除其他
package-cleanup --oldkernels --count=2

3.2 logrotate配置详解

logrotate是Linux下管理日志文件的标准工具。配置得当可以避免90%的日志撑爆磁盘问题。

主配置文件:/etc/logrotate.conf

# 每周轮转一次
weekly

# 保留4份历史日志
rotate 4

# 轮转后创建新的日志文件
create

# 使用日期作为后缀
dateext

# 压缩历史日志
compress

# 延迟压缩,保留最近一份不压缩(便于查看)
delaycompress

# 日志为空时不轮转
notifempty

# 包含其他配置文件
include /etc/logrotate.d

为应用创建专门的轮转配置,以nginx为例:

# /etc/logrotate.d/nginx
/var/log/nginx/*.log {
    daily
    missingok
    rotate 14
    compress
    delaycompress
    notifempty
    create 0640 www-data adm
    sharedscripts
    postrotate
        [ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
    endscript
}

关键参数解释:

  • daily:每天轮转
  • rotate 14:保留14份历史
  • compress:gzip压缩
  • delaycompress:延迟一天再压缩
  • missingok:日志不存在也不报错
  • notifempty:空文件不轮转
  • create 0640 www-data adm:创建新文件的权限和属主
  • sharedscripts:多个日志文件共享postrotate脚本
  • postrotate/endscript:轮转后执行的命令

按大小轮转(适合增长不规律的日志):

# /etc/logrotate.d/app
/var/log/app/*.log {
    size 100M
    rotate 10
    compress
    missingok
    notifempty
    copytruncate
}

copytruncate参数很重要:它会先复制日志文件,然后清空原文件。这样即使应用没有处理SIGHUP信号的能力,也能正常轮转。缺点是在复制和清空之间可能丢失几行日志。

手动测试logrotate配置:

# 测试配置是否正确(不实际执行)
logrotate -d /etc/logrotate.d/nginx

# 强制执行轮转
logrotate -f /etc/logrotate.d/nginx

# 查看轮转状态
cat /var/lib/logrotate/status

3.3 tmpwatch/tmpreaper配置

tmpwatch(RHEL系)和tmpreaper(Debian系)用于自动清理临时文件。

CentOS/RHEL安装配置:

# 安装
yum install tmpwatch

# 配置cron任务(/etc/cron.daily/tmpwatch)
/usr/sbin/tmpwatch -umc 720 /tmp
/usr/sbin/tmpwatch -umc 720 /var/tmp

参数说明:

  • -u:基于atime(访问时间)判断
  • -m:基于mtime(修改时间)判断
  • -c:基于ctime(状态改变时间)判断
  • 720:小时数(30天)

Ubuntu/Debian安装配置:

# 安装
apt install tmpreaper

# 配置文件:/etc/tmpreaper.conf
TMPREAPER_TIME=7d
TMPREAPER_PROTECT_EXTRA=''
TMPREAPER_DIRS='/tmp/. /var/tmp/.'
TMPREAPER_DELAY='256'
TMPREAPER_ADDITIONALOPTIONS=''

systemd-tmpfiles(现代方案):

现代Linux发行版更推荐使用systemd-tmpfiles来管理临时文件:

# 查看当前配置
systemd-tmpfiles --cat-config

# 配置文件位置
/etc/tmpfiles.d/       # 管理员自定义配置
/usr/lib/tmpfiles.d/   # 软件包默认配置
/run/tmpfiles.d/       # 运行时配置

创建自定义清理规则:

# /etc/tmpfiles.d/cleanup.conf
# 类型 路径 模式 用户 用户组 生命周期 参数

# 清理超过10天的/tmp文件
d /tmp 1777 root root 10d

# 清理超过30天的/var/tmp文件
d /var/tmp 1777 root root 30d

# 清理超过7天的应用临时文件
D /var/app/tmp 0755 app app 7d

# 每次启动时清空指定目录
D /run/app 0755 app app -

手动执行清理:

# 清理临时文件
systemd-tmpfiles --clean

# 创建目录和文件
systemd-tmpfiles --create

3.4 完整的磁盘清理脚本

下面是我在生产环境使用的一个磁盘清理脚本,包含了常见的清理项目和安全检查:

#!/bin/bash
# disk_cleanup.sh - 生产环境磁盘清理脚本
# Author: SRE Team
# Version: 2.1
# Last Modified: 2025-01-05

set -euo pipefail

# 配置参数
LOG_RETAIN_DAYS=30
TMP_RETAIN_DAYS=7
DOCKER_IMAGE_AGE="720h"  # 30天
MIN_FREE_PERCENT=10
DRY_RUN=${DRY_RUN:-false}

# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'

log_info() {
    echo -e "${GREEN}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S') $1"
}

log_warn() {
    echo -e "${YELLOW}[WARN]${NC} $(date '+%Y-%m-%d %H:%M:%S') $1"
}

log_error() {
    echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S') $1"
}

# 获取磁盘使用率
get_disk_usage() {
    local mount_point=${1:-/}
    df -h "$mount_point" | awk 'NR==2 {gsub(/%/,""); print $5}'
}

# 获取可用空间(GB)
get_free_space() {
    local mount_point=${1:-/}
    df -BG "$mount_point" | awk 'NR==2 {gsub(/G/,""); print $4}'
}

# 清理前后对比
show_cleanup_result() {
    local before=$1
    local after=$2
    local item=$3
    local freed=$((before - after))
    if [ $freed -gt 0 ]; then
        log_info "$item: 释放了 ${freed}GB 空间"
    else
        log_info "$item: 无可清理空间"
    fi
}

# 清理系统日志
cleanup_system_logs() {
    log_info "开始清理系统日志..."
    local before=$(get_free_space)

    if [ "$DRY_RUN" = "true" ]; then
        log_info "[DRY-RUN] 将清理 $LOG_RETAIN_DAYS 天前的日志"
        find /var/log -type f -name "*.log" -mtime +$LOG_RETAIN_DAYS 2>/dev/null | head -20
        find /var/log -type f -name "*.gz" -mtime +$LOG_RETAIN_DAYS 2>/dev/null | head -20
    else
        # 清理压缩的旧日志
        find /var/log -type f -name "*.gz" -mtime +$LOG_RETAIN_DAYS -delete 2>/dev/null || true
        find /var/log -type f -name "*.old" -mtime +$LOG_RETAIN_DAYS -delete 2>/dev/null || true
        find /var/log -type f -name "*.[0-9]" -mtime +$LOG_RETAIN_DAYS -delete 2>/dev/null || true

        # 清理journal日志
        if command -v journalctl &>/dev/null; then
            journalctl --vacuum-time=${LOG_RETAIN_DAYS}d 2>/dev/null || true
        fi
    fi

    local after=$(get_free_space)
    show_cleanup_result $before $after "系统日志"
}

# 清理临时文件
cleanup_temp_files() {
    log_info "开始清理临时文件..."
    local before=$(get_free_space)

    if [ "$DRY_RUN" = "true" ]; then
        log_info "[DRY-RUN] 将清理 $TMP_RETAIN_DAYS 天前的临时文件"
        find /tmp -type f -atime +$TMP_RETAIN_DAYS 2>/dev/null | wc -l
        find /var/tmp -type f -atime +$TMP_RETAIN_DAYS 2>/dev/null | wc -l
    else
        find /tmp -type f -atime +$TMP_RETAIN_DAYS -delete 2>/dev/null || true
        find /var/tmp -type f -atime +$TMP_RETAIN_DAYS -delete 2>/dev/null || true
        find /tmp -type d -empty -delete 2>/dev/null || true
        find /var/tmp -type d -empty -delete 2>/dev/null || true
    fi

    local after=$(get_free_space)
    show_cleanup_result $before $after "临时文件"
}

# 清理包管理器缓存
cleanup_package_cache() {
    log_info "开始清理包管理器缓存..."
    local before=$(get_free_space)

    if [ "$DRY_RUN" = "true" ]; then
        log_info "[DRY-RUN] 将清理包管理器缓存"
    else
        # APT (Debian/Ubuntu)
        if command -v apt &>/dev/null; then
            apt clean 2>/dev/null || true
            apt autoclean 2>/dev/null || true
        fi

        # YUM/DNF (RHEL/CentOS)
        if command -v yum &>/dev/null; then
            yum clean all 2>/dev/null || true
        fi
        if command -v dnf &>/dev/null; then
            dnf clean all 2>/dev/null || true
        fi
    fi

    local after=$(get_free_space)
    show_cleanup_result $before $after "包管理器缓存"
}

# 清理Docker资源
cleanup_docker() {
    if ! command -v docker &>/dev/null; then
        log_info "Docker未安装,跳过"
        return
    fi

    log_info "开始清理Docker资源..."
    local before=$(get_free_space)

    if [ "$DRY_RUN" = "true" ]; then
        log_info "[DRY-RUN] Docker资源占用情况:"
        docker system df
    else
        # 清理停止的容器
        docker container prune -f 2>/dev/null || true

        # 清理悬空镜像
        docker image prune -f 2>/dev/null || true

        # 清理指定时间前的未使用镜像
        docker image prune -a -f --filter "until=$DOCKER_IMAGE_AGE" 2>/dev/null || true

        # 清理未使用的网络
        docker network prune -f 2>/dev/null || true

        # 清理构建缓存
        docker builder prune -f 2>/dev/null || true
    fi

    local after=$(get_free_space)
    show_cleanup_result $before $after "Docker资源"
}

# 检查已删除但未释放的文件
check_deleted_files() {
    log_info "检查已删除但未释放的文件..."

    local deleted_size=$(lsof +L1 2>/dev/null | awk '{sum+=$7} END {print int(sum/1024/1024/1024)}')

    if [ "$deleted_size" -gt 0 ]; then
        log_warn "发现 ${deleted_size}GB 已删除但未释放的文件:"
        lsof +L1 2>/dev/null | awk '$7 > 104857600 {print $1, $2, $7/1024/1024/1024"GB", $NF}'
        log_warn "建议重启相关进程以释放空间"
    else
        log_info "无已删除但未释放的文件"
    fi
}

# 主函数
main() {
    log_info "========== 磁盘清理开始 =========="
    log_info "当前磁盘使用率: $(get_disk_usage)%"
    log_info "当前可用空间: $(get_free_space)GB"

    if [ "$DRY_RUN" = "true" ]; then
        log_warn "当前为DRY-RUN模式,不会实际删除文件"
    fi

    cleanup_system_logs
    cleanup_temp_files
    cleanup_package_cache
    cleanup_docker
    check_deleted_files

    log_info "========== 磁盘清理完成 =========="
    log_info "清理后磁盘使用率: $(get_disk_usage)%"
    log_info "清理后可用空间: $(get_free_space)GB"
}

# 显示帮助信息
show_help() {
    echo "Usage: $0 [options]"
    echo ""
    echo "Options:"
    echo "  -d, --dry-run    模拟运行,不实际删除文件"
    echo "  -h, --help       显示帮助信息"
    echo ""
    echo "Environment Variables:"
    echo "  LOG_RETAIN_DAYS      日志保留天数 (默认: 30)"
    echo "  TMP_RETAIN_DAYS      临时文件保留天数 (默认: 7)"
    echo "  DOCKER_IMAGE_AGE     Docker镜像保留时间 (默认: 720h)"
}

# 解析参数
while [[ $# -gt 0 ]]; do
    case $1 in
        -d|--dry-run)
            DRY_RUN=true
            shift
            ;;
        -h|--help)
            show_help
            exit 0
            ;;
        *)
            log_error "未知参数: $1"
            show_help
            exit 1
            ;;
    esac
done

# 检查root权限
if [ "$EUID" -ne 0 ]; then
    log_error "请使用root权限运行此脚本"
    exit 1
fi

main

使用方法:

# 模拟运行
./disk_cleanup.sh --dry-run

# 实际执行
./disk_cleanup.sh

# 自定义参数
LOG_RETAIN_DAYS=7 TMP_RETAIN_DAYS=3 ./disk_cleanup.sh

四、最佳实践和注意事项

4.1 性能优化

4.1.1 分区规划最佳实践

一个好的分区规划可以从源头避免磁盘空间问题。我推荐的分区方案:

/           20-50GB    系统根分区,只装系统和核心软件
/boot       1GB        启动分区,放内核和引导文件
/var        50-100GB   日志和变动数据,根据业务量调整
/var/log    20-50GB    单独的日志分区,防止日志撑爆系统
/home       按需       用户数据
/data       剩余空间   业务数据
swap        内存的1-2倍 交换分区

把/var/log单独分区是一个很重要的经验。这样即使日志文件失控,也不会影响系统的正常运行。

4.1.2 日志级别控制

生产环境不要开DEBUG级别的日志!这是血的教训。

我曾经接手过一个项目,前任运维在生产环境开着DEBUG日志,每天产生50GB的日志。改成INFO级别后,日志量降到了每天200MB。

各应用的日志级别配置:

# Nginx: 注释掉access_log或者改成warn级别
access_log off;
error_log /var/log/nginx/error.log warn;

# Java应用(logback.xml)
<root level="INFO">
    <appender-ref ref="FILE"/>
</root>

# Python应用
logging.basicConfig(level=logging.INFO)

4.1.3 日志采样

对于高QPS的服务,可以考虑日志采样:

# Nginx: 只记录1%的access日志
map $request_uri $loggable {
    default 0;
    "~^/health" 0;  # 健康检查不记录
}

map $msec $sample {
    default 0;
    "~[0-9]\.01" 1;  # 1%采样
}

access_log /var/log/nginx/access.log combined if=$sample;

4.2 安全加固

4.2.1 设置磁盘配额

对于多用户系统,设置磁盘配额可以防止单个用户占用过多空间:

# 启用配额支持(编辑/etc/fstab)
# /dev/sda1 /home ext4 defaults,usrquota,grpquota 0 2

# 重新挂载
mount -o remount /home

# 创建配额文件
quotacheck -cum /home
quotaon /home

# 设置用户配额(软限制10GB,硬限制12GB)
setquota -u username 10485760 12582912 0 0 /home

# 查看配额使用情况
repquota -a

4.2.2 目录大小限制

使用tmpfs或者项目级配额限制特定目录的大小:

# 使用tmpfs限制/tmp大小为2GB
mount -t tmpfs -o size=2G tmpfs /tmp

# 写入fstab持久化
# tmpfs /tmp tmpfs size=2G,mode=1777 0 0

4.2.3 防止日志炸弹攻击

有些攻击会故意触发大量错误日志,试图撑满磁盘。防护措施:

# 限制单个日志文件大小(rsyslog配置)
$outchannel log_rotation,/var/log/messages,52428800,/var/log/rotate_script.sh
*.* :omfile:$log_rotation

# fail2ban防护日志爆破
[nginx-limit]
enabled = true
filter = nginx-limit-req
action = iptables-multiport[name=nginx-limit, port="http,https"]
logpath = /var/log/nginx/error.log
maxretry = 10
findtime = 60
bantime = 3600

4.3 常见错误及解决方案

错误1:rm删除大文件后空间不释放

原因:文件被进程占用

解决:

# 方法1:找到占用进程并重启
lsof +L1 | grep deleted
systemctl restart <service>

# 方法2:清空而不是删除
truncate -s 0 /path/to/file

# 方法3:通过/proc释放
: > /proc/<pid>/fd/<fd_number>

错误2:df和du显示的大小不一致

原因:已删除但未释放的文件、稀疏文件、预留空间

排查:

# 检查已删除文件
lsof +L1

# 检查预留空间
tune2fs -l /dev/sda1 | grep Reserved

# du和df的区别
# du统计的是文件实际占用的块大小
# df统计的是文件系统使用的总块数

错误3:空间充足但无法创建文件

原因:inode耗尽

排查:

df -i
# 如果IUse%接近100%,就是inode问题

# 找出哪个目录文件最多
find / -xdev -type f | cut -d "/" -f 2-3 | sort | uniq -c | sort -rn | head -10

错误4:logrotate不生效

常见原因和解决方案:

# 1. 配置文件语法错误
logrotate -d /etc/logrotate.d/nginx  # 用-d参数调试

# 2. 权限问题
ls -la /var/log/nginx/
# 确保logrotate有权限读取和写入

# 3. selinux阻止
audit2why < /var/log/audit/audit.log

# 4. 日志文件被锁定
lsattr /var/log/nginx/access.log
# 如果显示i属性,用chattr解除
chattr -i /var/log/nginx/access.log

五、故障排查和监控

5.1 日志查看

磁盘相关问题的排查,可以从以下日志入手:

# 系统日志(查找磁盘相关错误)
grep -i "disk\|space\|full\|no space" /var/log/syslog
grep -i "disk\|space\|full\|no space" /var/log/messages

# dmesg(内核级别的磁盘错误)
dmesg | grep -i "error\|fail\|disk\|sda"

# systemd journal
journalctl -p err -b | grep -i disk

5.2 实时监控命令

5.2.1 iostat查看磁盘IO

# 每2秒刷新一次,显示详细信息
iostat -x 2

# 输出示例:
Device  r/s   w/s  rkB/s  wkB/s  await  svctm  %util
sda     0.50  45.00  2.00  180.00  0.50   0.10   0.45
sdb     120.00 30.00 15360.00 3840.00 8.50  2.00   30.00

关键指标:

  • %util:磁盘利用率,超过80%需要关注
  • await:IO等待时间,超过10ms需要优化
  • r/s, w/s:每秒读写次数

5.2.2 iotop查看进程IO

# 安装
apt install iotop  # Debian/Ubuntu
yum install iotop  # CentOS/RHEL

# 使用
iotop -ao  # 只显示有IO的进程,累计模式

# 输出示例:
Total DISK READ: 15.00 M/s | Total DISK WRITE: 3.00 M/s
  PID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN     IO>    COMMAND
12345 be/4  mysql    14.50 M/s    2.80 M/s  0.00 %  15.00 %  mysqld
23456 be/4  app       0.50 M/s    0.20 M/s  0.00 %   2.00 %  java

5.3 Prometheus磁盘监控告警配置

这是我在生产环境使用的完整Prometheus监控配置:

5.3.1 Node Exporter配置

首先确保node_exporter正常运行,它会自动采集磁盘相关指标:

# 安装node_exporter
wget https://github.com/prometheus/node_exporter/releases/download/v1.7.0/node_exporter-1.7.0.linux-amd64.tar.gz
tar xzf node_exporter-1.7.0.linux-amd64.tar.gz
mv node_exporter-1.7.0.linux-amd64/node_exporter /usr/local/bin/

# 创建systemd服务
cat > /etc/systemd/system/node_exporter.service << 'EOF'
[Unit]
Description=Node Exporter
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/node_exporter \
    --collector.filesystem.mount-points-exclude="^/(sys|proc|dev|host|etc)($|/)" \
    --collector.diskstats.device-exclude="^(ram|loop|fd|dm-)\d+$"
Restart=always

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable --now node_exporter

5.3.2 Prometheus告警规则

# /etc/prometheus/rules/disk_alerts.yml
groups:
  - name: disk_alerts
    rules:
      # 磁盘空间告警
      - alert: DiskSpaceWarning
        expr: |
          (node_filesystem_avail_bytes{fstype=~"ext4|xfs|btrfs"}
          / node_filesystem_size_bytes{fstype=~"ext4|xfs|btrfs"}) * 100 < 20
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "磁盘空间不足警告 - {{ $labels.instance }}"
          description: "{{ $labels.mountpoint }} 可用空间低于20%,当前: {{ $value | printf \"%.1f\" }}%"

      - alert: DiskSpaceCritical
        expr: |
          (node_filesystem_avail_bytes{fstype=~"ext4|xfs|btrfs"}
          / node_filesystem_size_bytes{fstype=~"ext4|xfs|btrfs"}) * 100 < 10
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "磁盘空间严重不足 - {{ $labels.instance }}"
          description: "{{ $labels.mountpoint }} 可用空间低于10%,当前: {{ $value | printf \"%.1f\" }}%,需要立即处理!"

      - alert: DiskSpaceEmergency
        expr: |
          (node_filesystem_avail_bytes{fstype=~"ext4|xfs|btrfs"}
          / node_filesystem_size_bytes{fstype=~"ext4|xfs|btrfs"}) * 100 < 5
        for: 1m
        labels:
          severity: emergency
        annotations:
          summary: "磁盘空间即将耗尽 - {{ $labels.instance }}"
          description: "{{ $labels.mountpoint }} 可用空间低于5%,当前: {{ $value | printf \"%.1f\" }}%,紧急!"

      # inode告警
      - alert: InodeWarning
        expr: |
          (node_filesystem_files_free{fstype=~"ext4|xfs"}
          / node_filesystem_files{fstype=~"ext4|xfs"}) * 100 < 20
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "inode不足警告 - {{ $labels.instance }}"
          description: "{{ $labels.mountpoint }} 可用inode低于20%,当前: {{ $value | printf \"%.1f\" }}%"

      - alert: InodeCritical
        expr: |
          (node_filesystem_files_free{fstype=~"ext4|xfs"}
          / node_filesystem_files{fstype=~"ext4|xfs"}) * 100 < 10
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "inode严重不足 - {{ $labels.instance }}"
          description: "{{ $labels.mountpoint }} 可用inode低于10%,当前: {{ $value | printf \"%.1f\" }}%"

      # 磁盘空间增长预测
      - alert: DiskWillFillIn24Hours
        expr: |
          predict_linear(node_filesystem_avail_bytes{fstype=~"ext4|xfs|btrfs"}[6h], 24*3600) < 0
        for: 30m
        labels:
          severity: warning
        annotations:
          summary: "磁盘空间预计24小时内耗尽 - {{ $labels.instance }}"
          description: "{{ $labels.mountpoint }} 按当前增长速度,预计24小时内将耗尽空间"

      - alert: DiskWillFillIn4Hours
        expr: |
          predict_linear(node_filesystem_avail_bytes{fstype=~"ext4|xfs|btrfs"}[1h], 4*3600) < 0
        for: 10m
        labels:
          severity: critical
        annotations:
          summary: "磁盘空间预计4小时内耗尽 - {{ $labels.instance }}"
          description: "{{ $labels.mountpoint }} 按当前增长速度,预计4小时内将耗尽空间,需要立即处理!"

      # 磁盘IO告警
      - alert: DiskIOHigh
        expr: |
          rate(node_disk_io_time_seconds_total[5m]) * 100 > 80
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "磁盘IO过高 - {{ $labels.instance }}"
          description: "{{ $labels.device }} IO利用率超过80%,当前: {{ $value | printf \"%.1f\" }}%"

      # 磁盘读写延迟告警
      - alert: DiskLatencyHigh
        expr: |
          rate(node_disk_read_time_seconds_total[5m])
          / rate(node_disk_reads_completed_total[5m]) * 1000 > 20
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "磁盘读延迟过高 - {{ $labels.instance }}"
          description: "{{ $labels.device }} 平均读延迟超过20ms,当前: {{ $value | printf \"%.1f\" }}ms"

5.3.3 Alertmanager配置

# /etc/alertmanager/alertmanager.yml
global:
  resolve_timeout: 5m
  smtp_smarthost: 'smtp.company.com:587'
  smtp_from: 'alertmanager@company.com'
  smtp_auth_username: 'alertmanager@company.com'
  smtp_auth_password: 'password'

route:
  group_by: ['alertname', 'instance']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h
  receiver: 'default-receiver'
  routes:
    # 紧急告警立即发送
    - match:
        severity: emergency
      receiver: 'emergency-receiver'
      group_wait: 10s
      repeat_interval: 30m

    # 严重告警
    - match:
        severity: critical
      receiver: 'critical-receiver'
      group_wait: 30s
      repeat_interval: 1h

receivers:
  - name: 'default-receiver'
    email_configs:
      - to: 'ops-team@company.com'
    webhook_configs:
      - url: 'https://hooks.slack.com/services/xxx/yyy/zzz'

  - name: 'critical-receiver'
    email_configs:
      - to: 'ops-team@company.com,sre-oncall@company.com'
    webhook_configs:
      - url: 'https://hooks.slack.com/services/xxx/yyy/zzz'
      - url: 'https://api.pagerduty.com/generic/xxx'

  - name: 'emergency-receiver'
    email_configs:
      - to: 'ops-team@company.com,sre-oncall@company.com,manager@company.com'
    webhook_configs:
      - url: 'https://hooks.slack.com/services/xxx/yyy/zzz'
      - url: 'https://api.pagerduty.com/generic/xxx'
    # 电话告警(通过第三方服务)
    webhook_configs:
      - url: 'https://api.phonecall-service.com/alert'

5.3.4 Grafana Dashboard

创建一个磁盘监控的Dashboard,关键Panel配置:

{
  "panels": [
    {
      "title": "磁盘使用率",
      "type": "gauge",
      "targets": [
        {
          "expr": "(1 - node_filesystem_avail_bytes{mountpoint=\"/\"} / node_filesystem_size_bytes{mountpoint=\"/\"}) * 100",
          "legendFormat": "{{ mountpoint }}"
        }
      ],
      "fieldConfig": {
        "defaults": {
          "thresholds": {
            "steps": [
              {"color": "green", "value": null},
              {"color": "yellow", "value": 70},
              {"color": "orange", "value": 80},
              {"color": "red", "value": 90}
            ]
          },
          "max": 100,
          "min": 0,
          "unit": "percent"
        }
      }
    },
    {
      "title": "磁盘空间趋势(7天)",
      "type": "timeseries",
      "targets": [
        {
          "expr": "node_filesystem_avail_bytes{mountpoint=\"/\"} / 1024 / 1024 / 1024",
          "legendFormat": "可用空间 (GB)"
        },
        {
          "expr": "node_filesystem_size_bytes{mountpoint=\"/\"} / 1024 / 1024 / 1024",
          "legendFormat": "总空间 (GB)"
        }
      ]
    },
    {
      "title": "空间耗尽预测",
      "type": "stat",
      "targets": [
        {
          "expr": "(node_filesystem_avail_bytes{mountpoint=\"/\"}) / (rate(node_filesystem_avail_bytes{mountpoint=\"/\"}[6h]) * -1) / 3600",
          "legendFormat": "预计耗尽时间"
        }
      ],
      "fieldConfig": {
        "defaults": {
          "unit": "h",
          "thresholds": {
            "steps": [
              {"color": "red", "value": null},
              {"color": "orange", "value": 24},
              {"color": "yellow", "value": 72},
              {"color": "green", "value": 168}
            ]
          }
        }
      }
    },
    {
      "title": "Top 10 文件系统使用率",
      "type": "table",
      "targets": [
        {
          "expr": "topk(10, (1 - node_filesystem_avail_bytes / node_filesystem_size_bytes) * 100)",
          "format": "table"
        }
      ]
    }
  ]
}

5.4 自动化告警响应

配合告警,可以设置一些自动化响应动作。但要注意,自动清理需要非常谨慎,我一般只自动清理那些绝对安全的内容:

# Prometheus Alertmanager webhook -> 自动清理服务
# cleanup-service.py (简化示例)

from flask import Flask, request
import subprocess
import logging

app = Flask(__name__)
logging.basicConfig(level=logging.INFO)

# 安全的清理命令列表
SAFE_CLEANUP_COMMANDS = {
    'journal': 'journalctl --vacuum-time=3d',
    'apt_cache': 'apt clean',
    'docker_prune': 'docker container prune -f',
    'tmp_old': 'find /tmp -type f -atime +3 -delete',
}

@app.route('/webhook', methods=['POST'])
def handle_alert():
    data = request.json

    for alert in data.get('alerts', []):
        if alert['status'] == 'firing':
            mountpoint = alert['labels'].get('mountpoint', '/')
            severity = alert['labels'].get('severity', 'warning')

            logging.info(f"收到告警: {mountpoint}, 严重程度: {severity}")

            # 只有warning级别才自动清理,critical和emergency需要人工介入
            if severity == 'warning':
                for name, cmd in SAFE_CLEANUP_COMMANDS.items():
                    logging.info(f"执行清理: {name}")
                    try:
                        subprocess.run(cmd, shell=True, timeout=300)
                    except Exception as e:
                        logging.error(f"清理失败: {name}, 错误: {e}")

    return 'OK', 200

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

六、总结

6.1 要点回顾

写到这里,把整个磁盘空间排查的流程梳理一下:

  1. 快速确认:df -h 看空间,df -i 看inode
  2. 定位问题:ncdu交互式分析,找到空间大户
  3. 检查陷阱:lsof +L1 检查已删除但未释放的文件
  4. 对症下药
    • 日志文件:logrotate + truncate
    • 临时文件:定期清理脚本
    • Docker:docker system prune
    • inode耗尽:清理小文件
  5. 预防为主
    • 合理分区规划
    • 配置logrotate
    • 设置磁盘配额
    • 完善监控告警

记住一个原则:永远不要等到磁盘满了才去处理。设置好监控告警,在80%的时候就开始关注,90%的时候就要行动。

6.2 进阶方向

如果你想在磁盘管理方面更进一步,可以学习这些内容:

  1. LVM逻辑卷管理:支持在线扩容,更灵活的空间管理
  2. Ceph/GlusterFS分布式存储:解决单机存储容量限制
  3. 对象存储(S3/MinIO):日志和备份的归档存储方案
  4. Kubernetes存储:PV/PVC/StorageClass的管理
  5. ZFS/Btrfs:高级文件系统特性,如快照、压缩、去重

6.3 参考资料

  • Linux Filesystem Hierarchy Standard: https://refspecs.linuxfoundation.org/FHS_3.0/fhs/index.html
  • Prometheus Node Exporter: https://github.com/prometheus/node_exporter
  • ncdu官方文档: https://dev.yorhel.nl/ncdu
  • logrotate手册: man logrotate
  • systemd-tmpfiles手册: man tmpfiles.d

附录

附录A:命令速查表

# ========== 磁盘空间检查 ==========
df -h                          # 查看磁盘使用情况
df -i                          # 查看inode使用情况
df -Th                         # 显示文件系统类型
lsblk                          # 列出块设备
fdisk -l                       # 列出分区信息

# ========== 空间分析 ==========
du -sh /path                   # 目录总大小
du -sh /* | sort -rh | head    # 根目录下各目录大小排序
ncdu /                         # 交互式空间分析
dust -d 2 /                    # 树状图展示

# ========== 文件查找 ==========
find / -type f -size +100M     # 查找大于100MB的文件
find / -type f -mtime +30      # 查找30天前修改的文件
find / -type f -atime +30      # 查找30天未访问的文件
find /tmp -type f -delete      # 删除/tmp下所有文件

# ========== 进程相关 ==========
lsof +L1                       # 已删除但未释放的文件
lsof /var/log/syslog          # 查看文件被哪些进程使用
fuser -v /var/log/syslog      # 同上

# ========== 清理命令 ==========
truncate -s 0 /path/file      # 清空文件内容
> /path/file                   # 清空文件(另一种写法)
journalctl --vacuum-size=500M  # 清理journal日志
docker system prune -a -f      # 清理Docker资源
apt clean                      # 清理APT缓存
yum clean all                  # 清理YUM缓存

# ========== 文件系统操作 ==========
tune2fs -l /dev/sda1          # 查看ext文件系统信息
tune2fs -m 1 /dev/sda1        # 修改预留空间比例
xfs_info /dev/sda1            # 查看XFS文件系统信息
resize2fs /dev/sda1           # 调整ext文件系统大小
xfs_growfs /mountpoint        # 扩展XFS文件系统

# ========== 配额管理 ==========
quotacheck -cum /home          # 创建配额数据库
quotaon /home                  # 启用配额
setquota -u user 10G 12G 0 0 /home  # 设置用户配额
repquota -a                    # 查看所有配额使用情况

# ========== IO监控 ==========
iostat -x 2                    # 磁盘IO统计
iotop -ao                      # 进程IO监控
pidstat -d 1                   # 进程磁盘统计

附录B:关键参数详解

df命令参数

参数说明
-h 人类可读格式(KB/MB/GB)
-i 显示inode信息
-T 显示文件系统类型
-a 显示所有文件系统
-l 只显示本地文件系统
-x TYPE 排除指定类型的文件系统

du命令参数

参数说明
-s 只显示总计
-h 人类可读格式
-a 显示所有文件(不只是目录)
-c 显示总计
-d N 限制递归深度
--exclude 排除指定模式
-x 不跨越文件系统

find命令常用参数

参数说明
-type f/d 文件/目录
-size +100M 大于100MB
-mtime +7 修改时间超过7天
-atime +7 访问时间超过7天
-ctime +7 状态改变时间超过7天
-delete 删除找到的文件
-exec CMD {} ; 对每个文件执行命令
-xdev 不跨越文件系统

logrotate配置参数

参数说明
daily/weekly/monthly 轮转周期
rotate N 保留N份历史
size SIZE 按大小轮转
compress 压缩历史日志
delaycompress 延迟一个周期再压缩
missingok 日志不存在不报错
notifempty 空文件不轮转
create MODE OWNER GROUP 创建新文件的属性
copytruncate 复制后清空(适合不支持重开日志的程序)
postrotate/endscript 轮转后执行的脚本

附录C:术语表

术语英文解释
inode Index Node 存储文件元数据的数据结构,包含权限、时间戳等,不包含文件名和内容
文件描述符 File Descriptor 进程用来访问文件的整数索引
挂载点 Mount Point 文件系统附加到目录树的位置
分区 Partition 磁盘的逻辑划分
块设备 Block Device 以固定大小块为单位进行IO的设备
稀疏文件 Sparse File 实际占用空间小于逻辑大小的文件
硬链接 Hard Link 指向同一inode的多个目录项
软链接 Symbolic Link 指向另一个文件路径的特殊文件
预留空间 Reserved Blocks 文件系统预留给root用户的空间
LVM Logical Volume Manager 逻辑卷管理器,提供灵活的磁盘管理
RAID Redundant Array of Independent Disks 磁盘阵列,提供冗余和性能
文件系统 File System 组织和管理存储设备上数据的方法,如ext4、xfs
日志轮转 Log Rotation 定期归档和清理日志文件的过程

 

最后送大家一句话:磁盘空间就像房间,不定期清理,早晚会堆满杂物。养成良好的运维习惯,比什么都重要。

 

posted on 2026-01-23 15:49  我和你并没有不同  阅读(3)  评论(0)    收藏  举报