应用层部署(Helm)
应用层由 chatkit Helm chart 部署 FinClaw 全部业务微服务(api-gateway / identity-service / orchestrator / ai-infra-rs / claw-fleet-manager 等)。基础层(RKE2 + 状态服务)必须已就绪。
Chart 结构
Chart.yaml 元数据:apiVersion: v2、name: chatkit、version: 0.1.0、appVersion: 1.0.0,描述为 "ChatKit Middleware on Kubernetes (dual-track with Docker Compose / fleet.sh)"。
templates/ 目录清单:
| 模板 | 渲染对象 | 用途 |
|---|---|---|
platform-service.yaml | platform namespace | 全部平台微服务 Deployment + Service(api-gateway / identity-service / orchestrator / ag-ui-server / claw-fleet-manager 等);nodePort 可选 |
shared-claw.yaml | platform namespace | Profile A(B0 PoC)共享 ai-infra-rs Deployment + Service |
tenant-claw.yaml | claw namespace | Profile B 静态 gitops 租户 claw StatefulSet + headless Service(动态租户由 claw-fleet-manager 经 K8s API 应用同样 manifest) |
restate-bundled.yaml | platform namespace | 可选的集群内 Restate stub,仅 PoC 用;生产用 externalServices 指向基础层 |
restate-claw-namespace-alias.yaml | claw namespace | ExternalName alias,让租户 claw 通过 ext-restate-svc 跨命名空间解析 Restate |
ingress.yaml | cluster | 入口 Ingress,支持 splitStreamingPaths 把 SSE 路径直接打到 ag-ui-server |
storage.yaml | platform namespace | 平台 PVC(workspaces / aiInfraData / skillsData / docToolkitCache) |
storage-nfs.yaml | cluster | NFS CSI 静态 PV(profile=csi-nfs) |
storage-claw-namespace.yaml | claw namespace | 租户 claw 命名空间下的 NFS PV 与 PVC |
storage-extra.yaml | platform namespace | 额外存储卷 |
fleet-manager-rbac.yaml | platform + claw namespace | Namespace + ServiceAccount + Role/RoleBinding,让 claw-fleet-manager 能管理租户 claw StatefulSet |
identity-keys.yaml | platform namespace | identity-service 的 JWT 签名密钥 Secret(existingSecret 或 chart files 两种来源) |
secrets.yaml | platform namespace | internal-service-token / cron-internal-token / llm-api-key;chart 在 render 时即拒绝空 / CHANGE_ME / 旧占位符 |
infra-configmap.yaml | platform namespace | 把 infra.* 端点文档化进 ConfigMap |
app-config.yaml | platform namespace | 把 chart-config/* 配置文件(services.yaml / ai-infra.yaml / identity-service.yaml / skill-service.yaml / mcp-servers.yaml / persona-templates.json / nginx.conf / inbound.yaml)打包成 ConfigMap 并按 subPath 挂载 |
external-services.yaml | cluster | 把集群外服务(基础层 Restate / TEI)以 Headless Service + Endpoints 暴露给集群内 DNS |
searxng.yaml | platform namespace | Claw web_search 用的 SearXNG Deployment + Service |
_helpers.tpl | - | chatkit.image / chatkit.storageProfile / chatkit.storageClaimName 等 helper |
服务镜像默认通过 global.registry + global.tag 组合,单服务 hotfix 可经 images.overrides.<svc>.full 完整覆盖。
values 层次
Helm 多文件 overlay,按以下顺序 -f 合并(后者覆盖前者):
| 文件 | 用途 | 何时使用 |
|---|---|---|
values.generated.yaml | SSOT:服务端口 / 副本 / peer URL / servicesEnv,由 config/services.yaml 自动生成 | 始终 |
values.yaml | 全局 registry / tag / secrets 默认 / claw / storage / ingress / infra.restate / clawFleet 默认 | 始终 |
values-basic.yaml | 默认 stack overlay(stack: default;文件名 basic 为历史遗留) | 始终 |
values-poc-basic.yaml | Profile A(B0 PoC 共享单 claw) | PoC / 共享 claw 场景 |
values-k8s-fleet.yaml | Profile B(per-tenant claw via claw-fleet-manager K8s backend) | 生产多租户 |
values-k8s-ingress.yaml | 启用外部 Ingress + SSE sticky session | 需要外部域名接入时 |
values-k8s-nfs-example.yaml | NFS CSI 静态 PV 接入示例 | 用 NFS 做存储时复制为 site overlay |
values-k8s-external-example.yaml | 集群外 TEI / Restate 经 Headless Service 接入示例 | TEI 在集群外时参考 |
values-k8s-tenant-examples.yaml | 静态 gitops 租户 claw items 示例 | 用静态 claw 时参考 |
values-site.yaml(自建) | 站点特有:image registry、密钥、infra.* 主机 IP、NodePort、license | 始终 |
deploy/sites/<customer>/*(可选) | 多客户拆分:site.yaml / infra.yaml / images.yaml / secrets.yaml / services.env.yaml | 多客户环境 |
values.generated.yaml是 SSOT,由发布流程自动生成,禁止手工编辑;如需修改服务端口或 infra 端点,请联系凡泰极客支持。
内部 registry 镜像拉取
基础层 ansible 部署的内部 registry 域名为 registry.finogeeks.internal:5000。站点 values-site.yaml 中设置:
global:
registry: registry.finogeeks.internal:5000/finclaw
tag: "1.1.11" # 与离线包版本一致
imagePullPolicy: IfNotPresent
imagePullSecrets: [] # 私有仓库需要凭证时填 [{ name: harbor-pull }]私有 registry 凭证:
- 若 registry 启用认证,先在集群内创建 imagePullSecret,再在
global.imagePullSecrets引用 - 单服务 hotfix 不必整包升级,用
images.overrides.<svc>.full完整覆盖一条镜像即可
Ingress 与 SSE 长连接
ingress.yaml 默认 disabled(ingress.enabled: false)。需要外部域名接入时合并 values-k8s-ingress.yaml,并设置 ingress.host:
ingress:
enabled: true
host: chatkit.example.com
className: nginx
splitStreamingPaths: true
agUiServer:
stickySessions:
enabled: true
services:
ag-ui-server:
replicas: 2关键设计:
- api-gateway catch-all:
/路径打到 api-gateway,承担静态资源、REST、auth_request子请求;proxy-read-timeout默认 1200s - SSE 长连接路径(
splitStreamingPaths: true):/agent(Prefix)与/events(Exact)绕过 api-gateway 直接打到 ag-ui-server,proxy-read-timeout提升到 86400s、关闭 proxy-buffering、proxy_http_version 1.1、X-Accel-Buffering: no - Sticky session:ag-ui-server 多副本时通过 ingress cookie affinity 把客户端固定到某个 Pod,因为 ag-ui-server 进程内有运行态注册表;cookie 名
chatkit-ag-ui-affinity、max-age 86400 - clientMaxBodySize:默认 100m,与 api-gateway 一致
Restate 外置 / NFS 存储 / external-services
Restate 外置(生产默认)
infra.restate.mode: external(默认),chart 不部署 Restate,而是经 externalServices 用 Headless Service + Endpoints 把基础层 Restate 暴露成集群内 DNS ext-restate-svc:
externalServices:
enabled: true
items:
restate:
name: ext-restate-svc
ips: ["10.0.1.187", "10.0.1.188", "10.0.1.189"] # 基础层 restate 分组所有节点 IP
ports: [26180, 26182]
infra:
restate:
ingressUrl: http://ext-restate-svc:26180
adminUrl: http://ext-restate-svc:26182
clawNamespaceAlias:
enabled: true
serviceName: ext-restate-svcrestate-claw-namespace-alias.yaml 会在 chatkit-claw 命名空间再创建一个 ExternalName,让租户 claw Pod 跨命名空间解析 Restate。
PoC 场景若要 chart 内置 Restate stub,设 infra.restate.bundled.enabled: true,渲染 restate-bundled.yaml(StatefulSet,单副本,docker.restate.dev/restatedev/restate:1.3.2,10Gi 存储)。生产禁用。
NFS 存储
storage.profile: csi-nfs 时,chart 渲染 storage-nfs.yaml 与 storage-claw-namespace.yaml,创建一组静态 PV(reclaimPolicy: Retain、storageClassName: ""、nfs.csi.k8s.io driver)。NFS server 需提前 export 每个 PV 对应的子目录:
mkdir -p /nfs/finclaw/{workspaces-pvc,ai-infra-data-pvc,doc-toolkit-cache-pv,skills-data-pvc}
echo '/nfs/finclaw/workspaces-pvc *(rw,sync,no_subtree_check,no_root_squash)' >> /etc/exports
# 其余三条同上
exportfs -raProfile B 还需为每租户 claw 的 volumeClaimTemplates 准备动态 StorageClass(不要用未 export 的 /nfs 根,CSI 会报 "read-only file system")。
External-services
external-services.yaml 是通用机制,不只用于 Restate。集群外 TEI 也可同样接入:
externalServices:
items:
tei:
name: chatkit-tei
ips: ["10.0.6.11", "10.0.6.3"]
ports: [26117]
infra:
memory:
enabled: true
embeddingEndpoint: "http://chatkit-tei:26117/embed"安装步骤
Step 1:生成 / 复用 Identity JWT 密钥
./deploy/scripts/generate-identity-keys-for-k8s.sh \
--output-dir ./identity-keys \
--create-secret --namespace chatkit \
--secret-name chatkit-identity-keyssite overlay 中引用:
identityKeys:
enabled: true
existingSecret: chatkit-identity-keysStep 2:准备 values-site.yaml
从 values-site-example.yaml 复制,至少填写:
global.registry/global.tag:与push-images-to-registry.sh的 REGISTRY 一致secrets.internalServiceToken:强随机串,chart 在 render 时即拒绝空 /CHANGE_ME/ 旧占位符secrets.llmApiKey:LLM 凭证infra.postgres.host/infra.timescaledb.host:基础层 PG/Timescale 主库 IP,不要用ext-*-svcHeadless DNS 做写库infra.redis.url/infra.nats.url/infra.restate.ingressUrl/infra.restate.adminUrlexternalServices.items.restate.ips:基础层 restate 分组所有节点 IP
Step 3:推镜像到内部 registry
./deploy/scripts/load-images.sh # 加载到本机 docker
./deploy/scripts/push-images-to-registry.sh registry.finogeeks.internal:5000/finclawStep 4:helm install
Profile B(生产推荐):
helm upgrade --install chatkit deploy/k8s/helm/chatkit \
-f deploy/k8s/helm/chatkit/values.generated.yaml \
-f deploy/k8s/helm/chatkit/values.yaml \
-f deploy/k8s/helm/chatkit/values-basic.yaml \
-f deploy/k8s/helm/chatkit/values-k8s-fleet.yaml \
-f deploy/k8s/helm/chatkit/values-k8s-ingress.yaml \
-f values-site.yaml \
--namespace chatkit --create-namespaceProfile A(PoC 共享 claw):
helm upgrade --install chatkit deploy/k8s/helm/chatkit \
-f deploy/k8s/helm/chatkit/values.generated.yaml \
-f deploy/k8s/helm/chatkit/values.yaml \
-f deploy/k8s/helm/chatkit/values-basic.yaml \
-f deploy/k8s/helm/chatkit/values-poc-basic.yaml \
-f values-site.yaml \
--namespace chatkit --create-namespace建议正式安装前先 --dry-run=server 校验模板渲染与 K8s API server 端的引用完整性。
License 硬门禁
identity-service 启动时校验 DEPLOYMENT_LICENSE_KEY + DEPLOYMENT_LICENSE_CIPHER,缺失或解密失败会 fail-fast。两者通过 servicesEnv(或 services.env.yaml)注入:
DEPLOYMENT_LICENSE_KEY:base64url(no padding) 的 32 字节随机密钥DEPLOYMENT_LICENSE_CIPHER:0x01 || IV(12) || tag(16) || AES-256-GCM(plaintext),AAD 为ck-deployment-license-v1,plaintext 为{"expiresAt","customer","issuedAt"}JSON
KEY/CIPHER 由凡泰签发并提供(离线包默认内置 30 天有效期),到期或需续期时联系凡泰,更新流程见 更新 License。
安装后验证
./scripts/verify-k8s-install.sh --gateway http://<node-ip>:30001
# 完整 e2e
./scripts/verify-k8s-install.sh --gateway http://10.0.0.5:30001 --profile b --e2e
# 仅 HTTP 检查(无 kubectl 访问的机器)
./scripts/verify-k8s-install.sh --gateway http://10.0.0.5:30001 --skip-kubectlverify-k8s-install.sh 检查项:
- Kubernetes 集群访问与命名空间权限
- 平台 Pod 状态:api-gateway / identity-service / orchestrator / ag-ui-server / claw-fleet-manager Deployment ready;无 Pending / CrashLoop
- 平台 PVC 全部 Bound
- claw-fleet-manager
/health:status: ok、runtime_backend与 Profile 一致(Profile B 应为kubernetes) - Profile B 租户 claw Pod ready、PVC Bound、cron Restate 已注册、跨命名空间访问 identity、INTERNAL_SERVICE_TOKEN 已注入
- 选项
--e2e:注册 / 登录 / agent SSE 烟雾测试
补充手工检查:
kubectl get all --all-namespaces | grep -E "chatkit|claw"
kubectl -n chatkit get deploy,svc,ingress,pvc,cm,secret
kubectl -n chatkit-claw get pods,svc,pvc
helm -n chatkit history chatkit更多排障命令见 排障与巡检。
