技术博客

Docker 存储进阶:Volume 驱动、NFS/Ceph 共享存储与 tmpfs 实战

深入讲解 Docker 存储的进阶方案:Volume 插件驱动架构与常用插件(local/nfs/vieux-sshfs)、NFS 共享存储实现多容器跨主机数据访问、Ceph RBD 块存储集成、tmpfs 内存挂载的安全应用场景、存储性能基准对比,以及 Docker Swarm 环境下的有状态服务存储方案选型。

Docker存储VolumeNFSCephtmpfs共享存储进阶

基础篇讲了 Named Volume 和 Bind Mount,本文进入存储进阶:当多台机器上的容器需要共享数据时,单机的 Named Volume 就不够用了。NFS、Ceph 等分布式存储通过 Volume 驱动接入 Docker,实现跨主机数据共享。

Volume 驱动(Plugin)架构

Docker Volume 驱动架构:

docker CLI

Docker Engine
    ↓ Volume Plugin API
Volume Driver(插件)

实际存储后端
  ├── local(默认,本地磁盘)
  ├── nfs(NFS 共享)
  ├── ceph(Ceph RBD/CephFS)
  ├── s3(AWS S3/MinIO)
  └── vieux/sshfs(SSH 远程目录)

Volume 插件提供统一的 API:
  Create/Remove/Mount/Unmount/Path/List/Get
# 查看已安装的 Volume 驱动
docker info | grep -A5 "Plugins"
# Plugins:
#   Volume: local    ← 默认

# 安装 Volume 插件(以 vieux/sshfs 为例)
docker plugin install vieux/sshfs

# 查看插件
docker plugin ls

一、local 驱动的高级用法

local 是默认驱动,支持通过 --opt 使用 mount 命令的选项。

# 创建 tmpfs 类型的 Volume(内存存储)
docker volume create \
    --driver local \
    --opt type=tmpfs \
    --opt device=tmpfs \
    --opt o=size=256m,uid=1000 \
    my-tmpfs

docker run -d --mount source=my-tmpfs,target=/cache myapp

# 创建 NFS 挂载的 Volume(local 驱动通过 mount 选项)
docker volume create \
    --driver local \
    --opt type=nfs \
    --opt o=addr=192.168.1.100,vers=4,rw \
    --opt device=:/data/shared \
    nfs-shared

# 创建绑定指定路径的 Volume(相当于有名字的 bind mount)
docker volume create \
    --driver local \
    --opt type=none \
    --opt device=/data/myapp \
    --opt o=bind \
    myapp-data

二、NFS 共享存储

NFS 是最常见的跨主机共享存储方案,配置简单,适合中小规模部署。

NFS 服务端配置

# 在 NFS 服务器上(192.168.1.100)

# 安装 NFS 服务
apt-get install -y nfs-kernel-server   # Ubuntu
yum install -y nfs-utils               # CentOS

# 创建共享目录
mkdir -p /data/docker-shared
chmod 777 /data/docker-shared

# 配置 exports
cat >> /etc/exports << 'EOF'
/data/docker-shared  192.168.1.0/24(rw,sync,no_subtree_check,no_root_squash)
# rw            → 读写权限
# sync          → 写入时同步到磁盘(更安全)
# no_root_squash → 允许 root 用户访问(Docker 容器需要)
EOF

# 重新加载
exportfs -ra
systemctl restart nfs-kernel-server

# 验证
showmount -e localhost
# Export list for localhost:
# /data/docker-shared  192.168.1.0/24

Docker 使用 NFS Volume

# 方法1:docker volume create
docker volume create \
    --driver local \
    --opt type=nfs \
    --opt o=addr=192.168.1.100,vers=4,rw,sync \
    --opt device=:/data/docker-shared \
    nfs-shared-vol

# 使用
docker run -d \
    --mount source=nfs-shared-vol,target=/shared \
    myapp

# 方法2:直接在 docker run 中指定(不预创建 Volume)
docker run -d \
    --mount type=volume,\
source=nfs-shared,\
target=/shared,\
volume-driver=local,\
volume-opt=type=nfs,\
"volume-opt=o=addr=192.168.1.100,vers=4",\
volume-opt=device=:/data/docker-shared \
    myapp

