topfans/k8s/README.md
2026-07-06 13:26:46 +08:00

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 首版)