Skip to content

nuwa 使用教程

面向平台管理员(我们自己)的操作手册。讲清楚 nuwa 是什么、怎么跑起来、四块功能各自怎么用、 一个新客户从零到交付要点哪些按钮。

架构设计与开发排期看 nuwa 交付中心,这篇只讲怎么用


一、nuwa 是什么

一个 Go 服务(hiapi-cloud-nuwa,Gin + GORM/MySQL)+ 一个 Vue3 后台(store-web)。 Go module 名叫 hiapi-cloud-appstore,env 前缀是 APPSTORE_* —— "nuwa" 是仓库名/习惯叫法, 代码里基本叫 appstore,看到两个名字别以为是两个东西。

它一个进程里塞了四件事:

模块代码表前缀干什么
应用商店internal/app无前缀(apps/developers…)开发者账号、应用登记、前后端版本发布
授权中心internal/licenselic_授权码、配额、心跳、吊销、水印
中央模块库internal/license(同上)lic_module_*保护模块 jar 加密存储 + 按通道下发
私有化交付internal/deploydep_客户集群拓扑、中间件、应用清单

为什么合在一起:同一批客户、同一套身份、同一把 MASTER_KEY为什么代码分开:license 管"能不能用",deploy 管"装什么",变更节奏不同; 且 deploy 刻意不 import license(M4 心跳要反查 deploy 的目标版本,直接互 import 会成环), 靠 Cipher / CustomerLookup / AppLister 三个接口在 main.go 里注进去。


二、跑起来

2.1 后端

bash
cd hiapi-cloud-nuwa
GOOS=darwin GOARCH=arm64 go test ./...     # 本机 go env GOOS 是 windows,必须显式指定
go run ./cmd/server

必配的 env(其余有默认值):

env默认说明
APPSTORE_MYSQL_DSNMySQL DSN,不配起不来
APPSTORE_ADDR:8080监听地址
APPSTORE_TOKEN_SECRETchange-me-in-production开发者 token 签发密钥
APPSTORE_TOKEN_TTL24htoken 有效期
APPSTORE_LICENSE_ED25519_PRIVATE_KEY内置一把授权票据签名私钥
APPSTORE_LICENSE_MASTER_KEY空(启动时随机生成)模块加密 + 部署密码加密都用它
APPSTORE_LICENSE_MONITOR_MODEtruetrue=只观测不拦截;生产灰度完再关
APPSTORE_LICENSE_ALERT_WEBHOOK巡检告警推送地址
APPSTORE_AGENT_TOKEN不配则 POST /v1/agent/modules 返回 503
APPSTORE_OSS_*endpoint / access key / secret / bucket / public url

⚠️ APPSTORE_LICENSE_MASTER_KEY 不配 = 每次重启换一把随机密钥,启动日志会 WARNING。 后果:上次存的模块密文解不开、上次存的部署中间件密码解不开。生产必须固定,且这把丢了 所有客户的中间件密码都取不回来

所有 APPSTORE_* 都有 NUWA_* 的兼容别名(如 NUWA_MYSQL_DSN),老配置不用改。

建表是启动时自动跑的(AutoMigrate),三个模块各跑各的,还会跑一次 V2 数据回填(幂等)。

2.2 前端

bash
cd hiapi-cloud-nuwa/store-web
npm install
npm run dev        # http://localhost:5173,/api 代理到 127.0.0.1:8080
npm run build      # vue-tsc 类型检查 + 产物到 dist/

生产地址:https://appstore.cloud.adinz.com,API 基址 https://appstore.cloud.adinz.com/api

2.3 账号:怎么拿到平台管理员

这是第一个坑。 注册接口写死发 ECOSYSTEM(生态开发者):

go
// internal/app/auth.go
dev := &Developer{ ..., Type: DeveloperTypeEcosystem, ... }

服务层虽然支持传 Type,但 HTTP handler 根本没把它传下去 —— 没有任何接口能创建 PLATFORM 账号。 授权管理、模块发布、私有化部署三块全部挂 platformOnly 中间件,前端路由也用 requiresPlatform 挡。

所以第一个管理员只能改库:

sql
UPDATE developers SET type = 'PLATFORM' WHERE email = '你的邮箱';

改完重新登录(前端把 developerType 缓存在 store 里)。这不是 bug,是有意不给提权入口 —— 但你得知道去哪改,否则会以为后台坏了。

登录后左侧才会出现「授权客户 / 模块发布 / 授权大盘 / 私有化部署」四个菜单。


三、模块一:应用商店

路径 /apps。这是 nuwa 最早的功能,给生态开发者用,平台自己也用它登记自营应用。

3.1 应用的身份

一个应用有三个 ID,别搞混:

  • appId —— P-10001 这种。类型前缀 + 从 10000 起的全局序号。
    • P 平台主应用、A 平台开发应用(两者只有 PLATFORM 开发者能建)、C 第三方生态应用。
  • appSecret —— 32 位 hex,只在创建响应里返回一次,发版时当密码用。忘了只能 regenerate。
  • project —— 应用的项目标识,全局唯一。

🔑 project 是最关键的一个字段,因为私有化部署的勾选清单就是靠它跟 chart 对齐的: App.project 必须等于 hiapi-chart 里的服务名(project=vemvalues.yamlservices.vem)。 对不上不影响勾选和保存,但 M2 渲染时这个应用会落进 Resolution.Unknown,渲染不出东西来。 新建应用时想清楚这个值,建完 pageDir / componentDir 就不能改了。

3.2 状态流转

DEVELOPINGREVIEWONLINE,反向只能走「取消审核」回 DEVELOPINGONLINE 不能直接回 REVIEW,得先取消。

只有 ONLINE 的应用才会出现在:公开市场接口、私有化部署的勾选清单。

3.3 发版

三个入口都是不用 bearer token 的,用 appId + appSecret 表单字段鉴权,方便 CI 直接调:

POST /v1/releases/frontend   # 前端包:components/pages/pageDir/componentDir/file(js)/zip(源码)
POST /v1/releases/backend    # 后端:docker 镜像地址
POST /v1/releases/admin      # 管理端子应用

前端发版会校验 pageDir / componentDir 必须和建应用时填的完全一致,不一致直接拒。 产物传到 OSS 的 hiapi-cloud/<project>/<version>/ 下。

版本是双轨的:前端(F)和后端(B)各自一条版本线,都只追加不删。

3.4 公开市场接口(无需登录)

GET /v1/market/categories
GET /v1/market/apps                                 # 只返 ONLINE,不含 appSecret
GET /v1/market/apps/batch?appIds=C-10001,C-10002    # 逗号分隔,去重,未上线的静默跳过
GET /v1/market/apps/:appId

四、模块二:授权中心

路径 /license(客户列表)、/license/overview(大盘)。只有 PLATFORM 能进。

4.1 概念先理清

客户 (Customer)              一个采购方。lic_customers。
 └── 授权码 (License)         HLIC-XXXX…。一个客户可以有多个:
      ├── PROD                生产授权码
      └── DEV                 开发/测试授权码,默认低配额(2 实例 / 5 租户)
           └── 实例 (Instance) 一个跑着的进程。按心跳窗口算在线数。
           └── 租户 (Tenant)   客户系统里的商户,由业务侧上报。
           └── 机器 (Machine)  设备指纹台账,Java DeviceReporter 上报。

配额卡在两处:实例数卡在模块下发(module/fetch),租户数卡在租户上报(tenant/report)。

4.2 开一个新客户

后台 /license → 新建客户,填名称、套餐、实例上限、租户上限、到期时间。 建的瞬间 nuwa 自动生成:客户级 license key + 模块密钥 + 水印标识。

进客户详情,九个 Tab:

Tab用途
授权码发 PROD / DEV 码、改配额、换码、吊销、钉版(pin 某模块到某版本)
机器设备指纹台账,看客户到底装在几台机器上
事件激活/心跳/超额/吊销的时间线,排查纠纷全靠它
客户信息客户级配额、状态、配额强制开关(逐客户,默认关)
部署该客户的 install 记录
租户上报上来的商户列表
闸门模块⚠️ 旧的按客户传模块,已 DEPRECATED,改用全局「模块发布」
水印客户水印标识,泄漏溯源用
吊销记录吊销过哪些票据

