资讯动态

Docker私有镜像仓库迁移恢复实战:registry.tar.gz结构解析与避坑指南

发布时间:2026/10/20 22:00:28 来源:尧图企业网站定制
简介本资源为面向内网离线环境的Docker Registry镜像包主要服务于无法连接外网、需要在内网搭建私有镜像仓库的运维与开发人员。包内共18个文件以json元数据、tar层文件、version版本标识及repositories索引为主完整保留了镜像的分层结构与清单信息压缩包整体约25.15MB体积轻便便于内网传输与分发。目前已有436人学习下载适合需要快速部署私有仓库、规避外网依赖的团队参考使用。借助该镜像包读者可在隔离网络中直接导入并启动Registry服务省去逐层拉取镜像的繁琐过程同时借助其中的清单与版本文件核对镜像完整性为内网镜像存储、分发与版本管理提供基础支撑降低离线环境下的部署门槛。1. registry.tar.gz 到底是什么从一次镜像仓库迁移翻车说起同事把registry.tar.gz丢过来的时候只留了一句「这是私有镜像仓库的备份你恢复一下」。我解压一看里面是docker/registry/v2/目录树blobs、repositories、manifests 层层嵌套跟平时docker save出来的镜像包完全不是一回事。这就是 Docker Registry也就是常说的 registry:2的数据目录打包它保存的是整个仓库的存储层不是单个镜像。搞混这两者是后面所有翻车的起点。registry.tar.gz解决的核心问题是把一台机器上跑着的私有镜像仓库完整搬到另一台机器或者做冷备份。它适合自建 Harbor、registry:2 的团队尤其是内网环境、镜像层数多、不想重新 push 一遍的场景。读完你能搞清楚它内部结构、怎么恢复、怎么迁移、怎么瘦身以及哪些操作会让仓库直接报废。2. 拆开 registry.tar.gz存储布局与恢复前的判断2.1 registry:2 的目录结构长什么样Docker Registry v2 的存储后端默认是 filesystem根目录下就是docker/registry/v2/。解压后你会看到三个关键目录blobs/真正的内容寻址存储按 sha256 前两位分桶比如blobs/sha256/ab/abcdef...。镜像层、配置、manifest 全在这里文件名就是内容的 sha256。repositories/按仓库名组织repositories/name/_manifests/下存 tag 到 digest 的映射_layers/下存 layer 的链接。_uploads/分块上传的临时目录正常关闭的仓库里应该是空的有残留说明上次上传中断过。判断一个registry.tar.gz能不能直接用先看它是不是从docker/registry/v2/这一层打包的。如果打包时多套了一层目录恢复后 registry 会找不到数据表现为 push 报错、pull 404。2.2 恢复前必须确认的三件事第一registry 的版本。registry:2 和 registry:3 的存储布局有差异v3 对 OCI artifact 支持更好但直接拿 v2 的数据目录喂给 v3 不一定兼容。常见做法是先看原环境docker inspect registry的镜像 tag。第二存储驱动。如果原仓库用的是 S3、Azure Blob 这类后端registry.tar.gz里可能只有配置没有数据或者数据是缓存。filesystem 后端的包才是自包含的。第三config.yml。registry 的行为由配置文件决定storage.filesystem.rootdirectory指向数据目录http.addr决定监听端口。恢复时如果配置对不上数据在也读不出来。# 查看包内顶层结构确认打包层级 tar -tzf registry.tar.gz | head -20 # 期望看到 docker/registry/v2/ 开头 # 如果看到 registry/docker/registry/v2/ 说明多了一层tar -tzf只列出内容不解压head -20看前 20 行足够判断层级。如果层级不对恢复时要么改rootdirectory要么重新打包去掉多余前缀。3. 把 registry.tar.gz 跑起来最小恢复流程与参数3.1 准备目录与配置文件恢复的核心就一句话把数据放到 registry 能读到的位置然后用匹配的配置启动容器。我一般会在宿主机建/data/registry解压后确保rootdirectory指向它。# 建数据目录 mkdir -p /data/registry # 解压注意 -C 指定目标目录 tar -xzf registry.tar.gz -C /data/registry # 确认最终路径是 /data/registry/docker/registry/v2 ls /data/registry/docker/registry/v2-C把解压结果直接落到目标目录避免先解压再 mv 这种多余操作。ls确认blobs、repositories都在。配置文件config.yml最小可用版本version: 0.1 storage: filesystem: rootdirectory: /var/lib/registry delete: enabled: true http: addr: :5000rootdirectory在容器内是/var/lib/registry所以启动时要把宿主机的/data/registry挂到容器的/var/lib/registry。delete.enabled: true是后面清理镜像的前提默认是 false不开启的话删 tag 会报 405。3.2 启动容器并验证docker run -d \ --name registry \ -p 5000:5000 \ -v /data/registry:/var/lib/registry \ -v /data/config.yml:/etc/docker/registry/config.yml \ registry:2-v两个挂载一个数据一个配置。启动后先看日志docker logs registry # 正常输出包含 listening on [::]:5000验证仓库里有什么# 列出所有仓库 curl -s http://localhost:5000/v2/_catalog # 列出某个仓库的 tag curl -s http://localhost:5000/v2/myapp/tags/list_catalog返回仓库名列表tags/list返回具体 tag。如果_catalog为空但repositories目录有内容多半是权限问题——registry 容器内以 root 跑宿主机目录如果属主不对会读不到。3.3 迁移到另一台机器的完整链路迁移不是简单拷贝要保证 digest 不变。步骤是停写、打包、传输、恢复、验证。# 1. 停掉 registry避免打包时数据在变 docker stop registry # 2. 打包注意在数据目录的父级执行 cd /data/registry tar -czf registry-$(date %Y%m%d).tar.gz docker/ # 3. 传到目标机器后按 3.1 恢复打包时cd到数据目录父级再tar docker/这样包内路径就是docker/registry/v2/...恢复时不用额外处理层级。$(date %Y%m%d)给包加日期方便回滚。验证迁移是否成功不能只看_catalog要拉一个镜像跑一下docker pull localhost:5000/myapp:latest docker images | grep myappdigest 一致才算真正成功。docker pull会校验每层 sha256任何一层对不上都会报错。4. 避坑与排查registry.tar.gz 恢复的 5 个血泪教训4.1 现象恢复后 push 报 500日志显示 blob 写入失败原因宿主机目录权限不对。registry 容器内进程以 root 运行但如果你用-u指定了非 root 用户或者宿主机目录属主是别的用户写blobs/时会 permission denied。解决chown -R 1000:1000 /data/registry或直接chmod -R 755确保容器内进程有写权限。更稳妥的做法是不加-u让容器用默认 root。4.2 现象_catalog有仓库但 pull 某个 tag 报 manifest unknown原因repositories/name/_manifests/下的 tag 链接指向的 digest 在blobs/里不存在。常见于打包时 registry 还在运行manifest 写了一半或者打包过程中有 GC 在删 blob。解决停写后再打包。如果已经这样了只能从源仓库重新 push 缺失的 tag或者用registry garbage-collect清理掉悬空引用后重新同步。4.3 现象删除 tag 报 405 Method Not Allowed原因config.yml里没开storage.delete.enabled。registry 默认禁止删除这是防止误删的保护。解决加上delete: enabled: true后重启容器。注意开启后删除只是删 tag 引用blob 还在要真正释放空间得跑 GC。4.4 现象GC 之后仓库变小了但某些镜像 pull 失败原因GC 删掉了「没有被任何 manifest 引用」的 blob但如果此时还有 manifest 正在被上传或引用关系没更新会误删。registry 的 GC 需要只读模式或停写执行。解决GC 前先docker stop registry用registry garbage-collect --dry-run先看会删什么确认无误再去掉--dry-run执行。生产环境建议在低峰期做并提前备份。4.5 现象迁移后镜像能 pull但docker push新 tag 报 digest 不匹配原因目标仓库的repositories/目录是从包里恢复的但_uploads/里有残留的临时文件新 push 时 registry 复用了错误的 upload 状态。解决恢复后清空_uploads/目录rm -rf /data/registry/docker/registry/v2/_uploads/*。这个目录本来就是临时区清掉不影响已有数据。5. 给 registry.tar.gz 瘦身与长期维护的实操技巧registry.tar.gz用久了会越来越大因为每次 push 新 tag 都会新增 blob旧 blob 不会自动删。我一般每季度做一次清理流程是先删无用 tag再 GC最后重新打包备份。删 tag 用 registry 的 API# 获取某个 tag 的 digest DIGEST$(curl -s -I \ -H Accept: application/vnd.docker.distribution.manifest.v2json \ http://localhost:5000/v2/myapp/manifests/old-tag \ | grep -i docker-content-digest | awk {print $2} | tr -d \r) # 删除该 digest curl -X DELETE http://localhost:5000/v2/myapp/manifests/$DIGEST-I只取响应头docker-content-digest就是 manifest 的 sha256。拿到 digest 后 DELETE 掉tag 就没了。注意这一步只是解除引用blob 还在。然后停服 GCdocker stop registry docker run --rm \ -v /data/registry:/var/lib/registry \ registry:2 garbage-collect /etc/docker/registry/config.ymlGC 会遍历所有 manifest标记可达 blob删除不可达的。跑完后du -sh /data/registry通常能降 30% 到 50%取决于历史 tag 数量。最后重新打包并保留最近两份备份cd /data/registry tar -czf /backup/registry-$(date %Y%m%d).tar.gz docker/ # 只保留最近两个备份 ls -t /backup/registry-*.tar.gz | tail -n 3 | xargs rm -fls -t按时间倒序tail -n 3跳过前两个xargs rm -f删掉旧的。这个习惯让我在一次磁盘写满的事故里省了半小时排查时间——备份目录自己不会无限膨胀。还有一个容易忽略的点registry.tar.gz里如果包含大量小文件解压和 GC 都会很慢。我一般会在打包前用find看一下文件数超过 50 万就考虑分片或者换存储后端。filesystem 后端在文件数上确实有天花板这是它和 S3 后端最大的差别。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