☰
Docker Root目录迁移实战:用daemon.json和rsync安全搬移data-root
2026/9/30 8:08:15 网站建设 项目流程

1. 磁盘满了才明白的事:Docker Root目录为什么要动

很多用Docker的人都会遇到一个很尴尬的场景:业务没多大,但磁盘告警邮件一封接一封。排半天发现根分区被占满,一查罪魁祸首基本都在/var/lib/docker下面。这里堆着镜像分层、容器读写层、卷数据、构建缓存,随便几套镜像加日志就能吃掉几十个G。你使用df -h一看,根分区快满了,旁边挂载的/home或数据盘倒是空荡荡,这就是典型的Docker Root目录迁移需求。

Docker的Root目录,术语叫># 查看当前data-root路径 docker info --format '{{.DockerRootDir}}' # 查看整个Docker Root目录占用空间 sudo du -sh /var/lib/docker # 查看各子目录占用,方便确认大头在哪 sudo du -sh /var/lib/docker/* | sort -rh | head -20

我见过不少人迁移完才发现自己数据量远没有想象的大,磁盘空间问题其实是日志或别的东西占的。docker info输出里显示的就是Docker实际使用的Root目录,确认它是不是默认的/var/lib/docker。如果已经改过,那就不用迁了。du命令是看真实占用,docker system df也是好工具,但du更直接,能看到物理文件到底多大。

这一步的价值是让你知道:搬家的东西有多少、搬完之后能释放多少空间。如果目标分区可用空间不大,果断做镜像清理或先导出再处理,不要等到迁移中才发现目标盘满了,骑虎难下。

2.2 摸清容器是怎么跑起来的

迁移过程中最容易被忽略的坑,是容器的启动方式。Docker服务重启后,能不能自动拉起容器,取决于你当时是怎么创建容器的。

  • 用docker run创建的容器:如果启动时带了--restart=always或--restart=unless-stopped,Docker服务重启后容器会自动恢复。如果没带,重启后它就是Exited状态,服务就断了。
  • 用docker-compose管理的容器:Docker服务重启后,restart策略同样生效,但docker-compose up手动创建的容器组,有时需要重新up -d才能恢复。
  • 用docker run -v挂载的宿主机目录:这些目录通常在/var/lib/docker之外,不在Root目录迁移范围内,但写进容器配置的路径信息不会因为迁移改变。

所以在动工之前,把每个重要容器都看一眼:

# 查看所有容器的重启策略 docker inspect --format '{{.Name}} -> {{.HostConfig.RestartPolicy.Name}}' $(docker ps -aq)

如果发现有容器没设restart策略,要么现在补上,要么迁移后手动启动。我曾遇到过生产环境一批容器没设重启策略,迁移完重启Docker,服务看着正常,实际上业务全断了——因为容器没跟着起来。这个细节特别容易栽跟头。

2.3 备份与回滚预案:别把老底都清了

迁移有一个安全底线:旧目录不要立刻删除。原因很简单,迁移本质上就是“把数据复制到新位置 → 让Docker使用新位置 → 验证一切正常”,旧数据是回滚的保命底牌。

具体思路是这样的:

  1. 迁移前,保留/var/lib/docker原封不动。
  2. 同步数据用rsync复制到新目录,而不是mv直接搬家。复制完校验没问题,再把原目录改名(比如mv /var/lib/docker /var/lib/docker.bak)。
  3. 验证新目录一切正常后,再把.bak目录删掉。如果中途出问题,改回配置、恢复目录名,Docker就跟没迁过一样。

另外,docker-compose.yml、daemon.json、容器的docker inspect导出信息,这些配置文件是软资产,迁移前最好单独存一份。尤其是分布式的部署目录结构,有些容器依赖固定的挂载路径,这些内容与Root目录独立,但千万不要因为迁Root目录就忽略备份。

3. 主方案:修改daemon.json + rsync同步,稳妥且可控

Root目录迁移方案不少,什么改环境变量、软链接指向、直接mount绑定,我都试过。在单机场景下,最稳妥且可维护的方案是:停Docker服务 → rsync同步数据 → 修改/etc/docker/daemon.json→ 重启服务 → 验证。

3.1 为什么选daemon.json控制,而不是软链接

很多人图省事,直接用ln -s把/var/lib/docker软链接到大目录,这个方案确实能跑,但我不推荐。原因有两点:

第一,Docker每次启动都会往Root目录写文件,如果软链接断掉、或者新装环境没有建立软链接,Docker会在默认位置新建一个空的/var/lib/docker,让你误以为数据还在。这个行为极坑,排查起来绕一大圈。

第二,官方支持的配置入口就是daemon.json。这个文件里可以设置>sudo systemctl stop docker sudo systemctl stop docker.socket

为什么要把docker.socket也停掉?因为新版Docker使用socket激活机制,如果只停了docker.service、docker.socket还在监听,你执行任何docker命令都可能触发socket重新拉起dockerd,导致迁移过程中Docker突然开始写数据。这个坑不踩不知道,踩一次能让你怀疑人生。如果用的是service docker stop的老系统,注意看docker命令有没有被动触发。

同时确认进程确实结束:

sudo ps aux | grep dockerd | grep -v grep

正常应该没有dockerd进程。如果还有,等一会儿或手动处理残留进程,确保迁移期间没有进程写旧目录。

第2步:同步数据到新目录

sudo mkdir -p /data/docker sudo rsync -aAHSX --info=progress2 /var/lib/docker/ /data/docker/

