☰
离线环境部署实践:GitHub私有仓库到内网Harbor/Nexus的完整链路
2026/9/28 5:24:22 网站建设 项目流程

GitHub 上的代码还静静地躺在私有仓库里,目标服务器却在隔离网络环境中,上面连一个依赖包都没有。最要命的是,这次的交付不是一台机器,而是好几台 ARM 设备,架构还不一样。我当时的第一个想法是"完蛋,这怎么搞",第二个想法是"既然没有网络,那就自己做一个仓库中转站"。

于是就有了这份实践记录。它既包含了 GitHub 私有仓库侧的代码和制品准备,也包含了用 Harbor 承接容器镜像、用 Nexus 承接 tar.gz 和二进制包、最后在内网完成部署的完整链路。这篇东西不是官方文档的复述,而是我从零到一跑通之后留下的笔记,适合正在做私有化项目交付、离线环境部署、或者需要把外部开源组件系统性地搬进企业内网的运维和开发朋友参考。即便你的目标不是 ARM 设备,链路中的大部分环节同样可以直接照搬。

1. 为什么我把 GitHub 私有仓库当成部署链路的起点

先交代一下背景。我这次要交付的是一个边缘计算相关项目,代码托管在 GitHub 私有仓库里,编译产物、模型权重、运行环境镜像分散在不同位置。目标机器是 RK3588 开发板,部署 YOLOv8 推理服务,现场环境完全隔离,除了带着一台笔记本进现场,没有任何外网能力。

刚开始我很自然地想"直接把代码打包带过去不就行了",但很快就发现事情没那么简单。

1.1 这一轮部署面临的真实约束

首先,代码本身有依赖。YOLOv8 从 Python 侧有 ultralytics 包、opencv、torch,从部署侧有 ONNX Runtime、rknn-toolkit2 这类工具链,这些依赖散落在 GitHub、PyPI、Release 附件里,光靠 git clone 一次根本带不全。

其次,硬件平台特殊。RK3588 是 ARM 架构,有些组件在 PC 上装好了没用,必须在目标机上跑。你不可能指望现场慢慢编译,因为交叉编译工具链、系统依赖库、Python wheel 包都得提前准备好。

更麻烦的是团队协作。代码不止我一个人在动,分支、tag、发行说明都在 GitHub 私有仓库里维护。我如果只带一份代码拷贝进现场,回头升级、补 bug、同步变更会变得非常痛苦。所以必须建立一个可复用的机制:GitHub 私有仓库作为源头,内网私有仓库作为中转,目标机器从内网仓库拉取物料完成部署。

1.2 部署链路的三个角色与三种搬运模式

整条链路可以拆成三个角色,对应三种不同的搬运需求:

角色承载内容对应工具搬运模式
源头仓库源码、文档、构建脚本GitHub 私有仓库代码快照 / 增量同步
制品中转容器镜像Harbor(私有镜像仓库)公网拉取后转存 Harbor
制品中转tar.gz、wheel 包、二进制Nexus(Raw 仓库)直接上传 / 脚本自动同步

代码属于高频变动内容,适合用 git 协议本身来处理,哪怕离线也有git bundle这种稳妥方案。容器镜像则必须依赖 Docker Registry 协议,Harbor 是最好用也最稳的选择。tar.gz 这类静态制品,扔到 Nexus 的 Raw 仓库里管理最直观,因为它的生命周期和版本完全由你自己定义。

1.3 整体流程设计:从源头到内网的一次完整通路

我在动手之前先画了一张执行顺序图(不用任何工具,就是纸笔),整个流程分成四步:

  1. GitHub 私有仓库侧完成代码、令牌、Release 产物、Actions 流水线的准备。
  2. 在外网跳板机(或者你笔记本上)把需要的东西拉下来,转存到内网私有仓库。
  3. 目标环境登录内网 Harbor 和 Nexus,按部署脚本拉取对应版本。
  4. 在目标机器上完成安装和验证。