docker-compose 中使用 NFS

# compose.yml
services:
  api-1:
    image: myapp:v1
    volumes:
      - shared-uploads:/app/uploads
    deploy:
      replicas: 3               # 多副本共享同一个 NFS Volume

  api-2:
    image: myapp:v1
    volumes:
      - shared-uploads:/app/uploads

volumes:
  shared-uploads:
    driver: local
    driver_opts:
      type: nfs
      o: addr=192.168.1.100,vers=4,rw,sync,hard,intr
      device: ":/data/docker-shared/uploads"

# NFS 挂载选项说明:
# vers=4      → NFS v4(推荐)
# hard        → 连接断开时无限重试(不丢数据)
# intr        → 允许中断(用 Ctrl+C 可以停止)
# soft        → 超时后返回错误(rpc超时时不挂起)
# timeo=900   → 超时 90 秒(默认)
# retrans=3   → 重传 3 次

三、Ceph 存储集成

Ceph 提供块存储(RBD)和文件存储(CephFS)两种方式接入 Docker。

CephFS(文件存储,类似 NFS)

# 前提:已有 Ceph 集群,创建了 CephFS

# 安装 ceph-fuse
apt-get install -y ceph-fuse

# 挂载 CephFS(宿主机层面)
mkdir -p /mnt/cephfs
ceph-fuse -m 192.168.1.50:6789 /mnt/cephfs \
    --id=docker \
    --keyring=/etc/ceph/ceph.client.docker.keyring

# 验证
df -h /mnt/cephfs
# ceph-fuse  100G  10G  90G  10% /mnt/cephfs

# 用 bind mount 给容器使用
docker run -d \
    --mount type=bind,source=/mnt/cephfs/myapp,target=/data \
    myapp

# 或者创建 bind volume
docker volume create \
    --driver local \
    --opt type=none \
    --opt device=/mnt/cephfs/myapp \
    --opt o=bind \
    cephfs-myapp-vol

Ceph RBD(块存储,高性能)

# 安装 Docker Volume 插件(ceph/rbd-docker-plugin)
docker plugin install ceph/rbd

# 创建 Ceph RBD Volume
docker volume create \
    --driver ceph/rbd \
    --opt name=myapp-data \
    --opt size=20G \
    --opt pool=docker-pool \
    --opt monitors=192.168.1.50:6789 \
    --opt keyring=/etc/ceph/ceph.client.admin.keyring \
    rbd-myapp-vol

# 使用(每次只能挂载到一台宿主机,RBD 不支持多主机同时挂载)
docker run -d \
    --mount source=rbd-myapp-vol,target=/data \
    myapp

四、tmpfs 进阶用法

# tmpfs 基础(内存中的临时文件系统)
docker run -d \
    --tmpfs /tmp:rw,noexec,nosuid,size=128m \
    myapp

# --mount 格式(更完整的选项)
docker run -d \
    --mount type=tmpfs,target=/tmp,tmpfs-size=256m,tmpfs-mode=1777 \
    myapp

# tmpfs 选项说明:
# size     → 最大大小(不设置则用可用内存)
# mode     → 文件权限(1777 = sticky bit,类似 /tmp)
# noexec   → 禁止执行二进制(安全加固)
# nosuid   → 禁止 setuid 位(安全加固)

生产安全场景:密钥/凭证存内存

# 场景:应用启动时需要一个密钥文件,但不能写磁盘

# 1. tmpfs 挂载到 /secrets
docker run -d \
    --mount type=tmpfs,target=/secrets,tmpfs-mode=0700 \
    -e VAULT_ADDR=http://vault:8200 \
    -e VAULT_TOKEN=s.xxxxx \
    myapp

# 2. 应用启动时从 Vault 获取密钥,写入 /secrets/
# entrypoint.sh:
# vault kv get -field=key secret/myapp > /secrets/app.key
# chmod 600 /secrets/app.key
# exec python3 app.py

# 好处:密钥存在内存中,容器重启后自动消失,不写磁盘

