Appearance
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/license | lic_ | 授权码、配额、心跳、吊销、水印 |
| 中央模块库 | internal/license(同上) | lic_module_* | 保护模块 jar 加密存储 + 按通道下发 |
| 私有化交付 | internal/deploy | dep_ | 客户集群拓扑、中间件、应用清单 |
为什么合在一起:同一批客户、同一套身份、同一把 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_DSN | 空 | MySQL DSN,不配起不来 |
APPSTORE_ADDR | :8080 | 监听地址 |
APPSTORE_TOKEN_SECRET | change-me-in-production | 开发者 token 签发密钥 |
APPSTORE_TOKEN_TTL | 24h | token 有效期 |
APPSTORE_LICENSE_ED25519_PRIVATE_KEY | 内置一把 | 授权票据签名私钥 |
APPSTORE_LICENSE_MASTER_KEY | 空(启动时随机生成) | 模块加密 + 部署密码加密都用它 |
APPSTORE_LICENSE_MONITOR_MODE | true | true=只观测不拦截;生产灰度完再关 |
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=vem↔values.yaml的services.vem)。 对不上不影响勾选和保存,但 M2 渲染时这个应用会落进Resolution.Unknown,渲染不出东西来。 新建应用时想清楚这个值,建完pageDir/componentDir就不能改了。
3.2 状态流转
DEVELOPING → REVIEW → ONLINE,反向只能走「取消审核」回 DEVELOPING。 ONLINE 不能直接回 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 记得解钉。
六、模块四:私有化部署配置
路径 /deploy。M1(存配置)+ 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 做三件事,顺序有讲究:
- 建 namespace(幂等)
- 先删两个引导 Job(
<release>-db-init、<release>-nacos-bootstrap) 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.sh。kubectl apply 是声明式的,重复跑是安全的。
⚠️ releaseName 一旦 ACTIVE 就不能改(PVC、Service、Secret 名字全带它),nuwa 会拒绝。 配置在包被取走的那一刻从 DRAFT 变 ACTIVE —— 包一旦离开 nuwa,就得当成客户集群里 已经有这套资源来对待。
字段映射表(改 chart 字段名时必须回头改 internal/deploy/values.go)
| nuwa 字段 | chart values |
|---|---|
targetVersion | global.version(留空 → 回落 Chart.yaml 的 appVersion) |
imagePullSecret | global.imagePullSecrets(单个名字包成数组;留空不渲染 = 公开仓库) |
ingressHost | ingress.host |
ingressClassName | ingress.className(留空 → traefik;显式渲进去,不吃 chart 默认值) |
middleware.mysql.deploy=false | mysql.deploy=false + mysql.external.{host,port,user} |
middleware.redis.database | redis.database(不在 external 里面) |
middleware.rabbitmq.vhost | rabbitmq.vhost(同上,在 external 外面) |
middleware.nacos.{host,port} | nacos.external.addr(chart 这里是一个字符串,要拼) |
| 四个密码 | credentials.*(唯一来源,别往 mysql.auth 里另填) |
| 启用的服务 | services.<名>.enabled |
releaseName / namespace | helm 的 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,不敢让客户自己点) |
七、端到端:开一个新客户
按顺序,标了 ⬜ 的是现在还得手工干的。
- 应用商店:确认要交付的应用都已登记且
ONLINE,project和 chart 服务名对得上。 - 授权中心 → 新建客户,填配额和到期时间。
- 客户详情 → 授权码 → 发一个 PROD 码(要给客户开发环境的话再发一个 DEV 码)。
- 模块发布 → 确认
hiapi-core-public有可用的 stable 版本。 - 私有化部署 → 新建配置:选刚建的客户 → 填域名 → 中间件(自建还是用客户的)→ 四个密码 → 勾应用(记得勾 public)。
- 渲染下发 —— 见 §6.5: 后台先点「预检渲染」看有没有依赖缺口,再点「渲染下发」,拿一次性链接下交付包。
- ⬜ 装机前置 —— 手工:k3s、ingress controller、默认 StorageClass。 (业务库和 Nacos 初始配置由 chart 的 post-install Job 自动做,不用手工)
- 在客户集群上跑
./deploy.sh,并把授权码交给客户。🔴 交付包里带明文中间件密码 —— 这是一次性凭据,不是普通文件。 下载链接本身已经 限时+一次性,但包落到磁盘之后就归你管了:装完删掉,不要留在 IM 聊天记录或网盘里。
- 客户端配好
hiapi.license.key,先跑mode: monitor。 - 授权大盘 观察实例/租户水位正常之后,再切
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: true | hiapi-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相关文档
- nuwa 交付中心 —— 设计决策与 M0~M6 开发顺序(主文档)
- Helm Chart 指南 —— chart 结构、服务开关、RBAC
- 总览与架构 —— 为什么废弃 hiapi-customers/Argo
- 授权保护-上线切换手册 —— monitor→enforce 灰度步骤
hiapi-cloud-nuwa/AGENTS.md—— 代码级约定(改 nuwa 前先读)