
简介面向无法访问外网的内网环境运维与开发人员这份资源提供可直接导入的 Docker Registry 镜像文件用于在内网快速搭建私有镜像仓库解决离线部署时镜像拉取受阻、仓库组件缺失的问题。压缩包共 18 个文件约 25.15MB以 json 元数据、tar 层数据、version 版本标识及 repositories 仓库索引为主完整保留了镜像的分层结构与清单信息便于通过 docker load 等方式还原镜像。已有 436 人学习下载说明其在离线私有仓库搭建场景中具有一定参考价值。读者可借助该镜像包在隔离网络中完成 Registry 服务的部署与验证省去外网拉取环节同时通过分层文件理解镜像存储结构为后续私有仓库的迁移、备份与排错提供基础素材。1. registry.tar.gz 到手之后一个离线镜像仓库的落地场景内网环境里最常被问的一句话是「能不能把镜像仓库搬进来」。外网拉镜像慢、CI 流水线卡在 pull 阶段、生产环境不允许直连公网这些场景下registry.tar.gz这类资源包就是救急方案。它本质上是一个打包好的 Docker Registry 离线部署包通常包含 registry 二进制或镜像 tar、启动脚本、配置模板和证书生成工具解压后能在无外网机器上跑起一个私有镜像仓库。适合谁做内网 CI/CD 的运维、需要离线交付的交付工程师、以及想在自己机器上搭个本地仓库做镜像缓存的开发者。这篇笔记按「解压看结构 → 起服务 → 推拉验证 → 踩坑排查」的顺序拆参数和命令都能直接抄。2. 拆包与选型registry.tar.gz 里到底装了什么拿到压缩包先别急着解压到根目录先看大小和文件清单。离线包常见的组织方式有两种一种是直接放 registry 的静态二进制Go 编译产物单文件另一种是放registry:2的镜像 tar 包靠docker load导入。两种方式的启动逻辑完全不同选错了后面全是坑。2.1 先看清单再决定解压路径# 不解压先看包内结构避免解压出一堆文件污染当前目录 tar -tzf registry.tar.gz | head -50 # 看包大小判断是二进制包还是镜像包 ls -lh registry.tar.gztar -tzf只列目录不落盘输出里如果看到registry无扩展名的可执行文件加config.yml基本是二进制方案如果看到registry.tar或images/registry_2.tar就是镜像方案。包大小也能佐证二进制包通常 2030MB带镜像的包会到 60MB 以上。确认结构后再建目录解压mkdir -p /opt/registry-deploy cd /opt/registry-deploy tar -xzf /path/to/registry.tar.gz ls -la解压后重点看三个东西启动脚本start.sh或run.sh、配置文件config.yml、以及有没有certs/目录。启动脚本决定了它是用systemd托管还是docker run拉起配置文件决定了存储后端和监听端口。这一步花两分钟能省掉后面半小时的排错。2.2 二进制方案和容器方案怎么选两种方案的核心差异在依赖和可维护性上不是「哪个新用哪个」。维度二进制方案容器方案依赖无单文件直接跑需要宿主机有 Docker升级替换二进制重启重新 load 镜像换 tag配置直接读 config.yml挂载进容器日志走 stdout 或文件docker logs直接看适用极简内网、无 Docker 的机器已有 Docker 环境的宿主机我一般优先选容器方案因为日志和生命周期管理都现成但如果目标机器连 Docker 都没装二进制方案更省事不用为了一个仓库再装一套容器运行时。选型定了再往下走别两套混着试。2.3 配置文件里必须改的四个参数不管哪种方案config.yml里有四个参数必须按实际环境改默认值直接上线大概率翻车version: 0.1 storage: filesystem: rootdirectory: /var/lib/registry # 存储根目录确保磁盘够大 delete: enabled: true # 允许删除镜像默认关闭 http: addr: 0.0.0.0:5000 # 监听地址0.0.0.0 才能被外部访问 headers: X-Content-Type-Options: [nosniff]rootdirectory要指向大容量分区镜像层很吃空间默认路径经常落在系统盘上delete.enabled不开的话docker push上去的镜像删不掉只能手动清目录血泪经验addr写成127.0.0.1:5000是新手最常见的错误本机 curl 通、别的机器死活连不上就是这里。改完配置先别启动用registry --version或docker run --rm registry:2 --version确认二进制/镜像本身没坏。3. 起服务与推拉验证从 0 到第一个 push 成功配置改完启动只是第一步真正的验证是「另一台机器能 push 能 pull」。这一章把启动、TLS、认证、推拉四件事串起来每步都给可执行的命令和失败时的观察点。3.1 启动仓库并确认监听状态容器方案的启动命令# 挂载配置和存储目录映射 5000 端口 docker run -d --name registry \ -p 5000:5000 \ -v /opt/registry-deploy/config.yml:/etc/docker/registry/config.yml \ -v /data/registry:/var/lib/registry \ --restartalways \ registry:2-v两个挂载缺一不可配置不挂载容器里跑的是默认配置存储不挂载容器一删镜像全丢。--restartalways保证宿主机重启后仓库自动起来内网仓库最怕的就是「重启后没人记得拉起来」。启动后立刻验证docker ps | grep registry curl -s http://127.0.0.1:5000/v2/ | head/v2/返回{}且 HTTP 200说明仓库活着。如果返回 404检查配置里的http.addr如果连接被拒看docker logs registry里有没有端口占用或配置解析错误。二进制方案同理./registry serve config.yml前台跑一次看日志确认无误再交给 systemd。3.2 配 TLS 还是走 insecure-registry内网仓库有两种访问方式配自签证书走 HTTPS或者在客户端标记insecure-registries走 HTTP。生产环境建议配证书测试环境图快可以走 insecure但要知道代价。自签证书生成mkdir -p /opt/registry-deploy/certs cd /opt/registry-deploy/certs openssl req -newkey rsa:4096 -nodes -sha256 \ -keyout domain.key -x509 -days 3650 \ -out domain.crt \ -subj /CNregistry.internal \ -addext subjectAltNameDNS:registry.internal,IP:10.0.0.10-subj里的 CN 和subjectAltName必须和客户端访问用的地址一致用 IP 访问就把 IP 写进 SAN否则 Docker 会报x509: certificate is valid for ..., not ...。证书生成后配置里加http.tls.certificate和http.tls.key指向这两个文件重启容器。走 insecure 的话客户端每台机器都要改/etc/docker/daemon.json{ insecure-registries: [10.0.0.10:5000] }改完systemctl restart docker。注意这个配置是「客户端信任」不是「服务端降级」服务端该配的还得配。很多团队图省事全走 insecure结果某天安全扫描过不了回头补证书要改所有节点后悔药没得吃。3.3 push 和 pull 的完整验证链路服务起来、证书或 insecure 配好做一次端到端验证# 打 tag注意仓库地址要带端口 docker tag nginx:alpine 10.0.0.10:5000/demo/nginx:alpine # 推送 docker push 10.0.0.10:5000/demo/nginx:alpine # 换一台机器拉取验证跨机可用 docker pull 10.0.0.10:5000/demo/nginx:alpine # 查仓库里的镜像列表 curl -s http://10.0.0.10:5000/v2/_catalog_catalog返回{repositories:[demo/nginx]}说明元数据正常。push 报413 Request Entity Too Large是反向代理的 body 限制不是 registry 本身的问题push 报blob upload unknown多半是存储目录权限不对容器内用户写不进去。验证链路一定要跨机做本机 push 本机 pull 通不代表别的机器能用网络和证书问题只有跨机才暴露。3.4 用 systemd 托管二进制方案二进制方案别用nohup凑合进程挂了没人知道。写个 unit 文件[Unit] DescriptionPrivate Docker Registry Afternetwork.target [Service] ExecStart/opt/registry-deploy/registry serve /opt/registry-deploy/config.yml Restartalways RestartSec5 Userroot [Install] WantedBymulti-user.targetRestartalways加RestartSec5是标配进程异常退出 5 秒后自动拉起。systemctl daemon-reload systemctl enable --now registry之后用systemctl status registry和journalctl -u registry -f看状态和日志。二进制方案的日志默认走 stderrsystemd 会收进 journal比容器方案少一层docker logs排错反而直接。4. 避坑与排查五个真实翻车现场离线仓库的坑集中在「网络能通但推拉失败」和「重启后数据没了」两类。下面五条按「现象 → 原因 → 解决」写都是部署时高频遇到的。4.1 push 卡住不动最后超时现象docker push进度条走到一半停住几分钟后报net/http: TLS handshake timeout或connection reset by peer。原因通常是客户端和服务端 TLS 版本或证书链不匹配也可能是中间有防火墙对长连接做了超时切断。解决先用curl -v https://10.0.0.10:5000/v2/确认握手是否成功失败就看证书 SAN 是否覆盖访问地址握手成功但 push 超时检查链路上有没有设备限制单连接时长把大镜像拆小或改用分块上传验证。4.2 重启容器后镜像全没了现象docker restart registry之后_catalog返回空之前 push 的镜像消失。原因启动时没挂载/var/lib/registry数据写在容器可写层容器重建就丢。解决docker inspect registry看 Mounts 里有没有存储目录的绑定没有就停容器、加-v /data/registry:/var/lib/registry重新起。已经丢的数据找不回来只能重新 push所以第一次启动前就要把挂载配好。4.3 删除镜像报 405 或删了空间不释放现象调删除 API 返回405 Method Not Allowed或者删了 tag 但磁盘占用没降。原因前者是storage.delete.enabled没开后者是 registry 的删除只删元数据blob 要跑垃圾回收才真正释放。解决配置里开delete.enabled: true并重启然后定期跑 GC# 容器方案GC 要在仓库只读或停机时跑避免并发写冲突 docker exec registry registry garbage-collect /etc/docker/registry/config.ymlGC 期间别 push否则可能误删正在上传的 blob。建议放低峰期跑或者先docker stop registry再起一个临时容器跑 GC。4.4 跨机 pull 报 certificate signed by unknown authority现象本机 push 正常另一台机器 pull 报证书不受信任。原因自签证书没分发到客户端或者客户端把证书放错了目录。解决把domain.crt拷到客户端/etc/docker/certs.d/10.0.0.10:5000/ca.crt目录名必须是「地址:端口」文件名必须是ca.crt然后重启 Docker。放错目录名是高频错误certs.d下多一层或少一层都不生效。4.5 磁盘写满导致仓库假死现象仓库突然所有操作超时docker logs里刷no space left on device。原因镜像层堆积把rootdirectory所在分区写满registry 无法写元数据。解决先df -h确认分区清掉不用的镜像后跑 GC长期方案是把rootdirectory迁到大分区并加磁盘监控。离线仓库没人管存储是常态建议部署时就配个 80% 使用率的告警。5. 进阶把离线仓库接进 CI 和镜像清理策略仓库跑起来只是开始真正省事的是把它接进流水线并管住存储。CI 里最常见的用法是把基础镜像预推到内网仓库流水线只从内网拉构建速度能差出几倍。以常见的 CI 配置为例# 流水线里指定内网仓库作为镜像源 variables: REGISTRY: 10.0.0.10:5000 BASE_IMAGE: ${REGISTRY}/base/python:3.11-slim build: script: - docker pull ${BASE_IMAGE} - docker build -t ${REGISTRY}/app/demo:${CI_COMMIT_SHORT_SHA} . - docker push ${REGISTRY}/app/demo:${CI_COMMIT_SHORT_SHA}关键是BASE_IMAGE指向内网仓库里的预推镜像而不是 Docker Hub。预推动作在仓库部署时做一次之后流水线完全不碰外网。CI_COMMIT_SHORT_SHA做 tag 保证每次构建可追溯别用latest回滚时找不到对应版本。存储管理上我习惯用「保留最近 N 个 tag 定期 GC」的组合。先列 tag 再批量删# 列出某仓库的所有 tag curl -s http://10.0.0.10:5000/v2/app/demo/tags/list # 删除指定 tag 的 manifest需要先拿到 digest DIGEST$(curl -sI -H Accept: application/vnd.docker.distribution.manifest.v2json \ http://10.0.0.10:5000/v2/app/demo/manifests/old-tag \ | grep -i docker-content-digest | awk {print $2} | tr -d \r) curl -X DELETE http://10.0.0.10:5000/v2/app/demo/manifests/${DIGEST}Accept头必须带否则拿不到 manifest 的 digesttr -d \r是去掉响应头里的回车符不加的话 DELETE 请求的 URL 会带脏字符报 400。删完记得跑 GC否则空间不释放。这套流程我一般写成定时任务每周清一次超过 30 天的 tagGC 放凌晨跑。验证仓库健康有个简单习惯每次变更配置或升级后跑一遍「push 一个小镜像 → 跨机 pull → 删 tag → GC → 再 pull 确认 404」的闭环。这个闭环能覆盖挂载、证书、删除开关、GC 四个最容易出问题的点。从那以后我每次动仓库配置都强制走一遍这个闭环再收工比事后救火省心得多。希望帮到你。本文还有配套的精品资源点击获取