Btrfs 文件系统运维完全教程 / 第 7 章:配额管理
第 7 章:配额管理
7.1 Btrfs 配额概述
7.1.1 配额机制
Btrfs 使用配额组(Qgroup)来管理子卷的空间使用。与传统文件系统的用户/组配额不同,Btrfs 配额是基于子卷的:
| 传统配额 | Btrfs 配额 |
|---|---|
| 基于用户/组 | 基于子卷 |
| 限制 inode 数量 | 限制空间大小 |
| 需要内核支持 | 内建于 Btrfs |
| 实时生效 | 有延迟(COW 导致) |
7.1.2 配额组(Qgroup)层次
qgroup 层次结构:
0/5 ← FS_TREE(文件系统根)
├── 0/256 ← @ 子卷
├── 0/257 ← @home 子卷
├── 0/258 ← @var 子卷
├── 1/1 ← 自定义配额组(可包含多个子卷)
│ ├── 0/257
│ └── 0/258
└── ...
7.1.3 注意事项
⚠️ 性能警告: 启用配额后,Btrfs 需要维护额外的引用计数,这会增加写操作的开销。在大量小文件写入的场景下,性能影响可能达到 5-10%。在某些极端情况下(大量快照、频繁删除),可能导致 “enospc” 错误。
7.2 配额管理操作
7.2.1 启用配额
# 启用配额(需要卸载文件系统)
sudo umount /mnt/data
sudo btrfs quota enable /dev/sdb1
sudo mount /dev/sdb1 /mnt/data
# 或者在已挂载的文件系统上启用
sudo btrfs quota enable /mnt/data
# 确认配额已启用
sudo btrfs qgroup show /mnt/data
7.2.2 查看配额状态
# 查看配额组
sudo btrfs qgroup show /mnt/data
# 输出示例:
# qgroupid rfer excl max_rfer max_excl
# -------- ---- ---- -------- --------
# 0/5 10.00GiB 1.50GiB none none
# 0/256 5.00GiB 500MiB none none
# 0/257 3.00GiB 800MiB 10.00GiB none
# 0/258 2.00GiB 200MiB none none
# 显示列说明
| 字段 | 说明 |
|---|---|
| qgroupid | 配额组 ID(level/subvolume_id) |
| rfer (referenced) | 该子卷引用的总数据量 |
| excl (exclusive) | 该子卷独占的数据量 |
| max_rfer | 引用量上限(限制) |
| max_excl | 独占量上限(限制) |
7.2.3 创建和管理配额组
# 创建自定义配额组(level 1,ID 100)
sudo btrfs qgroup create 1/100 /mnt/data
# 将子卷添加到配额组
sudo btrfs qgroup assign 0/257 1/100 /mnt/data # @home
sudo btrfs qgroup assign 0/258 1/100 /mnt/data # @var
# 查看配额组成员
sudo btrfs qgroup show -p /mnt/data
7.2.4 设置配额限制
# 限制子卷最大引用量(10GB)
sudo btrfs qgroup limit 10G /mnt/data/@home
# 或
sudo btrfs qgroup limit 10G 0/257 /mnt/data
# 限制配额组总引用量
sudo btrfs qgroup limit 50G 1/100 /mnt/data
# 限制独占量
sudo btrfs qgroup limit_excl 5G 0/257 /mnt/data
# 取消限制
sudo btrfs qgroup limit 0 /mnt/data/@home
# 或
sudo btrfs qgroup limit none 0/257 /mnt/data
7.2.5 禁用配额
# 禁用配额
sudo btrfs quota disable /mnt/data
# ⚠️ 禁用配额会删除所有配额信息
7.3 配额监控脚本
7.3.1 空间监控脚本
#!/bin/bash
# btrfs-quota-monitor.sh - Btrfs 配额监控
set -euo pipefail
MOUNT_POINT="${1:?Usage: $0 /mount/point}"
WARN_THRESHOLD=80
CRIT_THRESHOLD=90
echo "=== Btrfs Quota Monitor ==="
echo "Mount point: $MOUNT_POINT"
echo "Time: $(date '+%Y-%m-%d %H:%M:%S')"
echo ""
# 检查配额是否启用
if ! sudo btrfs qgroup show "$MOUNT_POINT" &>/dev/null; then
echo "ERROR: Quota not enabled on $MOUNT_POINT"
exit 1
fi
# 遍历有配额限制的子卷
sudo btrfs qgroup show -p "$MOUNT_POINT" | while read -r line; do
qgroupid=$(echo "$line" | awk '{print $1}')
rfer=$(echo "$line" | awk '{print $2}')
max_rfer=$(echo "$line" | awk '{print $5}')
# 跳过没有配额限制的子卷
if [[ "$max_rfer" == "none" || "$max_rfer" == "0" ]]; then
continue
fi
# 计算使用百分比(简化版)
rfer_bytes=$(numfmt --from=iec "$rfer" 2>/dev/null || echo 0)
max_bytes=$(numfmt --from=iec "$max_rfer" 2>/dev/null || echo 0)
if [[ "$max_bytes" -gt 0 ]]; then
percent=$(( rfer_bytes * 100 / max_bytes ))
status="OK"
if (( percent >= CRIT_THRESHOLD )); then
status="CRITICAL"
elif (( percent >= WARN_THRESHOLD )); then
status="WARNING"
fi
printf "%-10s Used: %-10s Limit: %-10s %3d%% [%s]\n" \
"$qgroupid" "$rfer" "$max_rfer" "$percent" "$status"
fi
done
7.3.2 告警脚本
#!/bin/bash
# btrfs-quota-alert.sh - 配额使用告警
set -euo pipefail
MOUNT_POINT="/data"
ALERT_EMAIL="[email protected]"
WARN_PERCENT=80
CRIT_PERCENT=90
send_alert() {
local subject="$1"
local body="$2"
echo "$body" | mail -s "Btrfs Quota Alert: $subject" "$ALERT_EMAIL"
echo "Alert sent: $subject"
}
check_quotas() {
local alerts=""
while read -r line; do
qgroupid=$(echo "$line" | awk '{print $1}')
rfer=$(echo "$line" | awk '{print $2}')
max_rfer=$(echo "$line" | awk '{print $5}')
[[ "$max_rfer" == "none" || "$max_rfer" == "0" ]] && continue
rfer_bytes=$(numfmt --from=iec "$rfer" 2>/dev/null || echo 0)
max_bytes=$(numfmt --from=iec "$max_rfer" 2>/dev/null || echo 0)
[[ "$max_bytes" -eq 0 ]] && continue
percent=$(( rfer_bytes * 100 / max_bytes ))
if (( percent >= CRIT_PERCENT )); then
alerts+="[CRITICAL] $qgroupid: ${percent}% used ($rfer / $max_rfer)\n"
elif (( percent >= WARN_PERCENT )); then
alerts+="[WARNING] $qgroupid: ${percent}% used ($rfer / $max_rfer)\n"
fi
done < <(sudo btrfs qgroup show "$MOUNT_POINT" | tail -n +3)
if [[ -n "$alerts" ]]; then
send_alert "Quota Usage Alert" "$alerts"
fi
}
check_quotas
7.4 配额最佳实践
7.4.1 何时启用配额
| 场景 | 是否启用 | 原因 |
|---|---|---|
| 多用户共享服务器 | ✅ 推荐 | 防止某用户耗尽空间 |
| 容器存储 | ✅ 推荐 | 限制容器空间 |
| 单用户桌面 | ❌ 不推荐 | 性能开销不值得 |
| 快照管理 | ⚠️ 谨慎 | 快照的配额计算复杂 |
| Docker 存储 | ⚠️ 谨慎 | 可能导致 enospc 问题 |
7.4.2 配额与快照的交互
快照和源子卷共享数据,配额计算遵循以下规则:
子卷 @home 引用 10GB
快照 @snap1 引用 10GB(与 @home 几乎完全相同)
@home 独占(excl) = 被修改的数据
@snap1 独占(excl) = @snap1 创建后 @home 被修改的数据
📝 注意:
rfer(引用量)可能看起来超过实际磁盘使用,因为多个子卷/快照共享相同的数据块。excl(独占量)才是子卷真正独占的空间。
7.4.3 常见问题与解决
问题:配额导致 enospc 错误
# 症状:明明有空间,却报 "no space left on device"
# 原因:配额限制过低或配额计算延迟
# 解决方案 1:临时禁用配额
sudo btrfs quota disable /mnt/data
# 解决方案 2:增大配额限制
sudo btrfs qgroup limit 50G /mnt/data/@home
# 解决方案 3:清理快照释放配额
sudo btrfs subvolume delete /mnt/@snapshots/old-*
问题:配额数据不准确
# 清理并重建配额数据
sudo umount /mnt/data
sudo btrfs quota disable /dev/sdb1
sudo btrfs quota enable /dev/sdb1
sudo mount /dev/sdb1 /mnt/data
# 或使用 rescan
sudo btrfs quota rescan /mnt/data
# 这会在后台重新计算所有配额数据
sudo btrfs quota rescan -w /mnt/data # 等待完成
7.5 本章小结
| 操作 | 命令 |
|---|---|
| 启用配额 | btrfs quota enable /mnt |
| 禁用配额 | btrfs quota disable /mnt |
| 查看配额 | btrfs qgroup show /mnt |
| 设置限制 | btrfs qgroup limit 10G /mnt/@subvol |
| 创建配额组 | btrfs qgroup create 1/100 /mnt |
| 重建配额 | btrfs quota rescan -w /mnt |
关键要点:
- 配额基于子卷,不是用户
- 启用配额有性能开销(5-10%)
- 配额可能导致 enospc 错误
- 多用户/容器场景推荐启用
- 单用户桌面不推荐启用