后面所有章节,基本就是按这四步展开的。如果你觉得"我只需要其中某一段",可以直接跳到对应章节,每段都独立成篇。

2. GitHub 私有仓库侧的准备工作:代码、令牌与 Release 产物

很多人一提到 GitHub 私有仓库,第一反应就是"建立仓库、把代码推上去、加了权限就行"。但作为部署链路的源头,私有仓库要承担的东西比这多得多。它不仅要保证代码能溯源,还要保证制品有稳定的下载入口、权限边界清晰,甚至要为后续的自动化流转提供接口。

2.1 创建私有仓库时的几个参数选择

新建仓库时,大多数人只关心 Public / Private 的选择,但实际上这里埋了不少决策点。

  • 仓库名和描述:建议带统一前缀,比如edge-yolov8-inference,描述里写明硬件平台(RK3588)和用途。这会在你维护多个仓库时省下大量记忆成本。
  • 初始化选项:README、.gitignore建议勾上,尤其.gitignore对 Python 项目很重要,可以避免把weights/、venv/、__pycache__这些大目录推上来。License 先不选,企业私有项目通常在法务确认后才补,省得后面改来改去。
  • 默认分支:直接设为main,不要再用master,和现代工具链的默认值保持一致,也少一点无意义的转换操作。

2.2 Personal Access Token 的粒度与安全边界

私有仓库的 CI/CD 和脚本不能每次都用密码交互式登录。这里需要 Personal Access Token(PAT),而选择 PAT 类型时必须想清楚它会被用到什么场景。

我这次用了 fine-grained token,而不是传统的 classic token,原因很简单:fine-grained token 可以精确到某个仓库、某几项权限,即使泄露了,影响面也控制在一个很小的范围内。针对这次部署,我申请的权限是:

  • Contents:读取代码内容,方便脚本拉取指定 tag。
  • Packages:读取 GitHub Container Registry 的镜像。
  • Metadata:默认必需的最小权限。
  • Administration(只读):查看 release 状态,用于确认制品是否打好。

有效期我设了 90 天,因为这是一个阶段性交付项目。如果你做长期维护,建议考虑用 GitHub App 的安装令牌,它有自动轮换机制,比 PAT 更安全。另外,PAT 我从未放进代码里,也没有写进部署脚本,而是存放在密码管理器中;内网环境里需要用到时,由跳板机上的运维小号统一管理,这个后面会专门说。

2.3 用 Release 和 Package 功能固化部署制品

私有仓库里最容易被忽略的功能就是 Release。我强烈建议所有部署相关的产物,都通过 GitHub Actions 自动构建后上传到对应仓库的 Release 页面,而不是手动用scp或者网盘传来传去。

原因有两个。第一,Release 自带版本和完整性概念,你发布一个v1.2.0,附件就是明确的yolov8s_rk3588_v1.2.0.tar.gz,不会出现"那个最终版本到底在哪个文件夹里"的混乱。第二,Release 的下载链接是稳定 URL,内网同步脚本可以直接通过 API 解析最新版下载,完全不需要人工介入。

我这次在 Actions 里写了三步式流水线:先在 x86 的 runner 上完成基础构建,再把产物上传到 Release,最后执行一个同步任务把必要的 wheel 包推到 Nexus。GitHub Container Registry(ghcr.io)我也用起来了,比如ghcr.io/your-org/edge-yolov8-api:v1.2.0这样的镜像地址,后续可以直接转到 Harbor。

2.4 分支保护与 Actions Secrets 配置

部署项目的特殊性在于,main分支往往直接对应可发布状态,如果谁都能往上推,很容易把半成品带进交付现场。我在仓库设置里开启了分支保护规则,核心是两条:要求 Pull Request 通过后再合并、禁止直接推送main。这套规则在团队里运行了一个月,效果立竿见影——至少不会有人再把调试用的临时文件混进主线。

Actions Secrets 配置同样要重视。Nexus 的用户名密码、Harbor 的登录令牌、以及内网仓库地址,都放进了 Secrets 而不是写死在 workflow 文件里。这样做的好处是:即使仓库被 fork 或者某个开发者误公开,机密信息也不会跟着流出去。