五、Volume 备份与恢复

# 备份 Named Volume(通用方法)
docker run --rm \
    -v mysql_data:/source:ro \
    -v $(pwd):/backup \
    ubuntu:22.04 \
    tar czf /backup/mysql_data_$(date +%Y%m%d_%H%M%S).tar.gz \
    -C /source .

# 验证备份
ls -lh mysql_data_*.tar.gz

# 恢复 Volume
docker run --rm \
    -v mysql_data:/target \
    -v $(pwd):/backup \
    ubuntu:22.04 \
    tar xzf /backup/mysql_data_20260721_120000.tar.gz \
    -C /target

# 自动化备份脚本(每天凌晨 2 点)
cat > /etc/cron.d/docker-volume-backup << 'EOF'
0 2 * * * root /usr/local/bin/backup-volumes.sh >> /var/log/backup.log 2>&1
EOF

cat > /usr/local/bin/backup-volumes.sh << 'SCRIPT'
#!/bin/bash
BACKUP_DIR=/data/backups/volumes
RETENTION_DAYS=7
DATE=$(date +%Y%m%d)

for VOLUME in mysql_data redis_data; do
    docker run --rm \
        -v ${VOLUME}:/source:ro \
        -v ${BACKUP_DIR}:/backup \
        ubuntu:22.04 \
        tar czf /backup/${VOLUME}_${DATE}.tar.gz -C /source .
done

# 删除超过 7 天的备份
find ${BACKUP_DIR} -name "*.tar.gz" -mtime +${RETENTION_DAYS} -delete
SCRIPT
chmod +x /usr/local/bin/backup-volumes.sh

六、存储性能对比

存储方案          顺序读    顺序写    随机 IOPS   延迟   适用场景
──────────────────────────────────────────────────────────────────
本地 SSD overlay  ~1500MB/s ~900MB/s  ~200K      低     单机,最高性能
bind mount(SSD)   ~1500MB/s ~900MB/s  ~200K      低     配置文件,代码
Named Volume(本地) ~1500MB/s ~900MB/s ~200K      低     本地数据库
NFS v4            ~100MB/s  ~80MB/s   ~5K        中     共享文件,日志
Ceph RBD          ~500MB/s  ~400MB/s  ~50K       中低   有状态服务迁移
CephFS            ~200MB/s  ~150MB/s  ~20K       中     多写场景
tmpfs             ~10GB/s   ~10GB/s   ~1M        极低   临时数据,密钥

关键参考:
- 数据库(MySQL/Redis):用本地 SSD Volume,性能差距 10 倍以上
- 多副本共享上传文件:NFS 够用,或用 MinIO/S3
- 跨主机有状态服务:Ceph RBD(每次只能一个节点挂载)
- 会话/临时缓存:tmpfs(极高性能+自动清理)

七、Docker Swarm 中的存储方案

# swarm 中有状态服务(数据库)的存储推荐方案
version: "3.9"

services:
  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: secret
    volumes:
      - mysql_data:/var/lib/mysql
    deploy:
      replicas: 1              # 有状态服务只能 1 个副本(数据不能分裂)
      placement:
        constraints:
          - node.labels.storage == ssd   # 固定到有 SSD 的节点

volumes:
  mysql_data:
    driver: local              # 本地高性能
    driver_opts:
      type: none
      device: /data/mysql      # 宿主机的 SSD 路径
      o: bind
# 给节点打标签
docker node update --label-add storage=ssd node-01

小结

存储方案选型的核心逻辑:

单机场景:Named Volume(本地磁盘)= 最简单、最高性能,数据库首选。

多机共享文件:NFS Volume Driver,配置最简单;适合上传文件、日志聚合、共享配置。

多机高性能块存储:Ceph RBD,性能接近本地;适合需要跨机器迁移的有状态服务(但每次只能一个节点挂载)。

临时敏感数据:tmpfs,数据不落盘,容器重启自动清除,用于密钥、临时缓存。

无论选哪种存储,备份脚本都是必须的:用 docker run --rm -v volume:/source ubuntu tar czf 这个通用模式,可以备份任何 Docker Volume。