diff --git a/deploy/k8s/README.md b/deploy/k8s/README.md new file mode 100644 index 00000000..681a1b40 --- /dev/null +++ b/deploy/k8s/README.md @@ -0,0 +1,310 @@ +# Kubernetes 部署指南 + +本文档说明如何在 Kubernetes 集群中部署 SkillHub。 + +## 前置条件 + +- Kubernetes 集群 (v1.24+) +- kubectl 已配置并连接到集群 +- nginx ingress controller 已安装(可选,用于域名访问) +- 默认 StorageClass 已配置(用于 PVC) + +## 目录结构 + +``` +deploy/k8s/ +├── base/ # 基础配置(所有场景共用) +│ ├── kustomization.yaml +│ ├── configmap.yaml +│ ├── secret.yaml.example +│ ├── services.yaml +│ ├── backend-deployment.yaml +│ ├── frontend-deployment.yaml +│ ├── scanner-deployment.yaml +│ └── ingress.yaml +│ +└── overlays/ + ├── with-infra/ # 完整部署(包含内置数据库) + │ ├── kustomization.yaml + │ ├── postgres-statefulset.yaml + │ └── redis-statefulset.yaml + │ + └── external/ # 外部数据库 + └── kustomization.yaml +``` + +## 快速开始 + +### 1. 创建命名空间 + +```bash +kubectl create namespace skillhub +``` + +### 2. 配置 Secret + +```bash +cd deploy/k8s/base + +# 复制示例文件 +cp secret.yaml.example secret.yaml + +# 编辑 secret.yaml,修改敏感配置 +``` + +**Secret 配置项**: + +| 键 | 说明 | 必填 | +|---|---|---| +| spring-datasource-url | PostgreSQL 连接 URL | 是 | +| spring-datasource-username | 数据库用户名 | 是 | +| spring-datasource-password | 数据库密码 | 是 | +| bootstrap-admin-password | 管理员密码 | 是 | +| oauth2-github-client-id | GitHub OAuth ID | 否 | +| oauth2-github-client-secret | GitHub OAuth 密钥 | 否 | +| skill-scanner-llm-api-key | LLM API 密钥 | 否 | + +### 3. 选择部署方式 + +**方式一:完整部署(包含 PostgreSQL + Redis)** + +适合全新环境,自动部署数据库: + +```bash +kubectl apply -k overlays/with-infra/ +``` + +**方式二:使用外部数据库** + +适合已有 PostgreSQL 和 Redis 的环境: + +1. 修改 `base/configmap.yaml` 中的 Redis 配置: +```yaml +redis-host: your-redis-host +redis-port: "6379" +``` + +2. 修改 `base/secret.yaml` 中的数据库连接: +```yaml +spring-datasource-url: jdbc:postgresql://your-postgres-host:5432/skillhub +``` + +3. 部署: +```bash +kubectl apply -k overlays/external/ +``` + +### 4. 验证部署 + +```bash +# 检查 Pod 状态 +kubectl get pods -n skillhub + +# 等待所有 Pod 就绪 +kubectl wait --for=condition=ready pod --all -n skillhub --timeout=300s +``` + +### 5. 访问服务 + +**方式一:端口转发(推荐本地测试)** + +```bash +# 前端 +kubectl port-forward svc/skillhub-web -n skillhub 8080:80 + +# 后端 API +kubectl port-forward svc/skillhub-server -n skillhub 8081:8080 +``` + +访问 http://localhost:8080 + +**方式二:Ingress 域名访问** + +修改 `base/ingress.yaml` 中的域名: +```yaml +spec: + rules: + - host: your-domain.com # 修改为你的域名 +``` + +```bash +kubectl apply -k overlays/with-infra/ # 或 overlays/external/ +``` + +## 部署架构 + +``` +┌─────────────────────────────────────────────────────────────┐ +│ skillhub namespace │ +├─────────────────────────────────────────────────────────────┤ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ +│ │ skillhub-web│ │skillhub- │ │ skillhub-scanner │ │ +│ │ (前端) │ │ server │ │ (扫描器) │ │ +│ │ :80 │ │ (后端) │ │ :8000 │ │ +│ └─────────────┘ │ :8080 │ └─────────────────────┘ │ +│ └──────┬──────┘ │ +│ │ │ +│ ┌────────────────┴────────────────┐ │ +│ │ with-infra only │ │ +│ │ ┌─────────────┐ ┌───────────┐ │ │ +│ │ │ postgres-0 │ │ redis-0 │ │ │ +│ │ │ :5432 │ │ :6379 │ │ │ +│ │ └─────────────┘ └───────────┘ │ │ +│ └─────────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────────────────────────────────┐ │ +│ │ PersistentVolumeClaims │ │ +│ │ - skillhub-storage-pvc (10Gi) │ │ +│ │ - postgres-data-0 (10Gi) - with-infra only │ │ +│ │ - redis-data-0 (5Gi) - with-infra only │ │ +│ └─────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +## 配置说明 + +### ConfigMap 配置项 + +| 键 | 默认值 | 说明 | +|---|---|---| +| redis-host | redis | Redis 主机地址 | +| redis-port | 6379 | Redis 端口 | +| storage-base-path | /var/lib/skillhub/storage | 技能存储路径 | +| skillhub-storage-provider | local | 存储类型(local/s3) | +| skill-scanner-enabled | true | 是否启用扫描器 | +| skill-scanner-url | http://skillhub-scanner:8000 | 扫描器地址 | +| skill-scanner-mode | upload | 扫描模式 | +| bootstrap-admin-enabled | true | 是否创建默认管理员 | +| bootstrap-admin-user-id | docker-admin | 管理员用户 ID | +| bootstrap-admin-username | admin | 管理员用户名 | +| bootstrap-admin-display-name | Platform Admin | 管理员显示名称 | +| bootstrap-admin-email | admin@example.com | 管理员邮箱 | +| session-cookie-secure | false | HTTPS 环境设为 true | + +### Secret 配置项 + +| 键 | 说明 | 必填 | +|---|---|---| +| spring-datasource-url | PostgreSQL 连接 URL | 是 | +| spring-datasource-username | 数据库用户名 | 是 | +| spring-datasource-password | 数据库密码 | 是 | +| bootstrap-admin-password | 管理员密码 | 是 | +| oauth2-github-client-id | GitHub OAuth ID | 否 | +| oauth2-github-client-secret | GitHub OAuth 密钥 | 否 | +| skill-scanner-llm-api-key | LLM API 密钥 | 否 | +| skill-scanner-llm-model | LLM 模型名称 | 否 | + +### 存储配置 + +**本地存储(默认)** + +默认使用本地文件存储,数据保存在 PVC `skillhub-storage-pvc` 中。 + +**S3/OSS 存储** + +生产环境建议使用 S3 兼容的对象存储: + +1. 修改 ConfigMap: +```yaml +skillhub-storage-provider: s3 +``` + +2. 在 Secret 中添加: +```yaml +skillhub-storage-s3-access-key: your-access-key +skillhub-storage-s3-secret-key: your-secret-key +``` + +3. 在 backend-deployment.yaml 中添加环境变量: +```yaml +- name: SKILLHUB_STORAGE_S3_ENDPOINT + value: https://oss-cn-shanghai.aliyuncs.com +- name: SKILLHUB_STORAGE_S3_BUCKET + value: skillhub-prod +- name: SKILLHUB_STORAGE_S3_REGION + value: cn-shanghai +``` + +### 持久化存储 + +| PVC | 大小 | 说明 | +|-----|------|------| +| skillhub-storage-pvc | 10Gi | 技能文件存储 | +| postgres-data-0 | 10Gi | PostgreSQL 数据(with-infra only) | +| redis-data-0 | 5Gi | Redis 数据(with-infra only) | + +## 镜像说明 + +| 组件 | 镜像 | +|---|---| +| 后端服务 | ghcr.io/iflytek/skillhub-server:latest | +| 前端服务 | ghcr.io/iflytek/skillhub-web:latest | +| 扫描器 | ghcr.io/iflytek/skillhub-scanner:latest | +| PostgreSQL | postgres:16-alpine | +| Redis | redis:7-alpine | + +## 默认管理员 + +首次启动时,如果 `bootstrap-admin-enabled` 为 `true`,系统会自动创建管理员账户: + +- 用户名:`admin` +- 密码:在 `secret.yaml` 的 `bootstrap-admin-password` 中配置 + +**安全建议**:首次登录后,请立即修改默认密码。 + +## 常见问题 + +### Pod 一直 Pending + +```bash +# 检查 PVC 是否绑定 +kubectl get pvc -n skillhub + +# 检查节点资源 +kubectl describe node +``` + +### 镜像拉取失败 + +如果镜像私有,需要创建拉取凭证: + +```bash +kubectl create secret docker-registry ghcr-secret \ + --docker-server=ghcr.io \ + --docker-username= \ + --docker-password= \ + -n skillhub +``` + +### 数据库连接失败 + +```bash +# 检查 PostgreSQL 是否就绪 +kubectl logs postgres-0 -n skillhub + +# 检查 Secret 配置 +kubectl get secret skillhub-secret -n skillhub -o yaml +``` + +### 查看日志 + +```bash +# 后端日志 +kubectl logs -l app.kubernetes.io/name=skillhub-server -n skillhub -f + +# 前端日志 +kubectl logs -l app.kubernetes.io/name=skillhub-web -n skillhub -f + +# 扫描器日志 +kubectl logs -l app.kubernetes.io/name=skillhub-scanner -n skillhub -f +``` + +## 清理 + +```bash +# 删除所有资源 +kubectl delete -k overlays/with-infra/ # 或 overlays/external/ + +# 删除命名空间 +kubectl delete namespace skillhub +``` diff --git a/deploy/k8s/backend-deployment.yaml b/deploy/k8s/base/backend-deployment.yaml similarity index 63% rename from deploy/k8s/backend-deployment.yaml rename to deploy/k8s/base/backend-deployment.yaml index aa12d571..8f09feae 100644 --- a/deploy/k8s/backend-deployment.yaml +++ b/deploy/k8s/base/backend-deployment.yaml @@ -24,6 +24,8 @@ spec: env: - name: SPRING_PROFILES_ACTIVE value: docker + + # Database - name: SPRING_DATASOURCE_URL valueFrom: secretKeyRef: @@ -39,6 +41,8 @@ spec: secretKeyRef: name: skillhub-secret key: spring-datasource-password + + # Redis - name: SPRING_DATA_REDIS_HOST valueFrom: configMapKeyRef: @@ -49,11 +53,20 @@ spec: configMapKeyRef: name: skillhub-config key: redis-port + + # Storage - name: STORAGE_BASE_PATH valueFrom: configMapKeyRef: name: skillhub-config key: storage-base-path + - name: SKILLHUB_STORAGE_PROVIDER + valueFrom: + configMapKeyRef: + name: skillhub-config + key: skillhub-storage-provider + + # Scanner - name: SKILLHUB_SECURITY_SCANNER_ENABLED valueFrom: configMapKeyRef: @@ -69,8 +82,50 @@ spec: configMapKeyRef: name: skillhub-config key: skill-scanner-mode + + # Session - name: SESSION_COOKIE_SECURE - value: "true" + valueFrom: + configMapKeyRef: + name: skillhub-config + key: session-cookie-secure + + # Bootstrap Admin (non-sensitive from ConfigMap) + - name: BOOTSTRAP_ADMIN_ENABLED + valueFrom: + configMapKeyRef: + name: skillhub-config + key: bootstrap-admin-enabled + - name: BOOTSTRAP_ADMIN_USER_ID + valueFrom: + configMapKeyRef: + name: skillhub-config + key: bootstrap-admin-user-id + - name: BOOTSTRAP_ADMIN_USERNAME + valueFrom: + configMapKeyRef: + name: skillhub-config + key: bootstrap-admin-username + - name: BOOTSTRAP_ADMIN_DISPLAY_NAME + valueFrom: + configMapKeyRef: + name: skillhub-config + key: bootstrap-admin-display-name + - name: BOOTSTRAP_ADMIN_EMAIL + valueFrom: + configMapKeyRef: + name: skillhub-config + key: bootstrap-admin-email + + # Bootstrap Admin Password (sensitive from Secret) + - name: BOOTSTRAP_ADMIN_PASSWORD + valueFrom: + secretKeyRef: + name: skillhub-secret + key: bootstrap-admin-password + optional: true + + # OAuth2 GitHub (optional) - name: OAUTH2_GITHUB_CLIENT_ID valueFrom: secretKeyRef: @@ -83,18 +138,19 @@ spec: name: skillhub-secret key: oauth2-github-client-secret optional: true + volumeMounts: - name: skillhub-storage mountPath: /var/lib/skillhub/storage readinessProbe: httpGet: - path: /actuator/health/readiness + path: /actuator/health port: http initialDelaySeconds: 20 periodSeconds: 10 livenessProbe: httpGet: - path: /actuator/health/liveness + path: /actuator/health port: http initialDelaySeconds: 30 periodSeconds: 15 diff --git a/deploy/k8s/base/configmap.yaml b/deploy/k8s/base/configmap.yaml new file mode 100644 index 00000000..590052e2 --- /dev/null +++ b/deploy/k8s/base/configmap.yaml @@ -0,0 +1,44 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: skillhub-config +data: + # Redis 配置 + # 使用外部 Redis:修改为外部主机地址 + # 使用内置 Redis(overlays/with-infra):保持 redis + redis-host: redis + redis-port: "6379" + + # 技能存储路径 + storage-base-path: /var/lib/skillhub/storage + + # 存储配置 + # local: 本地存储(默认), s3: S3/OSS 对象存储 + skillhub-storage-provider: local + + # 技能扫描器配置 + skill-scanner-enabled: "true" + skill-scanner-url: http://skillhub-scanner:8000 + skill-scanner-mode: upload + + # Bootstrap 管理员配置(非敏感) + bootstrap-admin-enabled: "true" + bootstrap-admin-user-id: docker-admin + bootstrap-admin-username: admin + bootstrap-admin-display-name: Platform Admin + bootstrap-admin-email: admin@example.com + + # Session 配置 + # HTTP 环境设为 false,HTTPS 环境设为 true + session-cookie-secure: "false" +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: skillhub-storage-pvc +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi diff --git a/deploy/k8s/frontend-deployment.yaml b/deploy/k8s/base/frontend-deployment.yaml similarity index 88% rename from deploy/k8s/frontend-deployment.yaml rename to deploy/k8s/base/frontend-deployment.yaml index 05538617..35501c24 100644 --- a/deploy/k8s/frontend-deployment.yaml +++ b/deploy/k8s/base/frontend-deployment.yaml @@ -18,6 +18,9 @@ spec: - name: web image: ghcr.io/iflytek/skillhub-web:edge imagePullPolicy: IfNotPresent + env: + - name: SKILLHUB_API_UPSTREAM + value: http://skillhub-server:8080 ports: - containerPort: 80 name: http diff --git a/deploy/k8s/ingress.yaml b/deploy/k8s/base/ingress.yaml similarity index 100% rename from deploy/k8s/ingress.yaml rename to deploy/k8s/base/ingress.yaml diff --git a/deploy/k8s/base/kustomization.yaml b/deploy/k8s/base/kustomization.yaml new file mode 100644 index 00000000..0920c068 --- /dev/null +++ b/deploy/k8s/base/kustomization.yaml @@ -0,0 +1,26 @@ +# 基础配置(所有场景共用) +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +namespace: skillhub + +resources: + - configmap.yaml + - services.yaml + - backend-deployment.yaml + - frontend-deployment.yaml + - scanner-deployment.yaml + - ingress.yaml + +images: + - name: ghcr.io/iflytek/skillhub-server + newTag: latest + - name: ghcr.io/iflytek/skillhub-web + newTag: latest + - name: ghcr.io/iflytek/skillhub-scanner + newTag: latest + +labels: + - pairs: + app.kubernetes.io/part-of: skillhub + app.kubernetes.io/managed-by: kustomize diff --git a/deploy/k8s/scanner-deployment.yaml b/deploy/k8s/base/scanner-deployment.yaml similarity index 100% rename from deploy/k8s/scanner-deployment.yaml rename to deploy/k8s/base/scanner-deployment.yaml diff --git a/deploy/k8s/base/secret.yaml.example b/deploy/k8s/base/secret.yaml.example new file mode 100644 index 00000000..41b9ea5c --- /dev/null +++ b/deploy/k8s/base/secret.yaml.example @@ -0,0 +1,31 @@ +# SkillHub Secret 配置 +# 复制此文件为 secret.yaml 并修改值 +# cp secret.yaml.example secret.yaml + +apiVersion: v1 +kind: Secret +metadata: + name: skillhub-secret +type: Opaque +stringData: + # PostgreSQL 连接配置 + # 使用外部数据库:修改主机地址和端口 + # 使用内置数据库(overlays/with-infra):保持 postgres:5432 + spring-datasource-url: jdbc:postgresql://postgres:5432/skillhub + spring-datasource-username: skillhub + spring-datasource-password: change-me + + # Bootstrap 管理员密码(敏感) + bootstrap-admin-password: ChangeMe!2026 + + # GitHub OAuth(可选,用于 GitHub 登录) + oauth2-github-client-id: "" + oauth2-github-client-secret: "" + + # LLM 配置(可选,用于技能扫描) + skill-scanner-llm-api-key: "" + skill-scanner-llm-model: "" + + # S3 存储配置(可选,使用 S3/OSS 时配置) + skillhub-storage-s3-access-key: "" + skillhub-storage-s3-secret-key: "" diff --git a/deploy/k8s/services.yaml b/deploy/k8s/base/services.yaml similarity index 100% rename from deploy/k8s/services.yaml rename to deploy/k8s/base/services.yaml diff --git a/deploy/k8s/configmap.yaml b/deploy/k8s/configmap.yaml deleted file mode 100644 index bdd94f60..00000000 --- a/deploy/k8s/configmap.yaml +++ /dev/null @@ -1,22 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: skillhub-config -data: - redis-host: redis - redis-port: "6379" - storage-base-path: /var/lib/skillhub/storage - skill-scanner-enabled: "true" - skill-scanner-url: http://skillhub-scanner:8000 - skill-scanner-mode: upload ---- -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: skillhub-storage-pvc -spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 10Gi diff --git a/deploy/k8s/overlays/external/kustomization.yaml b/deploy/k8s/overlays/external/kustomization.yaml new file mode 100644 index 00000000..8fc2a3c7 --- /dev/null +++ b/deploy/k8s/overlays/external/kustomization.yaml @@ -0,0 +1,15 @@ +# 外部数据库部署 +# 使用方式:kubectl apply -k overlays/external/ +# 前置条件:修改 base/configmap.yaml 和 secret.yaml 中的数据库连接地址 + +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +namespace: skillhub + +resources: + - ../../base + +labels: + - pairs: + app.kubernetes.io/deployment: external-db diff --git a/deploy/k8s/overlays/with-infra/kustomization.yaml b/deploy/k8s/overlays/with-infra/kustomization.yaml new file mode 100644 index 00000000..e03736c4 --- /dev/null +++ b/deploy/k8s/overlays/with-infra/kustomization.yaml @@ -0,0 +1,16 @@ +# 完整部署(包含内置 PostgreSQL + Redis) +# 使用方式:kubectl apply -k overlays/with-infra/ + +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +namespace: skillhub + +resources: + - postgres-statefulset.yaml + - redis-statefulset.yaml + - ../../base + +labels: + - pairs: + app.kubernetes.io/deployment: with-infra diff --git a/deploy/k8s/overlays/with-infra/postgres-statefulset.yaml b/deploy/k8s/overlays/with-infra/postgres-statefulset.yaml new file mode 100644 index 00000000..24490c78 --- /dev/null +++ b/deploy/k8s/overlays/with-infra/postgres-statefulset.yaml @@ -0,0 +1,86 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: postgres + labels: + app.kubernetes.io/name: postgres +spec: + serviceName: postgres + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: postgres + template: + metadata: + labels: + app.kubernetes.io/name: postgres + spec: + containers: + - name: postgres + image: postgres:16-alpine + ports: + - containerPort: 5432 + name: postgres + env: + - name: POSTGRES_DB + value: skillhub + - name: POSTGRES_USER + valueFrom: + secretKeyRef: + name: skillhub-secret + key: spring-datasource-username + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: skillhub-secret + key: spring-datasource-password + volumeMounts: + - name: postgres-data + mountPath: /var/lib/postgresql/data + readinessProbe: + exec: + command: + - pg_isready + - -U + - skillhub + initialDelaySeconds: 10 + periodSeconds: 10 + livenessProbe: + exec: + command: + - pg_isready + - -U + - skillhub + initialDelaySeconds: 30 + periodSeconds: 15 + resources: + requests: + cpu: 100m + memory: 256Mi + limits: + cpu: 500m + memory: 512Mi + volumeClaimTemplates: + - metadata: + name: postgres-data + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi +--- +apiVersion: v1 +kind: Service +metadata: + name: postgres + labels: + app.kubernetes.io/name: postgres +spec: + type: ClusterIP + ports: + - port: 5432 + targetPort: postgres + name: postgres + selector: + app.kubernetes.io/name: postgres diff --git a/deploy/k8s/overlays/with-infra/redis-statefulset.yaml b/deploy/k8s/overlays/with-infra/redis-statefulset.yaml new file mode 100644 index 00000000..369dc823 --- /dev/null +++ b/deploy/k8s/overlays/with-infra/redis-statefulset.yaml @@ -0,0 +1,75 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: redis + labels: + app.kubernetes.io/name: redis +spec: + serviceName: redis + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: redis + template: + metadata: + labels: + app.kubernetes.io/name: redis + spec: + containers: + - name: redis + image: redis:7-alpine + ports: + - containerPort: 6379 + name: redis + command: + - redis-server + - --appendonly + - "yes" + volumeMounts: + - name: redis-data + mountPath: /data + readinessProbe: + exec: + command: + - redis-cli + - ping + initialDelaySeconds: 5 + periodSeconds: 10 + livenessProbe: + exec: + command: + - redis-cli + - ping + initialDelaySeconds: 10 + periodSeconds: 15 + resources: + requests: + cpu: 50m + memory: 128Mi + limits: + cpu: 200m + memory: 256Mi + volumeClaimTemplates: + - metadata: + name: redis-data + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 5Gi +--- +apiVersion: v1 +kind: Service +metadata: + name: redis + labels: + app.kubernetes.io/name: redis +spec: + type: ClusterIP + ports: + - port: 6379 + targetPort: redis + name: redis + selector: + app.kubernetes.io/name: redis diff --git a/deploy/k8s/secret.yaml.example b/deploy/k8s/secret.yaml.example deleted file mode 100644 index f5ee3530..00000000 --- a/deploy/k8s/secret.yaml.example +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: v1 -kind: Secret -metadata: - name: skillhub-secret -type: Opaque -stringData: - spring-datasource-url: jdbc:postgresql://postgres:5432/skillhub - spring-datasource-username: skillhub - spring-datasource-password: change-me - oauth2-github-client-id: your-client-id - oauth2-github-client-secret: your-client-secret - skill-scanner-llm-api-key: your-llm-api-key - skill-scanner-llm-model: openai/astron-code-latest diff --git a/docs/skillhub/.vitepress/config.ts b/docs/skillhub/.vitepress/config.ts index af004246..c99092f4 100644 --- a/docs/skillhub/.vitepress/config.ts +++ b/docs/skillhub/.vitepress/config.ts @@ -43,6 +43,7 @@ export default defineConfig({ { text: '更多', items: [ + { text: 'Kubernetes 部署', link: '/guide/kubernetes' }, { text: '常见问题', link: '/faq' }, ], }, @@ -87,6 +88,7 @@ export default defineConfig({ { text: 'More', items: [ + { text: 'Kubernetes Deployment', link: '/en/guide/kubernetes' }, { text: 'FAQ', link: '/en/faq' }, ], }, diff --git a/docs/skillhub/en/guide/kubernetes.md b/docs/skillhub/en/guide/kubernetes.md new file mode 100644 index 00000000..73eb2cee --- /dev/null +++ b/docs/skillhub/en/guide/kubernetes.md @@ -0,0 +1,289 @@ +# Kubernetes Deployment Guide + +This document explains how to deploy SkillHub on a Kubernetes cluster. + +## Prerequisites + +- Kubernetes cluster (v1.24+) +- kubectl configured and connected to the cluster +- nginx ingress controller installed (optional, for domain access) +- Default StorageClass configured (for PVC) + +## Directory Structure + +``` +deploy/k8s/ +├── base/ # Base configuration (shared by all scenarios) +│ ├── kustomization.yaml +│ ├── configmap.yaml +│ ├── secret.yaml.example +│ ├── services.yaml +│ ├── backend-deployment.yaml +│ ├── frontend-deployment.yaml +│ ├── scanner-deployment.yaml +│ └── ingress.yaml +│ +└── overlays/ + ├── with-infra/ # Full deployment (with built-in database) + │ ├── kustomization.yaml + │ ├── postgres-statefulset.yaml + │ └── redis-statefulset.yaml + │ + └── external/ # External database + └── kustomization.yaml +``` + +## Quick Start + +### 1. Create Namespace + +```bash +kubectl create namespace skillhub +``` + +### 2. Configure Secret + +```bash +cd deploy/k8s/base + +# Copy example file +cp secret.yaml.example secret.yaml + +# Edit secret.yaml with your configuration +``` + +**Secret Configuration Items**: + +| Key | Description | Required | +|---|---|---| +| spring-datasource-url | PostgreSQL connection URL | Yes | +| spring-datasource-username | Database username | Yes | +| spring-datasource-password | Database password | Yes | +| bootstrap-admin-password | Admin password | Yes | +| oauth2-github-client-id | GitHub OAuth ID | No | +| oauth2-github-client-secret | GitHub OAuth secret | No | +| skill-scanner-llm-api-key | LLM API key | No | + +### 3. Choose Deployment Method + +**Option 1: Full Deployment (with PostgreSQL + Redis)** + +Suitable for new environments, automatically deploys databases: + +```bash +kubectl apply -k overlays/with-infra/ +``` + +**Option 2: Use External Database** + +Suitable for environments with existing PostgreSQL and Redis: + +1. Modify Redis configuration in `base/configmap.yaml`: +```yaml +redis-host: your-redis-host +redis-port: "6379" +``` + +2. Modify database connection in `base/secret.yaml`: +```yaml +spring-datasource-url: jdbc:postgresql://your-postgres-host:5432/skillhub +``` + +3. Deploy: +```bash +kubectl apply -k overlays/external/ +``` + +### 4. Verify Deployment + +```bash +# Check Pod status +kubectl get pods -n skillhub + +# Wait for all Pods to be ready +kubectl wait --for=condition=ready pod --all -n skillhub --timeout=300s +``` + +### 5. Access Services + +**Option 1: Port Forwarding (recommended for local testing)** + +```bash +# Frontend +kubectl port-forward svc/skillhub-web -n skillhub 8080:80 + +# Backend API +kubectl port-forward svc/skillhub-server -n skillhub 8081:8080 +``` + +Visit http://localhost:8080 + +**Option 2: Ingress Domain Access** + +Modify the domain in `base/ingress.yaml`: +```yaml +spec: + rules: + - host: your-domain.com # Change to your domain +``` + +```bash +kubectl apply -k overlays/with-infra/ # or overlays/external/ +``` + +## Deployment Architecture + +``` +┌─────────────────────────────────────────────────────────────┐ +│ skillhub namespace │ +├─────────────────────────────────────────────────────────────┤ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ +│ │ skillhub-web│ │skillhub- │ │ skillhub-scanner │ │ +│ │ (frontend) │ │ server │ │ (scanner) │ │ +│ │ :80 │ │ (backend) │ │ :8000 │ │ +│ └─────────────┘ │ :8080 │ └─────────────────────┘ │ +│ └──────┬──────┘ │ +│ │ │ +│ ┌────────────────┴────────────────┐ │ +│ │ with-infra only │ │ +│ │ ┌─────────────┐ ┌───────────┐ │ │ +│ │ │ postgres-0 │ │ redis-0 │ │ │ +│ │ │ :5432 │ │ :6379 │ │ │ +│ │ └─────────────┘ └───────────┘ │ │ +│ └─────────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────────────────────────────────┐ │ +│ │ PersistentVolumeClaims │ │ +│ │ - skillhub-storage-pvc (10Gi) │ │ +│ │ - postgres-data-0 (10Gi) - with-infra only │ │ +│ │ - redis-data-0 (5Gi) - with-infra only │ │ +│ └─────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +## Configuration Reference + +### ConfigMap Items + +| Key | Default | Description | +|---|---|---| +| redis-host | redis | Redis host address | +| redis-port | 6379 | Redis port | +| storage-base-path | /var/lib/skillhub/storage | Skill storage path | +| skillhub-storage-provider | local | Storage type (local/s3) | +| skill-scanner-enabled | true | Enable scanner | +| skill-scanner-url | http://skillhub-scanner:8000 | Scanner URL | +| skill-scanner-mode | upload | Scan mode | +| bootstrap-admin-enabled | true | Create default admin | +| bootstrap-admin-user-id | docker-admin | Admin user ID | +| bootstrap-admin-username | admin | Admin username | +| bootstrap-admin-display-name | Platform Admin | Admin display name | +| bootstrap-admin-email | admin@example.com | Admin email | +| session-cookie-secure | false | Set to true for HTTPS | + +### Storage Configuration + +**Local Storage (Default)** + +Uses local file storage by default, data is stored in PVC `skillhub-storage-pvc`. + +**S3/OSS Storage** + +For production, S3-compatible object storage is recommended: + +1. Modify ConfigMap: +```yaml +skillhub-storage-provider: s3 +``` + +2. Add to Secret: +```yaml +skillhub-storage-s3-access-key: your-access-key +skillhub-storage-s3-secret-key: your-secret-key +``` + +3. Add environment variables to backend-deployment.yaml: +```yaml +- name: SKILLHUB_STORAGE_S3_ENDPOINT + value: https://s3.amazonaws.com +- name: SKILLHUB_STORAGE_S3_BUCKET + value: skillhub-prod +- name: SKILLHUB_STORAGE_S3_REGION + value: us-east-1 +``` + +### Image Reference + +| Component | Image | +|---|---| +| Backend | ghcr.io/iflytek/skillhub-server:latest | +| Frontend | ghcr.io/iflytek/skillhub-web:latest | +| Scanner | ghcr.io/iflytek/skillhub-scanner:latest | +| PostgreSQL | postgres:16-alpine | +| Redis | redis:7-alpine | + +## Default Admin + +On first startup, if `bootstrap-admin-enabled` is `true`, the system will automatically create an admin account: + +- Username: `admin` +- Password: Configured in `secret.yaml` as `bootstrap-admin-password` + +**Security Note**: Please change the default password immediately after first login. + +## Troubleshooting + +### Pod Stuck in Pending + +```bash +# Check PVC binding +kubectl get pvc -n skillhub + +# Check node resources +kubectl describe node +``` + +### Image Pull Failure + +If images are private, create pull credentials: + +```bash +kubectl create secret docker-registry ghcr-secret \ + --docker-server=ghcr.io \ + --docker-username= \ + --docker-password= \ + -n skillhub +``` + +### Database Connection Failure + +```bash +# Check PostgreSQL status +kubectl logs postgres-0 -n skillhub + +# Check Secret configuration +kubectl get secret skillhub-secret -n skillhub -o yaml +``` + +### View Logs + +```bash +# Backend logs +kubectl logs -l app.kubernetes.io/name=skillhub-server -n skillhub -f + +# Frontend logs +kubectl logs -l app.kubernetes.io/name=skillhub-web -n skillhub -f + +# Scanner logs +kubectl logs -l app.kubernetes.io/name=skillhub-scanner -n skillhub -f +``` + +## Cleanup + +```bash +# Delete all resources +kubectl delete -k overlays/with-infra/ # or overlays/external/ + +# Delete namespace +kubectl delete namespace skillhub +``` diff --git a/docs/skillhub/guide/kubernetes.md b/docs/skillhub/guide/kubernetes.md new file mode 100644 index 00000000..de8f505e --- /dev/null +++ b/docs/skillhub/guide/kubernetes.md @@ -0,0 +1,289 @@ +# Kubernetes 部署指南 + +本文档说明如何在 Kubernetes 集群中部署 SkillHub。 + +## 前置条件 + +- Kubernetes 集群 (v1.24+) +- kubectl 已配置并连接到集群 +- nginx ingress controller 已安装(可选,用于域名访问) +- 默认 StorageClass 已配置(用于 PVC) + +## 目录结构 + +``` +deploy/k8s/ +├── base/ # 基础配置(所有场景共用) +│ ├── kustomization.yaml +│ ├── configmap.yaml +│ ├── secret.yaml.example +│ ├── services.yaml +│ ├── backend-deployment.yaml +│ ├── frontend-deployment.yaml +│ ├── scanner-deployment.yaml +│ └── ingress.yaml +│ +└── overlays/ + ├── with-infra/ # 完整部署(包含内置数据库) + │ ├── kustomization.yaml + │ ├── postgres-statefulset.yaml + │ └── redis-statefulset.yaml + │ + └── external/ # 外部数据库 + └── kustomization.yaml +``` + +## 快速开始 + +### 1. 创建命名空间 + +```bash +kubectl create namespace skillhub +``` + +### 2. 配置 Secret + +```bash +cd deploy/k8s/base + +# 复制示例文件 +cp secret.yaml.example secret.yaml + +# 编辑 secret.yaml,修改敏感配置 +``` + +**Secret 配置项**: + +| 键 | 说明 | 必填 | +|---|---|---| +| spring-datasource-url | PostgreSQL 连接 URL | 是 | +| spring-datasource-username | 数据库用户名 | 是 | +| spring-datasource-password | 数据库密码 | 是 | +| bootstrap-admin-password | 管理员密码 | 是 | +| oauth2-github-client-id | GitHub OAuth ID | 否 | +| oauth2-github-client-secret | GitHub OAuth 密钥 | 否 | +| skill-scanner-llm-api-key | LLM API 密钥 | 否 | + +### 3. 选择部署方式 + +**方式一:完整部署(包含 PostgreSQL + Redis)** + +适合全新环境,自动部署数据库: + +```bash +kubectl apply -k overlays/with-infra/ +``` + +**方式二:使用外部数据库** + +适合已有 PostgreSQL 和 Redis 的环境: + +1. 修改 `base/configmap.yaml` 中的 Redis 配置: +```yaml +redis-host: your-redis-host +redis-port: "6379" +``` + +2. 修改 `base/secret.yaml` 中的数据库连接: +```yaml +spring-datasource-url: jdbc:postgresql://your-postgres-host:5432/skillhub +``` + +3. 部署: +```bash +kubectl apply -k overlays/external/ +``` + +### 4. 验证部署 + +```bash +# 检查 Pod 状态 +kubectl get pods -n skillhub + +# 等待所有 Pod 就绪 +kubectl wait --for=condition=ready pod --all -n skillhub --timeout=300s +``` + +### 5. 访问服务 + +**方式一:端口转发(推荐本地测试)** + +```bash +# 前端 +kubectl port-forward svc/skillhub-web -n skillhub 8080:80 + +# 后端 API +kubectl port-forward svc/skillhub-server -n skillhub 8081:8080 +``` + +访问 http://localhost:8080 + +**方式二:Ingress 域名访问** + +修改 `base/ingress.yaml` 中的域名: +```yaml +spec: + rules: + - host: your-domain.com # 修改为你的域名 +``` + +```bash +kubectl apply -k overlays/with-infra/ # 或 overlays/external/ +``` + +## 部署架构 + +``` +┌─────────────────────────────────────────────────────────────┐ +│ skillhub namespace │ +├─────────────────────────────────────────────────────────────┤ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ +│ │ skillhub-web│ │skillhub- │ │ skillhub-scanner │ │ +│ │ (前端) │ │ server │ │ (扫描器) │ │ +│ │ :80 │ │ (后端) │ │ :8000 │ │ +│ └─────────────┘ │ :8080 │ └─────────────────────┘ │ +│ └──────┬──────┘ │ +│ │ │ +│ ┌────────────────┴────────────────┐ │ +│ │ with-infra only │ │ +│ │ ┌─────────────┐ ┌───────────┐ │ │ +│ │ │ postgres-0 │ │ redis-0 │ │ │ +│ │ │ :5432 │ │ :6379 │ │ │ +│ │ └─────────────┘ └───────────┘ │ │ +│ └─────────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────────────────────────────────┐ │ +│ │ PersistentVolumeClaims │ │ +│ │ - skillhub-storage-pvc (10Gi) │ │ +│ │ - postgres-data-0 (10Gi) - with-infra only │ │ +│ │ - redis-data-0 (5Gi) - with-infra only │ │ +│ └─────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +## 配置说明 + +### ConfigMap 配置项 + +| 键 | 默认值 | 说明 | +|---|---|---| +| redis-host | redis | Redis 主机地址 | +| redis-port | 6379 | Redis 端口 | +| storage-base-path | /var/lib/skillhub/storage | 技能存储路径 | +| skillhub-storage-provider | local | 存储类型(local/s3) | +| skill-scanner-enabled | true | 是否启用扫描器 | +| skill-scanner-url | http://skillhub-scanner:8000 | 扫描器地址 | +| skill-scanner-mode | upload | 扫描模式 | +| bootstrap-admin-enabled | true | 是否创建默认管理员 | +| bootstrap-admin-user-id | docker-admin | 管理员用户 ID | +| bootstrap-admin-username | admin | 管理员用户名 | +| bootstrap-admin-display-name | Platform Admin | 管理员显示名称 | +| bootstrap-admin-email | admin@example.com | 管理员邮箱 | +| session-cookie-secure | false | HTTPS 环境设为 true | + +### 存储配置 + +**本地存储(默认)** + +默认使用本地文件存储,数据保存在 PVC `skillhub-storage-pvc` 中。 + +**S3/OSS 存储** + +生产环境建议使用 S3 兼容的对象存储: + +1. 修改 ConfigMap: +```yaml +skillhub-storage-provider: s3 +``` + +2. 在 Secret 中添加: +```yaml +skillhub-storage-s3-access-key: your-access-key +skillhub-storage-s3-secret-key: your-secret-key +``` + +3. 在 backend-deployment.yaml 中添加环境变量: +```yaml +- name: SKILLHUB_STORAGE_S3_ENDPOINT + value: https://oss-cn-shanghai.aliyuncs.com +- name: SKILLHUB_STORAGE_S3_BUCKET + value: skillhub-prod +- name: SKILLHUB_STORAGE_S3_REGION + value: cn-shanghai +``` + +### 镜像说明 + +| 组件 | 镜像 | +|---|---| +| 后端服务 | ghcr.io/iflytek/skillhub-server:latest | +| 前端服务 | ghcr.io/iflytek/skillhub-web:latest | +| 扫描器 | ghcr.io/iflytek/skillhub-scanner:latest | +| PostgreSQL | postgres:16-alpine | +| Redis | redis:7-alpine | + +## 默认管理员 + +首次启动时,如果 `bootstrap-admin-enabled` 为 `true`,系统会自动创建管理员账户: + +- 用户名:`admin` +- 密码:在 `secret.yaml` 的 `bootstrap-admin-password` 中配置 + +**安全建议**:首次登录后,请立即修改默认密码。 + +## 常见问题 + +### Pod 一直 Pending + +```bash +# 检查 PVC 是否绑定 +kubectl get pvc -n skillhub + +# 检查节点资源 +kubectl describe node +``` + +### 镜像拉取失败 + +如果镜像私有,需要创建拉取凭证: + +```bash +kubectl create secret docker-registry ghcr-secret \ + --docker-server=ghcr.io \ + --docker-username= \ + --docker-password= \ + -n skillhub +``` + +### 数据库连接失败 + +```bash +# 检查 PostgreSQL 是否就绪 +kubectl logs postgres-0 -n skillhub + +# 检查 Secret 配置 +kubectl get secret skillhub-secret -n skillhub -o yaml +``` + +### 查看日志 + +```bash +# 后端日志 +kubectl logs -l app.kubernetes.io/name=skillhub-server -n skillhub -f + +# 前端日志 +kubectl logs -l app.kubernetes.io/name=skillhub-web -n skillhub -f + +# 扫描器日志 +kubectl logs -l app.kubernetes.io/name=skillhub-scanner -n skillhub -f +``` + +## 清理 + +```bash +# 删除所有资源 +kubectl delete -k overlays/with-infra/ # 或 overlays/external/ + +# 删除命名空间 +kubectl delete namespace skillhub +```