|
|
||
|---|---|---|
| .. | ||
| templates | ||
| tests | ||
| .helmignore | ||
| Chart.lock | ||
| Chart.yaml | ||
| README.md | ||
| values.schema.json | ||
| values.yaml | ||
SkillHub Helm Chart
企业级 AI 技能中心私有化部署方案,基于 Kubernetes 和 Helm。
特性
- 微服务架构:Server(Spring Boot)、Web(Nginx)、Scanner 分离部署
- 高可用:支持 HPA 自动扩缩容、PDB Pod 中断预算
- 数据层:使用 Bitnami PostgreSQL/Redis,支持主从复制、哨兵模式
- 安全:TLS 证书管理、Secret 密码保护;Bitnami 数据组件默认提供 NetworkPolicy
- 可观测性:内置 Prometheus metrics exporter
快速开始
前置要求
- Kubernetes 1.24+
- Helm 3.8+
- kubectl configured
安装
先创建受保护的 values-production.yaml。以下值必须替换为实际随机强密码:
secrets:
allowAutoGenerated: false
bootstrapAdminPassword: "<固定管理员密码>"
downloadAnonCookieSecret: "<至少32字符的固定随机值>"
postgresql:
auth:
postgresPassword: "<固定PostgreSQL管理员密码>"
password: "<固定skillhub用户密码>"
redis:
auth:
password: "<固定Redis密码>"
helm dependency build ./charts/skillhub
kubectl create namespace skillhub
helm -n skillhub upgrade -i skillhub ./charts/skillhub \
-f values-production.yaml \
--set publicBaseUrl=https://skills.example.com
未显式设置 deviceAuthVerificationUri 时,Chart 使用
<publicBaseUrl>/cli/auth。所有 values 会先经过 values.schema.json 和跨字段校验,
无效的组件、Ingress、HPA 与存储组合会在安装前失败。
Ingress values 迁移: 当前版本只支持结构化的
ingress.hosts[]和ingress.tls[]。旧的ingress.host、ingress.tls.enabled与ingress.tls.secretName不再接受,升级前必须改成本文 Ingress 示例中的数组结构。
合并或发布前,可在一个空的测试 Kubernetes 集群中运行可重复的安装/升级 smoke:
for scenario in default sentinel s3 ingress-tls; do
HELM_SMOKE_SCENARIO="$scenario" \
bash charts/skillhub/tests/install-upgrade-smoke.sh
done
脚本验证 install -> Ready -> HTTP health -> upgrade -> Ready,并确认 Secret 数据、
PVC UID 与绑定 PV 在升级前后保持不变。四个场景分别覆盖默认依赖、Redis
Sentinel、实际 MinIO S3 连接,以及由 Kubernetes API 接受的 TLS Ingress 路由。
默认清理自己创建的 namespace;设置 KEEP_HELM_SMOKE=true 可保留现场用于排查。
高可用模式
helm -n skillhub upgrade -i skillhub ./charts/skillhub \
-f values-production.yaml \
--set postgresql.architecture=replication \
--set postgresql.auth.replicationPassword=your-replication-password \
--set redis.architecture=replication
外部数据库模式
helm -n skillhub upgrade -i skillhub ./charts/skillhub \
-f values-production.yaml \
--set postgresql.enabled=false \
--set redis.enabled=false \
--set externalDatabase.host=postgres.example.com \
--set externalDatabase.port=5432 \
--set externalDatabase.database=skillhub \
--set externalDatabase.username=skillhub \
--set externalDatabase.password=your-db-password \
--set externalRedis.host=redis.example.com \
--set externalRedis.port=6379 \
--set externalRedis.password=your-redis-password
使用 existingSecret
通过 existingSecret 引用已存在的 Secret 对象,避免在 values 中明文写入密码。
内置 PostgreSQL/Redis 使用各自的 Bitnami Secret,不需要复制到该 Secret。
| Key | 必填 | 说明 |
|---|---|---|
spring-datasource-password |
使用外部 PostgreSQL 时 | 数据库密码 |
redis-password |
使用外部 Redis 时 | Redis 密码 |
redis-sentinel-password |
使用外部 Sentinel 时 | Redis Sentinel 密码 |
bootstrap-admin-password |
是 | 初始管理员密码 |
skillhub-download-anon-cookie-secret |
是 | 至少 32 字符的匿名下载 Cookie 签名密钥 |
oauth2-github-client-id |
否 | GitHub OAuth2 Client ID |
oauth2-github-client-secret |
否 | GitHub OAuth2 Client Secret |
skill-scanner-llm-api-key |
否 | Scanner LLM API Key |
skill-scanner-llm-base-url |
否 | Scanner 自定义 LLM API 地址 |
skill-scanner-llm-model |
否 | Scanner LLM 模型名称 |
skillhub-storage-s3-access-key |
否 | S3 Access Key |
skillhub-storage-s3-secret-key |
否 | S3 Secret Key |
helm -n skillhub upgrade -i skillhub ./charts/skillhub \
-f values-production.yaml \
--set existingSecret=my-custom-secret
GitOps 稳定 Secret
Argo CD 等 GitOps 工具使用离线 helm template,无法通过 Helm lookup 读取集群
中已有的 Secret。Bitnami 子 Chart 和父 Chart 的空密码会在每次渲染时重新随机
生成。Chart 默认禁止自动生成并要求提供固定值:
secrets:
allowAutoGenerated: false
bootstrapAdminPassword: "<固定管理员密码>"
downloadAnonCookieSecret: "<至少32字符的固定随机值>"
postgresql:
auth:
postgresPassword: "<固定PostgreSQL管理员密码>"
password: "<固定skillhub用户密码>"
# replication 架构还必须配置 replicationPassword
redis:
auth:
password: "<固定Redis密码>"
也可以为三个组件分别配置 existingSecret。allowAutoGenerated=false 不会生成
可预测密码,而是在任何随机密码缺失时终止渲染并指出具体配置项。敏感值应放在
受保护的 values、External Secrets、Sealed Secrets 或密钥注入插件中。
内置 PostgreSQL、Redis、Sentinel 及 metrics exporter 镜像默认使用不可变的
多架构 manifest digest,避免 Bitnami 子 Chart 的 latest 默认值造成不可复现的
安装和回滚,同时保留 amd64/arm64 支持。覆盖私有镜像仓库或 tag 时,必须同时把
对应的 image.digest 设为空,或改成私有仓库中该镜像的真实 digest;digest
非空时会优先于 tag。
配置参考
副本数配置
| 参数 | 描述 | 默认值 |
|---|---|---|
server.replicaCount |
Server 副本数 | 1 |
web.replicaCount |
Web 副本数 | 1 |
scanner.replicaCount |
Scanner 副本数 | 1 |
# 差异化副本配置
helm -n skillhub upgrade -i skillhub ./charts/skillhub \
-f values-production.yaml \
--set server.replicaCount=3 \
--set server.storage.accessMode=ReadWriteMany \
--set web.replicaCount=2 \
--set scanner.replicaCount=1
本地存储运行多个 Server 副本时,必须显式设置 ReadWriteMany,并使用支持 RWX
的 StorageClass。无法提供 RWX 时应改用 S3。
服务配置
| 参数 | 描述 | 默认值 |
|---|---|---|
server.service.type |
Server Service 类型 | ClusterIP |
server.service.port |
Server 端口 | 8080 |
web.service.type |
Web Service 类型 | ClusterIP |
web.service.port |
Web 端口 | 80 |
scanner.service.port |
Scanner 端口 | 8000 |
私有镜像仓库
使用私有仓库时,需要分别覆盖 SkillHub 镜像、依赖等待镜像和 Bitnami 子 Chart
镜像。以下示例中的数据库镜像标签均为明确版本,不使用 latest:
global:
imagePullSecrets:
- private-registry
security:
allowInsecureImages: true
images:
registry: registry.example.com/library
tag: v0.2.14
pullPolicy: IfNotPresent
server:
dependencyWait:
image:
registry: registry.example.com
repository: library/busybox
tag: "1.37"
pullPolicy: IfNotPresent
imagePullSecrets:
- name: private-registry
web:
imagePullSecrets:
- name: private-registry
scanner:
imagePullSecrets:
- name: private-registry
postgresql:
image:
registry: registry.example.com
repository: library/postgresql
tag: 18.4.0
digest: ""
metrics:
image:
registry: registry.example.com
repository: library/postgres-exporter
tag: 0.20.1
digest: ""
redis:
image:
registry: registry.example.com
repository: library/redis
tag: 8.8.0
digest: ""
sentinel:
image:
registry: registry.example.com
repository: library/redis-sentinel
tag: 8.8.0
digest: ""
metrics:
image:
registry: registry.example.com
repository: library/redis-exporter
tag: 1.86.0
digest: ""
global.security.allowInsecureImages 是 Bitnami 对自定义镜像仓库和镜像名称的校验
开关,并不表示使用不安全的 HTTP 仓库。先在目标 namespace 创建拉取凭据:
kubectl create secret docker-registry private-registry \
-n skillhub \
--docker-server=registry.example.com \
--docker-username='<用户名>' \
--docker-password='<密码>'
数据库配置
| 参数 | 描述 | 默认值 |
|---|---|---|
postgresql.enabled |
启用内置 PostgreSQL | true |
postgresql.architecture |
架构模式 | standalone |
redis.enabled |
启用内置 Redis | true |
redis.architecture |
架构模式 | standalone |
数据库架构支持边界
以下内置数据库目标架构已完成独立 namespace 的全新安装和运行时验证:
| 数据组件 | 已验证架构 | 运行时验证 |
|---|---|---|
| PostgreSQL | standalone | Server 连接、Flyway 和应用启动 |
| PostgreSQL | replication | 1 Primary + 2 Read Replicas,两个副本均处于 recovery,流复制状态为 streaming |
| Redis | standalone | Server 读写和应用启动 |
| Redis | replication | 1 Master + 2 Replicas,角色和数据复制正常 |
| Redis | replication + Sentinel | 3 个 Sentinel 节点 master 视图一致,Server 可通过 Sentinel 读写 |
上述支持表示 Chart 能够全新部署目标架构,并为 SkillHub 配置正确的写节点或
Sentinel 地址。Chart 不负责数据库架构切换时的数据迁移,也不承诺仅修改
architecture 或 sentinel.enabled 就能保留已有数据。已有数据的 PostgreSQL
standalone → replication、Redis standalone/replication → Sentinel 等切换,必须由
运维人员在 Chart 之外完成备份、恢复、PVC 复用或其他迁移方案。
外部 Redis Cluster 由云服务或运维系统提供,Chart 只负责注入连接配置,不创建 Cluster,也不将其计入上述内置架构运行时验证范围。应用侧应另行验证 Spring Data、Spring Session 与 Redisson Stream 链路。
Redis Sentinel
内置 Sentinel 使用 Bitnami Redis 的同一份密码同时保护 Redis 数据节点和 Sentinel。节点地址由副本数自动生成,不需要手动配置。由于 Bitnami Sentinel 上报的 Pod 地址可能与客户端连接的 Headless Service FQDN 不同,Chart 仅在该 内置模式下关闭 Redisson 的 Sentinel 地址一致性检查:
helm -n skillhub upgrade -i skillhub ./charts/skillhub \
-f values-production.yaml \
--set redis.architecture=replication \
--set redis.sentinel.enabled=true
外部 Sentinel 必须提供至少一个 host:port 节点。Redis 数据密码和 Sentinel
密码可以不同;使用 existingSecret 时分别对应 redis-password 和
redis-sentinel-password。外部 Sentinel 默认保留 Redisson 地址一致性检查;
只有已确认服务发现会改写节点地址时,才通过 server.extraEnv 显式设置
SKILLHUB_REDIS_SENTINEL_CHECK_SENTINELS_LIST=false:
helm -n skillhub upgrade -i skillhub ./charts/skillhub \
-f values-production.yaml \
--set redis.enabled=false \
--set externalRedis.password=redis-password \
--set externalRedis.sentinel.enabled=true \
--set externalRedis.sentinel.password=sentinel-password \
--set-json 'externalRedis.sentinel.nodes=["sentinel-0.example.com:26379","sentinel-1.example.com:26379"]'
确需关闭检查时,在 values 文件中显式记录该兼容例外:
server:
extraEnv:
- name: SKILLHUB_REDIS_SENTINEL_CHECK_SENTINELS_LIST
value: "false"
外部 Redis Cluster
Chart 不创建内置 Redis Cluster。生产环境的 Cluster 由云 Redis 或独立运维系统 提供,SkillHub 通过标准 Spring Boot 配置连接。至少配置一个 seed 节点,且 Cluster 通告的所有节点地址都必须能从 Server Pod 访问:
redis:
enabled: false
externalRedis:
username: skillhub
tls:
enabled: true
connectTimeout: 5s
timeout: 3s
clientName: skillhub-server
cluster:
enabled: true
nodes:
- redis-0.example.com:6379
- redis-1.example.com:6379
- redis-2.example.com:6379
maxRedirects: 5
Redis Cluster 只支持数据库 0。maxRedirects 交给 Spring Data/Lettuce 处理;
Redisson 使用同一节点、ACL、TLS 与超时配置并自行处理 Cluster 路由。密码建议
通过 existingSecret 的 redis-password 提供:
helm -n skillhub upgrade -i skillhub ./charts/skillhub \
-f values-production.yaml \
--set redis.enabled=false \
--set existingSecret=skillhub-production-secret \
--set externalRedis.cluster.enabled=true \
--set-json 'externalRedis.cluster.nodes=["redis-0.example.com:6379","redis-1.example.com:6379","redis-2.example.com:6379"]'
externalRedis.sentinel.enabled 与 externalRedis.cluster.enabled 互斥;内置
redis.enabled=true 时也不能启用外部 Cluster。
存储配置
| 参数 | 描述 | 默认值 |
|---|---|---|
server.storage.accessMode |
留空时单副本使用 ReadWriteOnce;多副本必须显式使用 ReadWriteMany | "" |
server.storage.size |
PVC 大小 | 10Gi |
server.storage.storageClassName |
StorageClass | "" |
server.podSecurityContext.fsGroup |
Server 本地存储的可写组 ID,应与镜像内 app 用户组一致 | 101 |
server.podSecurityContext.fsGroupChangePolicy |
kubelet 调整 PVC 组权限的策略 | OnRootMismatch |
本地 PVC 会覆盖镜像内预先设置的目录所有者。Chart 默认通过 Pod fsGroup=101
使 Server 的非 root app 用户可以创建和更新技能文件。使用自定义 Server 镜像且其
运行组 ID 不同时,必须同步覆盖 server.podSecurityContext.fsGroup。
使用本地 ReadWriteOnce PVC 时,Server Deployment 自动采用 Recreate,避免
滚动升级期间新旧 Pod 同时挂载非共享卷而触发 Multi-Attach。单副本升级会有短暂
停机;使用支持 RWX 的 ReadWriteMany 存储或启用 S3 时,Chart 保留
RollingUpdate。
# 默认使用本地 PVC
helm -n skillhub upgrade -i skillhub ./charts/skillhub \
-f values-production.yaml
S3 对象存储
s3.enabled=true 时,不创建 PVC,应用使用 S3 作为存储后端。
| 参数 | 描述 | 默认值 |
|---|---|---|
s3.enabled |
启用 S3 | false |
s3.bucket |
Bucket 名称 | skillhub-storage |
s3.endpoint |
S3 端点,非空时必须是绝对 HTTP(S) URL | "" |
s3.publicEndpoint |
S3 公网访问端点,非空时必须是绝对 HTTP(S) URL | "" |
s3.region |
区域 | us-east-1 |
s3.forcePathStyle |
强制 path-style 访问 | true |
s3.disableChunkedEncoding |
禁用 aws-chunked 编码 | false |
s3.autoCreateBucket |
自动创建 Bucket | false |
s3.accessKey |
Access Key | "" |
s3.secretKey |
Secret Key | "" |
helm -n skillhub upgrade -i skillhub ./charts/skillhub \
-f values-production.yaml \
--set s3.enabled=true \
--set s3.bucket=your-bucket \
--set s3.endpoint=https://s3.amazonaws.com \
--set s3.region=us-east-1 \
--set s3.accessKey=your-access-key \
--set s3.secretKey=your-secret-key
Ingress + TLS
helm -n skillhub upgrade -i skillhub ./charts/skillhub \
-f values-production.yaml \
--set ingress.enabled=true \
--set-json 'ingress.hosts=[{"host":"skills.example.com","paths":[{"path":"/","pathType":"Prefix"}]}]' \
--set-json 'ingress.tls=[{"hosts":["skills.example.com"],"secretName":"skills-tls"}]' \
--set publicBaseUrl=https://skills.example.com \
--set ingress.certManager.enabled=true
配置非空 ingress.tls 或启用 ingress.certManager 时,Chart 会自动将 Session Cookie
标记为 Secure。Ingress 要求 Server 和 Web Service 均保持启用。
ingress.className 和旧式 kubernetes.io/ingress.class annotation 均受支持,
可以任选其一,也可以同时输出。仅使用旧式 annotation 时将 className 留空:
ingress:
enabled: true
className: ""
annotations:
kubernetes.io/ingress.class: alb
alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":6443}]'
hosts 是至少包含一个条目的对象数组。Chart 自动将 /api、/oauth2、
/login/oauth2 和 /.well-known 直接转发给 Server,确保 TLS 终止后的 OAuth
回调协议保持正确;hosts[].paths 中的其他路径转发给 Web,因此上述四个前缀
均为保留路径。tls 同样是数组,可为不同证书分别配置域名;TLS 域名会写入
cert-manager Certificate SAN:
ingress:
hosts:
- host: skills.example.com
paths:
- path: /
pathType: Prefix
- host: skills.internal.example.com
paths:
- path: /
pathType: Prefix
tls:
- hosts:
- skills.example.com
- skills.internal.example.com
secretName: skills-tls
自动扩缩容
helm -n skillhub upgrade -i skillhub ./charts/skillhub \
-f values-production.yaml \
--set server.autoscaling.enabled=true \
--set server.autoscaling.minReplicas=2 \
--set server.autoscaling.maxReplicas=10 \
--set server.storage.accessMode=ReadWriteMany
每个 HPA 至少需要一个非零 CPU 或内存利用率目标。本地存储的 Server HPA 同样 要求 RWX;也可以启用 S3 来避免共享 PVC。
发布
.github/workflows/publish-chart.yml 在 GitHub Release 发布后或手动
workflow_dispatch 时运行。Release tag 必须使用 vX.Y.Z、chart-vX.Y.Z 或
helm-vX.Y.Z;手动运行时显式输入 X.Y.Z。工作流按该版本打包 Chart,并推送到
oci://ghcr.io/iflytek/charts,同时保留构建 artifact。
卸载
helm -n skillhub uninstall skillhub
Server 数据 PVC 带有 helm.sh/resource-policy: keep,卸载 release 后仍会保留,
需要确认数据不再使用后手动删除。
依赖
| 依赖 | 版本 |
|---|---|
| postgresql | 18.6.10 |
| redis | 25.5.3 |