3. 把代码搬进隔离网络:git bundle 与中转仓库的组合打法

代码是所有部署链路里最"活"的部分,它不像镜像那样打包就定型,也不像 tar.gz 那样一次成型。我这次需要的代码包括推理服务源码、rknn-toolkit2 的 Python 绑定、以及几个配置仓库。把它们从 GitHub 私有仓库搬到隔离网络,我用的是git bundle加内网裸仓库的组合方案。

3.1 直接 git clone 到跳板机的现实困境

可能有人会觉得,先在一台能访问 GitHub 的跳板机上git clone,然后scp整个目录进去不就行了?实际操作时你会发现两个问题。

一是仓库里往往有 Git LFS 文件或者体积不小的二进制资源,直接在公网上 clone 会非常慢,而且一旦中间网络抖动,整个 clone 进程很容易失败,没有断点继续的能力。二是你 scp 过去的目录是带了.git的完整工作副本,到了内网之后很难再跟源仓库做增量同步,做不到"改一行代码,同步一个 commit"的轻量更新。

3.2 git bundle:一个完整到不能再完整的离线快照

git bundle可以把整个仓库的历史、分支、标签打包成单个文件。它比直接打包.git目录更规范,因为它本质上是一个"只读的 Git 传输表示",接收方可以像对待远程仓库一样对待这个 bundle 文件。

我当时的操作是这样的:

# 在能访问 GitHub 的机器上 git clone --bare https://github.com/your-org/edge-yolov8-inference.git cd edge-yolov8-inference.git git bundle create edge-yolov8-inference.bundle --all # 验证 bundle 文件完整性 git bundle verify edge-yolov8-inference.bundle

这里我用的是--bare克隆,生成的是纯 Git 数据库,没有工作目录,所以 bundle 文件会小一些,也避免把临时文件、未跟踪文件一并带进去。--all会把所有分支和 tag 都打进 bundle,交付现场如果需要某个历史版本,也能随时取出来。

3.3 内网目标环境如何接收和推送

把 bundle 文件带进内网之后,我建议先建一个裸仓库作为内网权威源,而不是直接在目标机器上还原工作副本。原因很简单:多台设备要从同一个源拉代码,内网总得有一个"服务端"角色。

# 内网服务器上 mkdir /srv/git/edge-yolov8-inference.git cd /srv/git/edge-yolov8-inference.git git init --bare # 从 bundle 文件导入完整历史 git fetch /path/to/edge-yolov8-inference.bundle 'refs/heads/*:refs/heads/*' git fetch /path/to/edge-yolov8-inference.bundle 'refs/tags/*:refs/tags/*'

执行完后,内网服务器就有了一个完整的 Git 仓库,目标机器再从这个本地仓库拉代码:

git clone ssh://git@internal-server/srv/git/edge-yolov8-inference.git

这里需要说明一下:当前内网服务器可能没有 Git 服务端软件(比如 Gitea、GitLab),直接用git init --bare配合 SSH 就能跑起来,SSH 权限通过系统用户的 authorized_keys 来控制。对团队协作要求高的场景,再上 Gitea 或 GitLab 不迟。

3.4 后续增量更新:避免全量 bundle 的笨办法

第一次全量 bundle 没问题,但每次小改都重新打一个几百 MB 的 bundle 就太蠢了。我后来改用git fetch加git push的方式做增量同步。

流程是这样的:外网机器上保留一个镜像仓库,定期执行:

git fetch origin --prune git push internal --prune --tags

这里internal是内网裸仓库的 SSH remote。--prune会删除远端已经不存在的分支引用,防止内网仓库留下过期分支。需要注意,git push internal --prune --tags会强制推送所有分支到对应 inner bare 仓库,适合"内网权威源完全跟随外网仓库"的场景。如果内网仓库同时还有本地自定义分支,就不能这么干,否则会把别人推上去的东西冲掉。

