Files
dsh_shenxian/docs/k8s-deployment.md
admin 68c0a320ed refactor(k8s): 删除 K8s 命名的死接口 LivePod;修正 K8s 文档横幅重复
1) src/supervisor/spawner.ts:LivePod —— 注释此前已中性化,但**类型名来自 K8s 的
   Pod**;全仓只有声明那一处出现(0 引用 = 死代码)⇒ 整块删除。属 cf8b7b1 的漏项。

2) docs/k8s-deploy.md · docs/k8s-deployment.md:cf8b7b1 里那行状态横幅被**重复
   插入 2 次并挤在同一行**(生成脚本当时不幂等,每跑一次多插一条)⇒ 收敛为 1 条。
   脚本已补「H1 后已有横幅就不再插」的负向断言 + 一条把重复收敛成 1 条的自愈规则。

验证:tsc --noEmit exit 0;npm test 36 测试 / 35 通过 / 0 失败 / 1 跳过;
LivePod 全仓(含 lib/)0 命中;两份文档横幅各 1 条。
2026-09-15 06:51:48 +08:00

167 lines
6.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# K8s 部署教程(模式 B)— DSH 服务端登录插件
> ⚠️ **2026-09-15:模式 B 的 K8s 后端代码已从本仓下线**(`k8s-spawner` / `leader` / `reconcile` / `k8s-user-fs` / `tcp-bridge` / `web/file-service` 六个模块 + 2 个测试 + 1 个脚本)。备份:`D:\github\_dsh_shenxian_K8s后端备份_20260915\`。本文按「**历史设计稿 + 未来可选路线**」保留;文中的 `dshs file-service` / `dshs tcp-bridge` 两条子命令**现已不存在**,照着敲会 `command not found`。
> 🧭 [← 返回 README](../README.md) · 卡住了:[踩坑记录](k8s-deploy.md) · 模式 A 教程:[deployment](deployment.md)
> 把 `dshs` 部署成**多机 HA、每用户独立 Pod** 的形态。本文是**可复制的分步部署教程**;
> 实机逐条踩坑与根因排查见 [k8s-deploy.md](k8s-deploy.md)。
>
> 实测环境:阿里云 ACK 智能托管(华东2)+ CNFS(NAS) + CloudNativePG + 私有 ACR。
---
## 0. 前置条件
- 一个 **ACK 智能托管集群**(网络插件 DataPath V2 / 勾选 NetworkPolicy;节点 ≥ 3 或启用自动扩容)。
- 一个 **NAS 文件系统**,并在集群里建 **CNFS**(控制台「容器网络文件系统」,名字记为 `nas`)。
- 一个 **私有 ACR 仓库**(本文用 `registry.example.com/dsh` 作占位)。
- 一个域名(本文用 `dsh.example.com` 作占位)+ 通配 TLS 证书(cert-manager 或 LB 证书)。
已安装:`kubectl`、`cnpg` operator(`kubectl apply --server-side -f cnpg.yaml`)。
---
## 1. 镜像
两种方式二选一:
- **CI(推荐)**:推代码到 master,GitHub Actions 自动 build + Trivy + push 两个镜像到 ACR。
- **手动**:
```sh
docker build -t <acr>/dshs:0.2.0 .
docker build -t <acr>/dsh:0.1.1-rc.2 -f Dockerfile.dsh .
docker push <acr>/dshs:0.2.0
docker push <acr>/dsh:0.1.1-rc.2
```
---
## 2. Namespace + Postgres
```sh
kubectl apply -f deploy/00-namespace.yaml
kubectl apply -f deploy/01-dsh-pg.yaml
# 等 healthy:
kubectl -n dsh wait --for=condition=Ready cluster/dsh-pg --timeout=600s
```
读连接串(后面建 `dsh-pg` secret 用):
```sh
kubectl -n dsh get secret dsh-pg-app -o jsonpath='{.data.uri}' | base64 -d
# postgresql://dsh:<password>@dsh-pg-rw.dsh:5432/dsh
```
---
## 3. Secrets(含动态值,不进 YAML)
```sh
# ① ACR 拉镜像凭证(生成 Pod 拉私有镜像用)
kubectl -n dsh create secret docker-registry dsh-acr-pull \
--docker-server=registry.example.com \
--docker-username='<ACR_USERNAME>' --docker-password='<ACR_PASSWORD>'
# ② 共享加密密钥(迁移时填旧 dataRoot/secret.key 内容,否则已加密的 API key 解不开)
kubectl -n dsh create secret generic dsh-secret --from-literal=key='<32字节hex>'
# ③ Postgres DSN(用 §2 读到的 uri)
kubectl -n dsh create secret generic dsh-pg --from-literal=url='postgresql://dsh:[email protected]:5432/dsh'
```
---
## 4. RBAC + 控制面
```sh
kubectl apply -f deploy/03-rbac.yaml # SA + Role(含 leases + list/watch,leader election 需要)
kubectl apply -f deploy/02-control-plane.yaml
kubectl -n dsh rollout status deploy/dsh-orchestrator --timeout=300s
```
初始化管理员:
```sh
kubectl -n dsh exec deploy/dsh-orchestrator -- node lib/cli.js bootstrap-admin --username admin --password '<强密码>'
```
> 控制面 3 副本,只有 leader 跑 reconcile(Lease 选主)。`deploy/02-control-plane.yaml` 里把
> `DSHS_BASE_DOMAIN` / `DSHS_COOKIE_DOMAIN` / 镜像地址改成你自己的。
---
## 5. 存储:NAS(CNFS) + PVC + bootstrap + PSA(严格时序)
> ⚠️ **顺序不能乱**:先建 PVC → 特权 bootstrap `chmod 1777` → 最后打 PSA restricted。先打 PSA,
> 非 root 的 initContainer 就写不了 PVC 根,用户目录永远建不出来。
```sh
# ① StorageClass(引用控制台建好的 CNFS `nas`)
kubectl apply -f deploy/05-storage.yaml
# ② 每用户共享 RWX PVC
kubectl apply -f deploy/04-pvc.yaml
kubectl -n dsh wait --for=jsonpath='{.status.phase}=Bound' pvc/dsh-users --timeout=300s
# ③ 特权 bootstrap:PVC 根 chmod 1777(world-writable + sticky)
kubectl apply -f deploy/08-bootstrap.yaml
kubectl -n dsh wait --for=condition=complete job/dsh-users-bootstrap --timeout=180s
# ④ 打 PSA restricted(放在最后)
kubectl apply -f deploy/07-psa.yaml
# ⑤ 配额(防单用户耗尽集群)
kubectl apply -f deploy/06-quota.yaml
```
---
## 6. 入口(域名 + TLS)
- 暴露控制面:给 `dsh-orchestrator` 加一个 **LoadBalancer Service**(`port 80 → targetPort 3080`),
或用 **Ingress**(Traefik / nginx-ingress)统一入口。
- DNS:把 `dsh.example.com` 与 `*.dsh.example.com` 解析到 LB 的 IP(或 Ingress 域名)。
- TLS:cert-manager 签通配证书(DNS-01),或直接用 LB/ALB 的证书。
- ⚠️ 域名经阿里云公网 SLB/ALB 暴露时需先做 **ICP 备案**,否则 80/443 会被备案校验拦(403)。
LoadBalancer Service 参考:
```yaml
apiVersion: v1
kind: Service
metadata: { name: dsh-orchestrator-lb, namespace: dsh }
spec:
type: LoadBalancer
selector: { app: dsh-orchestrator }
ports: [{ port: 80, targetPort: 3080 }]
```
---
## 7. 验收清单
1. `kubectl -n dsh get pods`:`dsh-orchestrator` 3/3、`dsh-pg-1` 1/1、`dsh-files-*`/`dsh-*`(按需出现)。
2. leader 单活:`kubectl -n dsh get lease dsh-orchestrator` holder 非空、`leaseTransitions` 随接管递增。
3. 域名全链路:注册 → 管理员审核 → 登录 → 桌面 tree/建文件夹/上传 → 启动 DSH → `https://<user>.dsh.example.com/` 200。
4. 模型请求:DSH 里配 key 后能解析并访问 `api.deepseek.com`(DNS + 443 出站通)。
5. 隔离:任意非控制面 Pod 访问某用户 DSH 的 8081/8082 被 NetworkPolicy 拒。
---
## 8. 配置参考(控制面 env)
| env | 默认 | 说明 |
|---|---|---|
| `DSHS_DEPLOY_MODE` | `local` | `k8s` 启用每用户 Pod |
| `DSHS_DB_URL` | 空 | Postgres DSN(k8s 必填) |
| `DSHS_SECRET` | 空 | 共享加密密钥 |
| `DSHS_NAMESPACE` | `dsh` | 每用户资源命名空间 |
| `DSHS_DSH_IMAGE` | 空 | 每用户 DSH 镜像 |
| `DSHS_CONTROL_PLANE_IMAGE` | 空 | file sidecar / tcp-bridge 镜像 |
| `DSHS_IMAGE_PULL_SECRET` | `dsh-acr-pull` | 生成 Pod 的拉镜像 secret |
| `DSHS_BASE_DOMAIN` | 空 | 子域路由基域 |
| `DSHS_COOKIE_DOMAIN` | 空 | cookie `Domain` |
| `DSHS_SECURE_COOKIES` | `false` | HTTPS 设 `true` |
| `DSHS_EGRESS_CIDRS` | 空 | 443 出站白名单(空=0.0.0.0/0) |
---
## 9. 常见问题 → [k8s-deploy.md](k8s-deploy.md)
- 部署顺序、NAS/CNFS、uid 错位、就绪探针、代理端口、leader 接管、DNS、备案、keepAlive 502 等全部踩坑与根因已记录在 `k8s-deploy.md` §5–§8。