238 lines
8.3 KiB
Markdown
238 lines
8.3 KiB
Markdown
# TopFans K8s / Helm 部署手册 (Phase 1)
|
|
|
|
> Phase 1: 单 namespace `topfans` 合并部署, 10 个 Go 服务 + gateway 共享一套基础设施,
|
|
> Postgres / Redis 走阿里云 RDS / ElastiCache ExternalName。
|
|
>
|
|
> 设计文档: [`../docs/superpowers/specs/2026-06-08-docker-to-k8s-migration-design.md`](../docs/superpowers/specs/2026-06-08-docker-to-k8s-migration-design.md)
|
|
>
|
|
> ⚠️ Phase 2 (按组隔离) 不在本手册范围, 见设计文档第十一章。
|
|
|
|
## 目录结构
|
|
|
|
```
|
|
k8s/
|
|
├── README.md # 本文件
|
|
├── deploy.sh # Helm / K8s 部署脚本 (主入口)
|
|
├── helm/
|
|
│ └── topfans/ # 单个 chart, 覆盖整个 Phase 1
|
|
│ ├── Chart.yaml
|
|
│ ├── values.yaml # 默认值 (CI/Dev 用)
|
|
│ ├── values-prod.example.yaml # 模板 (空值, 部署时 cp 后填)
|
|
│ └── templates/
|
|
│ ├── _helpers.tpl
|
|
│ ├── 00-namespace.yaml
|
|
│ ├── gateway/ ... # 13 个服务各自子目录
|
|
│ ├── hpa/ ... # 3 个 HPA
|
|
│ ├── secrets/ ... # 4 个 Secret
|
|
│ ├── external-db/ ... # postgres / redis ExternalName
|
|
│ ├── ingress.yaml
|
|
│ ├── pg-sequence-sync.yaml # 切流量前必跑的 hook Job
|
|
│ ├── oss-cors-init/job.yaml
|
|
│ └── future-services/ ... # 4 个未来服务 .gitkeep
|
|
└── jobs/
|
|
└── pg-sequence-sync/ # 独立手动跑版本 (备份用)
|
|
├── script.sh
|
|
└── job-template.yaml
|
|
```
|
|
|
|
## 首次部署 (Checklist)
|
|
|
|
按顺序完成:
|
|
|
|
### 1. 集群与基础设施
|
|
|
|
- [ ] 阿里云 ACK 测试集群创建 (按设计文档 §六 Step 1, 推荐)
|
|
- [ ] 装 `nginx-ingress-controller` (Helm, 集群级别)
|
|
- [ ] 装 `cert-manager` (Helm, 集群级别)
|
|
- [ ] 创建 ClusterIssuer `letsencrypt-prod` (用 DNS-01 校验)
|
|
- [ ] 创建 namespace `topfans` (helm install 时自动 create, 也可提前 `kubectl create ns topfans`)
|
|
- [ ] 创建阿里云 RDS (PostgreSQL) + 用阿里云 DTS 把 VM 的数据迁过去
|
|
- [ ] 创建阿里云 ElastiCache (Redis), 同步数据 (如果有)
|
|
- [ ] **PG 端连通验证**: `psql -h <rds-endpoint>` 确认能连上, schemata 在
|
|
|
|
### 2. CI 镜像仓库
|
|
|
|
- [ ] 阿里云 ACR 创建 namespace `topfans`, registry 是 `registry.cn-shanghai.aliyuncs.com`
|
|
- [ ] CI (GitHub Actions / 阿里云云效) 配 ACR push 凭据, 跑通 `gateway:v1.0.0` 一个 image
|
|
- [ ] (`Dockerfile.services` 多阶段构建已 OK, 无需改)
|
|
|
|
### 3. 真值 values-prod.yaml
|
|
|
|
**推荐用交互脚本生成** (避免在 example 里写假值被误部署):
|
|
|
|
```bash
|
|
./k8s/scripts/fill-values-prod.sh
|
|
# 交互式提示输入 DB / Redis / JWT / OSS / Dify / OpenAI / SMS 真值
|
|
# 写完自动 chmod 600 + 检查 .gitignore + 用 gitleaks/detect-secrets 扫描 (如已装)
|
|
|
|
# CI 模式: 全用环境变量
|
|
./k8s/scripts/fill-values-prod.sh --non-interactive
|
|
|
|
# 写完用 sops 加密 (加密文件可入 git)
|
|
./k8s/scripts/fill-values-prod.sh --sops
|
|
|
|
# 只看不写
|
|
./k8s/scripts/fill-values-prod.sh --dry-run
|
|
```
|
|
|
|
手写版 (不推荐, 易出拼错):
|
|
|
|
```bash
|
|
cd k8s/helm/topfans
|
|
cp values-prod.example.yaml values-prod.yaml
|
|
chmod 600 values-prod.yaml
|
|
$EDITOR values-prod.yaml
|
|
# 重点字段:
|
|
# global.dbExternalEndpoint: rm-xxxxxx.mysql.rds.aliyuncs.com
|
|
# global.redisExternalEndpoint: r-xxxxxx.redis.rds.aliyuncs.com
|
|
# services.<service>.image.tag: (CI 注入, 或先用 latest)
|
|
# secrets.* : DB / JWT / OSS / AI / SMS 真值
|
|
```
|
|
|
|
⚠️ **`values-prod.yaml` 不进 git** (`.gitignore` 已加, 脚本会二次确认)。
|
|
|
|
### 4. 首次部署
|
|
|
|
```bash
|
|
cd /path/to/repo
|
|
./k8s/deploy.sh install
|
|
```
|
|
|
|
会自动按顺序:
|
|
1. `helm.sh/hook pre-install` 创建 namespace
|
|
2. `helm.sh/hook pre-install` 跑 PG sequence sync (如果 `pgSequenceSync.enabled=true`)
|
|
3. `helm.sh/hook pre-install` 跑 oss-cors-init (Job 跑完即退)
|
|
4. 创建所有 Service / Deployment / Secret / ConfigMap / HPA
|
|
5. `helm.sh/hook pre-install` (其实是 `Install` 阶段) 跑完等 Pod Ready
|
|
|
|
### 5. Ingress 域名与 TLS
|
|
|
|
```bash
|
|
# 改 ingress 内的 host
|
|
$EDITOR k8s/helm/topfans/values-prod.yaml
|
|
# ingress.hosts[0].host: api.example.com
|
|
# ingress.tls[0].hosts[0]: api.example.com
|
|
|
|
# 解析到 nginx-ingress-controller 的 external IP
|
|
kubectl get svc -n ingress-nginx
|
|
# api.example.com A 记录 → <EXTERNAL-IP>
|
|
|
|
./k8s/deploy.sh upgrade --reuse-values
|
|
```
|
|
|
|
## 升级流程 (日常)
|
|
|
|
```bash
|
|
# 1. CI 已经把镜像推到 ACR:
|
|
# registry.cn-shanghai.aliyuncs.com/topfans/gateway:v1.0.1
|
|
# registry.cn-shanghai.aliyuncs.com/topfans/userservice:v1.0.1
|
|
# ... (13 个 image 都标 v1.0.1)
|
|
|
|
# 2. 把所有 service 的 image tag 改成新版本:
|
|
./k8s/deploy.sh upgrade v1.0.1
|
|
|
|
# 实质执行:
|
|
# helm upgrade topfans ./helm/topfans \
|
|
# -f values-prod.yaml \
|
|
# --set global.image.tag=v1.0.1 \
|
|
# --wait --timeout 10m
|
|
```
|
|
|
|
`helm upgrade --wait` 会按 readinessProbe 等到所有 Pod Ready 才返回,
|
|
失败的话 Exit code 非 0, deploy.sh 提示回滚。
|
|
|
|
## 回滚
|
|
|
|
```bash
|
|
# 看历史
|
|
./k8s/deploy.sh history
|
|
# (helm history 通过 deploy.sh status 子命令也看得到)
|
|
|
|
# 回滚到上一版
|
|
./k8s/deploy.sh rollback
|
|
|
|
# 回滚到指定 revision
|
|
./k8s/deploy.sh rollback 3
|
|
```
|
|
|
|
## PostgreSQL 序列同步 (Hard Blocker)
|
|
|
|
> **CLAUDE.md 强制规范 + 设计文档 §10.3 标记为 Hard Blocker**
|
|
> 任何手动 INSERT id 都必须配套 setval; 切流量前必跑。
|
|
|
|
```bash
|
|
./k8s/deploy.sh sync-pg # 同步 + 验证
|
|
./k8s/deploy.sh sync-pg --status # 只验证
|
|
```
|
|
|
|
挂在 Helm 上的版本 (设计文档 §六 Step 4 提到的方案):
|
|
- `helm install / upgrade` 自动跑 `templates/pg-sequence-sync.yaml` (helm hook, weight=-8)
|
|
- 失败 abort deployment
|
|
|
|
**何时必须手动跑** (用 `deploy.sh sync-pg`):
|
|
1. 首次切流量前
|
|
2. 任何 backfill SQL (含手动 INSERT) 后
|
|
3. 数据迁移测试期间, 反复切换期间
|
|
|
|
## Secret 管理 (最低要求)
|
|
|
|
真值 **必须** 通过以下任一方式入 K8s,**严禁**把 values-prod.yaml 入 git:
|
|
|
|
| 方式 | 说明 | 适用 |
|
|
|---|---|---|
|
|
| **kubectl create secret** | 手 kubectl create secret 加 Secret, helm 不管 | 1-2 人小团队 |
|
|
| **CI/CD 注入** | CI 流水线 kubectl apply / helm install --set 注入 | 推荐 |
|
|
| **SOPS + Git** | values-prod.yaml sops -e 加密后入 git, CI decrypt | 多环境共享 |
|
|
| **External Secrets Operator** | 接阿里云 KMS, 动态拉真值 | Phase 2+ |
|
|
|
|
当前 **Phase 1 默认用 kubectl create secret** (最快上手), 后续切 SOPS。
|
|
|
|
## 监控 / 日志 (设计文档 §六 Step 6, 可选)
|
|
|
|
不阻塞 Phase 1 上线, 上线后补:
|
|
- Prometheus Operator (集群级)
|
|
- Grafana dashboard (按 dubbo / DB / Redis 分类)
|
|
- Loki + Promtail (日志聚合)
|
|
- 阿里云 ARMS (一键接入, 推荐)
|
|
|
|
## 常见问题
|
|
|
|
### Q: helm install 报 `duplicate key on secret` / `ServerTimeout`
|
|
|
|
**A**: 上一次 helm install 没清干净。`helm uninstall topfans` 再重试。
|
|
|
|
### Q: PG 序列同步失败, 报 `setval: sequence_name not found`
|
|
|
|
**A**: 说明该 BIGSERIAL 表要么不存在, 要么 sequence 名拼错, 可能表没建好。
|
|
连 RDS 验证: `\d+ assets` 看最下方的 `Sequence: assets_id_seq` 是否对得上。
|
|
|
|
### Q: 容器启动后卡在 Init 或 CrashLoopBackOff
|
|
|
|
**A**: 看日志: `kubectl logs -n topfans <pod> --previous`
|
|
常见:
|
|
- DB 连接不上: 检查 `secrets/db/db.password` 是否对, RDS 安全组是否放行 K8s 节点 CIDR
|
|
- OSS 配置缺: `oss-credentials` Secret 没建, helm uninstall + 重装
|
|
|
|
### Q: HPA 不工作
|
|
|
|
**A**: `kubectl get hpa -n topfans` 看是不是 `Unknown`, 通常是没有 metrics-server。
|
|
Phase 1 实施时安装 metrics-server (Helm, 一般 ingress-nginx chart 一起装)。
|
|
|
|
### Q: 各服务的 healthcheck 通不过
|
|
|
|
**A**: 各服务的 healthcheck path 不一致, 见 `values.yaml.services.<svc>.healthPath`:
|
|
- 大多数用 `/health`
|
|
- `statisticservice` / `notificationservice` 用 `/healthz`
|
|
- `moderationservice` 用 `/` (历史原因, 不修, 见设计文档 §10.4)
|
|
|
|
---
|
|
|
|
## 参考
|
|
|
|
- 设计文档: `../docs/superpowers/specs/2026-06-08-docker-to-k8s-migration-design.md`
|
|
- Helm 官方文档: https://helm.sh/docs/
|
|
- 阿里云 ACK: https://help.aliyun.com/product/85222.html
|
|
- 阿里云 ACR: https://help.aliyun.com/product/60716.html
|
|
|
|
最后更新: 2026-07-06 (Phase 1 首版)
|