增量同步我建议做成定时任务,比如每 30 分钟跑一次。对于私有项目交付,同步频率别太高,避免频繁推送导致内网仓库 refs 膨胀。

4. 部署私有镜像仓库:为什么选 Harbor,以及那套让人头痛的证书

代码搬过去了,接下来处理容器镜像。YOLOv8 的推理服务我封装成了 Docker 镜像,里面包括推理 API、依赖库,甚至模型转换工具链。外网环境里一般直接docker pull官方镜像或ghcr.io里的私有镜像就行,但隔离网络里必须有中转站。

4.1 为什么不用裸 Docker Registry 而非要用 Harbor

第一反应是"搭一个 Docker Registry 不就行了",确实,裸 registry 也能用,但如果一个人用还好,一旦多人多团队用,痛点立刻出现:

  • 没有 Web 界面,查一个镜像 tag 存在与否都要敲 API。
  • 没有 Project 级权限,所有人默认是管理员,很容易误删镜像。
  • 没有镜像清理和回收机制,时间长了磁盘会被旧 tag 撑爆。
  • 没有镜像复制功能,从一个 Harbor 到另一个 Harbor 的跨环境同步得自己写脚本。

Harbor 把这些都做成开箱即用的功能。我用的是 v2.x 离线安装包,部署在离目标环境最近的服务器上,选择离线包是因为现场没有外网,需要一次性把 Harbor 本身的镜像和依赖全部带进去。

4.2 Harbor 离线安装与配置细节

离线包解开后,核心工作就是改harbor.yml。我遇到的关键配置项有三个。

hostname建议直接用内网 IP 或者内网 DNS 名,不要用 localhost,否则目标机 pull 镜像时会解析到自身。

harbor_admin_password必须改掉默认值,而且要足够长,因为 Harbor 是内网制品的核心入口,一旦被爆破,整个部署物料就暴露了。

这里有个非常容易踩的坑:Harbor 默认会启用 HTTPS。如果只在内网用,我们可以配置自签名证书,但目标机器很多,每台都要信任证书会很痛苦。我的做法是在内网 DNS 上给 Harbor 分配一个稳定域名,然后用内网 CA 签发一张证书,各目标机把这个 CA 加入系统信任列表。

如果你完全不想搞证书体系,Harbor 也支持 HTTP 部署,只需要在harbor.yml里把https段落注释掉,同时目标机的 Docker daemon 配置里加上insecure-registries。我这次为了减少现场踩坑,最终走了 HTTP 加内网白名单的方案,后面会说怎么配置。

安装过程本身不复杂:

sudo ./install.sh

但安装前的磁盘空间检查一定要做。Harbor 用的 PostgreSQL、Registry、Redis 组件加起来至少需要 20GB 空闲空间,如果/data分区不够,后面镜像转存跑到一半罢工,你会非常被动。建议在安装前执行docker info确认存储驱动和剩余空间。

4.3 用 Project 和机器人账号做环境隔离

Harbor 里最值得花心思的是 Project 设计。我按目标环境拆分了 project,比如rk3588-prod、rk3588-staging,每个 project 单独分配允许拉取的机器人账号。这样即使某个环境的凭据泄露,攻击者也只能拿到对应环境的镜像,不能把别的环境也扫一遍。

机器人账号的权限只勾选"拉取"(Pull),不勾推送到任意 project 的权限。真正推送镜像的操作,交给一个独立的 CI 账号或者管理员账号。这个习惯看起来繁琐,但在多环境交付场景里能避免非常尴尬的"测试镜像被推到生产仓库"事故。

4.4 外网镜像转存 Harbor 的标准操作

镜像转存是每次部署前都要做的事,不可手工docker tag+docker push一个一个来,我把它写成了脚本。假设需要从 ghcr.io 拉取官方镜像并同步到 Harbor:

#!/usr/bin/env bash set -euo pipefail SRC_IMAGE="ghcr.io/your-org/edge-yolov8-api:v1.2.0" DST_HARBOR="harbor.internal.example.com" DST_PROJECT="rk3588-prod" docker pull "$SRC_IMAGE" docker tag "$SRC_IMAGE" "$DST_HARBOR/$DST_PROJECT/edge-yolov8-api:v1.2.0" docker login "$DST_HARBOR" -u "$HARBOR_USER" -p "$HARBOR_PASS" docker push "$DST_HARBOR/$DST_PROJECT/edge-yolov8-api:v1.2.0"

这套命令在离网现场同样适用,只是把ghcr.io换成内网 Harbor 地址而已。需要注意docker tag时 tag 一定是不可变版本号,比如v1.2.0,千万别用latest,否则你会面临"昨天明明部署成功了,今天 pull 下来却不是同一个镜像"的诡异问题。

如果你的镜像数量很大,或者本机 Docker daemon 磁盘紧张,可以用skopeo直接完成跨仓库复制,不用在本地落盘:

skopeo copy docker://ghcr.io/your-org/edge-yolov8-api:v1.2.0 docker://harbor.internal.example.com/rk3588-prod/edge-yolov8-api:v1.2.0 --dest-creds "$HARBOR_USER:$HARBOR_PASS"

我实测下来这一招对转存大量镜像特别省事,速度也快,因为少了 docker daemon 这一层。

5. tar.gz 和二进制制品的私有化存放:Nexus Raw 仓库实战

模型权重、RKNN 转换工具链、Python wheel 包、一键部署脚本,这些既不是代码也不是镜像,但它们同样是部署链路里不可或缺的制品。GitHub 的 Release 适合承载,但隔离网络里必须有一个可以反复拉取的内网位置。我选了 Nexus Repository 的 Raw 仓库。

5.1 为什么不用 Git LFS 或者直接扔对象存储

Git LFS 是一个方案,但免费额度很小,企业自建 Git 服务虽然在带宽和存储上可控,LFS 却会让git clone变慢,而且在目标机器上每次拉取大文件都会卡很久。对象存储(如 MinIO)也可以,但用它管理软件包时,版本元数据、命名规范、依赖关系都得自己造轮子,对交付项目来说太重了。

Nexus 的 Raw 仓库本质是一个带 REST API 的通用文件托管服务。你可以把它理解成"内网的简单对象存储 + 版本化目录",用 curl 就能上传下载,脚本集成非常方便。

5.2 创建 Raw 仓库和上传命令

Nexus 安装后,在后台创建一个 Raw 仓库,名字我建议带环境上下文,比如edge-artifacts-prod,部署时 Blob Store 单独指向一个数据盘。仓库创建好后,上传一个 tar.gz 非常直接:

curl -u "$NEXUS_USER:$NEXUS_PASS" \ --upload-file yolov8s_rk3588_v1.2.0.tar.gz \ https://nexus.internal.example.com/repository/edge-artifacts-prod/yolov8/yolov8s_rk3588_v1.2.0.tar.gz

注意我在这里用了版本路径v1.2.0,而且在仓库里建了yolov8/子目录。Nexus Raw 仓库的路径规则完全由你决定,但强烈建议按"组件名/版本/文件名"三层组织,这样后期写批量同步脚本时,能直接用版本号正则匹配。

5.3 KubeKey 场景:把 tar.gz 推到私有仓库再部署

这次部署还有一个绕不开的场景:目标机器的 AI 推理环境需要先装一套容器运行时和 Kubernetes 工具链,我参考了 KubeKey 的离线部署思路。KubeKey 本身有一个很关键的问题:官方默认从 GitHub Release 下载所需的kubekey-bin等 tar.gz 包,离线环境根本拉不到。

我的做法是分两步。第一步,在外网机器上用curl -L把 GitHub Release 里所有需要的 tar.gz 拉到本地;第二步,写一个循环脚本,按 KubeKey 的目录结构全部推到 Nexus Raw 仓库。比如:

