强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

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

关键要点

  1. 配额基于子卷,不是用户
  2. 启用配额有性能开销(5-10%)
  3. 配额可能导致 enospc 错误
  4. 多用户/容器场景推荐启用
  5. 单用户桌面不推荐启用

扩展阅读