简介:本资源为面向内网离线环境的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.v2+json" \ 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 还在。
然后停服 GC:
docker 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 后端最大的差别。
希望帮到你。
本文还有配套的精品资源,点击获取