参数说明:

  • -a:归档模式,保留权限、属主、时间戳、符号链接等。
  • -A:保留ACL访问控制列表。
  • -X:保留扩展属性(比如user.开头的属性)。
  • -H:保留硬链接。Docker镜像分层有大量硬链接结构,没有-H会导致镜像损坏或空间统计失真。
  • --info=progress2:显示整体进度和速度。

有人用cp -a替代,也能完成,但rsync优势在于可断点续传、可增量同步,数据量大时更可靠。迁移过程时间长短取决于数据量,几十G可能需要几分钟,耐心等待,不要中途打断。

第3步:改配置指向新路径

编辑/etc/docker/daemon.json(没有则新建):

sudo vim /etc/docker/daemon.json

写入或合并以下内容:

{ "data-root": "/data/docker" }

如果你已有这个文件,比如配过镜像加速器,就只增加一行"data-root": "/data/docker",别覆盖原有内容。改完后可以用python3 -m json.tool /etc/docker/daemon.json验证JSON格式,格式错了Docker会直接起不来。

第4步:重载并启动Docker

sudo systemctl daemon-reload sudo systemctl start docker

注意systemctl daemon-reload不是可选项。改了配置文件后必须重载一次,让systemd读取最新的服务配置。启动后稍等几秒,让Docker完成初始化。

第5步:确认Root目录已切换成功

docker info --format '{{.DockerRootDir}}'

如果输出的是/data/docker,迁移主流程就成功了。接下来再看下容器状态:

docker ps -a

所有容器应该处于之前的运行状态,或者至少配置了自动重启的容器已经Up。这里有些容器会显示Restarting,别慌,稍后专门说怎么处理。

3.3 为什么同步数据要多留一步验证

直接rsync完就改配置,其实还少一个关键动作:校验新目录和旧目录的数据一致性。数据量不大时,可以比对一下文件数:

sudo diff -rq /var/lib/docker /data/docker | head -20

如果没有任何输出(或只有两边的lost+found等差异),说明一致性没问题。如果大量差异,说明迁移动作期间Docker还在偷偷写旧目录,基本就是socket没停干净,或者还有残留进程,需要回去检查。

数据量很大的情况下,diff -r会比较慢,可以用rsync -avnc做一次dry-run校验,比如:

sudo rsync -avnc /var/lib/docker/ /data/docker/

加上-n只显示将要同步的文件列表,加上-c基于校验和对比,而不是时间大小。如果输出为空或只有少量差异,就说明同步到位了。我习惯在rsync后追加这一步,虽然多花一点时间,但能提前暴露问题,避免启动Docker后才发现镜像损坏。

4. 启动后不可跳过的事项:容器状态检查与异常恢复

迁移完不是万事大吉,恰恰是问题集中暴露的阶段。按照下面的检查顺序走一遍,能帮你把风险降到最低。

4.1 容器状态与日志检查清单

# 查看所有容器状态,正常是Up,异常是Exited/Restarting docker ps -a # 查看最近容器日志,确认业务是否正常 docker logs --tail 50 <容器名> # 检查镜像列表,确认layer都还在 docker images # 检查卷列表,确认volume没有丢失 docker volume ls # 查看磁盘占用概览 docker system df

特别提醒:迁移后不要马上删旧目录。先让业务跑几天,确认一切正常后再决定清理。RyRoot目录迁移后,旧目录默认还在原地占空间。如果你要释放空间,等校验无误后,执行:

sudo rm -rf /var/lib/docker.bak

顺便强调一点:如果容器数量多,用docker ps -a看到的状态和迁移前对比一下。如果一个重要的容器是Exited,先检查它是不是本来就没设restart策略,直接手动启动即可。

4.2 启动失败排查:最常见的五个原因

如果执行systemctl start docker后服务是failed状态,别慌。用journalctl -u docker看日志,以下是我见过最多的几种原因:

症状大概率原因处理方式
Error starting daemon: ... not a directory>sudo systemctl stop docker sudo rm -rf /data/docker sudo mv /var/lib/docker.bak /var/lib/docker # 这里假设你之前把原目录改名备份了 sudo systemctl start docker

如果你改过daemon.json,启动前先把>docker info | grep "Storage Driver"

如果显示overlay2,新目录又在一个正常ext4/xfs分区,基本不会有问题。如果显示别的,迁移前停下来好好研究一下。

5.3 迁移后镜像拉取与网络问题

数据搬完、服务启动后,如果发现docker pull速度极慢或超时,很可能和daemon.json里原有的镜像加速配置有关。迁移后有些习惯性操作容易出岔子——比如你在编辑daemon.json时用了一个不完整的配置,覆盖了原来配好的镜像源。镜像源配置和>docker volume inspect <卷名> --format '{{.Mountpoint}}'

注意看输出路径是否已经指向新Root目录。凡是在Root目录下的卷,迁移后相当于跟着“搬家”了,完全没问题。反而是那些没有用volume、直接挂载宿主机路径的-v /host:/container容器,才是真·独立数据,跟Root目录迁移没有关系。

我个人在实际操作中的体会是:Root目录迁移本身不可怕,可怕的是迁移前没做环境摸底,迁移后没做系统验证。你只要把本文的步骤走完,特别注意socket停干净、rsync带-H、daemon.json别覆盖、旧数据先留后删这四个要点,基本就能平稳完成迁移。

如果你现在正好遇到磁盘告警,别急着开机清日志、删镜像。先去摸清楚Root目录的占用,按照上面的流程把数据挪到数据盘上,这个问题才算真正解决。数据搬完,空间腾出来了,以后容器跑起来也安心得多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询