4.3 发授权码

客户详情 → 授权码 Tab → 新建:

  • 环境:PROD 或 DEV。DEV 会自动 EnforceQuota=true + AllowBeta=true + 低配额默认值。
  • 上限填 0 = 按环境默认(PROD 走客户级配额,DEV 走 2/5)。
  • 到期时间留空 = 永久;DEV 建议给 6 个月。

生成的码形如 HLIC-A2E852229D3220D422EF259917BB4D42922F00F5A3DDD04D,这就是交给客户的东西

换码(rotate-key)会生成新码但保留宽限期,老码不会立刻死。

4.4 客户端怎么接

客户那边的 Java 服务(hiapi-cloud-public)在 application.yaml 里配三个键:

yaml
hiapi:
  license:
    key: HLIC-A2E852229D3220D422EF259917BB4D42922F00F5A3DDD04D
    mode: enforce                                         # monitor(只观测)/ enforce(拦截)
    server-url: https://appstore.cloud.adinz.com/api      # 不写就是这个默认值

启动时 C++ JNI 加载器(hiapi-core-cpp)通过 HiapiCloudLicense 走这几个接口:

GET  /license/pubkey          # 取签名公钥
POST /license/activate        # 激活,拿 install_id(缓存在 ~/.hiapi/install_id)
POST /license/heartbeat       # 心跳
POST /license/module/fetch    # 拉加密模块 ← 实例配额卡在这
POST /license/tenant/report   # 上报租户 ← 租户配额卡在这
POST /license/device/report   # 设备台账

两级开关,别搞混:

  • 服务端 APPSTORE_LICENSE_MONITOR_MODE=true → 全局只观测,任何超额都不拦。
  • 客户端 hiapi.license.mode → 那一个部署自己的模式。
  • 客户信息 Tab 的 配额强制 → 逐客户开关。

生产上线必须先 monitor 灰度,确认没误伤再切 enforce。三个开关任何一个是宽松的,结果就是宽松的。

4.5 授权大盘

/license/overview。全局水位:每个授权码的实例/租户用量、在线状态、告警。

后台有个 Sweeper 每分钟跑一次,盯四类异常:跨客户同指纹(串号/转卖)、DEV 超用超配额全量离线。配了 APPSTORE_LICENSE_ALERT_WEBHOOK 就会推出来。


五、模块三:中央模块库

路径 /license/modules这是保护模块(hiapi-core-public)的发布入口。

口径是 "传一次,全网可用" —— 不再按客户传。上传明文 jar,nuwa 用 MASTER_KEY 加密入库, 客户端下次 module/fetch 时按通道钉版规则拉。

5.1 手动发布

后台 → 模块发布 → 填:

  • 模块名:默认 hiapi-core-public
  • 版本号:必填
  • 通道:stable(默认)/ beta。只有 AllowBeta=true 的授权码(DEV 默认开)能拉到 beta。
  • 模块文件:明文 jar/zip,加密是 nuwa 干的,别自己先加密
  • 发布说明

moduleName + version 重传会覆盖并清掉派生缓存,防旧密文继续下发。

5.2 一键发布(推荐)

改完 hiapi-core-public 之后直接用 skill:

/report-public-module

它会 mvn -Pproguard package 出混淆件,再 POST /v1/agent/modules 上报。需要两个 env:

bash
export NUWA_BASE_URL=https://appstore.cloud.adinz.com/api
export NUWA_AGENT_TOKEN=<和服务端 APPSTORE_AGENT_TOKEN 一致>

服务端没配 APPSTORE_AGENT_TOKEN 的话这个接口整体 503 —— 是有意的,不配就等于关掉。

5.3 钉版

某个客户不想跟最新版,就在客户详情 → 授权码 → pins 里把某模块钉到某版本。 钉了之后这个授权码永远拉那一版,不受新发布影响。修完 bug 记得解钉。


六、模块四:私有化部署配置

