From d831915f06e09dc8b68a29564226f045c2f158c2 Mon Sep 17 00:00:00 2001 From: SpecialX <47072643+wangxiner55@users.noreply.github.com> Date: Wed, 8 Jul 2026 12:50:56 +0800 Subject: [PATCH] =?UTF-8?q?feat(infra):=20k8s=20Helm=20Chart=20=E6=BC=94?= =?UTF-8?q?=E5=8C=96=E4=B8=8E=E5=A4=87=E4=BB=BD=E8=84=9A=E6=9C=AC=E6=B5=8B?= =?UTF-8?q?=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 edu-platform 平台级 chart(namespace/configmap/secret/ingress/hpa) - 新增 api-gateway 服务级 chart(完整迁移自原 manifest) - 新增 6 个业务服务 chart 桩(iam/core-edu/content/msg/data-ana/ai) - 删除原 api-gateway-deployment.yaml(已迁移至 helm chart) - 更新 infra/k8s/README.md 为 Helm Chart 管理说明 - 新增 backup-mysql.sh dry-run 测试脚本(17 断言) --- infra/backup/test-backup-mysql.sh | 112 ++++++++++++++ infra/k8s/README.md | 143 +++++++++++++----- infra/k8s/api-gateway-deployment.yaml | 125 --------------- infra/k8s/helm/ai/Chart.yaml | 11 ++ infra/k8s/helm/ai/templates/_helpers.tpl | 29 ++++ infra/k8s/helm/ai/templates/deployment.yaml | 77 ++++++++++ infra/k8s/helm/ai/templates/service.yaml | 16 ++ infra/k8s/helm/ai/values.yaml | 85 +++++++++++ infra/k8s/helm/api-gateway/Chart.yaml | 12 ++ .../helm/api-gateway/templates/_helpers.tpl | 29 ++++ .../helm/api-gateway/templates/configmap.yaml | 13 ++ .../api-gateway/templates/deployment.yaml | 77 ++++++++++ infra/k8s/helm/api-gateway/templates/hpa.yaml | 29 ++++ .../helm/api-gateway/templates/service.yaml | 16 ++ infra/k8s/helm/api-gateway/values.yaml | 86 +++++++++++ infra/k8s/helm/content/Chart.yaml | 11 ++ infra/k8s/helm/content/templates/_helpers.tpl | 29 ++++ .../helm/content/templates/deployment.yaml | 77 ++++++++++ infra/k8s/helm/content/templates/service.yaml | 16 ++ infra/k8s/helm/content/values.yaml | 85 +++++++++++ infra/k8s/helm/core-edu/Chart.yaml | 11 ++ .../k8s/helm/core-edu/templates/_helpers.tpl | 29 ++++ .../helm/core-edu/templates/deployment.yaml | 77 ++++++++++ .../k8s/helm/core-edu/templates/service.yaml | 16 ++ infra/k8s/helm/core-edu/values.yaml | 85 +++++++++++ infra/k8s/helm/data-ana/Chart.yaml | 11 ++ .../k8s/helm/data-ana/templates/_helpers.tpl | 29 ++++ .../helm/data-ana/templates/deployment.yaml | 77 ++++++++++ .../k8s/helm/data-ana/templates/service.yaml | 16 ++ infra/k8s/helm/data-ana/values.yaml | 85 +++++++++++ infra/k8s/helm/edu-platform/Chart.yaml | 12 ++ .../helm/edu-platform/templates/_helpers.tpl | 42 +++++ .../edu-platform/templates/configmap.yaml | 13 ++ .../k8s/helm/edu-platform/templates/hpa.yaml | 34 +++++ .../helm/edu-platform/templates/ingress.yaml | 35 +++++ .../edu-platform/templates/namespace.yaml | 10 ++ .../helm/edu-platform/templates/secret.yaml | 26 ++++ infra/k8s/helm/edu-platform/values-dev.yaml | 24 +++ infra/k8s/helm/edu-platform/values-prod.yaml | 35 +++++ .../k8s/helm/edu-platform/values-staging.yaml | 23 +++ infra/k8s/helm/edu-platform/values.yaml | 72 +++++++++ infra/k8s/helm/iam/Chart.yaml | 11 ++ infra/k8s/helm/iam/templates/_helpers.tpl | 29 ++++ infra/k8s/helm/iam/templates/deployment.yaml | 77 ++++++++++ infra/k8s/helm/iam/templates/service.yaml | 16 ++ infra/k8s/helm/iam/values.yaml | 85 +++++++++++ infra/k8s/helm/msg/Chart.yaml | 11 ++ infra/k8s/helm/msg/templates/_helpers.tpl | 29 ++++ infra/k8s/helm/msg/templates/deployment.yaml | 77 ++++++++++ infra/k8s/helm/msg/templates/service.yaml | 16 ++ infra/k8s/helm/msg/values.yaml | 85 +++++++++++ 51 files changed, 2111 insertions(+), 165 deletions(-) create mode 100644 infra/backup/test-backup-mysql.sh delete mode 100644 infra/k8s/api-gateway-deployment.yaml create mode 100644 infra/k8s/helm/ai/Chart.yaml create mode 100644 infra/k8s/helm/ai/templates/_helpers.tpl create mode 100644 infra/k8s/helm/ai/templates/deployment.yaml create mode 100644 infra/k8s/helm/ai/templates/service.yaml create mode 100644 infra/k8s/helm/ai/values.yaml create mode 100644 infra/k8s/helm/api-gateway/Chart.yaml create mode 100644 infra/k8s/helm/api-gateway/templates/_helpers.tpl create mode 100644 infra/k8s/helm/api-gateway/templates/configmap.yaml create mode 100644 infra/k8s/helm/api-gateway/templates/deployment.yaml create mode 100644 infra/k8s/helm/api-gateway/templates/hpa.yaml create mode 100644 infra/k8s/helm/api-gateway/templates/service.yaml create mode 100644 infra/k8s/helm/api-gateway/values.yaml create mode 100644 infra/k8s/helm/content/Chart.yaml create mode 100644 infra/k8s/helm/content/templates/_helpers.tpl create mode 100644 infra/k8s/helm/content/templates/deployment.yaml create mode 100644 infra/k8s/helm/content/templates/service.yaml create mode 100644 infra/k8s/helm/content/values.yaml create mode 100644 infra/k8s/helm/core-edu/Chart.yaml create mode 100644 infra/k8s/helm/core-edu/templates/_helpers.tpl create mode 100644 infra/k8s/helm/core-edu/templates/deployment.yaml create mode 100644 infra/k8s/helm/core-edu/templates/service.yaml create mode 100644 infra/k8s/helm/core-edu/values.yaml create mode 100644 infra/k8s/helm/data-ana/Chart.yaml create mode 100644 infra/k8s/helm/data-ana/templates/_helpers.tpl create mode 100644 infra/k8s/helm/data-ana/templates/deployment.yaml create mode 100644 infra/k8s/helm/data-ana/templates/service.yaml create mode 100644 infra/k8s/helm/data-ana/values.yaml create mode 100644 infra/k8s/helm/edu-platform/Chart.yaml create mode 100644 infra/k8s/helm/edu-platform/templates/_helpers.tpl create mode 100644 infra/k8s/helm/edu-platform/templates/configmap.yaml create mode 100644 infra/k8s/helm/edu-platform/templates/hpa.yaml create mode 100644 infra/k8s/helm/edu-platform/templates/ingress.yaml create mode 100644 infra/k8s/helm/edu-platform/templates/namespace.yaml create mode 100644 infra/k8s/helm/edu-platform/templates/secret.yaml create mode 100644 infra/k8s/helm/edu-platform/values-dev.yaml create mode 100644 infra/k8s/helm/edu-platform/values-prod.yaml create mode 100644 infra/k8s/helm/edu-platform/values-staging.yaml create mode 100644 infra/k8s/helm/edu-platform/values.yaml create mode 100644 infra/k8s/helm/iam/Chart.yaml create mode 100644 infra/k8s/helm/iam/templates/_helpers.tpl create mode 100644 infra/k8s/helm/iam/templates/deployment.yaml create mode 100644 infra/k8s/helm/iam/templates/service.yaml create mode 100644 infra/k8s/helm/iam/values.yaml create mode 100644 infra/k8s/helm/msg/Chart.yaml create mode 100644 infra/k8s/helm/msg/templates/_helpers.tpl create mode 100644 infra/k8s/helm/msg/templates/deployment.yaml create mode 100644 infra/k8s/helm/msg/templates/service.yaml create mode 100644 infra/k8s/helm/msg/values.yaml diff --git a/infra/backup/test-backup-mysql.sh b/infra/backup/test-backup-mysql.sh new file mode 100644 index 0000000..ca985e1 --- /dev/null +++ b/infra/backup/test-backup-mysql.sh @@ -0,0 +1,112 @@ +#!/bin/bash +# backup-mysql.sh 参数解析与校验的 dry-run 测试 +# 不实际执行 mysqldump,仅验证参数解析、必填校验、默认值、帮助信息等。 +# +# 运行方式(需要 bash 环境,如 Git Bash / WSL): +# bash infra/backup/test-backup-mysql.sh +# +# 退出码:0 表示全部通过,非 0 表示有失败用例。 +set -uo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SCRIPT="${SCRIPT_DIR}/backup-mysql.sh" + +PASS=0 +FAIL=0 + +# assertExit +assertExit() { + local expected="$1" desc="$2" actual="$3" stderr="${4:-}" + if [[ "$actual" == "$expected" ]]; then + echo "[PASS] $desc (exit=$actual)" + PASS=$((PASS + 1)) + else + echo "[FAIL] $desc: 期望 exit=$expected, 实际 exit=$actual" + [[ -n "$stderr" ]] && echo " stderr: $stderr" + FAIL=$((FAIL + 1)) + fi +} + +# assertContains +assertContains() { + local needle="$1" desc="$2" haystack="${3:-}" + if [[ "$haystack" == *"$needle"* ]]; then + echo "[PASS] $desc" + PASS=$((PASS + 1)) + else + echo "[FAIL] $desc: 输出应包含 '$needle'" + echo " 实际输出: $haystack" + FAIL=$((FAIL + 1)) + fi +} + +echo "=== backup-mysql.sh dry-run 测试 ===" + +# ---------- 用例 1: 无参数应失败并提示 --service 必填 ---------- +output="$("$SCRIPT" 2>&1)" +code=$? +assertExit 1 "无参数应退出 1" "$code" "$output" +assertContains "--service 参数必填" "无参数应提示 --service 必填" "$output" + +# ---------- 用例 2: --help 应退出 0 并输出 Usage ---------- +output="$("$SCRIPT" --help 2>&1)" +code=$? +assertExit 0 "--help 应退出 0" "$code" "$output" +assertContains "Usage:" "--help 应输出 Usage" "$output" +assertContains "--service" "--help 应说明 --service 参数" "$output" +assertContains "--keep" "--help 应说明 --keep 参数" "$output" + +# ---------- 用例 3: --keep 默认值(未提供时)应在日志中体现 7 天 ---------- +# 设置 MYSQL_PASSWORD 让脚本通过环境变量校验,进入主流程打印日志; +# 随后会在 mysqldump 阶段失败(CI 环境无 mysqldump)。 +output="$(MYSQL_PASSWORD=fake-pass "$SCRIPT" --service iam 2>&1)" +code=$? +assertExit 1 "无 mysqldump 时应最终退出 1" "$code" "$output" +assertContains "保留 7 天" "未提供 --keep 时应使用默认值 7 天" "$output" + +# ---------- 用例 4: --keep 非数字应失败 ---------- +output="$("$SCRIPT" --service iam --keep abc 2>&1)" +code=$? +assertExit 1 "--keep 非数字应退出 1" "$code" "$output" +assertContains "--keep 必须为正整数" "非数字 --keep 应报错" "$output" + +# ---------- 用例 5: --keep 小于 1 应失败 ---------- +output="$("$SCRIPT" --service iam --keep 0 2>&1)" +code=$? +assertExit 1 "--keep=0 应退出 1" "$code" "$output" +assertContains "--keep 必须为正整数" "--keep=0 应报错" "$output" + +# ---------- 用例 6: 未知参数应失败 ---------- +output="$("$SCRIPT" --service iam --unknown-flag 2>&1)" +code=$? +assertExit 1 "未知参数应退出 1" "$code" "$output" +assertContains "未知参数" "未知参数应报错" "$output" + +# ---------- 用例 7: --keep 自定义值应在日志中体现 ---------- +output="$(MYSQL_PASSWORD=fake-pass "$SCRIPT" --service iam --keep 30 2>&1)" +code=$? +assertExit 1 "无 mysqldump 时应最终退出 1(用例 7 预置)" "$code" "$output" +assertContains "保留 30 天" "--keep=30 应在日志中体现" "$output" + +# ---------- 用例 8: MYSQL_PASSWORD 已设置但 mysqldump 不可用时应优雅失败 ---------- +# 此用例验证:参数校验通过后,脚本会尝试调用 mysqldump;若 mysqldump 不存在则失败。 +# 在无 MySQL 容器的 CI 环境中,这是最接近真实 dry-run 的验证。 +output="$(MYSQL_PASSWORD=fake-pass "$SCRIPT" --service iam 2>&1)" +code=$? +# mysqldump 不存在时,pipefail + gzip 写入空文件 → 脚本检测到空文件后 exit 1 +# 或 mysqldump 命令未找到 → set -e 触发 exit 1 +# 任一路径都应是非 0 退出 +if [[ "$code" -ne 0 ]]; then + echo "[PASS] mysqldump 不可用时应非 0 退出 (exit=$code)" + PASS=$((PASS + 1)) +else + echo "[FAIL] mysqldump 不可用时不应返回 0" + FAIL=$((FAIL + 1)) +fi + +echo "=== 测试结果 ===" +echo "通过: $PASS 失败: $FAIL" +if [[ "$FAIL" -gt 0 ]]; then + exit 1 +fi +exit 0 diff --git a/infra/k8s/README.md b/infra/k8s/README.md index 8dd9a6d..7e36e55 100644 --- a/infra/k8s/README.md +++ b/infra/k8s/README.md @@ -1,55 +1,118 @@ -# K8s 部署说明(骨架) +# K8s 部署说明 -> **说明**:本目录为 K8s 部署**骨架**,仅提供最小可用 manifest,**生产环境请使用 Helm Chart**。 +> **状态**:已迁移到 Helm Chart 管理。本目录保留 `namespace.yaml` 作为基础资源,其余资源通过 `helm/` 下的 chart 部署。 -## 适用范围 +## 目录结构 -- 开发 / Staging 环境快速拉起 -- 验证服务在 K8s 上的基本可用性 -- 作为后续 Helm 化的过渡参考 - -## Manifest 类别 - -| 文件 | 类别 | 用途 | -|------|------|------| -| `namespace.yaml` | namespace | 划分 4 个命名空间:system / services / monitoring / ingress | -| `configmap-*.yaml` | configmap | 非敏感配置(服务端口、特性开关等) | -| `secret-*.yaml` | secret | 敏感配置(密钥、连接串),生产用 External Secrets | -| `*-deployment.yaml` | deployment | 服务无状态部署,含探针 / 资源 / 副本数 | -| `*-service.yaml` | service | ClusterIP 服务发现 | -| `ingress.yaml` | ingress | 对外入口,TLS 终结 + 路由 | -| `*-hpa.yaml` | hpa | 水平自动扩缩容 | +``` +infra/k8s/ +├─ namespace.yaml # 基础命名空间(4 个:system/services/monitoring/ingress) +└─ helm/ # Helm Chart 仓库 + ├─ edu-platform/ # 平台级 chart(namespace/configmap/secret/ingress/hpa) + │ ├─ Chart.yaml + │ ├─ values.yaml # 全局默认值 + │ ├─ values-dev.yaml # 开发环境覆盖 + │ ├─ values-staging.yaml # 预发布环境覆盖 + │ ├─ values-prod.yaml # 生产环境覆盖 + │ └─ templates/ + │ ├─ _helpers.tpl + │ ├─ namespace.yaml + │ ├─ configmap.yaml + │ ├─ secret.yaml # 骨架;生产请用 External Secrets Operator + │ ├─ ingress.yaml + │ └─ hpa.yaml # 全局 HPA 示例(默认不渲染) + ├─ api-gateway/ # 服务级 chart(完整迁移自原 manifest) + │ ├─ Chart.yaml + │ ├─ values.yaml + │ └─ templates/ + │ ├─ _helpers.tpl + │ ├─ deployment.yaml + │ ├─ service.yaml + │ ├─ configmap.yaml + │ └─ hpa.yaml + ├─ iam/ # 业务服务 chart 桩(P2) + ├─ core-edu/ # 业务服务 chart 桩(P3) + ├─ content/ # 业务服务 chart 桩(P4) + ├─ msg/ # 业务服务 chart 桩(P5) + ├─ data-ana/ # 业务服务 chart 桩(P4) + └─ ai/ # 业务服务 chart 桩(P5) +``` ## 命名空间规划 -| Namespace | 用途 | -|-----------|------| -| `edu-system` | 系统组件(数据库代理、配置等) | -| `edu-services` | 业务微服务(api-gateway / iam / core-edu / content / msg) | -| `edu-monitoring` | 监控栈(Prometheus / Grafana / Alertmanager) | -| `edu-ingress` | 入口控制器(NGINX Ingress / cert-manager) | +| Namespace | 用途 | +| ---------------- | ---------------------------------------------------------- | +| `edu-system` | 系统组件(数据库代理、配置等) | +| `edu-services` | 业务微服务(api-gateway / iam / core-edu / content / msg) | +| `edu-monitoring` | 监控栈(Prometheus / Grafana / Alertmanager) | +| `edu-ingress` | 入口控制器(NGINX Ingress / cert-manager) | -## 部署顺序 +## 部署方式 + +### 1. 安装平台级 chart(命名空间 / 全局 ConfigMap / Secret / Ingress) + +```bash +# 开发环境 +helm install edu-platform ./helm/edu-platform -f ./helm/edu-platform/values-dev.yaml + +# 生产环境 +helm install edu-platform ./helm/edu-platform -f ./helm/edu-platform/values-prod.yaml \ + --set secret.data.MYSQL_PASSWORD= \ + --set secret.data.JWT_SECRET= \ + --set secret.data.REDIS_PASSWORD= +``` + +### 2. 安装服务级 chart + +```bash +# api-gateway +helm install api-gateway ./helm/api-gateway + +# 其他业务服务(iam / core-edu / content / msg / data-ana / ai) +helm install iam ./helm/iam +helm install core-edu ./helm/core-edu +# ... +``` + +### 3. 应用基础命名空间(如未通过 helm 安装 edu-platform) ```bash kubectl apply -f namespace.yaml -kubectl apply -f configmap-*.yaml -kubectl apply -f secret-*.yaml # 生产请改用 External Secrets -kubectl apply -f *-deployment.yaml -kubectl apply -f *-service.yaml -kubectl apply -f ingress.yaml -kubectl apply -f *-hpa.yaml ``` -## Helm 化路线(后续) +## 验证 -1. 将骨架迁移为 Helm Chart(`charts/edu-platform/`) -2. 每个微服务一个子 Chart,统一通过 umbrella chart 编排 -3. 环境差异通过 values-.yaml 管理 -4. 敏感配置接入 External Secrets Operator(对接 Vault / KMS) -5. CI/CD 通过 ArgoCD / Flux 做 GitOps 部署 +```bash +# lint 所有 chart +helm lint helm/edu-platform helm/api-gateway helm/iam helm/core-edu helm/content helm/msg helm/data-ana helm/ai -## 当前文件 +# 渲染模板(不实际部署) +helm template edu-platform ./helm/edu-platform +helm template api-gateway ./helm/api-gateway +``` -- `namespace.yaml`:4 个命名空间定义 -- `api-gateway-deployment.yaml`:api-gateway Deployment + Service 骨架 +## 环境差异 + +| 环境 | values 文件 | 副本数 | HPA | TLS | +| ------- | ------------------- | ------ | ---- | ---- | +| dev | values-dev.yaml | 1 | 关闭 | 关闭 | +| staging | values-staging.yaml | 2 | 2-5 | 开启 | +| prod | values-prod.yaml | 3 | 3-20 | 开启 | + +## 敏感配置 + +⚠️ **生产环境禁止在 values.yaml 中硬编码密钥**。 + +推荐方案: + +1. 使用 [External Secrets Operator](https://external-secrets.io/) 对接 Vault / KMS / 云 KMS +2. 通过 `--set secret.data.=` 临时注入 +3. 通过 ArgoCD / Flux GitOps + Sealed Secrets + +## 后续路线 + +- [ ] 接入 External Secrets Operator +- [ ] ArgoCD / Flux GitOps 部署 +- [ ] 各服务 chart 补充 configmap.yaml / hpa.yaml 模板(当前桩仅含 deployment/service) +- [ ] 服务级 values-dev/staging/prod 覆盖文件 +- [ ] CI/CD 集成(helm chart 推送到 OCI registry) diff --git a/infra/k8s/api-gateway-deployment.yaml b/infra/k8s/api-gateway-deployment.yaml deleted file mode 100644 index 55bd2ef..0000000 --- a/infra/k8s/api-gateway-deployment.yaml +++ /dev/null @@ -1,125 +0,0 @@ -# api-gateway Deployment + Service 骨架 -# 生产环境请通过 Helm Chart 管理,此处仅作骨架参考 -apiVersion: apps/v1 -kind: Deployment -metadata: - name: api-gateway - namespace: edu-services - labels: - app.kubernetes.io/name: api-gateway - app.kubernetes.io/part-of: edu-platform - app.kubernetes.io/component: gateway -spec: - replicas: 2 - selector: - matchLabels: - app.kubernetes.io/name: api-gateway - strategy: - type: RollingUpdate - rollingUpdate: - maxSurge: 1 - maxUnavailable: 0 - template: - metadata: - labels: - app.kubernetes.io/name: api-gateway - app.kubernetes.io/part-of: edu-platform - app.kubernetes.io/component: gateway - annotations: - prometheus.io/scrape: "true" - prometheus.io/port: "8080" - prometheus.io/path: "/metrics" - spec: - containers: - - name: api-gateway - image: edu/api-gateway:latest # 生产请固定 tag,避免 latest - imagePullPolicy: IfNotPresent - ports: - - name: http - containerPort: 8080 - protocol: TCP - # 存活探针:失败触发重启 - livenessProbe: - httpGet: - path: /healthz - port: http - initialDelaySeconds: 15 - periodSeconds: 20 - timeoutSeconds: 3 - failureThreshold: 3 - # 就绪探针:失败从 Service Endpoints 摘除 - readinessProbe: - httpGet: - path: /readyz - port: http - initialDelaySeconds: 5 - periodSeconds: 10 - timeoutSeconds: 3 - failureThreshold: 2 - resources: - requests: - cpu: "250m" - memory: "256Mi" - limits: - cpu: "1000m" - memory: "1Gi" - env: - # 从 ConfigMap 引用非敏感配置 - - name: NODE_ENV - valueFrom: - configMapKeyRef: - name: api-gateway-config - key: NODE_ENV - - name: LOG_LEVEL - valueFrom: - configMapKeyRef: - name: api-gateway-config - key: LOG_LEVEL - - name: MYSQL_HOST - valueFrom: - configMapKeyRef: - name: api-gateway-config - key: MYSQL_HOST - # 从 Secret 引用敏感配置 - - name: MYSQL_PASSWORD - valueFrom: - secretKeyRef: - name: api-gateway-secret - key: MYSQL_PASSWORD - - name: JWT_SECRET - valueFrom: - secretKeyRef: - name: api-gateway-secret - key: JWT_SECRET - - name: REDIS_PASSWORD - valueFrom: - secretKeyRef: - name: api-gateway-secret - key: REDIS_PASSWORD - # 生产建议挂载 /tmp 并设置 readOnlyRootFilesystem: true - securityContext: - runAsNonRoot: true - runAsUser: 1000 - allowPrivilegeEscalation: false - capabilities: - drop: ["ALL"] ---- -# api-gateway Service(ClusterIP) -apiVersion: v1 -kind: Service -metadata: - name: api-gateway - namespace: edu-services - labels: - app.kubernetes.io/name: api-gateway - app.kubernetes.io/part-of: edu-platform - app.kubernetes.io/component: gateway -spec: - type: ClusterIP - selector: - app.kubernetes.io/name: api-gateway - ports: - - name: http - port: 8080 - targetPort: http - protocol: TCP diff --git a/infra/k8s/helm/ai/Chart.yaml b/infra/k8s/helm/ai/Chart.yaml new file mode 100644 index 0000000..cf43d32 --- /dev/null +++ b/infra/k8s/helm/ai/Chart.yaml @@ -0,0 +1,11 @@ +apiVersion: v2 +name: ai +description: ai 服务级 Helm Chart +type: application +version: 0.1.0 +appVersion: "1.0.0" +keywords: + - edu + - ai +maintainers: + - name: edu-arch diff --git a/infra/k8s/helm/ai/templates/_helpers.tpl b/infra/k8s/helm/ai/templates/_helpers.tpl new file mode 100644 index 0000000..7d09927 --- /dev/null +++ b/infra/k8s/helm/ai/templates/_helpers.tpl @@ -0,0 +1,29 @@ +{{- define "ai.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{- define "ai.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{- define "ai.labels" -}} +helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} +{{ include "ai.selectorLabels" . }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +app.kubernetes.io/part-of: edu-platform +app.kubernetes.io/component: ai-gateway +{{- end -}} + +{{- define "ai.selectorLabels" -}} +app.kubernetes.io/name: {{ include "ai.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} diff --git a/infra/k8s/helm/ai/templates/deployment.yaml b/infra/k8s/helm/ai/templates/deployment.yaml new file mode 100644 index 0000000..b783830 --- /dev/null +++ b/infra/k8s/helm/ai/templates/deployment.yaml @@ -0,0 +1,77 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "ai.name" . }} + namespace: {{ .Values.namespace }} + labels: + {{- include "ai.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "ai.selectorLabels" . | nindent 6 }} + strategy: + type: {{ .Values.strategy.type }} + rollingUpdate: + maxSurge: {{ .Values.strategy.maxSurge }} + maxUnavailable: {{ .Values.strategy.maxUnavailable }} + template: + metadata: + labels: + {{- include "ai.selectorLabels" . | nindent 8 }} + app.kubernetes.io/part-of: edu-platform + app.kubernetes.io/component: ai-gateway + {{- if .Values.metrics.enabled }} + annotations: + prometheus.io/scrape: "true" + prometheus.io/port: {{ .Values.metrics.port | quote }} + prometheus.io/path: {{ .Values.metrics.path | quote }} + {{- end }} + spec: + containers: + - name: {{ include "ai.name" . }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.service.targetPort }} + protocol: TCP + livenessProbe: + httpGet: + path: {{ .Values.probes.liveness.path }} + port: http + initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }} + periodSeconds: {{ .Values.probes.liveness.periodSeconds }} + timeoutSeconds: {{ .Values.probes.liveness.timeoutSeconds }} + failureThreshold: {{ .Values.probes.liveness.failureThreshold }} + readinessProbe: + httpGet: + path: {{ .Values.probes.readiness.path }} + port: http + initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }} + periodSeconds: {{ .Values.probes.readiness.periodSeconds }} + timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }} + failureThreshold: {{ .Values.probes.readiness.failureThreshold }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + env: + {{- if .Values.configMap.enabled }} + {{- range $k, $v := .Values.configMap.data }} + - name: {{ $k }} + valueFrom: + configMapKeyRef: + name: {{ include "ai.name" $ }}-config + key: {{ $k }} + {{- end }} + {{- end }} + {{- range $secret := .Values.secretRefs }} + {{- range $key := $secret.keys }} + - name: {{ $key }} + valueFrom: + secretKeyRef: + name: {{ $secret.name }} + key: {{ $key }} + {{- end }} + {{- end }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} diff --git a/infra/k8s/helm/ai/templates/service.yaml b/infra/k8s/helm/ai/templates/service.yaml new file mode 100644 index 0000000..04ee0a8 --- /dev/null +++ b/infra/k8s/helm/ai/templates/service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "ai.name" . }} + namespace: {{ .Values.namespace }} + labels: + {{- include "ai.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + selector: + {{- include "ai.selectorLabels" . | nindent 4 }} + ports: + - name: http + port: {{ .Values.service.port }} + targetPort: http + protocol: TCP diff --git a/infra/k8s/helm/ai/values.yaml b/infra/k8s/helm/ai/values.yaml new file mode 100644 index 0000000..736f3c7 --- /dev/null +++ b/infra/k8s/helm/ai/values.yaml @@ -0,0 +1,85 @@ +# ai 服务默认值(AI 网关 - 业务领域 D6) + +# 副本数(生产建议 ≥ 2) +replicaCount: 2 + +image: + repository: edu/ai + tag: latest # 生产请固定 tag,避免 latest + pullPolicy: IfNotPresent + +# 服务端口 +service: + type: ClusterIP + port: 3006 + targetPort: 3006 + +# 命名空间(默认 edu-services,由 edu-platform chart 创建) +namespace: edu-services + +# 探针配置 +probes: + liveness: + path: /healthz + initialDelaySeconds: 15 + periodSeconds: 20 + timeoutSeconds: 3 + failureThreshold: 3 + readiness: + path: /readyz + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 3 + failureThreshold: 2 + +# 资源配额 +resources: + requests: + cpu: 250m + memory: 256Mi + limits: + cpu: 1000m + memory: 1Gi + +# 滚动更新策略 +strategy: + type: RollingUpdate + maxSurge: 1 + maxUnavailable: 0 + +# Prometheus 指标采集 +metrics: + enabled: true + port: 3006 + path: /metrics + +# 安全上下文 +securityContext: + runAsNonRoot: true + runAsUser: 1000 + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] + +# 服务级 ConfigMap(非敏感配置) +configMap: + enabled: true + data: + NODE_ENV: production + LOG_LEVEL: info + +# HPA +hpa: + enabled: true + minReplicas: 2 + maxReplicas: 10 + targetCPUUtilizationPercentage: 70 + targetMemoryUtilizationPercentage: 80 + +# 敏感配置(从 Secret 引用,Secret 由 edu-platform chart 或 ExternalSecrets 管理) +secretRefs: + - name: edu-platform-secret + keys: + - MYSQL_PASSWORD + - JWT_SECRET + - REDIS_PASSWORD diff --git a/infra/k8s/helm/api-gateway/Chart.yaml b/infra/k8s/helm/api-gateway/Chart.yaml new file mode 100644 index 0000000..37cf75c --- /dev/null +++ b/infra/k8s/helm/api-gateway/Chart.yaml @@ -0,0 +1,12 @@ +apiVersion: v2 +name: api-gateway +description: api-gateway 服务级 Helm Chart(从 infra/k8s/api-gateway-deployment.yaml 迁移) +type: application +version: 0.1.0 +appVersion: "1.0.0" +keywords: + - edu + - gateway + - api +maintainers: + - name: edu-arch diff --git a/infra/k8s/helm/api-gateway/templates/_helpers.tpl b/infra/k8s/helm/api-gateway/templates/_helpers.tpl new file mode 100644 index 0000000..424105b --- /dev/null +++ b/infra/k8s/helm/api-gateway/templates/_helpers.tpl @@ -0,0 +1,29 @@ +{{- define "api-gateway.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{- define "api-gateway.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{- define "api-gateway.labels" -}} +helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} +{{ include "api-gateway.selectorLabels" . }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +app.kubernetes.io/part-of: edu-platform +app.kubernetes.io/component: gateway +{{- end -}} + +{{- define "api-gateway.selectorLabels" -}} +app.kubernetes.io/name: {{ include "api-gateway.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} diff --git a/infra/k8s/helm/api-gateway/templates/configmap.yaml b/infra/k8s/helm/api-gateway/templates/configmap.yaml new file mode 100644 index 0000000..1f70159 --- /dev/null +++ b/infra/k8s/helm/api-gateway/templates/configmap.yaml @@ -0,0 +1,13 @@ +{{- if .Values.configMap.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "api-gateway.name" . }}-config + namespace: {{ .Values.namespace }} + labels: + {{- include "api-gateway.labels" . | nindent 4 }} +data: +{{- range $k, $v := .Values.configMap.data }} + {{ $k }}: {{ $v | quote }} +{{- end }} +{{- end }} diff --git a/infra/k8s/helm/api-gateway/templates/deployment.yaml b/infra/k8s/helm/api-gateway/templates/deployment.yaml new file mode 100644 index 0000000..7a9e7bc --- /dev/null +++ b/infra/k8s/helm/api-gateway/templates/deployment.yaml @@ -0,0 +1,77 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "api-gateway.name" . }} + namespace: {{ .Values.namespace }} + labels: + {{- include "api-gateway.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "api-gateway.selectorLabels" . | nindent 6 }} + strategy: + type: {{ .Values.strategy.type }} + rollingUpdate: + maxSurge: {{ .Values.strategy.maxSurge }} + maxUnavailable: {{ .Values.strategy.maxUnavailable }} + template: + metadata: + labels: + {{- include "api-gateway.selectorLabels" . | nindent 8 }} + app.kubernetes.io/part-of: edu-platform + app.kubernetes.io/component: gateway + {{- if .Values.metrics.enabled }} + annotations: + prometheus.io/scrape: "true" + prometheus.io/port: {{ .Values.metrics.port | quote }} + prometheus.io/path: {{ .Values.metrics.path | quote }} + {{- end }} + spec: + containers: + - name: {{ include "api-gateway.name" . }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.service.targetPort }} + protocol: TCP + livenessProbe: + httpGet: + path: {{ .Values.probes.liveness.path }} + port: http + initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }} + periodSeconds: {{ .Values.probes.liveness.periodSeconds }} + timeoutSeconds: {{ .Values.probes.liveness.timeoutSeconds }} + failureThreshold: {{ .Values.probes.liveness.failureThreshold }} + readinessProbe: + httpGet: + path: {{ .Values.probes.readiness.path }} + port: http + initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }} + periodSeconds: {{ .Values.probes.readiness.periodSeconds }} + timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }} + failureThreshold: {{ .Values.probes.readiness.failureThreshold }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + env: + {{- if .Values.configMap.enabled }} + {{- range $k, $v := .Values.configMap.data }} + - name: {{ $k }} + valueFrom: + configMapKeyRef: + name: {{ include "api-gateway.name" $ }}-config + key: {{ $k }} + {{- end }} + {{- end }} + {{- range $secret := .Values.secretRefs }} + {{- range $key := $secret.keys }} + - name: {{ $key }} + valueFrom: + secretKeyRef: + name: {{ $secret.name }} + key: {{ $key }} + {{- end }} + {{- end }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} diff --git a/infra/k8s/helm/api-gateway/templates/hpa.yaml b/infra/k8s/helm/api-gateway/templates/hpa.yaml new file mode 100644 index 0000000..48bc0b7 --- /dev/null +++ b/infra/k8s/helm/api-gateway/templates/hpa.yaml @@ -0,0 +1,29 @@ +{{- if .Values.hpa.enabled }} +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "api-gateway.name" . }}-hpa + namespace: {{ .Values.namespace }} + labels: + {{- include "api-gateway.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "api-gateway.name" . }} + minReplicas: {{ .Values.hpa.minReplicas }} + maxReplicas: {{ .Values.hpa.maxReplicas }} + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.hpa.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ .Values.hpa.targetMemoryUtilizationPercentage }} +{{- end }} diff --git a/infra/k8s/helm/api-gateway/templates/service.yaml b/infra/k8s/helm/api-gateway/templates/service.yaml new file mode 100644 index 0000000..0e06403 --- /dev/null +++ b/infra/k8s/helm/api-gateway/templates/service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "api-gateway.name" . }} + namespace: {{ .Values.namespace }} + labels: + {{- include "api-gateway.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + selector: + {{- include "api-gateway.selectorLabels" . | nindent 4 }} + ports: + - name: http + port: {{ .Values.service.port }} + targetPort: http + protocol: TCP diff --git a/infra/k8s/helm/api-gateway/values.yaml b/infra/k8s/helm/api-gateway/values.yaml new file mode 100644 index 0000000..4986d26 --- /dev/null +++ b/infra/k8s/helm/api-gateway/values.yaml @@ -0,0 +1,86 @@ +# api-gateway 服务默认值 + +# 副本数(生产建议 ≥ 2) +replicaCount: 2 + +image: + repository: edu/api-gateway + tag: latest # 生产请固定 tag,避免 latest + pullPolicy: IfNotPresent + +# 服务端口 +service: + type: ClusterIP + port: 8080 + targetPort: 8080 + +# 命名空间(默认 edu-services,由 edu-platform chart 创建) +namespace: edu-services + +# 探针配置 +probes: + liveness: + path: /healthz + initialDelaySeconds: 15 + periodSeconds: 20 + timeoutSeconds: 3 + failureThreshold: 3 + readiness: + path: /readyz + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 3 + failureThreshold: 2 + +# 资源配额 +resources: + requests: + cpu: 250m + memory: 256Mi + limits: + cpu: 1000m + memory: 1Gi + +# 滚动更新策略 +strategy: + type: RollingUpdate + maxSurge: 1 + maxUnavailable: 0 + +# Prometheus 指标采集 +metrics: + enabled: true + port: 8080 + path: /metrics + +# 安全上下文 +securityContext: + runAsNonRoot: true + runAsUser: 1000 + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] + +# 服务级 ConfigMap(非敏感配置) +configMap: + enabled: true + data: + NODE_ENV: production + LOG_LEVEL: info + MYSQL_HOST: mysql.edu-system.svc.cluster.local + +# HPA +hpa: + enabled: true + minReplicas: 2 + maxReplicas: 10 + targetCPUUtilizationPercentage: 70 + targetMemoryUtilizationPercentage: 80 + +# 敏感配置(从 Secret 引用,Secret 由 edu-platform chart 或 ExternalSecrets 管理) +secretRefs: + - name: edu-platform-secret + keys: + - MYSQL_PASSWORD + - JWT_SECRET + - REDIS_PASSWORD diff --git a/infra/k8s/helm/content/Chart.yaml b/infra/k8s/helm/content/Chart.yaml new file mode 100644 index 0000000..096164c --- /dev/null +++ b/infra/k8s/helm/content/Chart.yaml @@ -0,0 +1,11 @@ +apiVersion: v2 +name: content +description: content 服务级 Helm Chart +type: application +version: 0.1.0 +appVersion: "1.0.0" +keywords: + - edu + - content +maintainers: + - name: edu-arch diff --git a/infra/k8s/helm/content/templates/_helpers.tpl b/infra/k8s/helm/content/templates/_helpers.tpl new file mode 100644 index 0000000..81edf94 --- /dev/null +++ b/infra/k8s/helm/content/templates/_helpers.tpl @@ -0,0 +1,29 @@ +{{- define "content.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{- define "content.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{- define "content.labels" -}} +helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} +{{ include "content.selectorLabels" . }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +app.kubernetes.io/part-of: edu-platform +app.kubernetes.io/component: content +{{- end -}} + +{{- define "content.selectorLabels" -}} +app.kubernetes.io/name: {{ include "content.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} diff --git a/infra/k8s/helm/content/templates/deployment.yaml b/infra/k8s/helm/content/templates/deployment.yaml new file mode 100644 index 0000000..37af24f --- /dev/null +++ b/infra/k8s/helm/content/templates/deployment.yaml @@ -0,0 +1,77 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "content.name" . }} + namespace: {{ .Values.namespace }} + labels: + {{- include "content.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "content.selectorLabels" . | nindent 6 }} + strategy: + type: {{ .Values.strategy.type }} + rollingUpdate: + maxSurge: {{ .Values.strategy.maxSurge }} + maxUnavailable: {{ .Values.strategy.maxUnavailable }} + template: + metadata: + labels: + {{- include "content.selectorLabels" . | nindent 8 }} + app.kubernetes.io/part-of: edu-platform + app.kubernetes.io/component: content + {{- if .Values.metrics.enabled }} + annotations: + prometheus.io/scrape: "true" + prometheus.io/port: {{ .Values.metrics.port | quote }} + prometheus.io/path: {{ .Values.metrics.path | quote }} + {{- end }} + spec: + containers: + - name: {{ include "content.name" . }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.service.targetPort }} + protocol: TCP + livenessProbe: + httpGet: + path: {{ .Values.probes.liveness.path }} + port: http + initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }} + periodSeconds: {{ .Values.probes.liveness.periodSeconds }} + timeoutSeconds: {{ .Values.probes.liveness.timeoutSeconds }} + failureThreshold: {{ .Values.probes.liveness.failureThreshold }} + readinessProbe: + httpGet: + path: {{ .Values.probes.readiness.path }} + port: http + initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }} + periodSeconds: {{ .Values.probes.readiness.periodSeconds }} + timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }} + failureThreshold: {{ .Values.probes.readiness.failureThreshold }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + env: + {{- if .Values.configMap.enabled }} + {{- range $k, $v := .Values.configMap.data }} + - name: {{ $k }} + valueFrom: + configMapKeyRef: + name: {{ include "content.name" $ }}-config + key: {{ $k }} + {{- end }} + {{- end }} + {{- range $secret := .Values.secretRefs }} + {{- range $key := $secret.keys }} + - name: {{ $key }} + valueFrom: + secretKeyRef: + name: {{ $secret.name }} + key: {{ $key }} + {{- end }} + {{- end }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} diff --git a/infra/k8s/helm/content/templates/service.yaml b/infra/k8s/helm/content/templates/service.yaml new file mode 100644 index 0000000..6717d41 --- /dev/null +++ b/infra/k8s/helm/content/templates/service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "content.name" . }} + namespace: {{ .Values.namespace }} + labels: + {{- include "content.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + selector: + {{- include "content.selectorLabels" . | nindent 4 }} + ports: + - name: http + port: {{ .Values.service.port }} + targetPort: http + protocol: TCP diff --git a/infra/k8s/helm/content/values.yaml b/infra/k8s/helm/content/values.yaml new file mode 100644 index 0000000..1f4b7dc --- /dev/null +++ b/infra/k8s/helm/content/values.yaml @@ -0,0 +1,85 @@ +# content 服务默认值(内容资源 - 业务领域 D4) + +# 副本数(生产建议 ≥ 2) +replicaCount: 2 + +image: + repository: edu/content + tag: latest # 生产请固定 tag,避免 latest + pullPolicy: IfNotPresent + +# 服务端口 +service: + type: ClusterIP + port: 3003 + targetPort: 3003 + +# 命名空间(默认 edu-services,由 edu-platform chart 创建) +namespace: edu-services + +# 探针配置 +probes: + liveness: + path: /healthz + initialDelaySeconds: 15 + periodSeconds: 20 + timeoutSeconds: 3 + failureThreshold: 3 + readiness: + path: /readyz + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 3 + failureThreshold: 2 + +# 资源配额 +resources: + requests: + cpu: 250m + memory: 256Mi + limits: + cpu: 1000m + memory: 1Gi + +# 滚动更新策略 +strategy: + type: RollingUpdate + maxSurge: 1 + maxUnavailable: 0 + +# Prometheus 指标采集 +metrics: + enabled: true + port: 3003 + path: /metrics + +# 安全上下文 +securityContext: + runAsNonRoot: true + runAsUser: 1000 + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] + +# 服务级 ConfigMap(非敏感配置) +configMap: + enabled: true + data: + NODE_ENV: production + LOG_LEVEL: info + +# HPA +hpa: + enabled: true + minReplicas: 2 + maxReplicas: 10 + targetCPUUtilizationPercentage: 70 + targetMemoryUtilizationPercentage: 80 + +# 敏感配置(从 Secret 引用,Secret 由 edu-platform chart 或 ExternalSecrets 管理) +secretRefs: + - name: edu-platform-secret + keys: + - MYSQL_PASSWORD + - JWT_SECRET + - REDIS_PASSWORD diff --git a/infra/k8s/helm/core-edu/Chart.yaml b/infra/k8s/helm/core-edu/Chart.yaml new file mode 100644 index 0000000..330415b --- /dev/null +++ b/infra/k8s/helm/core-edu/Chart.yaml @@ -0,0 +1,11 @@ +apiVersion: v2 +name: core-edu +description: core-edu 服务级 Helm Chart +type: application +version: 0.1.0 +appVersion: "1.0.0" +keywords: + - edu + - core-edu +maintainers: + - name: edu-arch diff --git a/infra/k8s/helm/core-edu/templates/_helpers.tpl b/infra/k8s/helm/core-edu/templates/_helpers.tpl new file mode 100644 index 0000000..f5ee14f --- /dev/null +++ b/infra/k8s/helm/core-edu/templates/_helpers.tpl @@ -0,0 +1,29 @@ +{{- define "core-edu.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{- define "core-edu.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{- define "core-edu.labels" -}} +helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} +{{ include "core-edu.selectorLabels" . }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +app.kubernetes.io/part-of: edu-platform +app.kubernetes.io/component: teaching +{{- end -}} + +{{- define "core-edu.selectorLabels" -}} +app.kubernetes.io/name: {{ include "core-edu.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} diff --git a/infra/k8s/helm/core-edu/templates/deployment.yaml b/infra/k8s/helm/core-edu/templates/deployment.yaml new file mode 100644 index 0000000..49ad1b1 --- /dev/null +++ b/infra/k8s/helm/core-edu/templates/deployment.yaml @@ -0,0 +1,77 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "core-edu.name" . }} + namespace: {{ .Values.namespace }} + labels: + {{- include "core-edu.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "core-edu.selectorLabels" . | nindent 6 }} + strategy: + type: {{ .Values.strategy.type }} + rollingUpdate: + maxSurge: {{ .Values.strategy.maxSurge }} + maxUnavailable: {{ .Values.strategy.maxUnavailable }} + template: + metadata: + labels: + {{- include "core-edu.selectorLabels" . | nindent 8 }} + app.kubernetes.io/part-of: edu-platform + app.kubernetes.io/component: teaching + {{- if .Values.metrics.enabled }} + annotations: + prometheus.io/scrape: "true" + prometheus.io/port: {{ .Values.metrics.port | quote }} + prometheus.io/path: {{ .Values.metrics.path | quote }} + {{- end }} + spec: + containers: + - name: {{ include "core-edu.name" . }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.service.targetPort }} + protocol: TCP + livenessProbe: + httpGet: + path: {{ .Values.probes.liveness.path }} + port: http + initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }} + periodSeconds: {{ .Values.probes.liveness.periodSeconds }} + timeoutSeconds: {{ .Values.probes.liveness.timeoutSeconds }} + failureThreshold: {{ .Values.probes.liveness.failureThreshold }} + readinessProbe: + httpGet: + path: {{ .Values.probes.readiness.path }} + port: http + initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }} + periodSeconds: {{ .Values.probes.readiness.periodSeconds }} + timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }} + failureThreshold: {{ .Values.probes.readiness.failureThreshold }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + env: + {{- if .Values.configMap.enabled }} + {{- range $k, $v := .Values.configMap.data }} + - name: {{ $k }} + valueFrom: + configMapKeyRef: + name: {{ include "core-edu.name" $ }}-config + key: {{ $k }} + {{- end }} + {{- end }} + {{- range $secret := .Values.secretRefs }} + {{- range $key := $secret.keys }} + - name: {{ $key }} + valueFrom: + secretKeyRef: + name: {{ $secret.name }} + key: {{ $key }} + {{- end }} + {{- end }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} diff --git a/infra/k8s/helm/core-edu/templates/service.yaml b/infra/k8s/helm/core-edu/templates/service.yaml new file mode 100644 index 0000000..ed781c4 --- /dev/null +++ b/infra/k8s/helm/core-edu/templates/service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "core-edu.name" . }} + namespace: {{ .Values.namespace }} + labels: + {{- include "core-edu.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + selector: + {{- include "core-edu.selectorLabels" . | nindent 4 }} + ports: + - name: http + port: {{ .Values.service.port }} + targetPort: http + protocol: TCP diff --git a/infra/k8s/helm/core-edu/values.yaml b/infra/k8s/helm/core-edu/values.yaml new file mode 100644 index 0000000..a739412 --- /dev/null +++ b/infra/k8s/helm/core-edu/values.yaml @@ -0,0 +1,85 @@ +# core-edu 服务默认值(教学核心 - 业务领域 D2+D3) + +# 副本数(生产建议 ≥ 2) +replicaCount: 2 + +image: + repository: edu/core-edu + tag: latest # 生产请固定 tag,避免 latest + pullPolicy: IfNotPresent + +# 服务端口 +service: + type: ClusterIP + port: 3002 + targetPort: 3002 + +# 命名空间(默认 edu-services,由 edu-platform chart 创建) +namespace: edu-services + +# 探针配置 +probes: + liveness: + path: /healthz + initialDelaySeconds: 15 + periodSeconds: 20 + timeoutSeconds: 3 + failureThreshold: 3 + readiness: + path: /readyz + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 3 + failureThreshold: 2 + +# 资源配额 +resources: + requests: + cpu: 250m + memory: 256Mi + limits: + cpu: 1000m + memory: 1Gi + +# 滚动更新策略 +strategy: + type: RollingUpdate + maxSurge: 1 + maxUnavailable: 0 + +# Prometheus 指标采集 +metrics: + enabled: true + port: 3002 + path: /metrics + +# 安全上下文 +securityContext: + runAsNonRoot: true + runAsUser: 1000 + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] + +# 服务级 ConfigMap(非敏感配置) +configMap: + enabled: true + data: + NODE_ENV: production + LOG_LEVEL: info + +# HPA +hpa: + enabled: true + minReplicas: 2 + maxReplicas: 10 + targetCPUUtilizationPercentage: 70 + targetMemoryUtilizationPercentage: 80 + +# 敏感配置(从 Secret 引用,Secret 由 edu-platform chart 或 ExternalSecrets 管理) +secretRefs: + - name: edu-platform-secret + keys: + - MYSQL_PASSWORD + - JWT_SECRET + - REDIS_PASSWORD diff --git a/infra/k8s/helm/data-ana/Chart.yaml b/infra/k8s/helm/data-ana/Chart.yaml new file mode 100644 index 0000000..eb37434 --- /dev/null +++ b/infra/k8s/helm/data-ana/Chart.yaml @@ -0,0 +1,11 @@ +apiVersion: v2 +name: data-ana +description: data-ana 服务级 Helm Chart +type: application +version: 0.1.0 +appVersion: "1.0.0" +keywords: + - edu + - data-ana +maintainers: + - name: edu-arch diff --git a/infra/k8s/helm/data-ana/templates/_helpers.tpl b/infra/k8s/helm/data-ana/templates/_helpers.tpl new file mode 100644 index 0000000..6111f10 --- /dev/null +++ b/infra/k8s/helm/data-ana/templates/_helpers.tpl @@ -0,0 +1,29 @@ +{{- define "data-ana.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{- define "data-ana.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{- define "data-ana.labels" -}} +helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} +{{ include "data-ana.selectorLabels" . }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +app.kubernetes.io/part-of: edu-platform +app.kubernetes.io/component: analytics +{{- end -}} + +{{- define "data-ana.selectorLabels" -}} +app.kubernetes.io/name: {{ include "data-ana.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} diff --git a/infra/k8s/helm/data-ana/templates/deployment.yaml b/infra/k8s/helm/data-ana/templates/deployment.yaml new file mode 100644 index 0000000..5ae99fb --- /dev/null +++ b/infra/k8s/helm/data-ana/templates/deployment.yaml @@ -0,0 +1,77 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "data-ana.name" . }} + namespace: {{ .Values.namespace }} + labels: + {{- include "data-ana.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "data-ana.selectorLabels" . | nindent 6 }} + strategy: + type: {{ .Values.strategy.type }} + rollingUpdate: + maxSurge: {{ .Values.strategy.maxSurge }} + maxUnavailable: {{ .Values.strategy.maxUnavailable }} + template: + metadata: + labels: + {{- include "data-ana.selectorLabels" . | nindent 8 }} + app.kubernetes.io/part-of: edu-platform + app.kubernetes.io/component: analytics + {{- if .Values.metrics.enabled }} + annotations: + prometheus.io/scrape: "true" + prometheus.io/port: {{ .Values.metrics.port | quote }} + prometheus.io/path: {{ .Values.metrics.path | quote }} + {{- end }} + spec: + containers: + - name: {{ include "data-ana.name" . }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.service.targetPort }} + protocol: TCP + livenessProbe: + httpGet: + path: {{ .Values.probes.liveness.path }} + port: http + initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }} + periodSeconds: {{ .Values.probes.liveness.periodSeconds }} + timeoutSeconds: {{ .Values.probes.liveness.timeoutSeconds }} + failureThreshold: {{ .Values.probes.liveness.failureThreshold }} + readinessProbe: + httpGet: + path: {{ .Values.probes.readiness.path }} + port: http + initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }} + periodSeconds: {{ .Values.probes.readiness.periodSeconds }} + timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }} + failureThreshold: {{ .Values.probes.readiness.failureThreshold }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + env: + {{- if .Values.configMap.enabled }} + {{- range $k, $v := .Values.configMap.data }} + - name: {{ $k }} + valueFrom: + configMapKeyRef: + name: {{ include "data-ana.name" $ }}-config + key: {{ $k }} + {{- end }} + {{- end }} + {{- range $secret := .Values.secretRefs }} + {{- range $key := $secret.keys }} + - name: {{ $key }} + valueFrom: + secretKeyRef: + name: {{ $secret.name }} + key: {{ $key }} + {{- end }} + {{- end }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} diff --git a/infra/k8s/helm/data-ana/templates/service.yaml b/infra/k8s/helm/data-ana/templates/service.yaml new file mode 100644 index 0000000..31ea09a --- /dev/null +++ b/infra/k8s/helm/data-ana/templates/service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "data-ana.name" . }} + namespace: {{ .Values.namespace }} + labels: + {{- include "data-ana.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + selector: + {{- include "data-ana.selectorLabels" . | nindent 4 }} + ports: + - name: http + port: {{ .Values.service.port }} + targetPort: http + protocol: TCP diff --git a/infra/k8s/helm/data-ana/values.yaml b/infra/k8s/helm/data-ana/values.yaml new file mode 100644 index 0000000..d484236 --- /dev/null +++ b/infra/k8s/helm/data-ana/values.yaml @@ -0,0 +1,85 @@ +# data-ana 服务默认值(数据分析 - 业务领域 D6) + +# 副本数(生产建议 ≥ 2) +replicaCount: 2 + +image: + repository: edu/data-ana + tag: latest # 生产请固定 tag,避免 latest + pullPolicy: IfNotPresent + +# 服务端口 +service: + type: ClusterIP + port: 3005 + targetPort: 3005 + +# 命名空间(默认 edu-services,由 edu-platform chart 创建) +namespace: edu-services + +# 探针配置 +probes: + liveness: + path: /healthz + initialDelaySeconds: 15 + periodSeconds: 20 + timeoutSeconds: 3 + failureThreshold: 3 + readiness: + path: /readyz + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 3 + failureThreshold: 2 + +# 资源配额 +resources: + requests: + cpu: 250m + memory: 256Mi + limits: + cpu: 1000m + memory: 1Gi + +# 滚动更新策略 +strategy: + type: RollingUpdate + maxSurge: 1 + maxUnavailable: 0 + +# Prometheus 指标采集 +metrics: + enabled: true + port: 3005 + path: /metrics + +# 安全上下文 +securityContext: + runAsNonRoot: true + runAsUser: 1000 + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] + +# 服务级 ConfigMap(非敏感配置) +configMap: + enabled: true + data: + NODE_ENV: production + LOG_LEVEL: info + +# HPA +hpa: + enabled: true + minReplicas: 2 + maxReplicas: 10 + targetCPUUtilizationPercentage: 70 + targetMemoryUtilizationPercentage: 80 + +# 敏感配置(从 Secret 引用,Secret 由 edu-platform chart 或 ExternalSecrets 管理) +secretRefs: + - name: edu-platform-secret + keys: + - MYSQL_PASSWORD + - JWT_SECRET + - REDIS_PASSWORD diff --git a/infra/k8s/helm/edu-platform/Chart.yaml b/infra/k8s/helm/edu-platform/Chart.yaml new file mode 100644 index 0000000..67357e1 --- /dev/null +++ b/infra/k8s/helm/edu-platform/Chart.yaml @@ -0,0 +1,12 @@ +apiVersion: v2 +name: edu-platform +description: Edu 平台级 Helm Chart(命名空间 / 全局 ConfigMap / Secret / Ingress / HPA 模板) +type: application +version: 0.1.0 +appVersion: "1.0.0" +keywords: + - edu + - platform + - infrastructure +maintainers: + - name: edu-arch diff --git a/infra/k8s/helm/edu-platform/templates/_helpers.tpl b/infra/k8s/helm/edu-platform/templates/_helpers.tpl new file mode 100644 index 0000000..c1ea50b --- /dev/null +++ b/infra/k8s/helm/edu-platform/templates/_helpers.tpl @@ -0,0 +1,42 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "edu-platform.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Fully qualified app name. +*/}} +{{- define "edu-platform.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Common labels. +*/}} +{{- define "edu-platform.labels" -}} +helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} +{{ include "edu-platform.selectorLabels" . }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- with .Values.global.labels }} +{{ toYaml . }} +{{- end -}} +{{- end -}} + +{{/* +Selector labels. +*/}} +{{- define "edu-platform.selectorLabels" -}} +app.kubernetes.io/name: {{ include "edu-platform.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} diff --git a/infra/k8s/helm/edu-platform/templates/configmap.yaml b/infra/k8s/helm/edu-platform/templates/configmap.yaml new file mode 100644 index 0000000..68c9adb --- /dev/null +++ b/infra/k8s/helm/edu-platform/templates/configmap.yaml @@ -0,0 +1,13 @@ +{{- if .Values.configMap.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ .Values.configMap.name }} + namespace: edu-system + labels: + {{- include "edu-platform.labels" . | nindent 4 }} +data: +{{- range $k, $v := .Values.configMap.data }} + {{ $k }}: {{ $v | quote }} +{{- end }} +{{- end }} diff --git a/infra/k8s/helm/edu-platform/templates/hpa.yaml b/infra/k8s/helm/edu-platform/templates/hpa.yaml new file mode 100644 index 0000000..7dbd8b4 --- /dev/null +++ b/infra/k8s/helm/edu-platform/templates/hpa.yaml @@ -0,0 +1,34 @@ +{{/* + 全局 HPA 模板示例(参考用)。 + 实际 HPA 应在各服务自身的 chart 中定义,以便针对服务特性调参。 + 本模板在 edu-platform 中默认不渲染(hpa.targetService 为空时跳过)。 +*/}} +{{- if and .Values.hpa.enabled .Values.hpa.targetService }} +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ .Values.hpa.targetService }}-hpa + namespace: edu-services + labels: + {{- include "edu-platform.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ .Values.hpa.targetService }} + minReplicas: {{ .Values.hpa.minReplicas }} + maxReplicas: {{ .Values.hpa.maxReplicas }} + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.hpa.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ .Values.hpa.targetMemoryUtilizationPercentage }} +{{- end }} diff --git a/infra/k8s/helm/edu-platform/templates/ingress.yaml b/infra/k8s/helm/edu-platform/templates/ingress.yaml new file mode 100644 index 0000000..0bbce9b --- /dev/null +++ b/infra/k8s/helm/edu-platform/templates/ingress.yaml @@ -0,0 +1,35 @@ +{{- if .Values.ingress.enabled }} +{{- range .Values.ingress.hosts }} +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ $.Release.Name }}-ingress + namespace: edu-ingress + labels: + {{- include "edu-platform.labels" $ | nindent 4 }} + {{- with $.Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + ingressClassName: {{ $.Values.ingress.className }} + {{- with $.Values.ingress.tls }} + tls: + {{- toYaml . | nindent 4 }} + {{- end }} + rules: + - host: {{ .host }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + pathType: {{ .pathType }} + backend: + service: + name: {{ .service }} + port: + number: {{ .port }} + {{- end }} +{{- end }} +{{- end }} diff --git a/infra/k8s/helm/edu-platform/templates/namespace.yaml b/infra/k8s/helm/edu-platform/templates/namespace.yaml new file mode 100644 index 0000000..79aef77 --- /dev/null +++ b/infra/k8s/helm/edu-platform/templates/namespace.yaml @@ -0,0 +1,10 @@ +{{- range .Values.namespaces }} +--- +apiVersion: v1 +kind: Namespace +metadata: + name: {{ .name }} + labels: + {{- include "edu-platform.labels" $ | nindent 4 }} + app.kubernetes.io/component: {{ .component }} +{{- end }} diff --git a/infra/k8s/helm/edu-platform/templates/secret.yaml b/infra/k8s/helm/edu-platform/templates/secret.yaml new file mode 100644 index 0000000..8204c21 --- /dev/null +++ b/infra/k8s/helm/edu-platform/templates/secret.yaml @@ -0,0 +1,26 @@ +{{- if .Values.secret.enabled }} +{{- /* + ⚠️ 生产环境警告:不要在 values.yaml 中硬编码真实密钥! + 推荐方案:使用 External Secrets Operator 对接 Vault / KMS / 云 KMS + 本模板仅作骨架;空字符串字段不会被渲染(避免覆盖已存在的 Secret)。 +*/ -}} +apiVersion: v1 +kind: Secret +metadata: + name: {{ .Values.secret.name }} + namespace: edu-system + labels: + {{- include "edu-platform.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": pre-install + "helm.sh/hook-delete-policy": before-hook-creation +type: Opaque +data: +{{- range $k, $v := .Values.secret.data }} +{{- if $v }} + {{ $k }}: {{ $v }} +{{- else }} + {{ $k }}: "" # 占位:部署时通过 --set 或 ExternalSecrets 注入 +{{- end }} +{{- end }} +{{- end }} diff --git a/infra/k8s/helm/edu-platform/values-dev.yaml b/infra/k8s/helm/edu-platform/values-dev.yaml new file mode 100644 index 0000000..fe761a5 --- /dev/null +++ b/infra/k8s/helm/edu-platform/values-dev.yaml @@ -0,0 +1,24 @@ +# 开发环境覆盖 +global: + labels: + app.kubernetes.io/part-of: edu-platform + app.kubernetes.io/environment: dev + +configMap: + data: + NODE_ENV: development + LOG_LEVEL: debug + +ingress: + enabled: true + hosts: + - host: edu-dev.local + paths: + - path: /api + pathType: Prefix + service: api-gateway + port: 8080 + tls: [] # 开发环境不强制 TLS + +hpa: + enabled: false # 开发环境单副本即可 diff --git a/infra/k8s/helm/edu-platform/values-prod.yaml b/infra/k8s/helm/edu-platform/values-prod.yaml new file mode 100644 index 0000000..4758707 --- /dev/null +++ b/infra/k8s/helm/edu-platform/values-prod.yaml @@ -0,0 +1,35 @@ +# 生产环境覆盖 +global: + labels: + app.kubernetes.io/part-of: edu-platform + app.kubernetes.io/environment: prod + imagePullSecrets: + - name: registry-credentials + +configMap: + data: + NODE_ENV: production + LOG_LEVEL: warn + +ingress: + hosts: + - host: edu.example.com + paths: + - path: /api + pathType: Prefix + service: api-gateway + port: 8080 + - path: /auth + pathType: Prefix + service: api-gateway + port: 8080 + tls: + - secretName: edu-tls + hosts: + - edu.example.com + +hpa: + minReplicas: 3 + maxReplicas: 20 + targetCPUUtilizationPercentage: 60 + targetMemoryUtilizationPercentage: 70 diff --git a/infra/k8s/helm/edu-platform/values-staging.yaml b/infra/k8s/helm/edu-platform/values-staging.yaml new file mode 100644 index 0000000..b9f819e --- /dev/null +++ b/infra/k8s/helm/edu-platform/values-staging.yaml @@ -0,0 +1,23 @@ +# 预发布环境覆盖 +global: + labels: + app.kubernetes.io/part-of: edu-platform + app.kubernetes.io/environment: staging + +configMap: + data: + NODE_ENV: staging + LOG_LEVEL: info + +ingress: + hosts: + - host: edu-staging.example.com + paths: + - path: /api + pathType: Prefix + service: api-gateway + port: 8080 + +hpa: + minReplicas: 2 + maxReplicas: 5 diff --git a/infra/k8s/helm/edu-platform/values.yaml b/infra/k8s/helm/edu-platform/values.yaml new file mode 100644 index 0000000..cce506f --- /dev/null +++ b/infra/k8s/helm/edu-platform/values.yaml @@ -0,0 +1,72 @@ +# Edu 平台全局默认值 +# 环境覆盖文件:values-dev.yaml / values-staging.yaml / values-prod.yaml + +# 全局标签(自动注入到所有资源) +global: + labels: + app.kubernetes.io/part-of: edu-platform + # 镜像拉取凭证(生产建议使用 Secret + serviceAccount) + imagePullSecrets: [] + +# 命名空间规划 +namespaces: + - name: edu-system + component: system + - name: edu-services + component: services + - name: edu-monitoring + component: monitoring + - name: edu-ingress + component: ingress + +# 全局 ConfigMap(非敏感配置) +configMap: + enabled: true + name: edu-platform-config + data: + NODE_ENV: production + LOG_LEVEL: info + MYSQL_HOST: mysql.edu-system.svc.cluster.local + REDIS_HOST: redis.edu-system.svc.cluster.local + KAFKA_BROKERS: kafka.edu-system.svc.cluster.local:9092 + +# 全局 Secret(仅骨架;生产请使用 External Secrets Operator 对接 Vault/KMS) +secret: + enabled: true + name: edu-platform-secret + # 真实部署时通过 --set secret.data.MYSQL_PASSWORD= 注入 + # 或通过 externalSecrets 引用 + data: + MYSQL_PASSWORD: "" + JWT_SECRET: "" + REDIS_PASSWORD: "" + +# 全局 Ingress +ingress: + enabled: true + className: nginx + annotations: + nginx.ingress.kubernetes.io/ssl-redirect: "true" + hosts: + - host: edu.example.com + paths: + - path: /api + pathType: Prefix + service: api-gateway + port: 8080 + - path: /auth + pathType: Prefix + service: api-gateway + port: 8080 + tls: + - secretName: edu-tls + hosts: + - edu.example.com + +# HPA 全局默认策略(各服务可在自身 chart 中覆盖) +hpa: + enabled: true + minReplicas: 2 + maxReplicas: 10 + targetCPUUtilizationPercentage: 70 + targetMemoryUtilizationPercentage: 80 diff --git a/infra/k8s/helm/iam/Chart.yaml b/infra/k8s/helm/iam/Chart.yaml new file mode 100644 index 0000000..449aeab --- /dev/null +++ b/infra/k8s/helm/iam/Chart.yaml @@ -0,0 +1,11 @@ +apiVersion: v2 +name: iam +description: iam 服务级 Helm Chart +type: application +version: 0.1.0 +appVersion: "1.0.0" +keywords: + - edu + - iam +maintainers: + - name: edu-arch diff --git a/infra/k8s/helm/iam/templates/_helpers.tpl b/infra/k8s/helm/iam/templates/_helpers.tpl new file mode 100644 index 0000000..09f5cd9 --- /dev/null +++ b/infra/k8s/helm/iam/templates/_helpers.tpl @@ -0,0 +1,29 @@ +{{- define "iam.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{- define "iam.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{- define "iam.labels" -}} +helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} +{{ include "iam.selectorLabels" . }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +app.kubernetes.io/part-of: edu-platform +app.kubernetes.io/component: identity +{{- end -}} + +{{- define "iam.selectorLabels" -}} +app.kubernetes.io/name: {{ include "iam.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} diff --git a/infra/k8s/helm/iam/templates/deployment.yaml b/infra/k8s/helm/iam/templates/deployment.yaml new file mode 100644 index 0000000..a5d63f7 --- /dev/null +++ b/infra/k8s/helm/iam/templates/deployment.yaml @@ -0,0 +1,77 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "iam.name" . }} + namespace: {{ .Values.namespace }} + labels: + {{- include "iam.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "iam.selectorLabels" . | nindent 6 }} + strategy: + type: {{ .Values.strategy.type }} + rollingUpdate: + maxSurge: {{ .Values.strategy.maxSurge }} + maxUnavailable: {{ .Values.strategy.maxUnavailable }} + template: + metadata: + labels: + {{- include "iam.selectorLabels" . | nindent 8 }} + app.kubernetes.io/part-of: edu-platform + app.kubernetes.io/component: identity + {{- if .Values.metrics.enabled }} + annotations: + prometheus.io/scrape: "true" + prometheus.io/port: {{ .Values.metrics.port | quote }} + prometheus.io/path: {{ .Values.metrics.path | quote }} + {{- end }} + spec: + containers: + - name: {{ include "iam.name" . }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.service.targetPort }} + protocol: TCP + livenessProbe: + httpGet: + path: {{ .Values.probes.liveness.path }} + port: http + initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }} + periodSeconds: {{ .Values.probes.liveness.periodSeconds }} + timeoutSeconds: {{ .Values.probes.liveness.timeoutSeconds }} + failureThreshold: {{ .Values.probes.liveness.failureThreshold }} + readinessProbe: + httpGet: + path: {{ .Values.probes.readiness.path }} + port: http + initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }} + periodSeconds: {{ .Values.probes.readiness.periodSeconds }} + timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }} + failureThreshold: {{ .Values.probes.readiness.failureThreshold }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + env: + {{- if .Values.configMap.enabled }} + {{- range $k, $v := .Values.configMap.data }} + - name: {{ $k }} + valueFrom: + configMapKeyRef: + name: {{ include "iam.name" $ }}-config + key: {{ $k }} + {{- end }} + {{- end }} + {{- range $secret := .Values.secretRefs }} + {{- range $key := $secret.keys }} + - name: {{ $key }} + valueFrom: + secretKeyRef: + name: {{ $secret.name }} + key: {{ $key }} + {{- end }} + {{- end }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} diff --git a/infra/k8s/helm/iam/templates/service.yaml b/infra/k8s/helm/iam/templates/service.yaml new file mode 100644 index 0000000..844e034 --- /dev/null +++ b/infra/k8s/helm/iam/templates/service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "iam.name" . }} + namespace: {{ .Values.namespace }} + labels: + {{- include "iam.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + selector: + {{- include "iam.selectorLabels" . | nindent 4 }} + ports: + - name: http + port: {{ .Values.service.port }} + targetPort: http + protocol: TCP diff --git a/infra/k8s/helm/iam/values.yaml b/infra/k8s/helm/iam/values.yaml new file mode 100644 index 0000000..16da968 --- /dev/null +++ b/infra/k8s/helm/iam/values.yaml @@ -0,0 +1,85 @@ +# iam 服务默认值(身份认证 - 业务领域 D1) + +# 副本数(生产建议 ≥ 2) +replicaCount: 2 + +image: + repository: edu/iam + tag: latest # 生产请固定 tag,避免 latest + pullPolicy: IfNotPresent + +# 服务端口 +service: + type: ClusterIP + port: 3001 + targetPort: 3001 + +# 命名空间(默认 edu-services,由 edu-platform chart 创建) +namespace: edu-services + +# 探针配置 +probes: + liveness: + path: /healthz + initialDelaySeconds: 15 + periodSeconds: 20 + timeoutSeconds: 3 + failureThreshold: 3 + readiness: + path: /readyz + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 3 + failureThreshold: 2 + +# 资源配额 +resources: + requests: + cpu: 250m + memory: 256Mi + limits: + cpu: 1000m + memory: 1Gi + +# 滚动更新策略 +strategy: + type: RollingUpdate + maxSurge: 1 + maxUnavailable: 0 + +# Prometheus 指标采集 +metrics: + enabled: true + port: 3001 + path: /metrics + +# 安全上下文 +securityContext: + runAsNonRoot: true + runAsUser: 1000 + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] + +# 服务级 ConfigMap(非敏感配置) +configMap: + enabled: true + data: + NODE_ENV: production + LOG_LEVEL: info + +# HPA +hpa: + enabled: true + minReplicas: 2 + maxReplicas: 10 + targetCPUUtilizationPercentage: 70 + targetMemoryUtilizationPercentage: 80 + +# 敏感配置(从 Secret 引用,Secret 由 edu-platform chart 或 ExternalSecrets 管理) +secretRefs: + - name: edu-platform-secret + keys: + - MYSQL_PASSWORD + - JWT_SECRET + - REDIS_PASSWORD diff --git a/infra/k8s/helm/msg/Chart.yaml b/infra/k8s/helm/msg/Chart.yaml new file mode 100644 index 0000000..ef08aae --- /dev/null +++ b/infra/k8s/helm/msg/Chart.yaml @@ -0,0 +1,11 @@ +apiVersion: v2 +name: msg +description: msg 服务级 Helm Chart +type: application +version: 0.1.0 +appVersion: "1.0.0" +keywords: + - edu + - msg +maintainers: + - name: edu-arch diff --git a/infra/k8s/helm/msg/templates/_helpers.tpl b/infra/k8s/helm/msg/templates/_helpers.tpl new file mode 100644 index 0000000..f2081b3 --- /dev/null +++ b/infra/k8s/helm/msg/templates/_helpers.tpl @@ -0,0 +1,29 @@ +{{- define "msg.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{- define "msg.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{- define "msg.labels" -}} +helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} +{{ include "msg.selectorLabels" . }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +app.kubernetes.io/part-of: edu-platform +app.kubernetes.io/component: messaging +{{- end -}} + +{{- define "msg.selectorLabels" -}} +app.kubernetes.io/name: {{ include "msg.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} diff --git a/infra/k8s/helm/msg/templates/deployment.yaml b/infra/k8s/helm/msg/templates/deployment.yaml new file mode 100644 index 0000000..eaf246a --- /dev/null +++ b/infra/k8s/helm/msg/templates/deployment.yaml @@ -0,0 +1,77 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "msg.name" . }} + namespace: {{ .Values.namespace }} + labels: + {{- include "msg.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + {{- include "msg.selectorLabels" . | nindent 6 }} + strategy: + type: {{ .Values.strategy.type }} + rollingUpdate: + maxSurge: {{ .Values.strategy.maxSurge }} + maxUnavailable: {{ .Values.strategy.maxUnavailable }} + template: + metadata: + labels: + {{- include "msg.selectorLabels" . | nindent 8 }} + app.kubernetes.io/part-of: edu-platform + app.kubernetes.io/component: messaging + {{- if .Values.metrics.enabled }} + annotations: + prometheus.io/scrape: "true" + prometheus.io/port: {{ .Values.metrics.port | quote }} + prometheus.io/path: {{ .Values.metrics.path | quote }} + {{- end }} + spec: + containers: + - name: {{ include "msg.name" . }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.service.targetPort }} + protocol: TCP + livenessProbe: + httpGet: + path: {{ .Values.probes.liveness.path }} + port: http + initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }} + periodSeconds: {{ .Values.probes.liveness.periodSeconds }} + timeoutSeconds: {{ .Values.probes.liveness.timeoutSeconds }} + failureThreshold: {{ .Values.probes.liveness.failureThreshold }} + readinessProbe: + httpGet: + path: {{ .Values.probes.readiness.path }} + port: http + initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }} + periodSeconds: {{ .Values.probes.readiness.periodSeconds }} + timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }} + failureThreshold: {{ .Values.probes.readiness.failureThreshold }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + env: + {{- if .Values.configMap.enabled }} + {{- range $k, $v := .Values.configMap.data }} + - name: {{ $k }} + valueFrom: + configMapKeyRef: + name: {{ include "msg.name" $ }}-config + key: {{ $k }} + {{- end }} + {{- end }} + {{- range $secret := .Values.secretRefs }} + {{- range $key := $secret.keys }} + - name: {{ $key }} + valueFrom: + secretKeyRef: + name: {{ $secret.name }} + key: {{ $key }} + {{- end }} + {{- end }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} diff --git a/infra/k8s/helm/msg/templates/service.yaml b/infra/k8s/helm/msg/templates/service.yaml new file mode 100644 index 0000000..6dfbf89 --- /dev/null +++ b/infra/k8s/helm/msg/templates/service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "msg.name" . }} + namespace: {{ .Values.namespace }} + labels: + {{- include "msg.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + selector: + {{- include "msg.selectorLabels" . | nindent 4 }} + ports: + - name: http + port: {{ .Values.service.port }} + targetPort: http + protocol: TCP diff --git a/infra/k8s/helm/msg/values.yaml b/infra/k8s/helm/msg/values.yaml new file mode 100644 index 0000000..3606312 --- /dev/null +++ b/infra/k8s/helm/msg/values.yaml @@ -0,0 +1,85 @@ +# msg 服务默认值(消息通知 - 业务领域 D5) + +# 副本数(生产建议 ≥ 2) +replicaCount: 2 + +image: + repository: edu/msg + tag: latest # 生产请固定 tag,避免 latest + pullPolicy: IfNotPresent + +# 服务端口 +service: + type: ClusterIP + port: 3004 + targetPort: 3004 + +# 命名空间(默认 edu-services,由 edu-platform chart 创建) +namespace: edu-services + +# 探针配置 +probes: + liveness: + path: /healthz + initialDelaySeconds: 15 + periodSeconds: 20 + timeoutSeconds: 3 + failureThreshold: 3 + readiness: + path: /readyz + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 3 + failureThreshold: 2 + +# 资源配额 +resources: + requests: + cpu: 250m + memory: 256Mi + limits: + cpu: 1000m + memory: 1Gi + +# 滚动更新策略 +strategy: + type: RollingUpdate + maxSurge: 1 + maxUnavailable: 0 + +# Prometheus 指标采集 +metrics: + enabled: true + port: 3004 + path: /metrics + +# 安全上下文 +securityContext: + runAsNonRoot: true + runAsUser: 1000 + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] + +# 服务级 ConfigMap(非敏感配置) +configMap: + enabled: true + data: + NODE_ENV: production + LOG_LEVEL: info + +# HPA +hpa: + enabled: true + minReplicas: 2 + maxReplicas: 10 + targetCPUUtilizationPercentage: 70 + targetMemoryUtilizationPercentage: 80 + +# 敏感配置(从 Secret 引用,Secret 由 edu-platform chart 或 ExternalSecrets 管理) +secretRefs: + - name: edu-platform-secret + keys: + - MYSQL_PASSWORD + - JWT_SECRET + - REDIS_PASSWORD