curl -u "$NEXUS_USER:$NEXUS_PASS" \ --upload-file kubekey-bin-v3.0.0-linux-amd64.tar.gz \ https://nexus.internal.example.com/repository/edge-artifacts-prod/kubekey/kubekey-bin-v3.0.0-linux-amd64.tar.gz

然后在内网目标机器安装时,把安装命令里的下载 URL 替换成 Nexus 地址,或者预先下载到本地目录再执行安装。这样 KubeKey 在离网状态下一步一步校验哈希、解压、部署,完全不会卡在 GitHub 连接那一步。

5.4 部署脚本里的 URL 替换策略

制品一旦进了 Nexus,内网部署脚本就可以统一走"从仓库拉取"的逻辑。我在所有部署脚本里维护了一个变量文件,例如:

ARTIFACT_BASE="https://nexus.internal.example.com/repository/edge-artifacts-prod" KUBEKEY_PKG="$ARTIFACT_BASE/kubekey/kubekey-bin-v3.0.0-linux-amd64.tar.gz" YOLO_PKG="$ARTIFACT_BASE/yolov8/yolov8s_rk3588_v1.2.0.tar.gz"

这样当版本升级时,只需要更改变量文件里的版本号,后面所有curl -O和校验逻辑自动跟着走。这个习惯保证了部署过程可重复,不会出现"手动下载了旧包,脚本又拉了个新包"的版本错乱。

6. 一次完整落地:RK3588 设备离线部署 YOLOv8 的复盘

前面的章节都是组件级实践,这一节我会完整串一遍。按我的经验,端到端的部署才是最考验细节的,任何一环漏了都会在现场返工。

6.1 现场环境与前置物料清单

目标设备:RK3588 开发板,系统为 Ubuntu 22.04(ARM64),内存 16GB,磁盘 64GB。现场没有外网,但有一个小型内网交换机,Harbor、Nexus、Git 裸仓库部署在一台 x86 机架上。

进入现场前,物料清单如下:

物料来源中转位置体积
edge-yolov8-inference 源码GitHub 私有仓库内网裸仓库12MB
yolov8s_rk3588_v1.2.0.tar.gzGitHub ReleaseNexus3.1GB
rknn-toolkit2 相关 wheel 包GitHub Release / PyPINexus420MB
推理服务 Docker 镜像ghcr.ioHarbor1.8GB
Harbor / Nexus 离线包官网现场机架已安装

这里我特别想说一句:物料清单一定在出发前就要列好,到现场再发现少东西,就只能干瞪眼。

6.2 串行执行链路:从代码到推理服务的四步

第一步,代码。在目标板上git clone ssh://git@internal-server/srv/git/edge-yolov8-inference.git,切换到部署 tagv1.2.0。这一步依赖内网 SSH 用户,提前在 authorized_keys 里加了现场机器的公钥。

第二步,依赖。从 Nexus 拉取 wheel 包并安装:

cd /opt/edge-yolov8 pip install --no-index --find-links=/opt/wheels \ rknn-toolkit2-1.6.0-cp310-cp310-linux_aarch64.whl

这里必须强调--no-index,否则 pip 会尝试访问 PyPI,在隔离网络里直接卡死。把需要的 wheel 包都放到本地目录后,用--find-links指向本地目录,安装速度反而比在线还快。

第三步,镜像。RK3588 上的推理服务,一部分组件我选择用 Docker 跑,因为依赖冲突太多。在/etc/docker/daemon.json里配置好 insecure-registries:harbor.internal.example.com,然后systemctl restart docker。登录 Harbor,直接拉取目标 project 的镜像:

docker login harbor.internal.example.com -u "$ROBOT_USER" -p "$ROBOT_TOKEN" docker pull harbor.internal.example.com/rk3588-prod/edge-yolov8-api:v1.2.0 docker compose up -d

第四步,模型权重和配置。把目标设备要用的.rknn模型文件从 Nexus 拉取,放到指定目录。到这里,推理服务的前后端链路已经全部在本地打转,没有任何外网依赖。

6.3 现场实际踩到的三个坑

