【问题标题】:How to port data-only volumes from one host to another?如何将仅数据卷从一台主机移植到另一台主机?
【发布时间】:2014-02-06 08:20:40
【问题描述】:

正如Working with Volumes 上的 Docker 文档中所述,有所谓的 data-only 容器的概念,它提供了一个可以挂载到多个其他容器中的卷,无论是否纯数据容器是否实际运行。

基本上,这听起来很棒。但有一件事我不明白。

这些卷(出于可移植性原因, 明确映射到主机上的文件夹,如文档所述)由 Docker 在主机上的某个内部文件夹中创建和管理(/var/docker/volumes/… )。

假设我使用了这样一个卷,然后我需要将它从一台主机迁移到另一台主机 - 我如何移植该卷? AFAICS 它具有唯一的 ID - 我可以将卷及其相应的仅数据容器复制到新主机吗?如何找出要复制的文件?或者是否有一些我尚未发现的 Docker 内置支持?

【问题讨论】:

  • 您可以导出数据容器目录:docker run --volumes-from <data container> ubuntu tar -cO <volume path> | gzip -c > volume.tgz 这不依赖于卷的实现细节。并在第二台机器上用 tar 导入数据。
  • 哇,太棒了,谢谢:-)))!如果您写此评论作为答案,我会很乐意接受!

标签: docker


【解决方案1】:

官方答案见"Backup, restore, or migrate data volumes"部分:

备份:

sudo docker run --rm --volumes-from DATA -v $(pwd):/backup busybox tar cvf /backup/backup.tar /data
  • --rm:容器退出时移除容器
  • --volumes-from DATA:附加到 DATA 容器共享的卷
  • -v $(pwd):/backup:绑定挂载当前目录到容器中;将 tar 文件写入
  • busybox: 一个小而简单的图像 - 适合快速维护
  • tar cvf /backup/backup.tar /data: 为 /data 目录下的所有文件创建一个未压缩的 tar 文件

恢复:

# create a new data container
$ sudo docker create -v /data --name DATA2 busybox true
# untar the backup files into the new container᾿s data volume
$ sudo docker run --rm --volumes-from DATA2 -v $(pwd):/backup busybox tar xvf /backup/backup.tar
data/
data/sven.txt
# compare to the original container
$ sudo docker run --rm --volumes-from DATA -v `pwd`:/backup busybox ls /data
sven.txt

