Linux磁盘空间告警?三步快速定位
背景与目的
磁盘空间告警是运维工作中最常见的告警类型之一。当磁盘空间耗尽时,应用程序无法写入日志、数据库无法正常提交、容器无法创建新镜像,甚至系统日志写入失败会导致难以诊断的连锁故障。本文从实际运维经验出发,提供一套完整的磁盘空间问题定位和解决流程。
前置知识:本文假设你已经掌握Linux基本命令和文件目录操作。了解常见的Linux发行版(CentOS/RHEL/Rocky/Ubuntu等)。
环境说明:本文示例基于Rocky Linux 9.4(等同于RHEL 9.4),文件系统为XFS,工具版本:df(GNU coreutils 9.1)、du(GNU findutils 4.9)、ncdu(1.17)。
1. 磁盘告警的常见原因
1.1 日志文件暴涨
这是最常见的磁盘空间告警原因。应用程序日志、Web访问日志、数据库慢查询日志等,在高并发或异常情况下可能快速增长。
典型场景:
应用程序debug模式未关闭,日志量暴增10倍
错误循环导致重复写日志
日志轮转配置缺失或失效
大流量访问产生海量访问日志
表现特征:
/var/log/messages 单文件超过GB级别 /var/log/nginx/access.log 单文件超过10GB /var/lib/mysql/slow-query.log 持续增长
1.2 临时文件和缓存未清理
系统运行过程中产生的临时文件和缓存,如果未及时清理会占用大量空间。
典型场景:
/tmp 目录积累大量临时文件
包管理器缓存(yum/dnf/apt)未清理
编译产生的中间文件未删除
容器镜像/构建缓存未清理
表现特征:
/var/cache 目录占用数十GB /tmp 下有大量一个月前的文件 docker system df 显示异常大的空间占用
1.3 数据文件自然增长
数据库、KV存储、日志聚合系统等,数据会随时间自然增长。
典型场景:
MySQL InnoDB表空间持续增长
PostgreSQL数据目录膨胀
Elasticsearch索引无限增长
MongoDB数据文件未启用自动压缩
1.4 隐藏的大文件
某些场景下,大文件可能被删除但进程仍持有文件句柄,导致磁盘空间无法释放。
典型场景:
日志文件被删除但进程未重启
临时文件创建后删除但进程仍写入
镜像文件传输中断产生空洞文件
表现特征:
df显示已满,但du显示使用量很少 文件明明删除了但空间不释放 重启进程后空间释放
1.5 inode耗尽
虽然少见,但inode耗尽也会导致"磁盘满"的问题。inode用于存储文件元数据,每个文件至少占用一个inode。
典型场景:
小文件过多(大量缓存文件、邮件队列)
文件系统创建时inode数量配置不足
2. 第一步:快速定位
当收到磁盘告警时,第一步是快速判断是哪个分区、哪个挂载点出现了问题。
2.1 df命令基础用法
df是磁盘空间查看的基础命令,必须掌握。
# 查看文件系统使用情况(人类可读格式)
$ df -h
Filesystem Size Used Avail Use% Mounted on
devtmpfs 16G 0 16G 0% /dev
tmpfs 16G 0 16G 0% /dev/shm
tmpfs 16G 1.2G 15G 8% /run
/dev/sda1 100G 45G 55G 45% /
/dev/sda2 200G 180G 20G 90% /data
/dev/sdb1 500G 450G 50G 90% /var/lib/mysql
# 查看inode使用情况
$ df -i
Filesystem Inode IUsed IFree IUse% Mounted on
/dev/sda1 524288 123456 400832 23% /
/dev/sda2 1048576 523456 525120 50% /data
# 显示文件系统类型
$ df -hT
Filesystem Type Size Used Avail Use% Mounted on
/dev/sda1 xfs 100G 45G 55G 45% /
/dev/sda2 ext4 200G 180G 20G 90% /data
# 排除伪文件系统,只看真实磁盘
$ df -h --output=source,size,used,avail,pcent,target
| grep -E '^/dev|^/mnt|^/vol'
# 查找使用率超过80%的分区
$ df -h | awk 'NR>1 && $5+0 > 80 {print}'
/dev/sda2 200G 180G 20G 90% /data
2.2 快速定位脚本
#!/bin/bash
# script: quick_disk_check.sh
# 用途:收到告警后30秒内定位问题
echo "=== 磁盘空间快速检查 ==="
echo "检查时间:$(date)"
echo ""
# 1. 找出使用率超过80%的分区
echo "【1】使用率超过80%的分区:"
df -h | awk 'NR>1 && $5+0 > 80 {
printf " %s: %s/%s (使用率%s)
", $1, $3, $2, $5
}'
echo ""
# 2. 如果是/分区告警,检查是否boot分区问题
echo "【2】各分区使用率概览:"
df -h | awk 'NR>1 {
printf " %-30s %6s 使用率: %s
", $6, $2, $5
}'
echo ""
# 3. 检查inode
echo "【3】Inode使用情况:"
df -i | awk 'NR>1 && $5+0 > 80 {
printf " 警告:%s inode使用率%s
", $6, $5
}'
echo ""
# 4. 检查最近磁盘增长趋势(需要历史数据支持)
echo "【4】近期活跃目录(按修改时间):"
find / -maxdepth 3 -type d -mtime -1 2>/dev/null | head -20
echo ""
echo "=== 快速检查完成 ==="
2.3 du命令基础用法
du用于查看目录或文件的磁盘使用量,是精确定位的核心工具。
# 查看当前目录各子目录大小 $ du -sh * # 查看指定目录大小(人类可读) $ du -sh /var/log # 查看子目录大小并排序(人类可读) $ du -h /var | sort -rh | head -20 # 深度2层子目录大小 $ du -h --max-depth=2 /var # 只显示超过100M的目录 $ du -h --threshold=100M /var # 显示磁盘使用总量(不显示子目录) $ du -sh /var/log # 排除某些挂载点 $ du -sh --exclude=/proc --exclude=/sys / # du结果导出 $ du -h / 2>/dev/null | sort -rh > /tmp/du_result.txt
2.4 ncdu交互式工具
ncdu是du的增强版,提供交互式界面,特别适合快速定位大目录。
# 安装ncdu dnf install ncdu -y # CentOS/RHEL/Rocky apt install ncdu -y # Ubuntu/Debian # 使用ncdu分析目录 ncdu -x / # -x 不跨文件系统,跟随挂载点 ncdu -q / # q退出模式,适合脚本调用 # 常用快捷键(在ncdu界面中) # 上下箭头:选择目录 # 回车:进入目录 # d:删除选中文件/目录 # n:按名称排序 # s:按大小排序 # t:显示目录优先 # g:显示百分比 # q:退出
2.5 快速定位大文件
#!/bin/bash
# script: find_large_files.sh
# 用途:快速找到系统中的大文件
echo "=== 大文件扫描开始 ==="
echo "扫描时间:$(date)"
echo ""
# 1. 查找超过1GB的文件
echo "【1】超过1GB的文件:"
find / -type f -size +1G -exec ls -lh {} ; 2>/dev/null |
awk '{print $5, $9}'
echo ""
# 2. 查找超过100MB的文件(限制数量)
echo "【2】超过100MB的文件(前50个):"
find / -type f -size +100M 2>/dev/null |
head -50 | xargs -I{} ls -lh {} 2>/dev/null |
awk '{print $5, $9}'
echo ""
# 3. 查找最大的10个目录
echo "【3】系统中最大的10个目录:"
du -Sh / 2>/dev/null | sort -rh | head -10
echo ""
# 4. 检查特定目录
for dir in /var /home /tmp /opt /usr; do
if [ -d "$dir" ]; then
echo "【4.1】${dir} 目录大小:$(du -sh $dir 2>/dev/null | cut -f1)"
fi
done
3. 第二步:深度分析
定位到问题分区后,第二步是深度分析,找出占用空间的具体文件和目录。
3.1 分析日志文件
#!/bin/bash
# script: analyze_logs.sh
# 用途:分析/var/log目录,定位占用空间大的日志
echo "=== 日志目录分析 ==="
echo ""
# 列出/var/log下所有目录和文件大小
echo "【1】/var/log 各子目录大小:"
du -sh /var/log/* 2>/dev/null | sort -rh | head -15
echo ""
# 找出超过100MB的日志文件
echo "【2】超过100MB的日志文件:"
find /var/log -type f -size +100M 2>/dev/null | while read f; do
size=$(ls -lh "$f" | awk '{print $5}')
modified=$(stat -c %y "$f" | cut -d' ' -f1)
echo " $size $modified $f"
done
echo ""
# 统计日志文件增长趋势(需要对比)
echo "【3】日志文件详情:"
find /var/log -type f -exec ls -lh {} ; 2>/dev/null |
awk '{print $5, $6, $7, $9}' | sort -rh | head -20
echo ""
# 检查日志轮转配置
echo "【4】日志轮转配置状态:"
for logfile in /var/log/messages /var/log/secure /var/log/nginx/*.log; do
if [ -f "$logfile" ]; then
echo " $logfile:"
ls -lh "${logfile}"* 2>/dev/null | head -5
echo ""
fi
done
3.2 分析数据库数据目录
#!/bin/bash
# script: analyze_db_space.sh
# 用途:分析数据库目录占用情况
echo "=== 数据库空间分析 ==="
echo ""
# MySQL数据目录
MYSQL_DATA="/var/lib/mysql"
if [ -d "$MYSQL_DATA" ]; then
echo "【1】MySQL数据目录:${MYSQL_DATA}"
echo " 总大小:$(du -sh $MYSQL_DATA 2>/dev/null | cut -f1)"
# 按表大小排序
echo " 按表大小排序(前10):"
find "$MYSQL_DATA" -name "*.ibd" -exec du -h {} ; 2>/dev/null |
sort -rh | head -10 | while read size file; do
echo " $size $(basename $file)"
done
# 检查InnoDB表空间
echo " InnoDB表空间文件:"
ls -lh "$MYSQL_DATA"/ibdata* 2>/dev/null
echo ""
fi
# PostgreSQL数据目录
POSTGRES_DATA="/var/lib/pgsql/data"
if [ -d "$POSTGRES_DATA" ]; then
echo "【2】PostgreSQL数据目录:${POSTGRES_DATA}"
echo " 总大小:$(du -sh $POSTGRES_DATA 2>/dev/null | cut -f1)"
echo " Base目录内容:"
du -sh "$POSTGRES_DATA/base" 2>/dev/null
echo " WAL目录:"
du -sh "$POSTGRES_DATA/pg_wal" 2>/dev/null
echo ""
fi
# MongoDB数据目录
MONGO_DATA="/var/lib/mongodb"
if [ -d "$MONGO_DATA" ]; then
echo "【3】MongoDB数据目录:${MONGO_DATA}"
echo " 总大小:$(du -sh $MONGO_DATA 2>/dev/null | cut -f1)"
ls -lh "$MONGO_DATA"/*.wt 2>/dev/null | head -10
echo ""
fi
3.3 分析临时文件和缓存
#!/bin/bash
# script: analyze_temp_cache.sh
# 用途:分析临时文件和缓存占用
echo "=== 临时文件和缓存分析 ==="
echo ""
# /tmp目录
echo "【1】/tmp 目录分析:"
tmp_count=$(find /tmp -type f 2>/dev/null | wc -l)
tmp_size=$(du -sh /tmp 2>/dev/null | cut -f1)
echo " 文件数量:$tmp_count"
echo " 总大小:$tmp_size"
echo " /tmp 前10大文件:"
find /tmp -type f -exec du -h {} ; 2>/dev/null |
sort -rh | head -10 | while read size file; do
age=$(stat -c %y "$file" 2>/dev/null | cut -d' ' -f1)
echo " $size $age $(basename "$file")"
done
echo ""
# /var/tmp目录
if [ -d /var/tmp ]; then
echo "【2】/var/tmp 目录分析:"
echo " 总大小:$(du -sh /var/tmp 2>/dev/null | cut -f1)"
echo " 前10大文件:"
find /var/tmp -type f -exec du -h {} ; 2>/dev/null |
sort -rh | head -10
echo ""
fi
# 包管理器缓存
echo "【3】包管理器缓存:"
# DNF/YUM缓存
if [ -d /var/cache/dnf ]; then
echo " DNF缓存大小:$(du -sh /var/cache/dnf 2>/dev/null | cut -f1)"
echo " DNF缓存包数量:$(find /var/cache/dnf -name "*.rpm" 2>/dev/null | wc -l)"
fi
# APT缓存
if [ -d /var/cache/apt ]; then
echo " APT缓存大小:$(du -sh /var/cache/apt 2>/dev/null | cut -f1)"
fi
# Yarn缓存
if [ -d /home/*/.cache/yarn ]; then
echo " Yarn缓存总大小:$(du -sh /home/*/.cache/yarn 2>/dev/null | cut -f1)"
fi
# npm缓存
if [ -d /home/*/.npm ]; then
echo " npm缓存总大小:$(du -sh /home/*/.npm 2>/dev/null | cut -f1)"
fi
echo ""
# Docker相关
if command -v docker &> /dev/null; then
echo "【4】Docker空间占用:"
docker system df 2>/dev/null || echo " Docker命令执行失败"
echo ""
fi
# 旧内核
echo "【5】旧内核占用:"
if [ -d /boot ]; then
echo " /boot总大小:$(du -sh /boot | cut -f1)"
echo " 已安装内核版本:"
ls /boot/vmlinuz-* 2>/dev/null | sed 's/.*vmlinuz-//' | while read ver; do
size=$(ls -lh /boot/vmlinuz-$ver 2>/dev/null | awk '{print $5}')
echo " $ver ($size)"
done
fi
3.4 分析隐藏的大文件
#!/bin/bash
# script: find_deleted_but_open.sh
# 用途:查找已删除但仍被进程占用的文件
echo "=== 已删除但未释放空间的文件 ==="
echo ""
# 遍历所有进程,查找已删除但仍打开的文件
for pid in $(ls /proc | grep -E '^[0-9]+$'); do
if [ -d "/proc/$pid/fd" ]; then
# 查找符号链接指向(deleted)的文件描述符
for fd in /proc/$pid/fd/*; do
target=$(readlink "$fd" 2>/dev/null)
if [[ "$target" == *"(deleted)"* ]]; then
size=$(ls -lh "$fd" 2>/dev/null | awk '{print $5}')
cmd=$(cat /proc/$pid/cmdline 2>/dev/null | tr '�' ' ' | head -c 50)
echo "PID: $pid | CMD: $cmd"
echo " FD: $fd -> $target | Size: $size"
fi
done
fi
done | head -50
echo ""
echo "说明:上述文件已删除但进程仍在使用,重启相关进程可释放空间"
3.5 分析容器和镜像
#!/bin/bash
# script: analyze_container_space.sh
# 用途:分析容器和镜像占用的磁盘空间
if ! command -v docker &> /dev/null; then
echo "Docker未安装或未运行"
exit 0
fi
echo "=== Docker空间占用分析 ==="
echo ""
# 总体空间占用
echo "【1】Docker总体空间:"
docker system df
echo ""
# 详细分类
echo "【2】空间占用明细:"
docker system df -v 2>/dev/null | head -50
echo ""
# 悬空镜像( dangling)
echo "【3】悬空镜像(无标签):"
dangling=$(docker images -f "dangling=true" -q)
if [ -n "$dangling" ]; then
echo " 悬空镜像数量:$(echo $dangling | wc -w)"
docker images -f "dangling=true"
else
echo " 无悬空镜像"
fi
echo ""
# 停止的容器
echo "【4】停止的容器:"
stopped=$(docker ps -a -f status=exited -q)
if [ -n "$stopped" ]; then
echo " 停止容器数量:$(echo $stopped | wc -w)"
docker ps -a -f status=exited --format "{{.ID}} {{.Names}} {{.Status}}"
else
echo " 无停止的容器"
fi
echo ""
# 各镜像大小
echo "【5】镜像大小排序(前10):"
docker images --format "{{.Repository}}:{{.Tag}} {{.Size}}" |
sort -t$' ' -k2 -rh | head -10
echo ""
echo "清理命令:"
echo " docker system prune -a # 删除所有未使用的镜像和容器"
echo " docker container prune # 删除停止的容器"
echo " docker image prune # 删除悬空镜像"
echo " docker volume prune # 删除未使用的卷"
4. 第三步:清理与预警
定位问题后,第三步是执行清理操作并建立预警机制。
4.1 日志清理
手动清理日志:
#!/bin/bash
# script: clean_old_logs.sh
# 用途:清理超过指定天数的日志文件
MAX_DAYS=30
LOG_DIRS="/var/log /opt/*/logs"
echo "=== 日志清理脚本 ==="
echo "清理超过${MAX_DAYS}天的日志..."
echo ""
count=0
total_size=0
for logdir in $LOG_DIRS; do
if [ -d "$logdir" ]; then
# 查找符合条件的日志文件
find "$logdir" -type f -name "*.log" -mtime +$MAX_DAYS 2>/dev/null | while read logfile; do
size=$(du -k "$logfile" | cut -f1)
total_size=$((total_size + size))
rm -f "$logfile"
count=$((count + 1))
echo " Deleted: $logfile (${size}KB)"
done
# 清理gz压缩包(保留180天)
find "$logdir" -type f -name "*.gz" -mtime +180 2>/dev/null | while read gzfile; do
size=$(du -k "$gzfile" | cut -f1)
total_size=$((total_size + size))
rm -f "$gzfile"
count=$((count + 1))
echo " Deleted: $gzfile (${size}KB)"
done
fi
done
echo ""
echo "清理完成:删除${count}个文件,释放约$((total_size/1024))MB"
清空当前日志文件(保留文件):
# 方法1:使用truncate truncate -s 0 /var/log/messages # 方法2:使用重定向 > /var/log/messages # 方法3:使用dd(对于需要保留文件句柄的场景) dd if=/dev/null of=/var/log/messages # 验证 ls -lh /var/log/messages
4.2 配置日志轮转
# /etc/logrotate.d/nginx 示例配置
/var/log/nginx/*.log {
daily # 每天轮转
missingok # 忽略文件不存在错误
rotate 14 # 保留14个轮转文件
compress # 压缩旧日志
delaycompress # 延迟压缩(保留最近一个不压缩)
notifempty # 空日志不轮转
create 0640 nginx adm # 创建新文件权限
sharedscripts # 脚本只执行一次
postrotate
if [ -f /var/run/nginx.pid ]; then
kill -USR1 `cat /var/run/nginx.pid`
fi
endscript
}
# /etc/logrotate.d/mysql 示例配置
/var/log/mysql/slow.log {
daily
rotate 7
compress
delaycompress
missingok
notifempty
create 0600 mysql mysql
}
/var/log/mysql/error.log {
daily
rotate 14
compress
delaycompress
missingok
notifempty
create 0600 mysql mysql
}
4.3 清理临时文件
#!/bin/bash # script: clean_temp_files.sh # 用途:清理临时文件和缓存 echo "=== 临时文件清理 ===" echo "开始时间:$(date)" echo "" # 清理/tmp下超过7天的文件 echo "【1】清理/tmp下7天前的文件:" tmp_deleted=$(find /tmp -type f -atime +7 2>/dev/null | wc -l) find /tmp -type f -atime +7 -delete 2>/dev/null echo " 删除文件数:$tmp_deleted" # 清理/var/tmp下超过30天的文件 echo "【2】清理/var/tmp下30天前的文件:" vartmp_deleted=$(find /var/tmp -type f -atime +30 2>/dev/null | wc -l) find /var/tmp -type f -atime +30 -delete 2>/dev/null echo " 删除文件数:$vartmp_deleted" # 清理dnf缓存 echo "【3】清理DNF缓存:" dnf clean all echo " DNF缓存已清理" # 清理旧内核 echo "【4】清理旧内核:" if command -v yum &> /dev/null; then # 列出已安装的内核 current_kernel=$(uname -r) installed_kernels=$(rpm -q kernel | wc -l) if [ "$installed_kernels" -gt 1 ]; then # 保留当前内核和最新的一个旧内核 rpm -q kernel | tail -n +2 | head -n -1 | while read kernel; do echo " 移除内核:$kernel" dnf remove -y "$kernel" 2>/dev/null || true done fi fi # 清理孤儿包 echo "【5】清理孤儿包:" dnf autoremove -y 2>/dev/null || true echo "" echo "清理完成:$(date)" echo "当前磁盘使用情况:" df -h /
4.4 清理Docker空间
#!/bin/bash # script: clean_docker_space.sh # 用途:清理Docker占用的磁盘空间 echo "=== Docker空间清理 ===" echo "" # 查看当前占用 echo "【1】清理前Docker空间:" docker system df echo "" # 1. 删除停止的容器 echo "【2】删除停止的容器:" stopped_count=$(docker ps -a -f status=exited -q | wc -l) if [ "$stopped_count" -gt 0 ]; then docker container prune -f echo " 已删除 $stopped_count 个停止的容器" else echo " 无停止的容器" fi echo "" # 2. 删除悬空镜像 echo "【3】删除悬空镜像:" dangling_count=$(docker images -f "dangling=true" -q | wc -l) if [ "$dangling_count" -gt 0 ]; then docker image prune -f echo " 已删除 $dangling_count 个悬空镜像" else echo " 无悬空镜像" fi echo "" # 3. 删除未使用的卷 echo "【4】删除未使用的卷:" unused_volumes=$(docker volume ls -f dangling=true -q | wc -l) if [ "$unused_volumes" -gt 0 ]; then docker volume prune -f echo " 已删除 $unused_volumes 个未使用的卷" else echo " 无未使用的卷" fi echo "" # 4. 删除build缓存(可选,会删除所有build缓存) read -p "是否删除所有build缓存?(y/N): " confirm if [ "$confirm" = "y" ]; then docker builder prune -f echo " 已删除build缓存" fi echo "" # 5. 全面清理(可选) read -p "是否执行全面清理(删除所有未使用的对象)?(y/N): " full_clean if [ "$full_clean" = "y" ]; then docker system prune -a -f --volumes echo " 已执行全面清理" fi echo "" echo "【5】清理后Docker空间:" docker system df
4.5 建立磁盘监控预警
#!/bin/bash
# script: disk_alert_monitor.sh
# 用途:监控磁盘使用率,超过阈值时发送告警
# 配置
WARNING_THRESHOLD=80
CRITICAL_THRESHOLD=90
ALERT_LOG="/var/log/disk_alert.log"
EMAIL_TO="ops@example.com"
# 获取磁盘使用信息
check_disk() {
local mount_point="$1"
local usage=$(df "$mount_point" | awk 'NR==2 {print $5}' | sed 's/%//')
local device=$(df "$mount_point" | awk 'NR==2 {print $1}')
local size=$(df -h "$mount_point" | awk 'NR==2 {print $2}')
local used=$(df -h "$mount_point" | awk 'NR==2 {print $3}')
local avail=$(df -h "$mount_point" | awk 'NR==2 {print $4}')
echo "$usage|$device|$size|$used|$avail"
}
# 发送告警
send_alert() {
local level="$1"
local message="$2"
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $message" >> "$ALERT_LOG"
# 邮件告警(需要mailx)
if command -v mailx &> /dev/null; then
echo "$message" | mailx -s "[$level] Disk Alert on $(hostname)" "$EMAIL_TO"
fi
}
# 主监控逻辑
echo "=== 磁盘监控检查 ==="
echo "检查时间:$(date)"
echo ""
CHECK_MOUNTS="/ /data /var /home /boot"
for mount_point in $CHECK_MOUNTS; do
if [ -d "$mount_point" ]; then
info=$(check_disk "$mount_point")
usage=$(echo "$info" | cut -d'|' -f1)
device=$(echo "$info" | cut -d'|' -f2)
size=$(echo "$info" | cut -d'|' -f3)
used=$(echo "$info" | cut -d'|' -f4)
avail=$(echo "$info" | cut -d'|' -f5)
echo "$mount_point ($device): $usage% used ($used/$size, 可用$avail)"
if [ "$usage" -ge "$CRITICAL_THRESHOLD" ]; then
send_alert "CRITICAL" "磁盘使用率危急!${mount_point}使用率${usage}%,设备${device},可用空间${avail}"
elif [ "$usage" -ge "$WARNING_THRESHOLD" ]; then
send_alert "WARNING" "磁盘使用率告警!${mount_point}使用率${usage}%,设备${device},可用空间${avail}"
fi
fi
done
4.6 自动化清理脚本
#!/bin/bash
# script: automated_disk_cleanup.sh
# 用途:自动化磁盘空间维护脚本,建议通过cron定期执行
# 配置
MAX_LOG_AGE_DAYS=30
MAX_TMP_AGE_DAYS=7
DOCKER_PRUNE_WEEKLY=true
LOG_DIR="/var/log/cleanup"
# 创建日志目录
mkdir -p "$LOG_DIR"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_DIR/cleanup_$(date +%Y%m%d).log"
}
# 主流程
log "========== 开始磁盘清理 =========="
# 1. 清理旧日志
log "步骤1:清理${MAX_LOG_AGE_DAYS}天前的日志文件"
find /var/log -type f -name "*.log" -mtime +$MAX_LOG_AGE_DAYS -delete 2>/dev/null
find /var/log -type f -name "*.gz" -mtime +180 -delete 2>/dev/null
log " 日志清理完成"
# 2. 清理临时文件
log "步骤2:清理${MAX_TMP_AGE_DAYS}天未访问的临时文件"
find /tmp -type f -atime +$MAX_TMP_AGE_DAYS -delete 2>/dev/null
find /var/tmp -type f -atime +$MAX_TMP_AGE_DAYS -delete 2>/dev/null
log " 临时文件清理完成"
# 3. 清理包管理器缓存
log "步骤3:清理包管理器缓存"
dnf clean all 2>/dev/null
log " 包管理器缓存清理完成"
# 4. 清理旧内核(仅保留最新2个)
log "步骤4:清理旧内核"
current_kernel=$(uname -r)
kernel_count=0
for k in $(rpm -q kernel --last | sed 's/kernel-//' | cut -d' ' -f1); do
if [ "$kernel_count" -gt 1 ]; then
if [ "$k" != "$current_kernel" ]; then
dnf remove -y "kernel-$k" 2>/dev/null && log " 移除内核:$k" || true
fi
fi
kernel_count=$((kernel_count + 1))
done
# 5. Docker清理(每周执行)
day_of_week=$(date +%u)
if [ "$DOCKER_PRUNE_WEEKLY" = true ] && [ "$day_of_week" = 1 ]; then
log "步骤5:Docker空间清理(周一执行)"
docker system prune -f --filter "until=168h" 2>/dev/null || true
log " Docker清理完成"
fi
# 6. 清理旧日志轮转文件
log "步骤6:清理过期的logrotate文件"
find /var/lib/logrotate -type f -mtime +60 -delete 2>/dev/null
# 7. 清理旧tmp文件(系统)
log "步骤7:清理系统tmp目录"
find /var/tmp -type f -mtime +30 -delete 2>/dev/null
log "========== 磁盘清理完成 =========="
log "清理后磁盘使用情况:"
df -h / | tee -a "$LOG_DIR/cleanup_$(date +%Y%m%d).log"
5. inode满了怎么办
5.1 排查inode使用
#!/bin/bash # script: check_inode_usage.sh # 用途:检查inode使用情况 echo "=== Inode使用情况 ===" echo "" # 查看各分区inode使用 echo "【1】各分区inode使用率:" df -i echo "" # 查找inode占用最多的目录 echo "【2】/var目录inode分布:" find /var -type d 2>/dev/null | while read dir; do count=$(find "$dir" -maxdepth 1 -type f 2>/dev/null | wc -l) echo " $count $dir" done | sort -rn | head -20 echo "" # 统计各类型文件数量 echo "【3】文件类型统计:" find /var -type f 2>/dev/null | sed 's/.*.//' | sort | uniq -c | sort -rn | head -20
5.2 清理inode占用
#!/bin/bash # script: free_inodes.sh # 用途:释放inode占用的空间 echo "=== 释放inode空间 ===" echo "" # 1. 清理/var/spool下的旧邮件 echo "【1】清理邮件队列:" mail_count=$(find /var/spool/mail -type f 2>/dev/null | wc -l) echo " 邮件文件数:$mail_count" find /var/spool/mail -type f -mtime +30 -delete 2>/dev/null echo " 已清理30天前的邮件" # 2. 清理临时socket文件 echo "【2】清理临时socket文件:" socket_count=$(find /tmp -type s 2>/dev/null | wc -l) echo " socket文件数:$socket_count" find /tmp -type s -mtime +1 -delete 2>/dev/null # 3. 清理空目录 echo "【3】清理空目录:" find /var -type d -empty -delete 2>/dev/null # 4. 清理小的tmp文件 echo "【4】清理小的临时文件:" find /tmp -type f -size -1k -mtime +1 -delete 2>/dev/null # 5. 清理日志目录中的old文件 echo "【5】清理日志目录中的归档文件:" find /var/log -type f -name "*.old" -mtime +7 -delete 2>/dev/null find /var/log -type f -name "*.bak" -mtime +7 -delete 2>/dev/null echo "" echo "清理完成。当前inode使用:" df -i /
6. LVM逻辑卷扩展
当磁盘空间真的不够用时,需要扩展逻辑卷。
6.1 查看LVM状态
# 查看PV(物理卷) pvs # 查看VG(卷组) vgs # 查看LV(逻辑卷) lvs # 详细查看 pvdisplay vgdisplay lvdisplay
6.2 扩展逻辑卷
#!/bin/bash
# script: extend_lvm.sh
# 用途:扩展LVM逻辑卷
# 配置:要扩展的LV和要扩展的大小
VG_NAME="vg00"
LV_NAME="lv_data"
EXTEND_SIZE="+50G"
echo "=== LVM逻辑卷扩展 ==="
echo ""
# 1. 查看当前状态
echo "【1】当前状态:"
lvs
echo ""
lvdisplay /dev/${VG_NAME}/${LV_NAME}
echo ""
# 2. 检查VG剩余空间
echo "【2】卷组剩余空间:"
vgs
vgextend --help | head -1
echo ""
# 3. 如果VG有足够空间,直接扩展
echo "【3】执行扩展..."
lvextend -L +50G /dev/${VG_NAME}/${LV_NAME}
# 4. 扩展文件系统(XFS)
echo "【4】扩展XFS文件系统..."
xfs_growfs /dev/${VG_NAME}/${LV_NAME}
# 5. 验证
echo "【5】扩展后状态:"
df -h /dev/${VG_NAME}/${LV_NAME}
6.3 添加新磁盘到VG
#!/bin/bash # script: add_disk_to_vg.sh # 用途:将新磁盘添加到卷组 NEW_PV="/dev/sdb" VG_NAME="vg00" echo "=== 添加新磁盘到VG ===" echo "" # 1. 创建物理卷 echo "【1】创建物理卷:" pvcreate "$NEW_PV" # 2. 添加到卷组 echo "【2】添加到卷组:" vgextend "$VG_NAME" "$NEW_PV" # 3. 验证 echo "【3】卷组状态:" vgs # 4. 现在可以扩展LV echo "【4】可以执行:lvextend -L +/dev/${VG_NAME}/ "
7. 磁盘quota配额管理
7.1 启用磁盘配额
# 1. 编辑/etc/fstab,添加usrquota和grpquota # /dev/mapper/vg00-lv_home /home xfs defaults,usrquota,grpquota 0 0 # 2. 重新挂载 mount -o remount /home # 3. 初始化配额数据库 quotacheck -cug /home # 4. 启用配额 quotaon /home
7.2 设置用户配额
# 设置用户磁盘限制 edquota -u username # 示例配置: # Disk quotas for user username (uid 1000): # Filesystem blocks soft hard inodes soft hard # /dev/mapper/vg00-lv_home # 1024 5000 6000 100 150 200 # blocks: 当前使用(KB) # soft: 软限制(超过后警告) # hard: 硬限制(不能超过) # inodes: 文件数量限制
7.3 配额管理脚本
#!/bin/bash
# script: quota_report.sh
# 用途:生成磁盘配额报告
echo "=== 磁盘配额报告 ==="
echo "生成时间:$(date)"
echo ""
# 查看所有用户的配额状态
echo "【1】用户配额汇总:"
repquota -aug
echo ""
# 查看超过配额的用户
echo "【2】超配额用户:"
repquota -aug | grep -E "^*" | while read line; do
echo " $line"
done
echo ""
# 清理超过soft limit的用户的部分文件
echo "【3】可以建议清理的文件(按用户分组):"
for user in $(ls /home/); do
quota=$(quota -u "$user" 2>/dev/null | tail -1 | awk '{print $3}')
if [ -n "$quota" ] && [ "$quota" -gt 0 ]; then
usage=$(quota -u "$user" 2>/dev/null | tail -1 | awk '{print $2}')
if [ "$usage" -gt "$quota" ]; then
echo " 用户 $user:使用${usage}KB,配额${quota}KB"
fi
fi
done
8. 预防性监控方案
8.1 Zabbix监控模板
# Zabbix Agent配置 - 磁盘监控项
# /etc/zabbix/zabbix_agent2.d/disk.conf
# 磁盘空间(百分比)
UserParameter=disk.space[*],df -h "$1" | awk 'NR>1 {gsub(/%/,"",$5); print $5}'
# 磁盘inode(百分比)
UserParameter=disk.inode[*],df -i "$1" | awk 'NR>1 {gsub(/%/,"",$5); print $5}'
# 磁盘IO
UserParameter=disk.io[*],iostat -d "$1" | awk 'NR>4 {print $2}'
# 大文件检测
UserParameter=disk.large_files,find / -type f -size +1G 2>/dev/null | wc -l
8.2 Prometheus告警规则
# prometheus/alerts/disk.yml
groups:
- name: disk
rules:
# 磁盘空间告警
- alert: DiskSpaceWarning
expr: (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) < 0.2
for: 5m
labels:
severity: warning
annotations:
summary: "磁盘空间告警"
description: "根分区剩余空间不足20%"
- alert: DiskSpaceCritical
expr: (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}) < 0.1
for: 1m
labels:
severity: critical
annotations:
summary: "磁盘空间危急"
description: "根分区剩余空间不足10%,立即处理!"
# inode告警
- alert: DiskInodesWarning
expr: (node_filesystem_files_free{mountpoint="/"} / node_filesystem_files{mountpoint="/"}) < 0.1
for: 10m
labels:
severity: warning
annotations:
summary: "Inode告警"
description: "Inode使用率超过90%"
8.3 自动化监控脚本
#!/bin/bash
# script: comprehensive_disk_monitor.sh
# 用途:综合磁盘监控脚本,可配置cron执行
# 配置文件
CONFIG_FILE="/etc/sysconfig/disk_monitor.conf"
# 默认配置
WARNING_THRESHOLD=80
CRITICAL_THRESHOLD=90
LOG_FILE="/var/log/disk_monitor.log"
EXCLUDE_MOUNTS="/proc|/sys|/dev/shm|/run"
# 读取配置(如果存在)
[ -f "$CONFIG_FILE" ] && source "$CONFIG_FILE"
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
}
check_mount() {
local mount="$1"
# 获取使用率
local usage=$(df "$mount" 2>/dev/null | awk 'NR==2 {gsub(/%/,"",$5); print $5}')
# 获取设备
local device=$(df "$mount" 2>/dev/null | awk 'NR==2 {print $1}')
# 获取可用空间
local avail=$(df -h "$mount" 2>/dev/null | awk 'NR==2 {print $4}')
if [ -z "$usage" ]; then
return
fi
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
if [ "$usage" -ge "$CRITICAL_THRESHOLD" ]; then
log "CRITICAL|${timestamp}|${mount}|${device}|${usage}%|${avail}"
return 2
elif [ "$usage" -ge "$WARNING_THRESHOLD" ]; then
log "WARNING|${timestamp}|${mount}|${device}|${usage}%|${avail}"
return 1
else
log "OK|${timestamp}|${mount}|${device}|${usage}%|${avail}"
return 0
fi
}
# 主监控
log "========== 磁盘监控开始 =========="
# 获取所有挂载点
mounts=$(df -h | awk 'NR>1 {print $6}' | grep -vE "^(${EXCLUDE_MOUNTS})$")
critical_count=0
warning_count=0
for mount in $mounts; do
check_mount "$mount"
result=$?
if [ $result -eq 2 ]; then
critical_count=$((critical_count + 1))
elif [ $result -eq 1 ]; then
warning_count=$((warning_count + 1))
fi
done
log "========== 监控完成 =========="
log "结果:${critical_count}个危急,${warning_count}个告警"
# 如果有告警,输出汇总
if [ $critical_count -gt 0 ] || [ $warning_count -gt 0 ]; then
echo ""
echo "磁盘告警汇总:"
grep -E "CRITICAL|WARNING" "$LOG_FILE" | tail -20
fi
9. 自动化清理脚本分享
9.1 完整清理脚本
#!/bin/bash
# script: full_disk_cleanup.sh
# 用途:一次性执行所有常见磁盘清理操作
set -e
echo "========================================"
echo " 磁盘空间全面清理工具"
echo "========================================"
echo ""
echo "开始时间:$(date)"
echo ""
# 配置
MAX_LOG_DAYS=30
MAX_TMP_DAYS=7
DO_DOCKER_PRUNE=false
DO_DNF_CLEAN=false
DO_KERNEL_CLEAN=false
DRY_RUN=false
# 解析参数
while [[ $# -gt 0 ]]; do
case $1 in
--docker)
DO_DOCKER_PRUNE=true
shift
;;
--dnf)
DO_DNF_CLEAN=true
shift
;;
--kernel)
DO_KERNEL_CLEAN=true
shift
;;
--dry-run)
DRY_RUN=true
shift
;;
--all)
DO_DOCKER_PRUNE=true
DO_DNF_CLEAN=true
DO_KERNEL_CLEAN=true
shift
;;
*)
echo "未知参数: $1"
echo "用法: $0 [--docker] [--dnf] [--kernel] [--all] [--dry-run]"
exit 1
;;
esac
done
# 清理函数
run_cmd() {
if [ "$DRY_RUN" = true ]; then
echo "[DRYRUN] $1"
else
echo "[EXEC] $1"
eval "$1"
fi
}
# 1. 清理日志
echo "【1】清理${MAX_LOG_DAYS}天前的日志文件..."
run_cmd "find /var/log -type f -name '*.log' -mtime +${MAX_LOG_DAYS} -delete"
run_cmd "find /var/log -type f -name '*.gz' -mtime +180 -delete"
echo " 完成"
# 2. 清理临时文件
echo "【2】清理${MAX_TMP_DAYS}天未访问的临时文件..."
run_cmd "find /tmp -type f -atime +${MAX_TMP_DAYS} -delete 2>/dev/null || true"
run_cmd "find /var/tmp -type f -atime +${MAX_TMP_DAYS} -delete 2>/dev/null || true"
echo " 完成"
# 3. 清理dnf缓存
if [ "$DO_DNF_CLEAN" = true ]; then
echo "【3】清理DNF缓存..."
run_cmd "dnf clean all"
echo " 完成"
fi
# 4. 清理旧内核
if [ "$DO_KERNEL_CLEAN" = true ]; then
echo "【4】清理旧内核..."
current_kernel=$(uname -r)
installed_kernels=$(rpm -q kernel 2>/dev/null | wc -l)
if [ "$installed_kernels" -gt 2 ]; then
for kernel in $(rpm -q kernel 2>/dev/null | sed 's/kernel-//'); do
if [ "$kernel" != "$current_kernel" ]; then
run_cmd "rpm -e kernel-${kernel} 2>/dev/null || true"
fi
done
fi
echo " 完成"
fi
# 5. Docker清理
if [ "$DO_DOCKER_PRUNE" = true ]; then
echo "【5】清理Docker空间..."
if command -v docker &> /dev/null; then
run_cmd "docker system prune -f"
else
echo " Docker未安装,跳过"
fi
fi
# 6. 清理yum缓存
echo "【6】清理YUM/DNF缓存..."
run_cmd "yum clean all 2>/dev/null || true"
echo " 完成"
# 7. 清理孤儿包
echo "【7】清理孤儿包..."
run_cmd "package-cleanup --leaves 2>/dev/null | head -20 || true"
echo " 完成"
# 8. 清理旧日志轮转状态
echo "【8】清理logrotate状态..."
run_cmd "find /var/lib/logrotate -type f -mtime +60 -delete 2>/dev/null || true"
echo " 完成"
echo ""
echo "========================================"
echo "清理完成!"
echo "结束时间:$(date)"
echo ""
echo "当前磁盘使用情况:"
df -h /
echo "========================================"
# 估算释放空间(如果可能)
if [ "$DRY_RUN" = false ]; then
echo ""
echo "各目录清理后大小:"
for dir in /var/log /tmp /var/tmp /var/cache; do
if [ -d "$dir" ]; then
size=$(du -sh "$dir" 2>/dev/null | cut -f1)
echo " $dir: $size"
fi
done
fi
9.2 定时任务配置
# 将以下内容添加到crontab # 编辑: crontab -e # 每天凌晨2点执行快速磁盘检查 0 2 * * * /opt/scripts/quick_disk_check.sh >> /var/log/disk_check.log 2>&1 # 每天凌晨3点执行日志清理(保留30天) 0 3 * * * /opt/scripts/clean_old_logs.sh # 每周一凌晨4点执行全面清理 0 4 * * 1 /opt/scripts/full_disk_cleanup.sh --docker --dnf --kernel >> /var/log/weekly_cleanup.log 2>&1 # 每5分钟执行磁盘监控 */5 * * * * /opt/scripts/disk_alert_monitor.sh
10. 总结:磁盘管理Checklist
10.1 收到告警时的标准处理流程
【第1步:30秒内初步判断】 □ 查看df -h输出,确认是哪个挂载点告警 □ 判断是/、/data、/var还是其他挂载点 □ 快速判断使用率是否真的超过阈值 【第2步:5分钟内定位问题】 □ 使用du -h --max-depth=1 定位最大的子目录 □ 检查日志目录(/var/log) □ 检查临时目录(/tmp, /var/tmp) □ 如果是数据库,检查数据目录 □ 如果是容器环境,检查docker目录 【第3步:确定清理方案】 □ 日志问题:检查logrotate配置,执行手动清理 □ 临时文件:清理n天前的文件 □ 数据库:检查表空间,考虑归档或清理 □ 容器:执行docker system prune 【第4步:执行清理】 □ 先备份重要数据 □ 执行清理操作 □ 验证空间释放 □ 确认服务正常 【第5步:建立长效机制】 □ 检查logrotate配置 □ 配置监控告警 □ 考虑自动化清理 □ 记录问题根因
10.2 预防性维护清单
每日检查:
□ 确认没有磁盘告警 □ 检查关键服务的日志写入是否正常
每周维护:
□ 审查日志轮转日志 □ 检查临时文件增长趋势 □ 确认监控告警配置正常
每月维护:
□ 执行全面的磁盘审计 □ 清理旧的日志和临时文件 □ 检查inode使用率 □ 审查磁盘空间增长趋势 □ 更新容量规划
10.3 容量规划参考
| 服务类型 | 单机容量建议 | 监控阈值 |
|---|---|---|
| Web服务器 | /var/log至少50GB | 告警80% |
| MySQL | 数据目录200GB起步 | 告警70% |
| PostgreSQL | 数据目录100GB起步 | 告警70% |
| Redis | 内存2倍大小 | 告警80% |
| Elasticsearch | 磁盘2倍内存 | 告警80% |
| Docker Host | 空闲50%以上 | 告警75% |
10.4 快速命令速查表
| 场景 | 命令 |
|---|---|
| 查看磁盘使用 | df -h |
| 查看目录大小 | du -sh /path |
| 找大文件 | find / -type f -size +1G |
| 找大目录 | ncdu -x / |
| 清理日志 | > /var/log/xxx.log |
| 清理临时文件 | find /tmp -type f -atime +7 -delete |
| 清理Docker | docker system prune -a |
| 查看inode | df -i |
| 清理inode | find / -type f -empty -delete |
| LVM扩展 | lvextend -L +50G /dev/vg00/lv |
| 配额的启用 | quotaon /home |
| 检查配额 | repquota -aug |
10.5 常见问题解答
Q: 删除文件后空间不释放?A: 可能是进程持有文件句柄。检查lsof +L1,找到占用文件然后重启相关进程。
Q: inode满了怎么办?A: 查找小文件过多的目录:find / -type f -size -10k | wc -l,然后清理。
Q: 如何预测磁盘增长?A: 记录每日df -h结果到时序数据库,绘制趋势图。简单方法:df -h >> /var/log/df_history.log。
Q: LVM卷已满但VG没有空间?A: 需要扩展VG:添加新磁盘,创建PV,vgextend加入VG,然后lvextend扩展LV。
参考信息
环境版本:
操作系统:Rocky Linux 9.4
内核版本:6.8.5
XFS文件系统版本:1.2.1
df/du版本:GNU coreutils 9.1
ncdu版本:1.17
全部0条评论
快来发表一下你的评论吧 !