路径 /deployM1(存配置)+ M2(服务端渲染下发)已完成,后台点一下就能拿到 可直接 kubectl apply 的交付包(见 §6.5)。装机前置还是手工(M3)。

6.1 一客户一集群

dep_customer_deployments.customer_id 是唯一索引。同一个客户建第二份配置直接 409。 客户实体复用授权中心的 lic_customers,所以要先在授权那边建客户,才能在这里建部署配置

6.2 新建配置:字段逐个说

/deploy/new,分四段。

① 基本信息

字段说明
客户下拉,来自 lic_customers
访问域名ingress host,必填,要合法域名
Ingress class默认 traefik(我们用 install-k3s.sh 装出来的就是它);客户已有 ingress-nginx 的填 nginx。⚠️ 填错的表现是域名一直 404,而且没有任何报错指向它 —— Ingress 建得出来,只是没有控制器认领。装完 kubectl get ingressclass 对一眼
目标版本镜像 tag。留空则回落 chart 的 appVersion
release 名默认 hiapi。⚠️ 小写、≤20 字符,下发之后不可改
命名空间默认 hiapi。⚠️ 同样下发后不可改
状态DRAFT(还没发过)/ ACTIVE(跑着)/ SUSPENDED(欠费下线)

⚠️ release 名和命名空间为什么锁死:PVC、Service、Secret 的名字全带 release 名, 改了等于换一套资源,老的全成孤儿。所以 service 层在状态变成 ACTIVE 之后会直接拒绝这两个字段的修改。 要改就趁 DRAFT。

② 中间件四件套(MySQL / Redis / RabbitMQ / Nacos)

每个中间件两条路:

  • 装在集群里(deploy: true)→ 用 chart 的 bitnami 子 chart,地址由 chart 自己算。 这时填了 host 也会被清掉,免得误导后面看配置的人。
  • 用客户已有的(deploy: false)→ 必须填 host,不填直接拦。 端口留空会补默认值:MySQL 3306 / Redis 6379 / RabbitMQ 5672 / Nacos 8848。

③ 密码

四个密码。新建时四个都必须填,少一个报错会点名是哪个中间件。

  • 密码用 MASTER_KEY 加密后存 credentials_enc 列,任何接口都不回显明文, 详情接口只给一个 credentialsSet: {mysql: true, ...} 表示"设过了"。
  • 改配置时密码留空 = 保持原值。因为前端根本拿不到原值,如果把留空当清空, 管理员改个域名就会把四个密码全抹掉。

nuwa 库里集中着所有客户的生产中间件密码,credentials_enc 这一列是整个系统最高价值的攻击目标。 这也是 MASTER_KEY 必须固定且必须妥善保管的原因。

④ 部署哪些应用

这块的规矩很硬,记住一句话:清单 = App 表,一条不多一条不少。

  • 列表来自 GET /v1/deploy/catalog,内容是 apps 表里全部 ONLINE 的应用,按 project 排序。
  • 卡片上显示的是 App 表的东西:图标、名称、project、简介。
  • chart 里有、但没在应用商店登记过的服务,一个字都不会出现 —— 测试环境常见的 public / finance / socket 就属于这类。这是刻意的,包括"缺依赖:xxx"这种顺带印服务名的提示也一并删了。
  • 全部人工勾选,不做依赖联动。vem 不会自动带上 finance/user/socket。 依赖关系由人把控,校验留给 M2 渲染期的 chart(templates/services.yaml 里有硬 fail)。
  • App 表读失败时显示错误横幅 + 空列表,不会退回 chart 的兜底清单。宁可空着也不能显示错的。

底座不在这个列表里。 chart 里 enabled: true 的服务默认就装,界面上看不见:

nacos(走 nacos.deploy)+ gateway + admin-ui + task-worker