【讨论】:

  • 现在最好将docker create 用于纯数据容器,这样它们就不会被启动。请参阅关闭中的示例。文档:docs.docker.com/userguide/dockervolumes/…
  • 所以...如果我要备份 Postgres 数据库,我会用 /var/lib/postgresql/data 替换什么 /data,对吗?
  • 取决于你使用的发行版,通常在/var/lib/postgresql/[version]/data/
  • “备份、恢复或迁移数据卷”部分似乎已从 Docker 文档中删除:-(
  • @Datz 这只是一个调用来创建数据容器的命令,它可以是实际上什么都不做的任何命令。容器启动并立即退出,但它用于持久化数据。
【解决方案2】:

从Docker docs和top answer here扩展官方答案,您可以在.bashrc或.zshrc中具有以下功能:

# backup files from a docker volume into /tmp/backup.tar.gz
function docker-volume-backup-compressed() {
  docker run --rm -v /tmp:/backup --volumes-from "$1" debian:jessie tar -czvf /backup/backup.tar.gz "${@:2}"
}

# restore files from /tmp/backup.tar.gz into a docker volume
function docker-volume-restore-compressed() {
  docker run --rm -v /tmp:/backup --volumes-from "$1" debian:jessie tar -xzvf /backup/backup.tar.gz "${@:2}"
  echo "Double checking files..."
  docker run --rm -v /tmp:/backup --volumes-from "$1" debian:jessie ls -lh "${@:2}"
}

# backup files from a docker volume into /tmp/backup.tar
function docker-volume-backup() {
  docker run --rm -v /tmp:/backup --volumes-from "$1" busybox tar -cvf /backup/backup.tar "${@:2}"
}

# restore files from /tmp/backup.tar into a docker volume
function docker-volume-restore() {
  docker run --rm -v /tmp:/backup --volumes-from "$1" busybox tar -xvf /backup/backup.tar "${@:2}"
  echo "Double checking files..."
  docker run --rm -v /tmp:/backup --volumes-from "$1" busybox ls -lh "${@:2}"
}

注意备份保存到/tmp,所以你可以在docker主机之间移动保存在那里的备份文件。

还有两对备份/恢复别名。一种使用压缩和 debian:jessie,另一种没有压缩但使用busybox。如果要备份的文件很大,建议使用压缩。

【讨论】:

    【解决方案3】:

    在这里添加一个答案,因为我没有评论的声誉。虽然上述所有答案都对我有所帮助,但我想可能还有像我这样的其他人也希望将backup.tar 文件的内容复制到协作者机器上的named docker volume 中。我没有看到上面或docker volumes documentation 中专门讨论过这个问题。

    为什么要将backup.tar 文件复制到named docker volume?

    这对于在现有docker-compose.yml 文件中指定named docker volume 以供某些容器使用的情况可能会有所帮助。

    将backup.tar的内容复制到named docker volume中

    1. 在主机上,按照接受的答案或docker volumes documentation 中的步骤创建一个backup.tar 文件并将其推送到某个存储库。

    2. 将backup.tar 从存储库中拉到协作者的机器中。

    3. 在协作者的机器上,创建一个临时容器和一个命名的 docker 卷。

    docker run -v named_docker_volume:/dbdata --name temp_db_container ubuntu /bin/bash

    • --name temp_db_container : 创建一个名为 temp_db_container

    • ubuntu /bin/bash :使用ubuntu 图像 构建temp_db_container,启动命令为/bin/bash

    • -v named_docker_volume:/dbdata : 挂载/dbdata 文件夹 temp_db_container 到一个名为 named_docker_volume。我们使用这个特别命名的卷 named_docker_volume 与我们指定的卷名匹配 docker-compose.yml 文件。

    1. 在协作者的机器上,将 backup.tar 的内容复制到命名的 docker 卷中。

    docker run --rm --volumes-from temp_db_container -v $(pwd):/backup ubuntu bash -c "cd /dbdata && tar xvf /backup/backup.tar --strip 1"

    • --volumes-from temp_db_container : temp_db_container 容器的 /dbdata 文件夹在上一步中映射到 named_docker_volume 卷。因此,任何存储在 /dbdata 文件夹中的文件都会立即被复制到 named_docker_volume docker 卷中。
    • -v $(pwd):/backup :将本地机器的当前工作目录映射到位于temp_db_container 内的/backup 文件夹
    • ubuntu bash -c "cd /dbdata && tar xvf /backup/backup.tar --strip 1" :解压backup.tar 文件并将解压后的内容存储在/dbdata 文件夹中。
    1. 在协作者的机器上,清除临时容器temp_db_container

    docker rm temp_db_container

    【讨论】:

      【解决方案4】:

      这是一个单线,以防它可以在机器之间建立 SSH 连接:

      docker run --rm -v <SOURCE_DATA_VOLUME_NAME>:/from alpine ash -c "cd /from ; tar -cf - . " | ssh <TARGET_HOST> 'docker run --rm -i -v <TARGET_DATA_VOLUME_NAME>:/to alpine ash -c "cd /to ; tar -xpvf - " '
      

      归功于 Guido Diepen 的 post。

      【讨论】:

      • 对我不起作用,从mac到aws,它会复制但没有发送
      • 你检查SSH连接是否可以正确建立?
      • 我试过类似ssh -i ~/python/second-for-floo.pem ubuntu@X.X.X.X insead ssh &lt;TARGET_HOST&gt;
      【解决方案5】:

      我将在此处添加 IBM 的另一个最新工具,它实际上是为从一个容器主机到另一个容器主机的卷迁移而设计的。这是一个目前正在进行的项目。因此,您将来可能会发现具有附加功能的不同版本。

      Cargo 旨在将容器连同其数据一起从一台主机迁移到另一台主机,同时将停机时间降至最低。 Cargo 使用 联合文件系统 的数据联合功能在源主机和目标主机之间创建统一的数据视图(主要是根文件系统)。这允许 Cargo 几乎立即(在几毫秒内)在目标主机上启动容器,因为源根文件系统中的数据被按需复制到目标主机(使用 copy-on-write (COW) 分区)或在后台延迟 (使用 rsync)。

      重点是: - centralized 服务器处理迁移过程

      这里给出了项目的链接:

      https://github.com/nadgowdas/cargo
      

      【讨论】:

        【解决方案6】:

        如果您的机器位于不同的 VPC 中,或者您想从/复制到本地机器(如我的情况),您可以使用我创建的 dvsync。它基本上是 ngrok 结合 rsync 通过 SSH 打包成两个小(约 25MB)图像。首先,您在要从中复制数据的机器上启动dvsync-server(您需要从ngrok dashboard 获得的NGROK_AUTHTOKEN):

        $ docker run --rm -e NGROK_AUTHTOKEN="$NGROK_AUTHTOKEN" \
          --mount source=MY_VOLUME,target=/data,readonly \
          quay.io/suda/dvsync-server
        

        然后您可以在要将文件复制到的机器上启动dvsync-client,传递服务器显示的DVSYNC_TOKEN:

        docker run -e DVSYNC_TOKEN="$DVSYNC_TOKEN" \
          --mount source=MY_TARGET_VOLUME,target=/data \
          quay.io/suda/dvsync-client 
        

        复制完成后,客户端将退出。这也适用于 Docker CLI、Compose、Swarm 和 Kubernetes。

        【讨论】:

          【解决方案7】:

          您可以将卷导出到 tar 并转移到另一台机器。并在第二台机器上用 tar 导入数据。这不依赖于卷的实现细节。

          # you can list shared directories of the data container
          docker inspect <data container> | grep "/vfs/dir/"
          
          # you can export data container directory to tgz
          docker run --cidfile=id.tmp --volumes-from <data container> ubuntu tar -cO <volume path> | gzip -c > volume.tgz
          
          # clean up: remove exited container used for export and temporary file
          docker rm `cat id.tmp` && rm -f id.tmp
          

          【讨论】:

          • 感谢您的回答。如何将数据容器从一台主机移动到另一台主机?
          • @nXqd 数据容器由docker run -v /data-volume -name datacointainer busybox true 创建——你可以在任何地方运行它。创建数据容器后,您可以按照答案中的说明导入 tar 存档。
          • 感谢您的回答。但是我遇到了另一个问题,我们需要删除用于以后备份的僵尸容器。因为这不会返回 id。你有什么好办法吗:D
          • @nXqd 当然 - 你必须使用 --cidfile=id.txt 作为运行参数。容器 ID 将存储在文件 id.txt 中。我已经更新了答案。
          • 你可以用docker run --rm代替docker run --cidfile ... ; docker rm。
          【解决方案8】:

          改编自已接受的答案,但提供了更大的灵活性,您可以在 bash 管道中使用它:

          #!/bin/bash
          
          if [ $# != 2 ]; then
              echo Usage "$0": volume /path/of/the/dir/in/volume/to/backup
              exit 1
          fi
          
          if [ -t 1 ]; then
              echo The output of the cmd is binary data "(tar)", \
                   and it should be redirected instead of printed to terminal
              exit 1
          fi
          
          volume="$1"
          path="$2"
          
          exec docker run --rm --mount type=volume,src="$volume",dst=/mnt/volume/ alpine tar cf - . -C /mnt/volume/"$path"
          

          如果要定期增量备份卷,则可以使用以下脚本:

          #!/bin/bash
          
          if [ $# != 3 ]; then
              echo Usage "$0": volume /path/of/the/dir/in/volume/to/backup /path/to/put/backup
              exit 1
          fi
          
          volume="$1"
          volume_path="$2"
          path="$3"
          
          if [[ "$path" =~ ^.*/$ ]]; then
              echo "The 3rd argument shouldn't end in '/', otherwise rsync would not behave as expected"
              exit 1
          fi
          
          container_name="docker-backup-rsync-service-$RANDOM"
          docker run --rm --name="$container_name" -d -p 8738:873 \
              --mount type=volume,src="$volume",dst=/mnt/volume/ \
              nobodyxu/rsyncd
          
          echo -e '\nStarting syncing...'
          
          rsync --info=progress2,stats,symsafe -aHAX --delete \
              "rsync://localhost:8738/root/mnt/volume/$volume_path/"  "$path"
          exit_status=$?
          
          echo -e '\nStopping the rsyncd docker...'
          docker stop -t 1 "$container_name"
          
          exit $exit_status
          

          它利用rsync 的服务器和客户端功能直接同步卷和主机目录之间的目录。

          【讨论】:

            【解决方案9】:

            我对使用tar 的答案不满意。我决定自己动手。由于我要经常同步数据,而且数据量很大,所以我特别想使用rsync。每次使用tar发送所有的数据只会浪费时间和传输。

            在如何解决两个远程docker容器之间的通信问题上花了几天时间,我终于得到了一个使用socat的解决方案。

            • 运行两个 docker 容器 - 一个在源上,另一个在目标上,每个都安装一个卷 - 源卷和目标卷。
            • 在将从卷中流式传输/加载数据的容器之一上运行 rsync --deamon
            • 运行docker exec source_container socat - TCP:localhost并运行docker exec desintation_container socat TCP-LISTEN:rsync -并将这两者的标准输入和标准输出连接在一起。所以一个socat 连接到rsync --daemon 并将数据从/重定向到stdout/stdin,另一个socat 侦听:rsync 端口(端口873)并重定向到/从stdin/stdout。然后将它们连接在一起,因此基本上我们将数据从一个容器端口传送到另一个容器端口。
            • 然后在另一个卷rsync 客户端上运行,该客户端将连接到localhost:rsync,通过“socat 管道”有效连接到rsync --daemon。

            基本上,它是这样工作的:

            log "Running both destination and source containers"
            src_did=$(
                env DOCKER_HOST=$src_docker_host docker run --rm -d -i -v \
                "$src_volume":/data:ro -w /data alpine_with_rsync_and_socat\
                sleep infinity
            )
            dst_did=$(
                env DOCKER_HOST=$dst_docker_host docker run --rm -d -i -v \
                "$dst_volume":/data:rw -w /data alpine_with_rsync_and_socat \
                sleep infinity
            )
            
            log "Running rsyncd on destination container"
                env DOCKER_HOST=$dst_docker_host docker exec "$dst_did" sh -c "
                    cat <<EOF > /etc/rsyncd.conf &&
            uid = root
            gid = root
            use chroot = no
            max connections = 1
            numeric ids = yes
            reverse lookup = no
            [data]
            path = /data/
            read only = no
            EOF
                    rsync --daemon
                "
            
            log "Setup rsync socat forwarding between containers"
            {
                coproc { env DOCKER_HOST=$dst_docker_host docker exec -i "$dst_did" \
                   socat -T 10 - TCP:localhost:rsync,forever; }
                env DOCKER_HOST=$src_docker_host docker exec -i "$src_did" \
                   socat -T 10 TCP-LISTEN:rsync,forever,reuseaddr - <&"${COPROC[0]}" >&"${COPROC[1]}"
            } &
            
            log "Running rsync on source that will connect to destination"
            env DOCKER_HOST=$src_docker docker exec -e RSYNC_PASSWORD="$g_password" -w /data "$src_did" \
                rsync -aivxsAHSX --progress /data/ rsync://root@localhost/data
            

            这种方法的另一个真正好处是,您可以在两个远程主机之间复制数据,而无需在本地存储数据。我也围绕这个想法share the script ,docker-rsync-volumes that I've written。使用该脚本,从两个远程主机复制卷很简单,docker-rsync-volumes --delete -f ssh://user@productionserver grafana_data -t ssh://user@backupserver grafana_data_backup。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2016-11-22
              • 1970-01-01
              • 2015-03-25
              • 2014-06-16
              • 2017-04-30
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多