第一个坑出在 Docker 证书上。我没有给 Harbor 配 HTTPS,而是用 HTTP 加 insecure-registries。但在 RK3588 上,Docker 的 systemd 启动脚本里有自己的insecure-registries默认值,直接修改/etc/docker/daemon.json后没有及时 reload。重启 docker 时一度提示certificate signed by unknown authority,后来确认是 daemon.json 配置没生效,systemctl daemon-reload再加systemctl restart docker才解决。实际配置正确后,HTTP 在内网里跑完全没问题。

第二个坑是镜像 tag 漂移。我在转存时不小心把一个镜像 tag 写成了latest,结果第二天在另一台机器上部署时拉到的镜像和昨天校验过的 SHA 不一致。排查了很久,最后发现是 Harbor 的 project 里有人(其实是脚本)重新推送了一次。从那之后,我把所有推送命令的 tag 强制改成版本号,并在 deploy 脚本里用--digest固定镜像摘要,彻底杜绝了漂移。

第三个坑是磁盘空间。Nexus 的 Blob Store 默认存放在系统盘,转了 3.1GB 的模型包后,系统盘一度满了。现场扩容很麻烦,我不得不把 Blob Store 迁移到独立数据盘。建议你在 Nexus 创建时就指定数据盘路径,别等到爆了再改。

6.4 部署完成的验证清单

我在每次部署结束后都会按清单逐项验证,缺一不可:

  • docker ps中推理服务容器处于 healthy 状态。
  • curl http://127.0.0.1:8080/health返回200。
  • 用测试图片调用推理 API,返回的检测框数量符合预期。
  • 模型版本和权重 SHA 与 Nexus 上的记录一致。
  • 重启设备后,服务能通过 systemd 或 Docker restart policy 自动拉起。

这套验证流程看起来简单,但它能挡住 90% 的交付类返工问题。在离网现场,宁可多花十分钟验证,也不要带着侥幸心理离开。

7. 这套实践里我认为值得长期保留的经验

跑完整个流程后,有几个经验已经沉淀成了我自己的固定做法,这里分享给遇到类似场景的朋友。

第一,所有制品必须不可变。不管代码 tag、镜像 tag、tar.gz 文件名,一旦发布,内容就不要再改。如果确实需要修复,就发一个新版本号v1.2.1。这个看似死板的规矩,在多人协作和内网交付里能避免大量"到底是哪个包"的争执。

第二,把同步工作脚本化并纳入版本管理。无论是 ghcr 到 Harbor 的镜像同步,还是 GitHub Release 到 Nexus 的 tar.gz 同步,都应该写成脚本放到 GitHub 私有仓库里管理。脚本本身也是代码,也要走版本控制;不要在外网机器上写一堆没有备份的临时代码。

第三,令牌和密码走最小权限原则。GitHub 的 PAT 只给该给的仓库权限,Harbor 只给拉取权限,Nexus 只给对应仓库的read/write。权限越大,泄露后不可控的破坏力越大。跳板机、CI 账号、现场装机账号全部单独创建,不要混用管理员。

第四,内网源优先考虑 HTTP 加白名单的折中方案。证书体系在团队规模小、网络隔离严格的项目里,投入产出比很低。先用 HTTP 把链路跑通,让业务顺利交付,后续如果安全审计要求严,再逐步引入内网 CA 和 TLS。我在 RK3588 现场就是用 HTTP 跑完验收的,事后在测试环境才把证书补上。

最后,我想说一点个人体会。GitHub 私有仓库在这个链路里不仅仅是"代码存放地",它实际上是整个交付体系的事实源头。所有版本、制品、发布说明、构建流程都以它为锚点。只要源头清晰,中间不管加多少层中转,最终部署的可控性都不会太差。反过来,如果源头乱糟糟,就算 Harbor 和 Nexus 搭得再好,现场也是一团浆糊。所以,动手搭仓库之前,先把版本规范和制品命名规范定清楚,这事比安装任何一个软件都重要。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询