Btrfs 文件系统运维完全教程 / 第 13 章:Docker 与 Btrfs
第 13 章:Docker 与 Btrfs
13.1 Docker 存储驱动概述
13.1.1 Docker 存储驱动对比
| 驱动 | 文件系统支持 | 性能 | 稳定性 | 推荐 |
|---|
| overlay2 | 所有 | 好 | ✅ 生产推荐 | ✅ 默认 |
| btrfs | Btrfs | 好 | ✅ 稳定 | Btrfs 用户 |
| devicemapper | 块设备 | 中等 | ⚠️ 已弃用 | ❌ |
| zfs | ZFS | 好 | ✅ | ZFS 用户 |
| vfs | 所有 | 差 | ✅ | 测试用 |
13.1.2 选择 Btrfs 驱动的理由
| 优势 | 说明 |
|---|
| COW 快照 | 容器创建/复制瞬间完成 |
| 透明压缩 | 减少存储占用 |
| 配额管理 | 限制容器空间使用 |
| 增量备份 | 通过快照发送/接收 |
| 子卷隔离 | 每个容器独立子卷 |
| 劣势 | 说明 |
|---|
| 碎片化 | 大量小写入导致碎片 |
| 配额开销 | 启用配额有性能影响 |
| 兼容性 | 部分 Docker 功能可能不完全兼容 |
13.2 配置 Docker 使用 Btrfs
13.2.1 Docker 守护进程配置
// /etc/docker/daemon.json
{
"storage-driver": "btrfs",
"storage-opts": [
"btrfs.min_space=1G"
],
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
# 配置后重启 Docker
sudo systemctl restart docker
# 验证存储驱动
docker info | grep "Storage Driver"
# Storage Driver: btrfs
13.2.2 Btrfs 数据目录
# Docker 默认数据目录:/var/lib/docker
# 确保该目录在 Btrfs 文件系统上
# 方法 1:将整个 /var 放在 Btrfs 上
mount -o compress=zstd:3,ssd /dev/sdb1 /var
# 方法 2:为 Docker 创建专用子卷
btrfs subvolume create /data/@docker
mount -o subvol=/@docker,compress=zstd:3 /dev/sdb1 /var/lib/docker
# 方法 3:修改 Docker 数据目录
# daemon.json 中添加:
# "data-root": "/data/docker"
13.2.3 Docker 数据目录子卷布局
# 查看 Docker 创建的子卷
sudo btrfs subvolume list /var/lib/docker
# Docker Btrfs 驱动创建的子卷:
# ID 300 gen 50 top level 5 path docker
# ID 301 gen 51 top level 300 path docker/subvolumes/abc123...
# ID 302 gen 52 top level 300 path docker/subvolumes/def456...
13.3 Docker + Btrfs 快照管理
13.3.1 容器快照备份
# 1. 查看 Docker 子卷
DOCKER_ROOT=$(docker info --format '{{.DockerRootDir}}')
sudo btrfs subvolume list "$DOCKER_ROOT"
# 2. 对 Docker 数据目录创建快照
sudo btrfs subvolume snapshot -r /var/lib/docker /backup/docker-snap-$(date +%Y%m%d)
# 3. 增量备份
sudo btrfs send -p /backup/docker-snap-old /backup/docker-snap-new | \
ssh backup "btrfs receive /backup/"
13.3.2 快速容器复制
# Docker 利用 Btrfs COW 快速复制容器
docker run -d --name app-v1 myapp:latest
# docker commit 利用 COW,几乎瞬间完成
docker commit app-v1 app-v2
# docker cp 同样利用 COW
docker cp app-v1:/data ./backup/
13.3.3 镜像层管理
Docker 镜像在 Btrfs 上的结构:
Image Layer 1 (base) → Btrfs 子卷
Image Layer 2 (apt-get) → 基于 Layer 1 的快照 (COW)
Image Layer 3 (config) → 基于 Layer 2 的快照 (COW)
Container Writable → 基于 Image 的可写快照
13.4 存储池规划
13.4.1 Docker 存储池设计
# 推荐的 Docker Btrfs 子卷布局
# /dev/sdb1 → Btrfs 文件系统
# 子卷布局
# /@ → 系统根
# /@docker → /var/lib/docker
# /@docker-data → Docker 数据卷
# /@snapshots → 快照存储
# 创建
btrfs subvolume create /mnt/@docker
btrfs subvolume create /mnt/@docker-data
# fstab
UUID=xxx /var/lib/docker btrfs subvol=/@docker,compress=zstd:3,ssd,discard=async 0 0
UUID=xxx /var/lib/docker-data btrfs subvol=/@docker-data,compress=zstd:3,ssd 0 0
13.4.2 空间管理
# 查看 Docker 使用的空间
docker system df
# 输出示例:
# TYPE TOTAL ACTIVE SIZE RECLAIMABLE
# Images 15 8 5.2GB 2.1GB (40%)
# Containers 10 5 500MB 300MB (60%)
# Local Volumes 20 12 10GB 5GB (50%)
# Build Cache 0 0 0B 0B
# 清理未使用的资源
docker system prune -a
# 查看 Btrfs 层面的空间使用
sudo btrfs filesystem usage /var/lib/docker
sudo compsize /var/lib/docker
13.4.3 配额限制容器空间
# 启用配额
sudo btrfs quota enable /var/lib/docker
# 找到容器的子卷 ID
CONTAINER_ID=$(docker inspect --format '{{.Id}}' my-container)
SUBVOL_ID=$(sudo btrfs subvolume list /var/lib/docker | \
grep "$CONTAINER_ID" | awk '{print $2}')
# 设置配额(10GB)
sudo btrfs qgroup limit 10G "$SUBVOL_ID" /var/lib/docker
# 查看使用情况
sudo btrfs qgroup show /var/lib/docker
13.5 Docker Compose + Btrfs 示例
13.5.1 数据库服务
# docker-compose.yml
version: '3.8'
services:
postgres:
image: postgres:16
volumes:
- postgres-data:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: secret
redis:
image: redis:7-alpine
volumes:
- redis-data:/data
volumes:
postgres-data:
driver: local
driver_opts:
type: none
o: bind
device: /var/lib/docker-data/postgres # Btrfs 子卷上
redis-data:
driver: local
# 在 Btrfs 上为数据库创建专用目录(关闭 COW)
sudo mkdir -p /var/lib/docker-data/postgres
sudo chattr +C /var/lib/docker-data/postgres
# 验证 COW 已关闭
lsattr /var/lib/docker-data/postgres
13.6 监控与运维
13.6.1 Docker + Btrfs 监控脚本
#!/bin/bash
# docker-btrfs-monitor.sh
set -euo pipefail
DOCKER_ROOT=$(docker info --format '{{.DockerRootDir}}')
ALERT_PERCENT=80
echo "=== Docker + Btrfs Monitor ==="
echo "Docker Root: $DOCKER_ROOT"
echo "Time: $(date '+%Y-%m-%d %H:%M:%S')"
echo ""
# Docker 资源使用
echo "--- Docker Resources ---"
docker system df
echo ""
# Btrfs 空间使用
echo "--- Btrfs Space ---"
sudo btrfs filesystem usage "$DOCKER_ROOT" | head -8
echo ""
# 设备统计
echo "--- Device Stats ---"
sudo btrfs device stats "$DOCKER_ROOT"
echo ""
# 子卷统计
SUBVOL_COUNT=$(sudo btrfs subvolume list "$DOCKER_ROOT" | wc -l)
echo "Total subvolumes: $SUBVOL_COUNT"
# 检查空间使用
USAGE=$(sudo btrfs filesystem usage "$DOCKER_ROOT" | grep "Device size" | awk '{print $3}')
USED=$(sudo btrfs filesystem usage "$DOCKER_ROOT" | grep "^Used:" | awk '{print $2}')
echo "Usage: $USED / $USAGE"
13.6.2 自动清理旧容器/镜像
#!/bin/bash
# docker-btrfs-cleanup.sh
# 清理停止的容器
docker container prune -f
# 清理未使用的镜像
docker image prune -a -f --filter "until=168h" # 超过 7 天
# 清理未使用的卷
docker volume prune -f
# 清理构建缓存
docker builder prune -f
# 清理 Btrfs 旧快照
sudo btrfs subvolume list -s /var/lib/docker | \
awk '{print $NF}' | \
while read snap; do
snap_date=$(echo "$snap" | grep -oP '\d{8}' || true)
if [[ -n "$snap_date" ]]; then
cutoff=$(date -d "30 days ago" +%Y%m%d)
if [[ "$snap_date" < "$cutoff" ]]; then
echo "Deleting old snapshot: $snap"
sudo btrfs subvolume delete "/var/lib/docker/$snap" 2>/dev/null || true
fi
fi
done
13.7 本章小结
| 操作 | 说明 |
|---|
| 配置 Btrfs 驱动 | daemon.json 中 storage-driver: btrfs |
| 数据目录 | 建议专用子卷 /@docker |
| 关闭 COW | 数据库目录 chattr +C |
| 空间管理 | docker system prune + btrfs balance |
| 备份 | 基于快照的增量备份 |
| 监控 | 监控子卷数量和空间使用 |
关键要点:
- Docker Btrfs 驱动利用 COW 实现快速容器创建
- 数据库目录需要关闭 COW(
chattr +C) - 建议 Docker 数据目录使用专用子卷
- 定期清理未使用的容器和镜像
- 利用 Btrfs 快照进行 Docker 数据备份
扩展阅读