使用 Docker 时,常常需要备份存储在中的数据,或者为了测试而复制到另一个卷。

虽然可以简单地想到 docker cp 命令,但该命令仅支持在容器的文件系统和主机之间进行复制,而无法直接访问 Docker 管理的卷。

本文将介绍复制 Docker 卷的正确方法,我们将重点关注‘为何’要使用这种方式的原理。理解这个原理后,您可以在需要时自然地应用,而无需记住命令。

核心原理很简单。

操作 Docker 卷的最安全且‘道地’的方法就是利用可以访问该卷的‘临时容器’进行桥接。


1. 将现有卷复制到新卷 (卷到卷)



这是最常见的场景。您可能想将正在运行的 db_data 卷复制到测试用的 db_data_test 卷。

🤔 为什么要使用临时容器?

Docker 卷存储在主机文件系统的某个地方(如 /var/lib/docker/volumes/...),但由 Docker 守护进程管理。直接访问该路径并使用 cp 命令并不推荐,因为这可能导致权限问题或数据一致性问题。

相反,我们将运行一个同时挂载两个卷的临时容器。

  1. 容器将源卷source_volume)挂载到 /from 路径。

  2. 容器将目标卷new_volume)挂载到 /to 路径。

  3. 容器一启动,就会执行一个简单的 Linux 命令(cptar),将 /from 的所有数据复制到 /to

  4. 命令完成后,容器会自动销毁(--rm 选项)。

这个容器仅作为数据复制的'工具'。

🚀 如何:命令示例

source_data 卷的数据复制到 target_data 卷的示例。

  1. 创建测试用卷(可选)
docker volume create source_data
docker volume create target_data
# (假设 source_data 中已有数据。)
  1. 通过临时容器进行复制
docker run --rm \
       -v source_data:/from \
       -v target_data:/to \
       alpine \
       sh -c "cp -a /from/. /to/"

💡 命令解说

  • docker run --rm: --rm 标志表示在容器完成操作并退出时立即删除容器。这是临时操作的必要条件。

  • -v source_data:/from: 将 source_data 卷挂载到容器内的 /from 目录。

  • -v target_data:/to: 将 target_data 卷挂载到容器内的 /to 目录。

  • alpine: 使用体积非常小的 alpine Linux 镜像,其中包含基本的实用程序如 cpsh

  • sh -c "...": 在 alpine 容器运行时要执行的命令。

  • cp -a /from/. /to/:

    • -a(归档): 这个标志是关键。它不仅仅是简单复制(cp -r),而是保留所有属性,如所有权、权限、时间戳等。在处理数据库文件等敏感数据时,这一点非常重要。

    • /from/.: 这表示的是 /from 内部的所有内容(包括隐藏文件),而不是 /from 目录本身。


2. 将卷数据复制到主机文件系统(备份)

当您希望将卷的数据备份到本地机器或服务器的特定目录中(如 .tar 文件)时,可以使用此方法。

🤔 为什么:它是如何工作的?

原理与第一个相同。只不过目标从‘Docker 卷’变为‘主机目录’。

Docker 支持将主机的特定目录挂载到容器中的绑定挂载(Bind Mount)功能。

  1. 容器将源卷source_volume)挂载到 /data 路径。

  2. 容器将主机的特定目录$(pwd)/backup)挂载到 /backup 路径。

  3. 容器将 /data 的内容复制(或压缩)到 /backup 目录。

  4. 这个操作是在容器内部完成的,但由于 /backup 与实际的主机目录是连接的,因此结果会保留在主机中。

🚀 如何:命令示例

source_data 卷的数据复制到当前路径($(pwd))的 backup 目录中。

# 在主机上创建用于备份的目录
mkdir -p $(pwd)/backup

docker run --rm \
       -v source_data:/data:ro \
       -v $(pwd)/backup:/backup \
       alpine \
       cp -a /data/. /backup/

💡 命令解说

  • -v source_data:/data:ro: 添加了 :ro(只读)标志。备份时仅需读取数据,因此最好以只读方式挂载以防止意外修改原始卷。

  • -v $(pwd)/backup:/backup: 与 source_data(卷名)不同,以 / 开头或包含 $(pwd)(当前路径)的绝对/相对路径指的是主机的目录


3. 关键复制技巧(Tips)



技巧 1: 使用 tar 进行高效复制(压缩备份)

使用 tar 而不是 cp 命令有多个好处。特别是在将数据打包为存档文件(.tar.gz)进行备份时非常有用。

卷到卷(使用 tar)

可能比 cp 更快,特别是在文件较多时更有效。

docker run --rm \
       -v source_data:/from \
       -v target_data:/to \
       alpine \
       sh -c "cd /from && tar -cf - . | (cd /to && tar -xf -)"
  • cd /from && tar -cf - .: 先转到 /from,然后将当前目录(.)的内容打包为(c)而不是文件(f),结果发往标准输出(-

  • | (cd /to && tar -xf -): 通过管道(|)接收标准输出,并在 /to 目录中解压(x)。

卷到主机(压缩备份)

将 source_data 卷的内容压缩为 backup.tar.gz 文件并存储在主机上。

docker run --rm \
       -v source_data:/data:ro \
       -v $(pwd):/backup \
       alpine \
       tar -czf /backup/backup.tar.gz -C /data .
  • tar -czf /backup/backup.tar.gz ...: 创建(c),进行 gzip 压缩(z),并生成文件(f/backup/backup.tar.gz

  • -C /data .: 这一部分很重要。先转到 /data 目录,再压缩其中所有内容(.

    )这样做是为了确保 tar 文件中不会包含多余的 /data 路径。

技巧 2: 从主机文件恢复到卷

与备份相反,恢复也使用相同的原理。只需改变 cp 的方向即可。

# 将 $(pwd)/backup 目录的数据恢复到 new_data 卷
 docker run --rm \
       -v new_data:/data \
       -v $(pwd)/backup:/backup:ro \
       alpine \
       cp -a /backup/. /data/

总结

操作 Docker 卷的关键在于“将容器作为工具使用”的思维方式。必要时,通过运行挂载需要的卷和目录的临时容器,安全地执行 cptar 等标准 Linux 命令,而不是直接操作主机中的卷文件。

只要记住这个原理,您就能在任何情况下自由复制、备份和恢复卷数据。