前阵子去一个客户的隔离机房做CubeStudio私有化交付,进场之前一切看着都很正常,进场之后才发现问题比想象中大:这个环境属于典型的信创隔离网,机器装的是麒麟V10,CPU架构是ARM64,和办公网完全物理断开。没有外网就意味着没有apt源、没有pip源、没有Docker Hub,连一个依赖包都要先确认能不能拿到介质。CubeStudio本身的部署其实不难,难的是在“完全无外网”这几个字下面,把镜像、依赖、仓库、运行时一整条链路全部打通。
这篇文章不是产品功能介绍,而是把我们实际跑通的离线部署全流程整理出来:怎么用Harbor搭内网私有镜像仓库,怎么在出口机上下载和导出镜像,怎么通过合规的摆渡方式把数据导入隔离内网,以及最后怎么推送、部署和验证。不管你是正在做信创投标的售前,还是被安排进场干活的实施工程师,或是负责这类隔离环境日常运维的同学,这篇应该都能帮你绕开不少弯路。
1. 先搞清楚交底:这类环境到底卡在什么地方
1.1 隔离网环境下的“三无”现实
信创项目里的“无外网”和我们平时说的“没网”完全是两码事。日常环境下,在某台机器上下载一个tar包、跑一个pip install,都是顺手的事;但在隔离机房里,网络策略是经过安全审计和流程审批的,任何数据进出都要走登记流程。我这次遇到的客户机房属于物理隔离,所有机器只有内网IP,没有一根网线连到办公网,唯一的出口是一台经过审批的“出口机”,负责从外网下载指定软件包,再用移动介质导入内网。
这类环境有几个硬约束,进场第一天就得想清楚:
- 无软件源。操作系统自带仓库没有更新,也没有三方软件源,所有安装包都要自己带进去。
- 无公共镜像源。Docker Hub、GHCR、Quay这些外部仓库全部不可达,Harbor本身也要自己搭。
- 无外网DNS解析。内网通常有自建DNS,但域外解析往往是断的,配置任何服务都要优先考虑用IP访问。
这里的核心难点不是技术本身有多高深,而是“预判能力”。先把所有需要下载的东西列成清单,在出口机上一次性备齐,否则到场后一台机器卡住,整个工期就全乱了。别问我怎么知道的,我们这次就因为在介质审批上多等了两天。
1.2 两条链路决定方案设计:交付链路和运行链路
离线部署要想清楚两条链路。第一条是“交付链路”:软件包怎么从外网进入隔离内网;第二条是“运行链路”:内网里的机器怎么获取所需的镜像和依赖。这两条链路如果只在部署当天临时想,一定出乱子。我见过不少项目,交付的时候把tar包拷进去docker load完就完事,结果后面扩容一台机器、升级一个组件,又要重新申请出口机下载,来回折腾。
所以方案设计的核心是:交付链路用“出口机+移动介质”一次性把物资备齐,运行链路用Harbor作为内网镜像源统一供给。这样后续任何一台机器拉镜像,都只需要访问内网Harbor,而不再依赖外部网络。后面的所有步骤都是围绕这个思路展开的。
2. 方案选型:为什么最终落在这套组合拳上
2.1 镜像分发:为什么不是直接拷tar包
现场最简单的办法其实是把镜像docker save成tar包,拷进内网每台机器docker load一遍,也能跑起来。那为什么还要多搭一个Harbor?原因主要有三个。
第一是可维护性。CubeStudio这类平台一般由网关、任务调度、元数据、算法引擎等多个镜像组成,版本升级、回滚、节点扩容都是常态。如果靠tar包分发,每次都要重新打包、摆渡、导入,不同节点上的镜像tag很容易不一致,时间一长谁也说不清哪个节点跑的是哪个版本。Harbor里所有镜像有统一的仓库列表和tag,版本一目了然。
第二是权限和审计。隔离网项目通常有等保和验收要求,镜像来源、谁拉的什么镜像、什么时间拉的,Harbor的审计日志都能覆盖。tar包则完全没有这些痕迹,出了问题很难追溯。
第三是多架构支持。信创环境里ARM和x86混部很常见,Harbor可以在项目下按架构区分镜像,配合docker的--platform参数,更容易控制拉取的架构。
我列个对比表,方便你们做决策:
| 对比项 | 裸tar包 + docker load | Harbor私有仓库 |
|---|---|---|
| 多节点分发 | 每台机器手动load | 节点统一从Harbor拉取 |
| 版本管理 | 靠人工记录文件名 | 仓库列表 + tag + UI操作 |
| 权限控制 | 基本没有 | 用户 / 机器人账号 / RBAC |
| 操作审计 | 无 | 完整操作日志 |
| 扩容与升级 | 每次重新摆渡 | 节点加上仓库地址即可 |
| 适用场景 | 1-2台机器临时验证 | 正式交付、多节点、长期运维 |
如果项目特别小、只有一两台机器,那我不反对直接用tar包方案;但只要超过三台机器,或者后续有扩容和升级计划,Harbor的投入一定值得。
2.2 “出口机”的定位:是数据摆渡,不是流量通道
很多实施文档把这种模式叫“出口机代理”,听着很玄,实际上就是物理隔离网络里一台经过审批、允许外联的机器。它的职责非常单一:在外网下载指定的软件包和容器镜像,生成校验值,写入授权介质,再通过审批过的流程导入隔离内网。它不承担任何流量转发,也不改动任何网络路由,更不涉及绕过网络安全管控的操作。
这一步在实际项目里最容易被忽视,我建议进场第一天就去确认出口机的使用流程:谁审批、用哪种介质、登记表长什么样。别看这是“行政事项”,在实际项目里它比技术方案更容易卡进度。我们这次下载了一堆包,结果发现介质容量不够,又回去重新换了更大的安全U盘,白等了半天。
提醒一句:整个操作过程要留痕。申请单、操作日志、介质登记、导入记录,能带上的都带上,不然等保检查或项目验收的时候,缺一样都要补流程。
2.3 版本选型:决定“能不能装上”的三个关键点
离线部署最忌讳版本拍脑袋。有三个点必须在出口机下载之前就定下来。
第一,CPU架构要明确。在部署机上执行uname -m,x86_64对应amd64镜像,aarch64对应arm64镜像。信创环境常见的是鲲鹏、飞腾这种ARM平台,海光和兆芯虽然指令集兼容x86,但最好也按官方认证的镜像版本走。镜像拉错架构,启动时直接报exec format error,这是最典型的翻车现场,后面常见问题部分也会专门说。
第二,操作系统版本和内核。麒麟V10、统信UOS 20这些常见信创系统,有基于Debian的,也有基于CentOS的,容器运行时对cgroup版本、iptables兼容性都不一样。如果后面要跑Kubernetes,容器引擎的cgroup driver必须配成systemd,否则kubelet会一路报错。
第三,Harbor版本。建议选择2.x较新的长期维护版本,2.11及以上的安装包依赖Docker Compose v2。很多老教程还在用docker-composev1,在新版本Harbor上会直接安装失败。我们这次用的是harbor-offline-installer-v2.11.0.tgz,在出口机上下载后带进内网,后面细说。
这三个点落实了,后面才是纯粹的“体力活”。
3. 离线部署实操全流程
3.1 阶段一:在出口机整理清单并下载物资
先把所有需要的外网资源列成一张清单,宁可多下也不要少下。我这次整理的清单大概长这样:
| 类别 | 物件 | 备注 |
|---|---|---|
| 容器运行时 | docker-24.0.9.tgz | 官方静态二进制包,不依赖系统软件源 |
| 编排工具 | docker-compose-plugin、helm二进制 | 按CubeStudio交付方式准备 |
| 镜像仓库 | harbor-offline-installer-v2.11.0.tgz | Harbor离线安装包 |
| 平台镜像 | cube-studio相关的全量镜像 | 按架构区分,arm64/amd64 |
| 基础镜像 | ubuntu、python、node、jdk等 | 如果客户会用平台跑自定义任务,强烈建议提前备好 |
| 辅助工具 | jq、curl、vim、tar等 | 系统不自带的工具提前准备好 |
下载顺序建议是:先和厂商确认CubeStudio交付包形式和Harbor版本,再根据内部镜像清单拉取基础镜像。为了避免架构踩坑,在出口机上拉取CubeStudio镜像时,尽量用docker pull --platform=linux/arm64这样的参数,把目标架构固定下来。
如果出口机是Ubuntu/Debian系,另一个省事的办法是直接下载Docker官方提供的静态二进制tar包,解压后放到/usr/bin即可,不需要处理发行版的软件源依赖问题。这也是我在Harbor主机和应用服务器上统一采用的方式,后面不再为缺少某个依赖包发愁。
3.2 阶段二:镜像导出、压缩、分卷与校验
镜像导出看着简单,但大镜像在摆渡环节最容易出问题。安全U盘或者刻录光盘有容量限制,介质在携带过程中也可能损坏,所以导出这一步一定要做三件事:压缩、分卷、校验。
在出口机上的操作大致是这样:
# 拉取目标架构的镜像 docker pull --platform=linux/arm64 registry.example.com/library/cube-studio:2.1.0 # 导出并压缩,按4GB分卷 docker save registry.example.com/library/cube-studio:2.1.0 | gzip | split -b 4G -d -a 2 - cube-studio_2.1.0.tar.gz.part # 生成校验文件 sha256sum cube-studio_2.1.0.tar.gz.part* > cube-studio_2.1.0.SHA256SUMS这里拆开说一下。split -b 4G -d -a 2表示按4GB切分、数字后缀、两位编号,生成.part00、.part01这样的文件。为什么选4GB?因为很多安全介质和审批流程对单个文件大小有限制,4GB是一个比较通用的上限。校验文件一定不能省,目标机导入之前先跑一遍sha256sum -c,防止介质损坏导致脏数据进入内网。
导入的时候在目标机上反向操作:
cat cube-studio_2.1.0.tar.gz.part* | gzip -d | docker load docker images | grep cube-studio注意一个细节:docker load会把镜像的原始tag信息带进来,也就是出口机上带域名或IP的原始仓库地址。走到这一步,镜像已经在目标机上了,后面还需要重新打tag推到Harbor,所以先不用纠结原始tag是什么。
3.3 阶段三:在内网部署Harbor私有仓库
Harbor主机我习惯单独给一台,配置不用太高,CPU 4核、内存8GB、磁盘按镜像增长计划预留200GB以上基本够用。部署前先确认机器能不能装Docker。
以静态二进制方式安装Docker为例:
tar -xzf docker-24.0.9.tgz cp docker/docker docker/dockerd docker/containerd docker/containerd-shim-runc-v2 docker/runc /usr/bin/ # 创建docker systemd unit,然后启动 systemctl daemon-reload systemctl enable --now docker这里有一个细节容易被忽略:静态二进制方式安装的dockerd,默认数据目录是/var/lib/docker。如果系统盘分区不大,一定要先把数据目录迁到独立数据盘,比如在/etc/docker/daemon.json里设置"data-root": "/data/docker"。Harbor自身的业务数据会放在安装目录下的/data目录,比如/opt/harbor/data,这个也要提前规划好磁盘和挂载点。
然后把Harbor离线安装包拷进去解压,编辑harbor.yml:
hostname: 192.168.209.133 http: port: 80 # 如果内网不需要TLS,直接注释掉https段 # https: # port: 443 # certificate: /your/cert.pem # private_key: /your/key.pem harbor_admin_password: YourStrongPassword data_volume: /opt/harbor/data这里有几个容易踩坑的点,单独说下。hostname必须写成其他节点都能访问到的内网IP或域名,不要写localhost,否则后面所有节点推镜像都会失败。如果不打算用TLS,就把https段整体注释掉,只保留http段。自签名证书方案我不是很推荐,因为证书分发到所有节点本身就是一件麻烦事,隔离网里一张证书漏发了,某个节点就会一直报x509错误。
改完配置之后,执行安装:
./install.sh安装脚本会检查docker compose版本和端口占用。如果系统里没有docker compose命令,脚本会直接报错,需要先把compose plugin装上,或者下载docker-compose静态二进制放到/usr/local/bin目录。install完成之后直接验证:
curl -s http://192.168.209.133/v2/ docker compose ls访问/v2/返回{}就说明registry API已经正常起来了。
3.4 阶段四:把镜像推送到Harbor并部署CubeStudio
Harbor起来之后,先在Web界面上创建一个项目,我这里叫cstudio。然后把阶段二load进来的本地镜像重新打tag并推送:
docker tag registry.example.com/library/cube-studio:2.1.0 192.168.209.133/cstudio/cube-studio:2.1.0 docker login 192.168.209.133 -u admin -p YourStrongPassword docker push 192.168.209.133/cstudio/cube-studio:2.1.0如果推送时报http: server gave HTTP response to HTTPS client,几乎可以确定是dockerd默认走https,而Harbor只开了http。解决办法是在每台应用服务器的/etc/docker/daemon.json里配置insecure-registries:
{ "insecure-registries": ["192.168.209.133"], "data-root": "/data/docker", "exec-opts": ["native.cgroupdriver=systemd"] }然后systemctl restart docker。注意,这一步会让该机器上已有容器重启或停止,生产环境务必提前安排维护窗口,别在业务高峰期动daemon配置。
如果CubeStudio是用docker compose交付的,把官方给的compose文件拷到目标机,把里面的image字段全部替换成192.168.209.133/cstudio/...,再执行:
docker compose pull docker compose up -d如果CubeStudio是Kubernetes/Helm方式交付,同样是在出口机上下载helm二进制和chart包,在values文件里覆盖镜像仓库地址和imagePullSecrets,再执行helm install。以K8s方式部署时,先创建一个私有仓库的拉取凭据:
kubectl create secret docker-registry harbor-secret \ --docker-server=192.168.209.133 \ --docker-username=admin \ --docker-password=YourStrongPassword \ -n cstudio然后在Deployment里加上imagePullSecrets引用。哪个方式不是本质,本质是让所有镜像来源都指向内网Harbor,这样后续所有节点的镜像拉取行为都是可控、可追踪的。
3.5 阶段五:验证、备份与交付物整理
部署完不能只看容器状态。我一般按这个顺序验证:
docker compose ps或kubectl get pods确认没有异常退出的容器;- 检查业务端口是否监听,例如CubeStudio的控制台和API端口;
- 登录控制台走一遍登录、创建、运行的最小流程;
- 确认日志没有持续报错,特别是依赖数据库、消息队列的模块;
- 在另一台应用服务器上执行
docker pull 192.168.209.133/cstudio/...,验证Harbor作为内网镜像源的可用性。
验证通过之后,第一时间备份Harbor的配置和数据目录。Harbor的data_volume目录是整个仓库的根,后续恢复全靠它,应该纳入客户的备份体系。备份命令很简单,把整个目录打包拷贝走就行。
交付物这块,我强烈建议最终交付资料里至少包含三样东西:完整镜像清单(含sha256)、部署操作手册(含yaml和命令)、运维排障速查表。隔离网环境下排查问题不容易,文档做厚一点,后面的人会感谢你。
4. 常见问题与排查技巧实录
4.1 推送报错:get "https://192.168.209.133/v2/": dial tcp 192.168.209.133: connect: connection refused
这个错误在Harbor现场几乎每天都能看到。拆解一下:docker客户端以为Harbor是可信任的https仓库,所以去访问443端口,但对方服务根本没起来,或者压根没有监听443,于是TCP连接被拒绝。
排查顺序是:先看Harbor容器有没有正常运行,执行docker compose ps;再看端口有没有监听,执行ss -tlnp | grep 80;最后从应用服务器上curl测试。如果Harbor机器本机curl是通的,换一台机器就不通,那就要查防火墙、安全组和交换机ACL了。
还有一种隐藏情况:Harbor的install.sh安装过程中如果数据库或Redis初始化失败,Harbor核心容器会反复重启,端口时通时断。这时候要去看Harbor容器日志,比如docker compose logs core和docker compose logs db,通常会给出明确的报错信息。
4.2 镜像拉取报“x509: certificate signed by unknown authority”
这个问题常出在自签名证书场景。如果Harbor启用了https,但没有把CA证书分发到所有节点,docker就会因为不信任该证书而拒绝连接。内网环境我优先建议直接走http+insecure-registries,省掉维护证书的环节。
如果监管要求必须走TLS,那就要统一给所有需要拉镜像的节点配置Harbor的CA证书。Linux节点放到/etc/docker/certs.d/<harbor地址>/ca.crt,配置好后systemctl restart docker。注意目录名一定是Harbor的hostname和端口,比如/etc/docker/certs.d/192.168.209.133:443/ca.crt,写错路径一样不生效。
4.3 登录后推镜像还是报401 Unauthorized
Harbor的项目默认是私有的,docker login成功了,推送到一个当前用户没有权限的项目也会被拒绝。还有一个容易忽略的点:如果使用了Harbor的机器人账号,机器人账号只对它被授权的项目有效。
本地凭据缓存也是一个坑。可以用docker logout退出,再重新docker login一次,排除旧凭据干扰。在批量部署节点时,建议给每一台节点都配置独立的机器人账号或统一账号,避免一个账号多设备登录被Harbor的登录策略限制住。
4.4 容器起来报exec format error:架构没有对号入座
这个报错的意思很直接:二进制和CPU架构不匹配。ARM机器上跑amd64镜像,就会这样。赶紧用docker inspect <image> --format '{{.Architecture}}'看镜像架构,用uname -m确认机器架构。
提醒一个实际操作中的细节:如果你在出口机上用x86机器拉取ARM镜像,一定要加--platform=linux/arm64,否则docker会按当前机器架构拉取。如果已经拉错了,可以先docker rmi删掉再重新拉。信创环境多架构并存很常见,建议所有镜像清单里都把架构字段写明,避免时间久了搞混。
4.5 Windows Server 2022上跑WSL containers的离线准备
现在也有不少边缘节点是Windows Server 2022,客户要求用WSL containers方式跑Linux容器。这种环境离线部署更繁琐:WSL的内核更新包(msi)和发行版rootfs都要在出口机上下载好,然后在目标机离线安装WSL并手动注册Linux发行版。容器引擎本身用containerd或Docker静态二进制。
如果客户环境用了这种方式,建议在项目交付前做一次预验证,因为Windows安全补丁和WSL内核版本经常互相牵制,很容易遇到“WSL启动正常但containerd起不来”的情况。能不用WSL containers就尽量不要用,纯Linux物理机或虚拟机部署的稳定度要高一个量级。
4.6 Harbor版本升级的几个坑
Harbor版本升级,最怕的是数据卷丢失。升级前先执行docker compose down,注意千万别加-v参数,-v是删卷的,会把数据库和registry数据全清掉。然后打包备份data_volume整个目录,再用新版本的offline installer解压到新的目录,执行./install.sh。Harbor会自动检测已有数据并保留。
升级时还有一个常见问题:很多人直接替换了harbor.yml,导致数据库密码、Redis密码和已有组件不一致,Harbor起不来。正确做法是先备份原harbor.yml,再按官方模板逐项对比修改,只改必要的hostname、端口和数据目录,其他密码字段不要动。
最后再说几句
这个流程跑通之后,我自己最大的感受是:隔离网部署拼的不是技巧,而是前置准备和文档沉淀。出口机的流程确认、镜像清单的整理、校验值的留存,这些事看着琐碎,却是整个项目能不能按时交付的关键。后续如果要扩容或升级版本,直接照着交付包里的部署手册和镜像清单走一遍就行,不用再临时下载任何东西。
也建议所有做信创交付的团队,都把自己常用的离线部署包做成统一模板,每一次项目都往模板里补充新的依赖和踩过的坑,用着用着你就会发现,最复杂的现场反而变成了最标准化的现场。