Bookmarks

You haven't yet saved any bookmarks. To bookmark a post, just click .

  • Docker volume 跨服务器迁移

  • 前言

    本文是 Docker 系列文章的第四篇,Docker volume 的跨服务器迁移。本文暂不讨论 Docker 容器的迁移,只讨论数据的迁移,要解决的问题很明确,就是使用 Docker 搭建了各种服务后,万一需要更换服务器,数据如何无损的由一台服务器迁移到另一台服务器。

    这个问题比我想象的要复杂,在官方推荐我们使用卷(volume)进行数据存储时,曾提到过这将方便迁移,但实际上,当面临迁移服务器这一问题时,直接使用文件夹绑定的方式更为方便,只需要将文件夹复制到新服务器即可,若使用了 volume,直接复制到新的服务器中的/var/lib/docker/volumes文件夹下并不会生效,需要修改对应的配置文件,既不方便也不优雅。

    本文参考了官方指南以及 stackoverflow上的多个回答,将不省略任何细节,用最清晰的方式讲清楚这一问题。


    实现流程

    要对容器 volume 进行迁移,主要流程如下:

    • 打包现有 volume 内容为 tar 文件
    • 将 tar 文件传输至新服务器
    • 在新服务器中创建中间容器
    • 解压 tar 文件至 volume 对应目录
    • 清理中间容器等

    打包现有 volume 内容为 tar 文件

    实现这一步操作的方式主要使用了 --volumes-from CONTAINER这一命令,这一命令的功能是挂载其他容器的 volume。

    为了实现打包现有 volume 内容,我们需要创建一个中间容器,这个容器的作用就是在挂载了需要备份的 volume 之后,在该容器中使用 tar 命令将其对应的文件夹打包,并通过文件夹映射的方式,将打包的文件共享至宿主机中。

    作为中间容器的镜像可以自由选择, stackoverflow 回答中使用了 busybox,这里就以 busybox 为例:

    docker run --rm --volumes-from CONTAINER -v $(pwd):/backup busybox tar cvf /backup/backup.tar /DIR_TO_BAKUP
    

    为了更加清晰,将上面的命令进行分行如下:

    docker run --rm --volumes-from CONTAINER \
    -v $(pwd):/backup \
    busybox \
    tar cvf /backup/backup.tar -C /DIR_TO_BAKUP ./ 
    

    命令中大写字母内容就是我们需要修改的,CONTAINER 需要修改为需要备份 volume 的容器名,/DIR_TO_BAKUP 需要修改为 volume 在容器中的路径,对命令的各行进行解释如下:

    • --rm 表示该容器是一次性环境,生成后就自动删除,我们只需要运行一次备份文件而已
    • -v $(pwd):/backup 表示我们将容器的/backup目录映射到主机当前目录
    • busybox 镜像名,这里也可以替换为其他镜像
    • tar cvf /backup/backup.tar -C /DIR_TO_BAKUP ./表示将容器中/DIR_TO_BAKUP文件夹的内容打包,并存储在/backup文件夹下,文件名为backup.tar,结合前面那行-v $(pwd):/backup就可以实现将 volume 内容打包为宿主机当前目录下的backup.tar文件

    请注意,在测试后对这里进行了修改,增加了 -C 参数,增加 -C 参数可以避免出现打包的文件包括了我们不需要的目录,比如我们想要打包的是/var/lib/mysql目录下的全部文件,使用了 -C 参数并结合最后的 ./ 可以避免最终生成的 tar 文件解压后包括 /var/lib/mysql

    将 tar 文件传输至新服务器

    这一步可以各显神通,既可以通过 FTP 下载上传,也可以使用 rsync 工具进行传输,我们采用 rsync 进行传输,这需要你拥有原服务器的 SSH 权限。

    具体流程如下:

    • SSH连接新服务器,并在新服务器中安装 rsync
    sudo apt-get update
    sudo apt-get install rsync
    
    • 使用 rsync 将备份文件复制至新服务器

    rsync 的更多使用方式请自行搜索,这里我们通过 SSH 方式进行传输,命令形式为 rsync -av 原服务器IP:原服务器中文件 目标文件夹

    我们将前文备份的 backup.tar 文件复制至新服务器的当前目录为例:

    rsync -av --progress root@原服务器IP:/root/backup.tar .
    

    可以在本地安装 rsync 作为中转,下载完备份文件后再上传到新服务器:

    rsync -av --progress backup.tar root@新服务器IP:/root/
    

    在新服务器创建中间容器

    通过前面的操作,我们已经成功的将原服务器中的 volume 以 tar 文件形式传输到了新服务器,下面的工作就是如何将该 tar 文件转换为 volume。

    首先我们需要使用 docker create 命令创造一个仅用于迁移的容器:

    docker create -v TARGET_VOLUME_NAME:/data --name for_migrate busybox true
    

    其中TARGET_VOLUME_NAME请修改为你的目标 volume 名,/data是 volume 在容器内对应的文件夹,既然只是中间容器无须修改,for_migrate名字随你心情

    解压 tar 文件至 volume 对应目录

    我们通过一次性容器将备份的 tar 文件内容解压缩至 volume 中:

    docker run --rm --volumes-from for_migrate -v $(pwd):/backup busybox tar xvf /backup/backup.tar -C data/
    

    更清晰的分行形式:

    docker run --rm --volumes-from for_migrate \
    -v $(pwd):/backup \
    busybox \
    tar xvf /backup/backup.tar -C data/
    

    如果你按照前文的操作,在新服务器当前文件夹下有一个 backup.tar 文件,就可以直接使用上面的命令,如果你的操作与前文有差异,请自行修改对应的文件夹
    解压时同样使用了 -C 参数,读者可自行了解 tar 命令说明

    其他工作

    通过上面的操作,你已经成功的将旧服务器中的 volume 迁移到了新服务器。这时我们将中间容器 for_migrate 删除即可,全部工作到此就算基本完成了。

    现在你可以将生成的 volume 应用到其他容器了,通过 docker export从旧服务器恢复容器或者直接创建新容器都可以。

    实例操作

    本文已经通过本人 Nextcloud 服务器迁移测试成功,所有文件和配置全部成功迁移,之前生成的共享链接也没有发生变化,为了本文读者能更清晰的了解这一过程,下面将增加 Ghost 博客的迁移示例。

    本次示例将直接以本站进行示范,本站使用了反向代理隐藏了服务器真实IP,为避免服务器IP泄漏,下文中出现的原服务器IP均已经过处理,新服务器是一台 DigitalOcean 的服务器,本文完成后即销毁

    本次迁移的新服务器IP地址为 209.97.168.182,两台服务器均使用 Ubuntu 18.04 系统,本次迁移的 Ghost 数据库使用 sqlite 不使用 mysql,因此也不使用 docker-compose 搭建,仅包括一个 volume。

    原服务器打包

    原服务器 Ghost 容器名为 ghost,volume 名为ghost_content,对应的容器内目录为/var/lib/ghost/content,这些信息可以通过docker inspect CONTAINER获得。

    打包的命令如下:

    docker run --rm --volumes-from ghost -v $(pwd):/backup busybox tar cvf /backup/backup.tar -C /var/lib/ghost/content ./
    

    运行之后等待 tar 完成打包即可,当前目录下将出现 backup.tar 文件。

    传输数据至新服务器

    可参考上文使用 rsync 传输,也可以使用 sftp 传输,由于服务器仅开放了密钥登录,又不想绑定新服务器密钥(需要重启太麻烦),测试时使用了 sftp 传输至本地后再由本地 sftp 上传至目标服务器。

    新服务器创建中间容器

    我们沿用原服务器中 volume 的名称ghost_content,当然这个不沿用也没任何问题,需要注意的是,如果这是通过 docker-compose 创建的容器,在 docker-compose.yml中的 volume 名都会被添加前缀,在命名 volume 的时候需要注意匹配。

    docker create -v ghost_content:/data --name for_migrate busybox true
    

    将数据解压至 volume

    docker run --rm --volumes-from for_migrate -v $(pwd):/backup busybox tar xvf /backup/backup.tar -C data/
    

    删除中间容器

    docker rm for_migrate
    

    使用恢复的 volume 创建新的 Ghost 容器

    docker run -d --name ghost -p 2368:2368 \
    -v ghost_content:/var/lib/ghost/content \
    ghost
    

    运行上面的命令后,我们就使用恢复的 volume 创建了新的 Ghost 容器,如果需要保证兼容性,可以在 ghost 后增加原服务器中所使用的版本 Tag,相应的版本 Tag 可以查看其镜像的 Docker Hub 主页。

    我们访问http://209.97.168.182:2368,可以看到已经将原服务器数据完好的迁移到了新服务器:

    docker-migrate-sample.png

    此时到nginx反向代理服务器中再配置一下反代地址,我们就将网站完好的迁移到了新的服务器。
    docker-migrate-nginx.png

    一点小问题

    测试中发现左上角的 LOGO 会链接到http://localhost:2368,这需要我们对/var/lib/ghost目录下的config.production.json文件进行修改,我们需要进入容器内部,安装 vim 后对该文件进行修改。

    docker exec -it ghost /bin/bash
    

    修改上面命令后将会进入 Ghost 容器内部,我们安装 vim:

    apt-get update
    apt-get install vim
    

    之后继续在容器内部修改config.production.json:

    vim config.production.json
    

    将第一行的内容改成你的网站网址即可:

    docker-migrate-exec.png

    为了方便修改配置,也可以选择将 /var/lib/ghost文件夹映射到宿主机中。

    参考内容

    本文撰写参考了以下内容,向作者表示感谢:

    1. Manage data in containers
    2. Use volumes
    3. How to port data-only volumes from one host to another?
    4. tar解压tar.gz文件时报tar not found in archive解决办法