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创建的容器如果启动时带了--restartalways或--restartunless-stoppedDocker服务重启后容器会自动恢复。如果没带重启后它就是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使用新位置 → 验证一切正常”旧数据是回滚的保命底牌。具体思路是这样的迁移前保留/var/lib/docker原封不动。同步数据用rsync复制到新目录而不是mv直接搬家。复制完校验没问题再把原目录改名比如mv /var/lib/docker /var/lib/docker.bak。验证新目录一切正常后再把.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 --infoprogress2 /var/lib/docker/ /data/docker/参数说明-a归档模式保留权限、属主、时间戳、符号链接等。-A保留ACL访问控制列表。-X保留扩展属性比如user.开头的属性。-H保留硬链接。Docker镜像分层有大量硬链接结构没有-H会导致镜像损坏或空间统计失真。--infoprogress2显示整体进度和速度。有人用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步重载并启动Dockersudo 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如果没有任何输出或只有两边的lostfound等差异说明一致性没问题。如果大量差异说明迁移动作期间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目录的占用按照上面的流程把数据挪到数据盘上这个问题才算真正解决。数据搬完空间腾出来了以后容器跑起来也安心得多。