ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

离线部署私有镜像仓库:registry.tar.gz 落地指南

离线部署私有镜像仓库:registry.tar.gz 落地指南 简介适用于无法连接外网的环境这份Docker私有仓库离线镜像包可帮助运维人员在内网快速搭建Registry服务。压缩包采用gz格式体积25.15MB共包含18个文件其中7个json文件用于记录镜像元数据与配置5个tar文件为各层镜像数据5个version文件标识版本信息另有repositories文件维护镜像仓库索引。整体目录结构遵循Docker镜像标准的layer组织方式可直接导入Registry免去外网拉取步骤。对于离线机房、企业内网等场景该资源能显著缩短环境搭建时间并避免因网络隔离导致的镜像获取难题。已有436人学习下载适合具备基础Docker使用经验的运维工程师或系统管理员作为内网私有仓库搭建的即用组件。下载后结合docker load/import命令即可加载镜像快速落地离线仓库方案。1. registry.tar.gz离线环境下最被低估的私有镜像仓库启动包我刚接触它时也以为只是个普通的 Docker 镜像包但真正在内网服务器上把它跑起来后才发现这个东西是离线部署场景里最省事的 Way——一个文件一条 docker load 命令registry 2.x 官方仓库就起来了。现在很多实施项目里甲方给的交付物就是一个 registry.tar.gz里面装着完整的 registry 镜像目的就是让内网环境在没有外网的情况下也能搭建起自己的容器镜像分发节点。这个标题解决的核心问题很具体你在离线环境里拿到这个压缩包怎么把它变成可用的私有仓库怎么往里面推镜像推上去之后能否被其他机器拉取适合的读者是正在做 K8s 离线部署、边缘机房镜像同步或国产化系统迁移的运维和交付工程师——尤其是你手头只有这一个文件、没有 docker hub 访问权限的场合。这篇文章我把整个落地路径拆开讲清楚包括选型理由、部署步骤、参数调优和我在实际项目里踩过的那些坑。2. 为什么是 registry.tar.gz 而不是 Harbor离线仓库选型的第一课2.1 单文件交付背后的逻辑官方镜像的最小可用集在容器镜像仓库领域Harbor 功能强大但依赖多——它需要 PostgreSQL、Redis 和完整的 Helm Chart 支撑离线部署时你需要准备的东西远不止一个压缩包。而 registry.tar.gz 通常是 Docker 官方 registry:2.x 镜像的 docker save 产物它的优势在于一个镜像就是一个仓库不带任何外部依赖。我一般会在交付项目中明确告诉客户如果你只需要内网拉取、推送镜像的基本能力用 registry 就够了。它占内存很少默认配置下运行消耗不到 100MB而且没有 Web UI 这块开销。对于几十台节点的集群registry 完全可以扛住并发拉取。这里还要区分一个容易混淆的点registry.tar.gz 既可能是镜像文件用 docker save 打出来的也可能是包含 registry 数据目录和配置的压缩包。前者常见于 Kubekey、kubeadm 这类安装工具的离线依赖包后者多见于交付方把已有仓库数据整体打包给内网环境用。拿到文件后我建议先执行 tar -tzf registry.tar.gz | head 看结构化输出如果是分层的 JSON 文件列表就是镜像格式如果是 docker/registry/v2 目录结构就是数据包格式后续处理方式完全不同。2.2 镜像格式与数据格式的判断标准和处理差异判断格式不是学术问题它直接决定你下一步走哪条路。看压缩包内容是最可靠的方式——镜像格式的文件列表里会出现 manifest.json、layer 目录或一系列的 tar 层文件数据格式则会出现 docker/registry/v2/repositories 这样的层级目录。常见的 kubekey 下拉的离线包就是按镜像格式组织的因为它需要你把镜像 load 进本地 dockerd。处理逻辑我给一个简单的经验值镜像格式用 docker load 导入然后打 tag、启动容器、实现私有仓库功能数据格式直接解压到 registry 容器的 /var/lib/registry 挂载目录启动即生效里面已经有人推过的镜像。区分不清时两种方式的数据不会损坏但操作路径错了会浪费半小时所以这个判断值得认真做。判断动作镜像格式特征数据格式特征查看文件列表出现 manifest.json、layer 目录出现 docker/registry/v2 目录处理方式docker load docker tag直接解压到数据目录后续操作启动 registry 容器后推送启动 registry 容器即带数据数据格式的部署方式我单独说一句——把解压后的目录权限改成 1000:1000registry 容器内用户 UID否则启动后目录不可写容器一直在崩溃重启这是个让我排查过很久的隐蔽问题。2.3 官方镜像与第三方仓库镜像的选择边界Registry 镜像有很多变体都是 registry:2.6、2.7、2.8 等等。离线包里到底装的是哪个版本决定了你要不要面对后续补丁问题。我看到很多交付方的 registry.tar.gz 都是 2.6.x它存在已知的 CVE但如果你的内网环境不暴露到公网影响面可控。我一般建议优先确认版本大会上线的系统不强求追新版。如果离线包里的版本过旧确实有安全要求那就得重新找对应版本的 tar.gz 包而不是直接改镜像 tag 假装升级——registry 镜像 tag 和实际二进制版本需要匹配load 之后再打 tag 只是改了名字功能没有变化。判断版本的办法是加载后 docker run 一条临时命令跑registry --version查看。2.4 存储驱动的选择与目录规划Registry 容器把数据放在 /var/lib/registry也就是说 docker 的 volume 挂载点要指向这个目录。我习惯在数据盘上单独分一个目录比如 /data/registry避免系统盘被镜像撑满。镜像仓库这东西有个特点——数据只增不减即便你在客户端删除了镜像registry 里的存储层也不会立即释放。存储驱动默认是 filesystem不需要额外配置但要留意后端文件系统的类型。如果你用 NFS 做共享存储跑多副本 registry那就要更换存储驱动为 oss 或 s3 之类但这种方式我个人不推荐在内网小规模场景用——挂了 NFS 之后 registry 的启停顺序会给运维增加复杂度比如 NFS 未挂好时 registry 容器启动会报权限错误。单机磁盘最省心。3. 用 registry.tar.gz 在离线 Linux 上搭起私有仓库从解包到可用3.1 麒麟 v10 和主流 Linux 发行版的准备工作先讲系统准备。国产化环境现在很常见我现在就有不少项目跑在麒麟 v10 上。这个系统的软件源里带的 docker 有时是旧版本需要注意和容器运行时兼容。离线环境下你要做的是确认 docker 或 containerd 已存在——这一步卡住后面的步骤全部中断。先跑一个简单检查docker --version systemctl status docker --no-pager输出中 docker 版本低于 19.03 时后续构建步骤里需要留意存储驱动 overlay2 的兼容性docker 版本的差异不会影响 registry 部署本身。系统环境干净没有 docker 的情况下要找到符合系统架构的 docker 离线安装包先装上之后再处理 registry.tar.gz。麒麟 v10 上还有一个常见问题系统的防火墙默认放行规则可能拦截 5000 端口。内网环境很多人忽略这一步发现其他机器拉取镜像超时排查到最后是防火墙拦了。建议提前放行sudo firewall-cmd --permanent --add-port5000/tcp sudo firewall-cmd --reloadselinux 在麒麟上默认可能是 enforcing 状态docker 容器端口映射时会受影响。如果 registry 容器起来后访问不通先临时 setenforce 0 测试确认是 selinux 问题后再做策略调整。这一步属于排查阶段的常见操作不是安全降级别忽略。3.2 加载 registry.tar.gz 镜像并启动容器准备工作做完了接下来把镜像文件加载进来。这里分两种情况镜像格式的 tar.gz 直接用 docker load如果压缩包没有用 gzip 压缩过load 同样兼容gunzip -k registry.tar.gz docker load -i registry.tar-k 参数保留原始压缩包防止后续失误时没有后悔药。load 成功后 docker images 查看输出找到 REPOSITORY 为 registry 的记录。我现在给你一个完整的启动命令适合单节点内网仓库的场景sudo mkdir -p /data/registry sudo chown 1000:1000 /data/registry docker run -d \ --name registry \ --restartalways \ -p 5000:5000 \ -e REGISTRY_STORAGE_DELETE_ENABLEDtrue \ -v /data/registry:/var/lib/registry \ registry:2这里每个参数都有讲究。--restartalways 让机器重启后 registry 自动拉起这是内网无人值守场景的必要条件-e REGISTRY_STORAGE_DELETE_ENABLEDtrue 开启镜像删除能力默认情况下这个值是 false你想删掉某个镜像的存储层时 API 会直接返回 405。还有一点/data/registry 的属主设为 UID 1000 是和容器内 registry 进程的 UID 对齐的这样容器写数据时不会出现 permission denied设置数据的宿主机是 root 时尤其要注意。启动后一条命令验证容器状态docker ps | grep registry curl -X GET http://127.0.0.1:5000/v2/_catalog返回 {repositories:[]} 代表仓库已经能响应 API 请求。此时整个 registry 已经活了接下来需要把其他离线包里的镜像推上去。3.3 把已加载的 tar.gz 离线镜像推送到私有仓库实际场景里你从 kubekey 或其他工具拿到的 registry.tar.gz 只把 registry 本身带来了但集群部署还需要一堆组件镜像coredns、calico、kube-apiserver 等。这些镜像通常是另一个 tar.gz 包比如 kubekey 的离线包和二进制包体积压缩版里会同时包含。把这些镜像导入并推送到私有仓库的流程就是交付的核心。批量导入并推送的脚本逻辑如下for img_tar in $(ls /opt/offline-images/*.tar.gz); do docker load -i $img_tar done for img in $(docker images --format {{.Repository}}:{{.Tag}} | grep -v registry:2); do new_tag192.168.1.100:5000/$(echo $img | awk -F/ {print $NF}) docker tag $img $new_tag docker push $new_tag done脚本用 docker load 把离线镜像逐个导入本地 docker然后对每个镜像重新打标签。这里 awk 截取镜像名最后一段的目的是去掉可能的原始仓库前缀防止镜像名变成 192.168.1.100:5000/rancher/rancher/xxx 这种二次嵌套。整个推送过程速度受磁盘带宽内网传输限制几百个镜像可能要跑几十分钟中间如果 push 失败建议单独找出那个镜像重试而不是全部重来。注意脚本假设镜像 tag 都存在如果遇到 : 的镜像docker tag 会失败。处理办法是把筛选条件改成只处理特定名称开头的镜像或用 docker images 查看实际的 REPOSITORY 列。3.4 containerd 环境下的替代路径不用 docker 直接导入私有仓库很多 K8s 节点用的是 containerd 而不是 docker此时上面脚本依赖 docker CLI 的方式就失效了。离线环境里你手上可能只有 ctr 或 crictl没有 docker。我一般会引入 skopeo 这个工具处理镜像传输它可以直接把 docker-archive 格式的压缩包拷贝到 registry不需要 docker daemon 参与。安装在离线环境需要提前准备 skopeo 的 rpm 包或编译依赖这个工具包体积不大但导入到麒麟等系统时可能遇到依赖缺失。安装完成后推送命令长这样skopeo copy docker-archive:/opt/offline-images/registry.tar.gz \ docker://192.168.1.100:5000/library/registry:2 \ --dest-tls-verifyfalse \ --dest-creds admin:admin123参数含义docker-archive 表示源格式是本地压缩包docker:// 表示目标格式是 registry 仓库--dest-tls-verifyfalse 跳过证书验证内网环境基本没有证书--dest-creds 是仓库的认证信息如果仓库没开认证这个参数可以去掉。skopeo 的优势是它不经过 docker daemon自然避免了 docker 版本兼容问题也省掉了打 tag 的步骤。注意源路径里的 tar.gz 只需要是 docker save 格式就行名字不用 docker load 前置处理。skopeo 能直接解析。如果目标仓库开了认证而你没传 --dest-creds就会报 unauthorized 错误这个参数漏掉的频率很高。4. 客户端拉取配置insecure-registries 与镜像加速器4.1 Docker 客户端访问私有仓库的配置细节与参数说明私有仓库搭好只是第一步客户端能拉取才能形成闭环。Docker 默认用 HTTPS 访问 registry如果你没有配置证书客户端访问新仓库时 Docker 会直接拒绝报错信息是 http: server gave HTTP response to HTTPS client。内网环境没有任何证书所以要显式告诉 docker 这个仓库是 HTTP 的。改配置文件 /etc/docker/daemon.json 增加 insecure-registries这是我最常见到的配置项{ insecure-registries: [192.168.1.100:5000], registry-mirrors: [] }其中 registry-mirrors 如果已经配置了加速器保存时需要把加入的内容保留别覆盖。配置完 restart docker 服务daemon 才能读到新配置sudo systemctl restart docker重启 docker 会让正在运行的容器全部重启所以生产节点上改这个配置建议选在维护窗口操作。配置文件里加多个仓库地址时每个地址都要带端口号用逗号分隔后数组格式就行。4.2 拉取验证与常见报错识别配置完成以后从客户端机器上执行拉取验证docker pull 192.168.1.100:5000/library/nginx:1.25如果卡在等待响应或报连接超时多半是网络问题用 telnet 测端口是否通telnet 192.168.1.100 5000返回 Connected to 192.168.1.100 说明端口通了再回头找 docker 配置问题。如果报 unauthorized 而你没有开认证检查镜像 tag 里是否多了 /library/ 路径前缀——Docker 官方镜像在 Docker Hub 上的路径包含 library 这个命名空间但内网私有仓库如果没开项目隔离路径里不应该有额外的仓库嵌套。这是推送镜像时打 tag 不规范导致的问题。还有一种隐蔽报错是 server gave HTTP response to HTTPS client这意味着 docker 没有正确读取 insecure-registries 配置或者配置后 docker 没有重启。看到这条就只剩两个排查方向daemon.json 格式对没对、docker 服务重启没重启。4.3 使用 nerdctl 和 containerd 拉取的差异使用 K8s 的环境里节点上可能只有 containerd没有 docker CLI。containerd 配置文件位于 /etc/containerd/config.toml需要在 plugins.io.containerd.grpc.v1.cri.registry.configs 下配置 mirror 指向私有仓库。我通常建议先确认 containerd 的配置文件版本——不同版本字段有差异新版本配置项结构已经调整用旧教程里的写法容易不生效。配置后重启 containerd 的指令:systemctl restart containerd它和 docker 最大的区别是containerd 的 config.toml 里可以配置多个 mirror 以及特殊镜像的前缀但语法比较繁琐。另一个区别是不仅需要配置 endpoint还可能要配置 configs 里的 auth 部分——如果你给 registry 开了认证而 containerd 没配用户名和密码拉取时也会一直 401。5. 六大避坑从 registry 崩溃到镜像不可见的血泪排查记录5.1 现象推送镜像时报 500 Internal Server Error仓库服务崩溃这个坑我在一个大型实施项目里遇到过。docker push 刚开始还能推推到一半突然报 500registry 容器直接退出。原因排查后确认是磁盘空间满了。registry 的存储层在接收到镜像层数据时先写临时文件空间不足时进程 writes 直接报错容器因无法恢复而退出。解决清出磁盘空间或给 /data/registry 所在分区扩容然后重新启动容器。这里要强调监控数据盘容量——镜像仓库是磁盘杀手几十个节点同时推镜像时会迅速占满空间。建议在部署时就留足余量比如系统盘 40G 的用户不要直接把仓库目录放系统盘。5.2 现象curl 访问 /v2/ 返回 404但容器状态正常这个现象是启动时没有映射好路径。明明 docker ps 显示 Up但访问 /v2/ 不是返回 JSON 而是一个 404 页面。原因是我把容器内的数据目录挂载成了 /var/lib/registry 的父目录位置错了。registry 只在 /var/lib/registry 下读写你挂载成 /var/lib 或 /var 时容器内路径对不上API 响应异常。解决把挂载路径修正为 /var/lib/registry。命令重跑一遍容器注意先删除旧容器再启动新的docker rm -f registry docker run -d --name registry -p 5000:5000 -v /data/registry:/var/lib/registry registry:25.3 现象镜像推上去了但 /v2/_catalog 里看不到这个问题有时出现在使用 skopeo 或者 docker push 的客户端和 registry 版本不一致时。推送时返回成功但用 curl 查看仓库列表就是没有。原因是 registry 的 API 做了缓存。registry:2.6 及以上版本对 _catalog 有缓存机制不是实时刷新的我这里直接说结论——你等几十秒再刷新就能看到。另一个可能性是推送时用的 tag 带上了平台后缀比如 v1-amd64它在 _catalog 里会显示为带后缀的名字看起来和预期不一样。解决刷新之后重新确认或者直接访问具体镜像的 tag 列表接口curl -X GET http://192.168.1.100:5000/v2/myimage/tags/list这个接口返回 JSON 数组能精确看到某镜像的 tag 列表。5.4 现象其他节点拉取镜像时一直卡在等待这个原因特别多但最常见的就是 containerd 或 docker 没有正确配置 insecure-registries。现在的容器运行时对 HTTP 仓库的处理是直接协商失败表现为连接建立后不断重试最终超时。值得注意的另一个坑是客户端 DNS 或 hosts 解析错误。如果客户端无法解析 registry 所在主机名连接超时现象和上面完全一样。内网环境建议直接用 IP 地址作为仓库地址不用域名省去很多排查麻烦。解决先把仓库地址改成 IP 测试能通再考虑域名方案。如果必须用域名在客户端 /etc/hosts 里加上 IP 映射。5.5 现象registry 容器运行数天后无法启动日志报 permission denied这个坑和存储目录权限有关。容器运行时报 mkdir /var/lib/registry/docker: permission denied大概率是宿主机上 /data/registry 属主变了或者目录由 root 创建而 registry 进程以非 root 运行。解决重新 chown 1000:1000 /data/registry 再启动容器。这里有个注意点——不要在容器运行期间手动往数据目录里复制文件特别是用 root 用户操作后会让子目录权限错乱。后续要备份数据时用 docker cp 或挂载另一个临时目录别直接操作数据目录。5.6 现象push 镜像到 Harbor 或其他高级仓库时报 unauthorized这个现象常见于交付方提供的离线包理论上要推到客户已有的 Harbor 仓库里。docker push 时报 unauthorized 或 401。原因Harbor 需要项目名作为镜像路径的一部分。只推了没有项目前缀的镜像名是过不了认证的。解决docker tag 时把 Harbor 的项目名加进去例如docker tag myapp:1.0 harbor.internal/library/myapp:1.0 docker login harbor.internal -u admin -p xxx docker push harbor.internal/library/myapp:1.0注意项目名使用 library 这个默认项目时Harbor 也会要求显式带 library 前缀跳过是不行的。6. 更进一步的验证与进阶技巧从仓库健康检查到自动清理仓库上线只算第一步真正考验你的是长期运行中的稳定性。这个阶段我会做三件事健康检查、数据清理和按需自动同步。先说健康检查。registry 提供了基础的健康检查接口配合脚本可以做到自动报警CHECK$(curl -s -o /dev/null -w %{http_code} http://192.168.1.100:5000/v2/) if [ $CHECK ! 200 ]; then echo registry down | mail -s alert opsexample.com fi除了接口通不通我还会每隔一段时间拉一次日志看有没有网络层面的错误输出。日志里的 error 级别信息基本就对应了问题方向。数据清理这里有个容易忽略的细节registry 默认没有 GC 机制delete 镜像 API 即使开了也无法释放物理磁盘空间。tag 删了但 blob 层还在。如果导入的镜像包反复更新仓库体积会持续膨胀最终撑爆磁盘。定期跑一轮 garbage-collect 是有必要的docker exec registry /bin/registry garbage-collect /etc/docker/registry/config.yml这个命令会扫描所有 manifest把没有被引用的 blob 层删掉。执行前建议手动备份整个 /data/registry 目录GC 过程不可中断中断后仓库会处于损坏状态。我的操作习惯是选择凌晨执行执行完再用脚本统计一下镜像数量和磁盘占用。如果你需要把不同服务器上的 registry 做同步docker-registry 自带的全量同步工具没有内置但可以用 skopeo 手动完成同步。相比推拉整个仓库目录我一般建议在源仓库机器上执行 skopeo copy 到目标仓库优点是不会锁住源仓库实时性也更好。还有一个小技巧是把 registry 容器改为 docker compose 管理配置文件里设置好 environment、volumes 和 healthcheck。相比 docker run 的裸命令这样更利于版本管理和恢复。healthcheck 配置上在 registry 容器里测试 curl 不通时会自动触发重启而不是一直处于僵尸状态。最后说一句我的教训所有离线部署项目里registry.tar.gz 只是整个链路最前面的一环真正决定项目成败的是你把镜像推进仓库后整个集群能不能顺畅地拉取。每次收到新的离线包我都会先严格执行一个流程——确认镜像格式、验证版本、测试推送、检查客户端配置。这四个步骤走完后面集群部署就稳当得多。希望帮到你少走几个我在 registry 上走过的弯路。本文还有配套的精品资源点击获取
返回列表