第 10 章:Docker 与容器化
第 10 章:Docker 与容器化
让 Bcachefs 成为容器的最佳存储后端
10.1 概述
10.1.1 为什么在 Docker 中使用 Bcachefs
Bcachefs 在容器场景的优势:
1. CoW 快速创建容器
- 容器启动 = CoW 复制元数据
- 毫秒级创建,几乎零空间占用
2. 快照支持
- 一键快照容器状态
- 快速回滚到历史版本
3. 透明压缩
- 节省镜像和容器存储空间
- 降低存储成本
4. 数据完整性
- 校验和保护容器数据
- 避免静默数据损坏
5. 多设备分层
- 热数据放 SSD,冷数据放 HDD
- 最优性能与成本平衡
10.1.2 Docker 存储驱动对比
| 存储驱动 | CoW | 快照 | 压缩 | 性能 | 稳定性 |
|---|---|---|---|---|---|
| overlay2 (ext4) | ❌ | ❌ | ❌ | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| overlay2 (xfs) | ❌ | ❌ | ❌ | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| overlay2 (btrfs) | ✅ | ✅ | ✅ | ⭐⭐ | ⭐⭐⭐⭐ |
| overlay2 (bcachefs) | ✅ | ✅ | ✅ | ⭐⭐⭐ | ⭐⭐⭐ |
| devicemapper | ✅ | ✅ | ❌ | ⭐⭐ | ⭐⭐ |
| zfs | ✅ | ✅ | ✅ | ⭐⭐ | ⭐⭐⭐⭐ |
10.2 配置 Docker 使用 Bcachefs
10.2.1 方案一:将 Docker 数据目录放在 Bcachefs 上(推荐)
# 1. 创建并挂载 Bcachefs 文件系统
sudo bcachefs format /dev/sdb --compression=zstd
sudo mkdir -p /var/lib/docker-bcachefs
sudo mount -t bcachefs /dev/sdb /var/lib/docker-bcachefs
# 2. 停止 Docker
sudo systemctl stop docker docker.socket containerd
# 3. 迁移 Docker 数据(可选)
sudo rsync -aP /var/lib/docker/ /var/lib/docker-bcachefs/docker/
# 4. 配置 Docker 使用新数据目录
sudo mkdir -p /etc/docker
cat << 'EOF' | sudo tee /etc/docker/daemon.json
{
"data-root": "/var/lib/docker-bcachefs/docker"
}
EOF
# 5. 启动 Docker
sudo systemctl start docker
# 6. 验证
docker info | grep "Docker Root Dir"
# 输出: Docker Root Dir: /var/lib/docker-bcachefs/docker
10.2.2 方案二:将整个 /var/lib/docker 放在 Bcachefs 上
# 1. 创建 Bcachefs 文件系统
sudo bcachefs format /dev/sdb --compression=zstd
# 2. 备份现有数据
sudo systemctl stop docker
sudo mv /var/lib/docker /var/lib/docker.bak
# 3. 挂载 Bcachefs 到 /var/lib/docker
sudo mkdir -p /var/lib/docker
sudo mount -t bcachefs /dev/sdb /var/lib/docker
# 4. 添加到 fstab
UUID=$(sudo bcachefs show-super /dev/sdb | grep UUID | awk '{print $2}')
echo "UUID=$UUID /var/lib/docker bcachefs defaults,noatime 0 0" | sudo tee -a /etc/fstab
# 5. 恢复数据(可选)
sudo rsync -aP /var/lib/docker.bak/ /var/lib/docker/
# 6. 启动 Docker
sudo systemctl start docker
10.2.3 方案三:使用 Bcachefs 作为 Docker Volume
# 1. 创建 Bcachefs 挂载点
sudo mkdir -p /mnt/bcachefs-volumes
# 2. 创建 Docker Volume 指向 Bcachefs
docker volume create \
--driver local \
--opt type=bcachefs \
--opt device=/dev/sdb \
--opt o=noatime,compress=zstd \
bcachefs-data
# 3. 使用 Volume
docker run -v bcachefs-data:/data alpine echo "Hello Bcachefs"
10.3 Docker Compose 配置
10.3.1 基本配置
# docker-compose.yml
version: '3.8'
services:
app:
image: nginx:latest
volumes:
- app-data:/var/www/html
- app-logs:/var/log/nginx
ports:
- "80:80"
db:
image: postgres:15
volumes:
- db-data:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: example
volumes:
app-data:
driver: local
driver_opts:
type: none
o: bind
device: /mnt/bcachefs-docker/app-data
app-logs:
driver: local
driver_opts:
type: none
o: bind
device: /mnt/bcachefs-docker/app-logs
db-data:
driver: local
driver_opts:
type: none
o: bind
device: /mnt/bcachefs-docker/db-data
10.3.2 自动创建目录
#!/bin/bash
# setup-docker-volumes.sh
BCACHEFS_MOUNT="/mnt/bcachefs-docker"
VOLUMES=("app-data" "app-logs" "db-data")
# 创建挂载点
sudo mkdir -p "$BCACHEFS_MOUNT"
# 挂载 Bcachefs(如果尚未挂载)
if ! mountpoint -q "$BCACHEFS_MOUNT"; then
sudo mount -t bcachefs /dev/sdb "$BCACHEFS_MOUNT"
fi
# 创建卷目录
for vol in "${VOLUMES[@]}"; do
sudo mkdir -p "$BCACHEFS_MOUNT/$vol"
sudo chown 1000:1000 "$BCACHEFS_MOUNT/$vol"
done
echo "Docker volumes 准备完成"
10.4 快照管理与容器
10.4.1 快照 Docker 数据
#!/bin/bash
# docker-snapshot.sh
set -e
DOCKER_DATA="/var/lib/docker-bcachefs"
SNAPSHOT_DIR="/mnt/snapshots/docker"
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
echo "=== Docker 数据快照 ==="
# 1. 暂停所有容器
echo "暂停容器..."
docker pause $(docker ps -q) 2>/dev/null || true
# 2. 同步数据
sync
# 3. 创建快照
echo "创建快照..."
sudo bcachefs subvolume snapshot -r "$DOCKER_DATA" "$SNAPSHOT_DIR/snap-$TIMESTAMP"
# 4. 恢复容器
echo "恢复容器..."
docker unpause $(docker ps -q) 2>/dev/null || true
echo "快照完成: $SNAPSHOT_DIR/snap-$TIMESTAMP"
10.4.2 从快照恢复 Docker 数据
#!/bin/bash
# docker-restore.sh
set -e
if [ -z "$1" ]; then
echo "用法: $0 <快照路径>"
echo "可用快照:"
ls /mnt/snapshots/docker/
exit 1
fi
SNAPSHOT="$1"
DOCKER_DATA="/var/lib/docker-bcachefs"
echo "=== 从快照恢复 Docker 数据 ==="
echo "快照: $SNAPSHOT"
# 1. 停止 Docker
echo "停止 Docker..."
sudo systemctl stop docker docker.socket containerd
# 2. 备份当前数据
echo "备份当前数据..."
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
sudo mv "$DOCKER_DATA/docker" "$DOCKER_DATA/docker.bak.$TIMESTAMP"
# 3. 恢复快照
echo "恢复快照..."
sudo cp -a "$SNAPSHOT/docker" "$DOCKER_DATA/docker"
# 4. 启动 Docker
echo "启动 Docker..."
sudo systemctl start docker
echo "恢复完成"
docker ps
10.4.3 自动化快照策略
# /etc/systemd/system/docker-snapshot.timer
[Unit]
Description=Docker snapshot timer
[Timer]
OnCalendar=daily
OnCalendar=03:00
Persistent=true
[Install]
WantedBy=timers.target
# /etc/systemd/system/docker-snapshot.service
[Unit]
Description=Docker snapshot service
After=docker.service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/docker-snapshot.sh
StandardOutput=journal
StandardError=journal
sudo systemctl enable --now docker-snapshot.timer
10.5 Overlay 文件系统集成
10.5.1 Overlay 与 Bcachefs 的关系
Docker 默认使用 overlay2 存储驱动:
镜像层 (只读)
↓
overlay2
↓
容器层 (可写)
↓
底层文件系统 (ext4/xfs/btrfs/bcachefs)
当底层文件系统是 Bcachefs 时:
- overlay2 正常工作
- 容器创建层使用 CoW(如果支持)
- 透明压缩自动生效
10.5.2 验证 Overlay2 工作正常
# 检查 Docker 存储驱动
docker info | grep "Storage Driver"
# 输出: Storage Driver: overlay2
# 检查底层文件系统
df -hT /var/lib/docker
# 输出: /dev/sdb bcachefs ...
# 测试容器运行
docker run --rm alpine echo "Bcachefs + Docker works!"
# 检查镜像层
docker image inspect alpine | grep -A 5 "GraphDriver"
10.5.3 性能优化
# 在 Bcachefs 上运行 overlay2 时的优化建议:
# 1. 使用 noatime 挂载
sudo mount -o remount,noatime /var/lib/docker-bcachefs
# 2. 使用快速压缩
sudo mount -o remount,compress=lz4 /var/lib/docker-bcachefs
# 3. 确保启用 discard
sudo mount -o remount,discard /var/lib/docker-bcachefs
10.6 Kubernetes 与 Bcachefs
10.6.1 本地 PersistentVolume
# bcachefs-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: bcachefs-pv
spec:
capacity:
storage: 100Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: bcachefs
local:
path: /mnt/bcachefs-k8s/data
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- node1
---
# bcachefs-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: bcachefs-pvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: bcachefs
resources:
requests:
storage: 100Gi
10.6.2 StorageClass (需要 CSI 驱动)
# bcachefs-storageclass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: bcachefs
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Retain
10.6.3 StatefulSet 使用
# statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
spec:
serviceName: postgres
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:15
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: bcachefs
resources:
requests:
storage: 50Gi
10.7 容器化部署脚本
10.7.1 完整的 Docker + Bcachefs 设置脚本
#!/bin/bash
# setup-docker-bcachefs.sh
set -e
# 配置
DEVICE="/dev/sdb"
MOUNT_POINT="/var/lib/docker-bcachefs"
COMPRESSION="zstd"
echo "=== Docker + Bcachefs 配置 ==="
# 1. 检查 Bcachefs 支持
if ! modinfo bcachefs &>/dev/null; then
echo "错误: 内核不支持 Bcachefs"
exit 1
fi
# 2. 创建文件系统(如果需要)
if ! sudo bcachefs show-super "$DEVICE" &>/dev/null; then
echo "创建 Bcachefs 文件系统..."
read -p "这会清除 $DEVICE 上的所有数据!确认?(y/N) " confirm
if [ "$confirm" != "y" ]; then
echo "取消"
exit 0
fi
sudo bcachefs format "$DEVICE" --compression="$COMPRESSION"
fi
# 3. 挂载
sudo mkdir -p "$MOUNT_POINT"
if ! mountpoint -q "$MOUNT_POINT"; then
UUID=$(sudo bcachefs show-super "$DEVICE" | grep UUID | awk '{print $2}')
echo "UUID=$UUID $MOUNT_POINT bcachefs defaults,noatime 0 0" | sudo tee -a /etc/fstab
sudo mount "$MOUNT_POINT"
fi
# 4. 停止 Docker
sudo systemctl stop docker docker.socket containerd 2>/dev/null || true
# 5. 配置 Docker
sudo mkdir -p /etc/docker
cat << EOF | sudo tee /etc/docker/daemon.json
{
"data-root": "$MOUNT_POINT/docker",
"storage-driver": "overlay2",
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
EOF
# 6. 启动 Docker
sudo systemctl start docker
# 7. 验证
echo ""
echo "=== 配置完成 ==="
docker info | grep -E "Docker Root Dir|Storage Driver"
df -hT "$MOUNT_POINT"
10.8 备份与恢复
10.8.1 Docker 镜像备份
# 导出所有镜像
docker save $(docker images -q) -o /mnt/bcachefs-backup/docker-images.tar
# 导出特定镜像
docker save nginx:latest postgres:15 -o /mnt/bcachefs-backup/selected-images.tar
# 导入镜像
docker load -i /mnt/bcachefs-backup/docker-images.tar
10.8.2 Docker Volume 备份
# 备份 Volume
docker run --rm \
-v my-volume:/source:ro \
-v /mnt/bcachefs-backup:/backup \
alpine tar czf /backup/my-volume.tar.gz -C /source .
# 恢复 Volume
docker run --rm \
-v my-volume:/target \
-v /mnt/bcachefs-backup:/backup \
alpine sh -c "cd /target && tar xzf /backup/my-volume.tar.gz"
10.8.3 完整 Docker 环境备份
#!/bin/bash
# backup-docker-env.sh
BACKUP_DIR="/mnt/bcachefs-backup/docker-$(date +%Y%m%d)"
mkdir -p "$BACKUP_DIR"
echo "备份 Docker 环境到: $BACKUP_DIR"
# 1. 备份 Docker 配置
cp -a /etc/docker "$BACKUP_DIR/docker-config"
# 2. 备份 Docker Compose 文件
find /opt -name "docker-compose*.yml" -exec cp {} "$BACKUP_DIR/" \; 2>/dev/null
# 3. 导出镜像列表
docker images --format "{{.Repository}}:{{.Tag}}" > "$BACKUP_DIR/images.txt"
# 4. 导出容器列表
docker ps -a --format "{{.Names}}:{{.Image}}" > "$BACKUP_DIR/containers.txt"
# 5. 快照 Docker 数据
sudo bcachefs subvolume snapshot -r /var/lib/docker-bcachefs "/mnt/snapshots/docker-$(date +%Y%m%d)"
echo "备份完成"
10.9 故障排除
10.9.1 Docker 启动失败
# 检查 Docker 日志
sudo journalctl -u docker -n 50
# 常见原因 1: 数据目录不存在或未挂载
mount | grep docker-bcachefs
df -h /var/lib/docker-bcachefs
# 常见原因 2: 权限问题
ls -la /var/lib/docker-bcachefs/
# 常见原因 3: daemon.json 配置错误
sudo dockerd --debug
10.9.2 容器创建失败
# 检查存储空间
df -h /var/lib/docker-bcachefs
# 清理未使用的资源
docker system prune -a
# 检查 Bcachefs 文件系统
sudo bcachefs fsck /dev/sdb
10.9.3 性能问题
# 检查 I/O 瓶颈
iostat -x 1
# 检查压缩是否影响性能
mount | grep bcachefs
# 如果使用高压缩级别,考虑降低
# 检查 GC 是否在运行
dmesg | grep -i gc
10.10 本章小结
| 方案 | 复杂度 | 推荐场景 |
|---|---|---|
| 修改 data-root | ⭐ 最简单 | 大多数场景 |
| 挂载到 /var/lib/docker | ⭐⭐ | 新服务器 |
| Docker Volume | ⭐⭐ | 需要精细控制 |
核心配置:
{
"data-root": "/var/lib/docker-bcachefs/docker"
}
最佳实践:
- 使用 zstd 或 lz4 压缩
- 启用 noatime 和 discard
- 定期快照 Docker 数据
- 监控存储空间
扩展阅读
上一章: ← 第 9 章:性能调优 | 下一章: 第 11 章:数据恢复与故障处理 →