ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Docker 数据迁移:daemon.json 与 containerd 双目录方案

Docker 数据迁移:daemon.json 与 containerd 双目录方案 当你打开监控面板发现 /var/lib/docker 已经用了 87%第一反应是不是去网上搜“Docker 镜像路径迁移”搜出来的答案十有八九都是同一句话改 daemon.json把>docker version --format {{.Server.Version}} docker info | grep -E Storage Driver|Snapshotter|Docker Root Dir du -sh /var/lib/docker /var/lib/containerd 2/dev/null重点看 docker info 的输出里有没有 Snapshotter 这一项。如果输出类似于Storage Driver: overlay2 Snapshotter: containerd-snapshotter Docker Root Dir: /var/lib/docker那就说明你的 Docker 已经启用了 containerd image store。此时 /var/lib/containerd 的大小要重点关注。还有一个小技巧看 /var/lib/docker/image/overlay2 目录的大小变化。如果你发现这个目录一直不涨但 /var/lib/containerd 涨得飞快那几乎可以确定镜像层已经交给 containerd 管理了。另外注意Docker 29.x 的 client 和 server 端版本要分开看。有些服务器上 docker 命令行是新的但 daemon 可能还是旧版或者被系统包管理器锁住了这时候 docker info 的 Storage Driver 一栏会直接告诉你真实情况。2. 动手前必做的磁盘盘点与迁移方式选型2.1 数据量盘点先搞清楚要搬多少东西我之前见过不少同事迁移前不做盘点直接 systemctl stop docker然后 rsync 跑一半才发现目标盘空间不够。这是非常尴尬的。迁移前最好用 docker system df 看整体占用docker system df它会分别显示镜像、容器、本地卷、构建缓存占用的空间。然后再用 du 精确确认哪些路径占地方du -h --max-depth1 /var/lib/docker 2/dev/null du -h --max-depth1 /var/lib/containerd 2/dev/null这里有一个经验值如果 containerd 目录已经超过整个 Docker 数据占用的 30%说明镜像存储确实在 containerd 侧只搬 /var/lib/docker 是绝对不够的。目标磁盘的规划也别忘了。建议先用 df -h 确认目标分区的可用空间比源数据大至少 20%因为 overlayfs 的 snapshotter 需要一定的临时空间来做解压、合并操作。空间不够的话后面迁移过程中任何一步都可能卡住。2.2 方案选型矩阵根据 Docker 的实际存储模式我把常见场景整理成一张选型表大家可以对号入座场景典型现状推荐方案经典模式未启用 containerd image store/var/lib/docker 不断增大/var/lib/containerd 很小daemon.json 改>systemctl stop docker.socket docker containerd有人只停 docker不停 docker.socket结果 systemd 检测到 socket 由外部请求激活又自动把 dockerd 拉起来了导致 rsync 同步过程中有文件在写入迁移完数据不一致。docker.socket 必须一起停。第二步确认进程已经退出ps aux | grep -E dockerd|containerd | grep -v grep || true如果还有残留进程再等几秒或手动 kill。不要用 kill -9先给优雅退出机会否则 containerd 的 metadata 可能没落盘前面积累的镜像记录会丢。第三步全量同步。这里强烈推荐 rsync而不是 cp 或 mv。cp 不会保留硬链接、ACL、xattr迁移 overlay2 目录时这些信息非常关键mkdir -p /data/docker rsync -avxHAXP --numeric-ids --delete /var/lib/docker/ /data/docker/各参数说明-a归档模式保留权限、时间戳、软链-v显示进度-x不跨文件系统边界-H保留硬链接overlay2 层之间大量硬链接漏了这个会白增空间-A保留 ACL-X保留 xattrSELinux 上下文依赖这个-P显示进度并支持断点续传--numeric-ids保持 uid/gid 数字值避免迁移后属主错乱--delete删除目标端多余文件保证和源目录一致第四步改 daemon.json{ data-root: /data/docker, log-driver: json-file, log-opts: { max-size: 10m, max-file: 3 } }注意修改 daemon.json 后直接用 systemctl restart docker 重启即可不需要 systemctl daemon-reload。daemon-reload 是重载 systemd unit 文件用的很多人把这两个混为一谈。第五步启动并验证systemctl start containerd docker docker info --format {{.DockerRootDir}} docker ps -a | head -20 docker images | head -20docker info 显示 /data/docker 后再随机启动一个小容器测试确认容器读写层正常。第六步确认没问题后老目录先不要删除。建议保留两周mv /var/lib/docker /var/lib/docker.bak.$(date %F)线上环境我一般保留一个月。Docker 数据迁移这种事最怕跑一段时间发现某个老容器需要回滚老目录已经删了。3.2 为什么很多教程漏了 containerd 和 docker.socket现在回头看很多教程只写“systemctl stop docker”是大坑。首先 docker.socket 不停服务可能被 socket 激活自动拉起其次 containerd 虽然是 dockerd 的下游组件但在 systemd 里是独立服务你不显式停掉它rsync 期间它可能还在写自己的状态文件迁移出来的 containerd 目录你根本没法保证一致性。所以完整的停止顺序是先 docker再 containerd。启动顺序反过来先 containerd 再 docker。虽然多数 systemd 配置会自动维护依赖但你手动操作时按这个顺序最稳。4. 29.x 进阶containerd image store 的同步迁移与 bind mount 方案4.1 同时迁移 containerd 数据目录的标准做法如果确认开启了 containerd image store那就别自欺欺人了/var/lib/containerd 必须一起搬。我推荐的做法是用 bind mount而不是去改 /etc/containerd/config.toml 里的 root 参数。原因是 containerd 的 state 默认在 /run/containerdDocker 通过 /run/containerd/containerd.sock 与 containerd 通信。如果你把 root 和 state 都改了还得去 daemon.json 里同步 containerd address链条变长出错概率急剧上升。bind mount 保持原路径不变所有配置不用动。具体步骤如下systemctl stop docker.socket docker containerd mkdir -p /data/docker /data/containerd rsync -avxHAXP --numeric-ids --delete /var/lib/docker/ /data/docker/ rsync -avxHAXP --numeric-ids --delete /var/lib/containerd/ /data/containerd/ mount --bind /data/docker /var/lib/docker mount --bind /data/containerd /var/lib/containerd然后把挂载关系写入 /etc/fstab保证重启后自动挂载/data/docker /var/lib/docker none bind 0 0 /data/containerd /var/lib/containerd none bind 0 0之后启动服务systemctl start containerd docker这里有一个容易踩的坑bind mount 之前一定要确保 /var/lib/docker 和 /var/lib/containerd 已经迁完了。因为 bind mount 会把旧路径“遮住”原来目录里的内容不会消失但你在 mount 之后看到的已经是新目录了。如果你先 mount 再 rsync很容易把旧目录里的残留当成新数据最后磁盘空间“凭空消失”。4.2 软链方案为什么不推荐除非你理解透彻还有一种流行做法是在 /var/lib/docker 上做软链接指向数据盘ln -s /data/docker /var/lib/docker确实有人这么干但我不推荐。dockerd 对>systemctl restart docker journalctl -u docker -n 100日志里没有明显报错于是检查 /var/lib/docker/image/overlay2 目录发现它是空的说明镜像记录不在这一侧。再看 /var/lib/containerd一下子明白了containerd 的 io.containerd.content.v1.content 还有几十 GB 数据而且根本没被 rsync 覆盖。回到前面说的 bind mount 方案把 /var/lib/containerd 挂到新盘之后再执行 docker images镜像列表马上回来了。这个案例完美说明29.x 上“只改 daemon.json”确实是个陷阱。6.3 非 x86 平台的一点提醒在部分非 x86 平台比如 ARM 服务器、龙芯、飞腾等环境上containerd 的 snapshotter 可能不是默认的 overlayfs。迁移前最好先执行 docker info看 Snapshotter 的名字。如果显示的是 devicemapper 或者其他自定义 snapshotter那迁移逻辑会不一样rsync 之后还要检查对应的设备映射状态否则镜像层数据可能“看似在盘上实际没被挂载”。7. 避免再次迁移的几种长期姿势7.1 新机器直接独立盘挂载别等搬到半路再后悔如果你是在新机器上装 Docker不要图省事直接默认路径。我现在的习惯是准备一块独立数据盘格式化后挂载到 /data然后在 /etc/fstab 里预先配置好/data/docker /var/lib/docker none bind 0 0 /data/containerd /var/lib/containerd none bind 0 0再在 daemon.json 里加一行{ data-root: /data/docker }这样装完 Docker 之后所有数据直接落在规划好的数据盘上后面再也不会被根目录空间告警追着跑。Ubuntu 安装 Docker 的时候尤其适合这么做否则默认安装完 Docker 后/var/lib/docker 就在系统盘上时间一长非常被动。7.2 日常巡检与日志轮转控制镜像路径增长路径迁完不代表一劳永逸日常巡检还是要做。最实用的是定期执行docker system df docker system df -vdocker system df -v 能看到每个镜像、容器、构建缓存的具体占用。如果发现某些遗留容器大量使用可写层考虑把数据目录用 docker compose 的 volume 挂载到数据盘上别让它写在容器可写层里。日志轮转也非常重要。daemon.json 里配置 log-opts 之后容器日志单文件会被限制在 10MB 以内最多保留 3 个文件否则一个高频日志应用几天就能把刚迁移出来的空间又占满。7.3 后续扩展Compose 项目的数据卷再独立如果是跑 docker compose 的项目建议把数据库、缓存、上传文件这类高 IO 目录单独挂载到数据盘。一个典型写法是services: mysql: image: mysql:8.0 volumes: - /data/mysql:/var/lib/mysql这样即使 Docker 自己的镜像路径又出问题核心业务数据仍然在独立挂载目录里迁移和备份都会简单很多。日常操作中也更推荐用 docker compose 管理多容器项目配合独立的 volume 数据盘路径迁移的痛感会小很多。最后说点我个人实际操作中的体会。镜像路径迁移这件事最怕的不是命令不会写而是“以为迁完了”。我在迁移后一定会跑三遍校验第一遍 docker ps -a 看容器列表第二遍 docker images 看镜像列表第三遍 du -sh /var/lib/docker /var/lib/containerd 对比新旧盘。只有这三个结果都对得上我才会把旧目录改名为 .bak。另外删旧目录之前记得看一眼容器的重启策略确认哪些是 --restart always哪些是不小心创建的临时容器。迁移本身不复杂但后续的收尾和观察才是真正见经验的地方。
返回列表