
信创内网部署 CubeStudio 这件事我前前后后折腾了小两周。最麻烦的不是应用本身而是它的服务镜像怎么在没有外网的机房里跑起来。同行应该能体会——拿到一台只有内网 IP 的服务器光一个 Harbor 仓库、一份离线镜像包就能让人折腾掉一两天。这篇文章把我这次完整走通的离线部署流程写出来重点拆解镜像导出、Harbor 搭建、出口机中转这几个环节踩过的坑和排查思路也一并记录。适合正在做企业私有化交付、信创环境适配或者被要求“必须全内网部署”的开发、运维和交付同学参考。1. 离线部署的整体思路与架构设计1.1 为什么把 Harbor 作为内网镜像中枢很多人觉得离线部署嘛直接把镜像 tar 包拷进去docker load一下不就行了小项目确实可以这么干但 CubeStudio 这种带多个组件服务的平台镜像数量多、依赖关系复杂、升级频繁全用docker load管理很快就会乱套。镜像在 A 机器上 load 了B 机器又要重新拷升级一个组件得把对应 tar 包重新分发一遍中间还特别容易混淆版本。所以我在内网搭了一个 Harbor 私有仓库把它作为整个内网的镜像中枢。所有离线镜像先推到 Harbor各业务机器统一从 Harbor 拉取。这样做的好处很明显版本统一、权限可控、后续增量升级也只需要往 Harbor 推新 tag业务机器上一条docker pull就能更新。Harbor 本身支持基于项目的权限隔离比如给 CubeStudio 单独建一个cstudio项目避免和其他中间件镜像混在一起。另外Harbor 的离线安装包本身是自包含的installer 里面已经带好了 Harbor 自身的镜像不需要从外网拉任何东西。这一点在无外网环境里至关重要也是我选它而不是自己写一个 registry 的原因。虽然 Docker 官方 registry 也能用但 Harbor 的项目管理、复制、审计这些能力在信创交付场景下更省心。1.2 三机分工出口机、中转机、业务机完全无外网的内网不等于整个环境里一台能上公网的机器都没有。我这次用的方案是把整个流程拆成三个角色出口机这是整个内网里唯一有公网访问权限的机器可能是机房的一台跳板机也可能是临时申请的一台测试机。它的任务只有一个从公网镜像仓库拉取 CubeStudio 相关的镜像打成离线 tar 包。注意这只是利用了这台机器本身拥有的公网访问权限做合规的镜像获取不涉及任何绕过网络管控的手段。中转机离线包从出口机出来以后通过机房内部的文件传输通道、移动硬盘或者刻盘方式送到内网环境。中转机一般是内网里一台装了 Docker 的 Linux 服务器我用的是 Ubuntu负责把 tar 包 load 进本地 Docker再推送到 Harbor。业务机真正运行 CubeStudio 的服务器统一从内网 Harbor 拉取镜像启动服务。这三个角色不用是物理隔离的三台机器。如果内网环境小中转机和 Harbor 可以放在同一台机器上业务机多的话Harbor 单独部署一台配置好一点的服务器更稳。关键是把“拉取—传递—入库—消费”这条链路理清楚别让镜像在内网里靠 U 盘传来传去。1.3 信创环境的前置条件架构、操作系统、容器运行时信创环境最容易踩的坑是 CPU 架构不一致。出口机如果是 x86 的拉下来的镜像是 amd64 的内网如果是鲲鹏、飞腾这些 ARM 机器镜像根本起不来。所以动手之前必须确认出口机和最终业务机的架构一致至少在公共底座层面一致。我在这次项目里就见过有人在外网 x86 服务器上拉了一堆镜像拿到麒麟 ARM 机器上 load启动时报exec format error全白干。操作系统方面内网如果是麒麟、统信 UOS 这类信创系统Docker 或 containerd 的安装包也要提前准备好离线版本。Debian 系用.debRedHat 系用.rpmWindows Server 2022 则要考虑 Windows 容器和 WSL 两套路径。容器运行时我统一用 Docker Engine 加 docker-compose 插件原因是 CubeStudio 的编排脚本大多基于 compose 语法用 Docker 原生方案兼容性最好。2. 镜像获取与离线打包把公网镜像变成离线介质2.1 出口机上用 docker save 还是 skopeo拉取镜像最直接的方式是docker pull加docker save这也是大多数人的第一反应。但在出口机上我建议考虑另一个工具skopeo。它不需要启动 Docker daemon直接跟镜像仓库打交道而且能跨格式复制比如把 docker 仓库镜像直接导出为 docker-archive 格式也能导出成 oci 格式。我这里不是折腾而是有实际理由的。出口机如果本身没装 Docker或者 Docker daemon 因为内网策略被限制启动skopeo 是纯二进制工具拷贝过去就能用不依赖守护进程。另外skopeo copy可以直接指定目标 tag导出后的 tar 包在导入时更容易控制镜像的仓库和标签减少手滑出错。但如果你图省事、出口机上 Docker 也现成直接用docker pulldocker save完全没问题。我这次两种方式都用了大镜像用 skopeo稳妥需要改 tag 的小镜像用 Docker 手动操作。2.2 完整拉取与打包命令实录假设 CubeStudio 的镜像都放在某个镜像仓库里例如registry.example.com/cstudio/cstudio-server:2.1.0。在出口机上执行# 方法一skopeo 直接导出为 docker-archive skopeo copy \ --src-creds readuser:readpass \ docker://registry.example.com/cstudio/cstudio-server:2.1.0 \ docker-archive:cstudio-server-2.1.0.tar:2.1.0注意docker-archive后面那个:2.1.0它指定了镜像在 tar 包里的 tag。如果不写导入的时候 tag 会变成null后面还要手动重新打 tag多一步操作。# 方法二docker pull docker save docker pull registry.example.com/cstudio/cstudio-server:2.1.0 docker save registry.example.com/cstudio/cstudio-server:2.1.0 \ -o cstudio-server-2.1.0.tar这里有个经验导出之前先docker images看一眼镜像的实际大小。CubeStudio 的服务镜像动辄 1GB 起步几 GB 也不奇怪。如果 tar 包要过内部传输通道注意文件大小限制必要时分开导出或者用pigz压缩再传。2.3 离线包的校验和传递镜像打包完别急着拷走先算一组校验和。我习惯用 SHA256每个 tar 包对应一个.sha256文件sha256sum cstudio-server-2.1.0.tar cstudio-server-2.1.0.tar.sha256传到内网之后第一件事就是验证校验和免得传输过程中静默损坏。镜像 tar 包不像普通文件损坏一点就可能导致 docker load 失败而且报错信息通常很隐晦什么archive/tar: invalid tar header之类。校验和这一步省不了。传递通道方面有内网文件服务就用内网文件服务没有就用移动硬盘。移动硬盘要注意文件系统格式FAT32 单文件不能超过 4GB镜像 tar 包分分钟超限。我建议用 exFAT 或者 NTFS 格式的硬盘避免在拷贝中途发现写不进去的尴尬。3. Harbor 私有仓库离线安装与配置实录3.1 离线安装包的获取与前置软件准备Harbor 的离线安装包是harbor-offline-installer-v2.10.2.tgz这种命名格式内部包含了 Harbor 所有组件镜像和 installer 脚本。在出口机上下载好跟镜像 tar 包一起传进内网。Harbor 依赖 docker-compose这是离线环境最容易忽略的一环。Ubuntu 内网机器上如果没装 docker-compose 插件install.sh会直接报错。建议提前在内网准备好docker-compose 二进制从公网下载后拷贝进去放到/usr/local/bin/docker-compose并加执行权限或者 Docker Engine 自带的 compose v2 插件docker compose version能通就行另外Harbor 需要一个数据目录存放镜像和数据库默认是/data/harbor要保证磁盘空间充足。我建议至少预留镜像总容量 1.5 倍以上因为镜像的存储是分层去重的但加上数据库、日志、回收站空间留富余最稳。3.2 harbor.yml 关键配置与 install.sh 执行解压安装包后先把harbor.yml.tmpl复制成harbor.yml然后重点改这几项hostname: 192.168.209.133 http: port: 80 harbor_admin_password: YourStrongPassword data_volume: /data/harborhostname一定要写内网 IP 或者内网域名不能写 localhost否则后面推镜像时地址解析会出问题。http.port默认是 80如果你内网 80 端口被占用了改成 8080 之类的端口但后面所有docker pull/docker push的地址里都要带上这个端口。确认配置没问题后执行sudo ./install.sh离线安装包不会去公网拉取任何东西installer 会直接把离线镜像 load 进本地 Docker然后通过 docker-compose 把 Harbor 的组件拉起来。等待时间取决于机器性能一般几分钟。安装完成后访问http://192.168.209.133默认管理员账号admin密码就是你在 harbor.yml 里设置的那个。3.3 内网仓库的访问策略HTTP 与自签名证书的取舍Harbor 默认支持 HTTPS但对内网环境来说HTTPS 除了解析证书问题没有太多实际收益。Docker 客户端在访问 HTTP 仓库时会直接拒绝必须把仓库地址加入insecure-registries。我这次选择走 HTTP 方案也就是在 Harbor 的harbor.yml里不配置https段然后在内网所有 Docker 客户端的/etc/docker/daemon.json里加上{ insecure-registries: [192.168.209.133:80] }修改后执行systemctl restart docker。这是内网、全可信环境里最省事的方案不用处理证书分发排查问题时少一个变量。如果你的安全规范硬性要求 HTTPS那就得在出口机或内网一台机器上生成自签名 CA 证书把 CA 证书分别装到 Harbor 和所有 Docker 客户端。每次新增机器都要同步证书比 HTTP 方案麻烦得多。内网全可信的前提下我强烈建议不要在这个环节过度设计。4. 镜像导入与 CubeStudio 部署实战4.1 镜像导入 Harbor 的标准动作镜像 tar 包进了内网之后先把包 load 进中转机的 Dockerdocker load -i cstudio-server-2.1.0.tarload 完用docker images查看镜像的仓库名和 tag确认没有丢失。如果 tag 是null手动补一个docker tag image-id cstudio/cstudio-server:2.1.0接着登录 Harbor打上仓库前缀然后推送docker login 192.168.209.133:80 -u admin -p YourStrongPassword docker tag cstudio/cstudio-server:2.1.0 \ 192.168.209.133:80/cstudio/cstudio-server:2.1.0 docker push 192.168.209.133:80/cstudio/cstudio-server:2.1.0推送之前确保 Harbor 里先建好cstudio项目。不建项目直接 push 会报denied或者unauthorized这是 Harbor 的项目策略不允许 push 到不存在的项目。如果镜像很多建议写一个小脚本批量操作别一个个手敲。我当时的批量推送脚本大致长这样for img in cstudio-server cstudio-web cstudio-worker; do docker tag cstudio/$img:2.1.0 \ 192.168.209.133:80/cstudio/$img:2.1.0 docker push 192.168.209.133:80/cstudio/$img:2.1.0 done4.2 内网服务器配置 insecure-registries所有要跑 CubeStudio 的业务机都要先配置insecure-registries否则拉取镜像时报http: server gave HTTP response to HTTPS client。配置步骤sudo vim /etc/docker/daemon.json{ insecure-registries: [192.168.209.133:80] }sudo systemctl daemon-reload sudo systemctl restart docker然后测试拉取docker pull 192.168.209.133:80/cstudio/cstudio-server:2.1.0能拉下来之后用docker images确认镜像 id 和离线包里的一致。这一步的目的是提前发现问题别等 CubeStudio 编排文件执行到一半才报拉取失败。4.3 CubeStudio 容器编排部署CubeStudio 的部署文件一般是 docker-compose 形式的 YAML里面引用的镜像地址得改成内网 Harbor 地址例如把cstudio/cstudio-server:2.1.0替换成192.168.209.133:80/cstudio/cstudio-server:2.1.0。如果是首次安装建议先把docker-compose.yml里的依赖镜像清单过一遍确保每个镜像都已经推到 Harbor。编排文件里任何一个镜像漏了启动时都会被卡住。我有一次就是漏了个 Redis 的镜像结果 CubeStudio 的服务容器起来之后狂报连接错误排查了半天才发现是镜像缺了。启动命令docker compose pull docker compose up -dpull这一步会按编排文件从 Harbor 拉取所有镜像能拉下来基本就成功一半了。然后up -d启动服务再用docker compose ps看容器状态正常的话全部是Up。5. 常见问题与排查技巧实录5.1 Harbor 推送失败 dial tcp 超时怎么办热词里出现的harbor 推送失败 get https://192.168.209.133/v2/: dial tcp 192.168.209.133:...这个问题我几乎每次部署都能碰到。看到dial tcp报错先不要怀疑镜像包损坏问题通常出在网络或者配置上。排查顺序先确认 Harbor 容器本身活着docker ps | grep harbor如果核心容器没有起来跑一下docker compose -f /data/harbor/docker-compose.yml ps看哪个容器是Restarting状态。确认端口监听ss -lntp | grep 80没监听就说明 Harbor 服务没起来对应处理有监听就继续往下走。客户端这边看地址写没写对docker login 192.168.209.133:80能登录说明基本连通登录不了检查是不是把 HTTPS 协议写在地址里了。防火墙和安全组内网机器之间跨网段访问时常见的问题是防火墙拦了 80 端口。firewall-cmd --list-ports看一下必要时放行。如果你配了insecure-registries之后还是报get https://...说明 Docker 没读到配置或者配置后忘了重启 Docker。这个报错里的https://是 Docker 默认尝试的协议不是说你真的配了 HTTPS而是它还在用默认行为。我在这个坑上还总结了一个经验不要在 Harbor 服务器和客户端上混用 IP 和域名。客户端 push 用的是192.168.209.133:80那 insecure-registries 里也必须写192.168.209.133:80别写harbor.internal之类的域名否则明明是一致的地址Docker 的匹配逻辑也容易搞出幺蛾子。5.2 Windows Server 2022 离线部署 WSL Containers 的坑如果业务机是 Windows Server 2022CubeStudio 的容器化部署绕不开两个路径Windows 容器和 WSL 容器。Windows 容器跑 Linux 镜像需要虚拟化支持很多信创服务器或者虚拟机环境下嵌套虚拟化被禁用这条路很容易卡死。所以热词里提到的windows server 2022 离线部署 wsl containers是大家普遍关注的点。WSL 离线部署的难点在于WSL 2 需要两项 Windows 功能——VirtualMachinePlatform和Microsoft-Windows-Subsystem-Linux。Server 2022 默认可能未启用这些功能离线环境里只能手动开启Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux Enable-WindowsOptionalFeature -Online -FeatureName VirtualMachinePlatform启用后重启系统然后安装 WSL 的内核更新包wsl_update_x64.msi。这个包要在出口机提前下载好否则离线环境里 Windows 自己从网上拉更新是拉不到的。装完之后用wsl --set-default-version 2确认 WSL 2 是默认版本。在有 WSL 的 Windows Server 2022 上Docker 客户端通过 WSL 后端跑 Linux 容器。实际操作中注意受限于 WSL 的内存分配大镜像拉取和构建时容易卡死打包前调整.wslconfig给足内存和 CPU 配额。5.3 镜像 load 后 tag 丢失与 x509 证书报错docker load之后镜像的 tag 可能变成null尤其是用 skopeo 导出的包在导入时容易遇到。处理思路就一句话导入以后马上用docker tag改名别拖。我通常会写一个load_images.sh文件里把docker load和docker tag放在一起执行标签规规矩矩统一加上。x509 证书报错则分为两种场景。一是没有配insecure-registries去访问 HTTP 仓库错误信息会带着server gave HTTP response to HTTPS client或者certificate signed by unknown authority解决方法是配置daemon.json后重启 Docker。二是确实用了自签名 HTTPS但 CA 证书没有同步到客户端机器。解决方案是手动把 CA 证书放到/etc/docker/certs.d/192.168.209.133:80/ca.crt或者信任到系统 CA 仓库然后重启 Docker。我个人在内网私有化部署里基本都选 HTTP 方案不是因为我懒或者不重视安全而是对内网可信环境来说这个方案最少折腾、最好排查交付团队接手也容易。写在最后我自己的体会是离线部署这件事网络环境越差越要在流程设计上多花功夫。镜像从哪里来、从哪里进、在哪里存、从哪里分发这四个环节只要想清楚信创内网部署 CubeStudio 的复杂度会直线下降。最后再分享一个交付小技巧进机房之前先在一套跟目标环境一致的测试环境里完整跑一遍离线流程包括镜像导入、Harbor 搭建、业务镜像启动连出错的日志都提前看一眼。这套“预演”帮我避开的坑比任何经验总结都值钱。