⚠️ public 不在底座里(2026-07-29 定,它按普通应用走清单)。但网关把 /cloud-api/** 全路由到 public,登录和验证码都在那儿 —— 实际上每个客户都必须勾它,不勾的话 admin-ui 打得开但登不进去。它出清单只是为了交付时统一管理,不是说可以不装。

⚠️ 底座里还缺一个 web-ui(H5 构建产物的静态站)。它属于必装,但 chart 里还没有这个服务, 因为产物落在 hiapi-h5-dist 这个 PVC 上,而那个 PVC 需要 RWX 且写死 aliyun-nas,k3s 没有。 PVC 的事不解决它进不了 chart。见 TODO §0.6

存下来的时候两个字段都会写:

  • selectedServices —— 你实际勾的,改配置时回填勾选框用
  • enabledServices —— 勾的 + 底座,下发时直接用这份,不必再依赖当时的 chart 版本重算

6.3 改了 chart 必须重新同步

nuwa 的二进制里 go:embed 了一份 chart 副本(internal/deploy/chart/), 服务目录、依赖矩阵、底座标记全解析自它的 values.yaml源仓库 hiapi-chart 才是权威。

bash
cd hiapi-cloud-nuwa
./scripts/sync-chart.sh     # 从 hiapi-chart 同步 + 记录 commit
go build ./...

同步脚本会把 chart 的 commit 记进 CHART_SOURCE,hiapi-chart 有未提交改动时会标 chartDirty: true —— 看到这个说明同步的是脏副本,先去 commit chart 再同步。

改了 chart 不同步 = nuwa 用的还是旧的依赖矩阵和底座定义。 这是最容易忘的一步。

6.4 部署配置的接口

GET  /v1/deploy/catalog                    # 勾选清单(只含 App 表字段)
GET  /v1/deploy/deployments
POST /v1/deploy/deployments
GET  /v1/deploy/deployments/:id
PUT  /v1/deploy/deployments/:id
GET  /v1/deploy/deployments/:id/revisions  # 下发版本(每次下发一条)
POST /v1/deploy/deployments/:id/render     # 渲染下发,dryRun=true 只预检
GET  /v1/deploy/deployments/:id/artifacts  # 下发记录(不含包本体)

以上全部走平台管理员 bearer。只有下载不需要登录:

GET  /v1/deploy/artifacts/:token           # 一次性、限时,token 即凭据

装机的人常常是现场同事甚至客户自己的运维,不能为了拿一个包给出平台后台账号。 token 是 256 位随机、库里只存 sha256、下载一次即作废。失败原因(不存在/已用过/过期) 对外一律同一句 404 —— 区分了等于告诉爆破的人"这个 token 存在过"。

6.5 建完配置之后:怎么真的装到 k8s 上(M2 已完成,2026-07-30)

nuwa 现在自己渲染:后台点「渲染下发」→ 服务端把内嵌 chart 渲成完整 YAML → 打包成 tar.gz → 给一条一次性、限时的下载链接。

拿到包的人不需要 helm,不需要 chart,也不需要 nuwa 后台账号 —— 解压、跑 deploy.sh 就完事。这一点是有意的:装机的常常是现场同事甚至客户自己的运维,不能为了拿个包给出后台账号。

第 1 步:后台点两下

部署配置详情页底部两个按钮:

按钮干什么会不会留痕
预检渲染只校验 + 渲染,给出资源概览、警告、清单摘要不落记录、不产包
渲染下发真发:落一条 Revision + 产一张下载券会,且配置从 DRAFT 推到 ACTIVE

先点预检。 它把 chart 的所有校验跑一遍,依赖没开齐、密码没配、外部地址没填这类问题 现在就报出来,不用等到客户集群里 Pod CrashLoop:

硬依赖没开齐,补齐再下发:vem 需要 socket
以下中间件密码没配,先去补:nacos
这些应用在当前 chart 里没有对应服务,装不上:brand-new-app

最后一条是 Resolution.Unknown:后台勾了、但当前 chart 里没有同名服务的应用 (多半是应用商店先登记了新应用、chart 还没跟上,或者注册时 project 没填成 chart 里的 服务名)。默认直接拒绝下发 —— 静默跳过等于客户买了 A 却没装 A,而且没人会发现。 确认之后可以点「知道装不上,跳过并下发」,跳过的应用会写进警告和包里的 README。

密码不用你带了:nuwa 自己解密(和 credentials_enc 同一把 MASTER_KEY), 所以下发前必须确认四个密码都已配置,页面上「中间件密码」那块会显示"已配置/未配置"。

第 2 步:下载交付包

弹窗里给出文件名、大小、清单 sha256、有效期,以及下载/复制链接两个按钮。

🔴 链接是一次性的,而且弹窗关掉就再也拿不到 —— 库里只存 token 的 sha256。 需要的话重新点一次「渲染下发」(会生成新的一版)。 默认有效期 30 分钟(ttlMinutes 可调,上限 24 小时),过期或下载过一次即失效, 并且立刻把库里的包密文清空,只留摘要用于事后核对。

包里 manifests.yaml 的 Secret 段有四个明文生产密码。装完删掉整个包。

第 3 步:在客户集群上装

bash
tar xzf hiapi-<>-<>-<>.tar.gz
cd <解开的目>
cat README.txt        # 前置条件都写在这儿

sudo ./install-k3s.sh # 空机器才需要,一台机器只跑一次(已有 k8s 集群跳过)
./deploy.sh           # 会打印 kubectl context 并要求输 yes;--yes 可跳过

install-k3s.sh 装的是 k3s,顺带解决了 Ingress 控制器(traefik)、 Service type=LoadBalancer(servicelb,不用买 SLB)和默认 StorageClass(local-path)。 它是幂等的:已经装过就只做体检。常用开关:

bash
sudo DATA_DIR=/data ./install-k3s.sh              # 挂了数据盘,别让 PVC 落在系统盘
sudo K3S_VERSION=v1.31.5+k3s1 ./install-k3s.sh    # 按交付批次钉版本(推荐)
sudo K3S_MIRROR= ./install-k3s.sh                 # 境外机器,不走国内镜像

它会拦下几个"能让交付白跑一趟"的前置:内存不够(全是 JVM,内存永远比 CPU 先不够)、 80/443 被占、机器时间没和 NTP 同步(授权票据带有效期,时钟偏了会表现成"授权无效", 而没人会往这个方向想)。

deploy.sh 做三件事,顺序有讲究:

  1. 建 namespace(幂等)
  2. 先删两个引导 Job(<release>-db-init<release>-nacos-bootstrap)
  3. kubectl apply -f manifests.yaml

第 2 步是必须的:那两个 Job 在 chart 里是 helm 的 post-install hook,而 kubectl 完全不认 hook 注解,helm.sh/hook-delete-policy 对它没有意义;Job 的 spec.template 又是不可变的,所以重装/升级时不先删就会撞上 Job.batch "..." is invalid: spec.template: field is immutable。删掉重建是安全的 —— 两个 Job 都幂等,而且各自带 until 循环等中间件就绪,可以和中间件同时启动。

第 4 步:装机前置(自己装机的话 install-k3s.sh 已经全部满足)

只有"装在客户已有集群上"才需要人工核对。README.txt 里也列了同一份:

  • k8s ≥ 1.28(k3s 也行)
  • ingress controller,而且 class 名要和配置里的「Ingress class」一致 (我们装机出来的是 traefik;客户已有 ingress-nginx 的改成 nginx)。 ⚠️ 对不上的表现是:Ingress 建得出来、kubectl get ing 一切正常,但没有控制器认领它, 域名一直 404,而且没有任何日志指向这个原因。装完对一眼:kubectl get ingressclass
  • 一个能动态供给的 默认 StorageClass(集群内中间件要 PVC:mysql 100Gi / redis 8Gi / rabbitmq 8Gi / nacos 8Gi)
  • 集群能拉到镜像:我们的 registry.cn-shenzhen.aliyuncs.com/hiapi-cloud/*,以及 bitnami 的 docker.io/bitnami/*(完全无外网的客户要提前把两边都同步进内网仓库,做法见 Chart 指南 §十) ⚠️ 私有仓库还没打通:chart 支持 global.imagePullSecrets,但 nuwa 的部署配置里 还没有这个字段,渲出来的包里它是空的。目前只能靠"镜像可匿名拉取"。

业务库和 Nacos 初始配置不用手工建 —— 就是上面那两个引导 Job 干的 (hiapi-db-init 建库、hiapi-nacos-bootstrap 建 namespace + 灌占位配置)。

第 5 步:看装没装起来

bash
kubectl -n hiapi get pods -w
kubectl -n hiapi logs -f job/hiapi-db-init

头一两分钟业务 Pod 会重启几次,这是正常的 —— 它们在等建库和 Nacos 引导。 详细观察点、Nacos 控制台怎么进、常见故障对照表,见 Helm Chart 指南 §十二/§十三

之后:改配置怎么生效

nuwa 里改完(加应用、换版本、中间件搬到 RDS)→ 再点一次「渲染下发」→ 下新包 → 在客户集群上再跑一次 deploy.shkubectl apply 是声明式的,重复跑是安全的。

⚠️ releaseName 一旦 ACTIVE 就不能改(PVC、Service、Secret 名字全带它),nuwa 会拒绝。 配置在包被取走的那一刻从 DRAFT 变 ACTIVE —— 包一旦离开 nuwa,就得当成客户集群里 已经有这套资源来对待。

字段映射表(改 chart 字段名时必须回头改 internal/deploy/values.go)

nuwa 字段chart values
targetVersionglobal.version(留空 → 回落 Chart.yamlappVersion)
imagePullSecretglobal.imagePullSecrets(单个名字包成数组;留空不渲染 = 公开仓库)
ingressHostingress.host
ingressClassNameingress.className(留空 → traefik;显式渲进去,不吃 chart 默认值)
middleware.mysql.deploy=falsemysql.deploy=false + mysql.external.{host,port,user}
middleware.redis.databaseredis.database(不在 external 里面)
middleware.rabbitmq.vhostrabbitmq.vhost(同上,在 external 外面)
middleware.nacos.{host,port}nacos.external.addr(chart 这里是一个字符串,要拼)
四个密码credentials.*(唯一来源,别往 mysql.auth 里另填)
启用的服务services.<名>.enabled
releaseName / namespacehelm 的 release 名 / -n

映射写错了不会报错,只会渲出一份连不上中间件的清单,而它长得和正常的一模一样。 所以 internal/deploy/deliver_test.go 里专门有一个用例逐个断言这些地址真的注进去了。

启用清单按下发那一刻的内嵌 chart 重算(不是照抄库里的 enabledServices): 二进制里的 chart 才是这次要渲染的那份,chart 新增底座服务或改过依赖矩阵时, 用旧列表会漏装。重算结果有变化会作为警告显示出来,并写进 Revision 留档。

怎么确认"服务端渲染 = 本机 helm template"

这是 M2 的验收标准,已经做成用例(TestRenderMatchesHelmCLI):本机装了 helm 就跑 真实的 helm template 逐字节对比,没装就跳过。

nuwa 用的是 helm 官方 SDK(helm.sh/helm/v4),不是 exec 一个 helm 二进制 —— nuwa 是单文件交付,镜像里没有 helm。SDK 版本要和交付同事本机的 helm 大版本对齐, 否则那条验收无从谈起(升级 helm SDK 时记得回来跑一遍这个用例)。 渲染是纯客户端的(DryRunClient):不连任何集群、不查 API 版本,Capabilities 用 helm 内置默认值。 所以渲染通过 ≠ 客户集群一定装得上 —— k8s 版本、StorageClass、镜像可达性还是要到现场才知道。

手工兜底:hiapi-deploy/render-customer.sh

M2 之前的桥接脚本,仍然可用(读同一份配置、出同一份 values),但四个密码要你自己 从环境变量带进去。nuwa 部署验证完就删掉它,别维护两条路径。

还没有的(M4~M6)

内容卡在哪
M4版本回流:心跳带 currentVersion要先建 dep_install_status
M5自升级 agent + nuwa 侧分批/熔断依赖 M4
M6客户自助升级卡 Flyway(现在是 ddl-auto: update,不敢让客户自己点)

七、端到端:开一个新客户

按顺序,标了 ⬜ 的是现在还得手工干的。

  1. 应用商店:确认要交付的应用都已登记且 ONLINE,project 和 chart 服务名对得上。
  2. 授权中心 → 新建客户,填配额和到期时间。
  3. 客户详情 → 授权码 → 发一个 PROD 码(要给客户开发环境的话再发一个 DEV 码)。
  4. 模块发布 → 确认 hiapi-core-public 有可用的 stable 版本。
  5. 私有化部署 → 新建配置:选刚建的客户 → 填域名 → 中间件(自建还是用客户的)→ 四个密码 → 勾应用(记得勾 public)。
  6. 渲染下发 —— 见 §6.5: 后台先点「预检渲染」看有没有依赖缺口,再点「渲染下发」,拿一次性链接下交付包。
  7. 装机前置 —— 手工:k3s、ingress controller、默认 StorageClass。 (业务库和 Nacos 初始配置由 chart 的 post-install Job 自动做,不用手工)
  8. 在客户集群上跑 ./deploy.sh,并把授权码交给客户。

    🔴 交付包里带明文中间件密码 —— 这是一次性凭据,不是普通文件。 下载链接本身已经 限时+一次性,但包落到磁盘之后就归你管了:装完删掉,不要留在 IM 聊天记录或网盘里。

  9. 客户端配好 hiapi.license.key,先跑 mode: monitor
  10. 授权大盘 观察实例/租户水位正常之后,再切 enforce + 打开该客户的配额强制。

八、排错

现象原因
登录后看不到授权/部署菜单账号是 ECOSYSTEM。改库成 PLATFORM重新登录
接口 403 platform privilege required同上,前端放行了但后端 platformOnly 拦下了
重启后模块解不开 / 部署密码解不开APPSTORE_LICENSE_MASTER_KEY 没配,每次重启换随机密钥。看启动日志的 WARNING
POST /v1/agent/modules 返 503服务端没配 APPSTORE_AGENT_TOKEN,接口整体禁用
同上返 401本地 NUWA_AGENT_TOKEN 和服务端不一致
部署勾选清单是空的apps 表里没有 ONLINE 应用,或 App 表读失败(看页面上的错误横幅)
某个服务在 chart 里有但清单里没有正常。清单只认 App 表,去应用商店登记并上线它
改了 chart,nuwa 行为没变忘了跑 scripts/sync-chart.sh 并重新 build
同步后 chartDirty: truehiapi-chart 有未提交改动,先 commit 再同步
建第二份部署配置 409一客户一集群,customer_id 唯一
改 release 名被拒配置已 ACTIVE,这个字段锁死了
改完配置密码没了不会。留空 = 保持原值,这是有测试守着的行为
超额没被拦三个开关任一宽松就是宽松:服务端 MONITOR_MODE / 客户端 hiapi.license.mode / 客户配额强制

九、接口速查

公开(无鉴权)

GET  /healthz
POST /v1/developers/register        POST /v1/developers/login
GET  /v1/market/categories          GET  /v1/market/apps
GET  /v1/market/apps/batch          GET  /v1/market/apps/:appId
POST /v1/releases/frontend|backend|admin      # appId + appSecret 表单鉴权

开发者(Bearer)

GET/PATCH /v1/developers/me         POST /v1/developers/change-password
POST /v1/uploads/logo
POST/GET  /v1/apps                  GET/PATCH /v1/apps/:appId
PATCH     /v1/apps/:appId/status    POST /v1/apps/:appId/secret/regenerate
POST      /v1/apps/:appId/versions

授权客户端(客户的服务调,授权码鉴权)

GET  /license/pubkey
POST /license/activate | heartbeat | module/fetch | tenant/report | device/report

平台管理(Bearer + PLATFORM)

GET  /v1/license/server-config | quota-summary | overview
     /v1/license/customers…            客户 CRUD、installs、tenants、水印、吊销
     /v1/license/customers/:id/licenses          发码
     /v1/license/licenses/:id…         改码、换码、吊销、pins、machines、instances、events
POST/GET/PATCH/DELETE /v1/license/modules[/:id]  中央模块库
GET  /v1/deploy/catalog
     /v1/deploy/deployments…           部署配置 CRUD + revisions

自动化(X-Agent-Token)

POST /v1/agent/modules              # 未配 APPSTORE_AGENT_TOKEN 则 503

相关文档