# Kubernetes / k3s 部署指南 本文说明如何把 Claude Code Hub 部署到 Kubernetes(含轻量级 k3s)集群。若你只需要单机 Docker 部署,请参考 [`scripts/deploy.sh`](../scripts/deploy.sh) 与 [`docker-compose.yaml`](../docker-compose.yaml)。 --- ## 1. 概览 ### 组件架构 ```text Ingress / NodePort (可选) │ ▼ ┌──────────────────┐ │ Service (ClusterIP / NodePort) └────────┬─────────┘ │ ┌────────▼─────────┐ │ Deployment │ HPA 2~6 副本,CPU 70% / Memory 80% │ claude-code-hub │ PDB maxUnavailable=1 └────┬─────────┬────┘ │ │ ┌─────▼──┐ ┌───▼─────┐ │Postgres│ │ Redis │ StatefulSet + PVC └────────┘ └─────────┘ NetworkPolicy 仅允许 app 访问 ``` ### 与 Docker Compose 部署的差异 | 维度 | Docker Compose | Kubernetes | |------|----------------|-----------| | 高可用 | 单容器 | HPA + PDB,滚动更新不中断 | | 存储 | 本地卷 | PVC (由集群 StorageClass 管理) | | 域名 | Caddy (可选) | Ingress / Traefik IngressRoute / NodePort | | 密钥 | `.env` 文件 | Kubernetes Secret | | 升级 | `docker compose pull` | `cch update` (带迁移 + 回滚) | | 适用 | 个人/小团队 | 生产 / 多节点 / 企业 | --- ## 2. 前置条件 ### 🧾 快速清单(Prerequisites) | 类别 | 要求 | |------|------| | OS | Linux (Ubuntu 22.04+/Debian 12+/Rocky 9+ 等) 或 macOS(仅管理端) | | Shell | **bash ≥ 3.2** (含 macOS 默认 /bin/bash) | | 工具链 | `kubectl` / `curl` / `openssl` / `python3` | | 集群 | Kubernetes ≥ 1.26 (或用 `--install-k3s` 自动装 k3s) | | 存储 | 默认 StorageClass 可用,或通过 `--storage-class ` 指定 | | 域名(可选) | 有 Ingress Controller 时可启用 HTTPS/域名访问 | | 资源 | 建议 4 vCPU / 8GB RAM / 80GB 磁盘起步 | ### 操作系统与硬件 - **Linux**: 建议 4 vCPU / 8GB RAM 起步。Ubuntu 22.04+/Debian 12+/Rocky 9+/CentOS Stream 9 等主流发行版 - **macOS** 只用于管理端 (kubectl),集群侧建议 Linux - 磁盘 ≥ 80GB (PostgreSQL 50GB + Redis 10GB + 系统) ### 集群选型 **选项 A — 单机 k3s (推荐新手 / 家用 / 自建)** ```bash # 脚本会在检测不到集群时自动提示安装 bash scripts/deploy-k8s.sh --install-k3s -y ``` > `--install-k3s` 会执行官方安装脚本 `curl -fsSL https://get.k3s.io | sh -`。 > 生产环境建议先审阅脚本内容,确认来源与变更窗口后再执行。 **选项 B — 已有集群 (EKS / GKE / AKS / 自建标准 K8s)** 需要满足: - Kubernetes ≥ 1.26 - 默认 StorageClass 可用 (或通过 `--storage-class` 指定) - 可选:Ingress Controller (nginx / traefik / 其他) + 可解析域名 ### 工具 - `kubectl` (单独机器管理集群时必需) - `bash` ≥ 3.2 / `curl` / `openssl` / `python3` (脚本渲染占位符时使用) > macOS 默认的 /bin/bash 是 3.2 分支,脚本兼容该版本。BSD 工具链差异(`cp`、`date` > 等)已在脚本内做了 POSIX 兼容处理。 --- ## 3. 一键部署 ### 最简场景(本机 k3s) ```bash # 克隆仓库 git clone https://github.com/ding113/claude-code-hub.git cd claude-code-hub # 一键部署 (非交互;无集群时会提示安装 k3s) bash scripts/deploy-k8s.sh --install-k3s -y ``` 脚本执行完会输出: ```console +================================================================+ | Claude Code Hub Deployment Complete! | +================================================================+ Access URL: http://: Namespace: claude-code-hub Image: ghcr.io/ding113/claude-code-hub:latest Admin Token (保管好): 常用命令 (cch): cch status cch logs cch update cch backup cch info ``` ### 标准 K8s + 域名 ```bash bash scripts/deploy-k8s.sh \ --ingress-host hub.example.com \ --ingress-class nginx \ --storage-class standard \ -y ``` ### 自定义 Namespace + Dev 分支 ```bash bash scripts/deploy-k8s.sh -n hub-staging -b dev -y ``` ### 仅渲染 manifest(离线审阅) ```bash bash scripts/deploy-k8s.sh --dry-render --deploy-dir /tmp/cch-k8s -y kubectl apply --dry-run=client -R -f /tmp/cch-k8s/k8s/ ``` --- ## 4. 参数参考 ### `scripts/deploy-k8s.sh` | 分组 | 参数 | 默认值 | 说明 | |------|------|--------|------| | **Cluster** | `-n, --namespace ` | `claude-code-hub` | K8s namespace | | | `--kube-context ` | 当前 context | kubectl context | | | `--install-k3s` | off | 无集群时自动安装 k3s(需 sudo) | | **Application** | `-i, --image ` | `ghcr.io/ding113/claude-code-hub:latest` | 应用镜像 | | | `-b, --branch ` | main | 分支捷径: main→:latest, dev→:dev | | | `-t, --admin-token ` | 自动 48 位随机 | ADMIN_TOKEN | | | `--replicas ` | 2 | 基线副本数 | | | `--hpa-min ` / `--hpa-max ` | 2 / 6 | HPA 上下限 | | | `--timezone ` | `Asia/Shanghai` | 容器 TZ | | **Storage** | `--storage-class ` | 自动探测 | PVC storageClassName | | | `--pg-size ` | 50Gi | PostgreSQL PVC | | | `--redis-size ` | 10Gi | Redis PVC | | **Ingress** | `--ingress-host ` | 未设置 | 启用 Ingress 并绑定域名 | | | `--ingress-class ` | 自动探测 | Ingress className | | | `--disable-ingress` | off | 跳过 Ingress,使用 NodePort | | **Network** | `--disable-networkpolicy` | off | 跳过 NetworkPolicy(非标准 Ingress ns 时需要) | | **Deployment** | `-d, --deploy-dir ` | auto | manifest + cch 安装目录 | | | `--force-new` | off | 删除已有 namespace 后重装(Deployment / StatefulSet / Secret / PVC 全重建) | | | `--install-cch` | off | cch 软链到 `/usr/local/bin/cch` | | | `--dry-render` | off | 只渲染不 apply | | **Misc** | `-y, --yes` | 交互 | 非交互模式 | | | `-h, --help` | — | 显示帮助 | | | `--version` | — | 显示版本 | --- ## 5. 自定义配置 ### 使用内网镜像仓库 ```bash bash scripts/deploy-k8s.sh \ -i harbor.example.com/cch/claude-code-hub:v1.2.3 \ -y ``` 若 registry 需要认证:先手动创建 `docker-registry` Secret,再在 Deployment 追加 `imagePullSecrets`(脚本暂不自动生成,参见 [k8s 官方文档](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/))。 ### 云厂商存储类 | 云厂商 | storageClassName | |--------|-------------------| | AWS EKS | `gp3` / `gp2` | | GCP GKE | `standard-rwo` / `premium-rwo` | | Azure AKS | `managed-csi` / `managed-premium` | | k3s (单机) | `local-path` (默认) | 传入 `--storage-class ` 覆盖默认,或脚本会自动识别默认 SC。 ### 调整副本数 ```bash bash scripts/deploy-k8s.sh --replicas 3 --hpa-min 3 --hpa-max 10 -y ``` 默认模板保留 `replicas=2`,但 `AUTO_MIGRATE` 入口 `src/instrumentation.ts` 会先获取 PostgreSQL advisory lock, 因此首次多副本启动时迁移会串行执行。如果你更关心首启速度,也可以先用 `--replicas 1` 部署,确认健康后再扩容。 ### NetworkPolicy 自定义 默认 `deploy/k8s/app/networkpolicy.yaml` 只在 **Ingress 模式** 下应用,并放行以下三个 Ingress Controller namespace: - `kube-system` (k3s 内置 Traefik) - `ingress-nginx` (社区版 nginx-ingress) - `traefik` (独立安装的 Traefik) 如果你的 Ingress Controller 位于其他 namespace(例如 `istio-system`),有两种选择: 1. 部署时关闭内置 NP:`bash scripts/deploy-k8s.sh --disable-networkpolicy -y` 2. 编辑 `deploy/k8s/app/networkpolicy.yaml` 加入你的 namespace 标签,再部署 若脚本回落到 NodePort (`--disable-ingress` 或未提供 `--ingress-host`),会自动跳过 app NetworkPolicy, 避免默认的 Ingress namespace 白名单把外部流量挡掉。 ### 修改应用环境变量 `deploy/k8s/app/deployment.yaml` 里枚举了常用环境变量(连接池、超时、限流开关、MESSAGE_REQUEST 批量参数等)。建议流程: 1. 修改 `deploy/k8s/app/deployment.yaml` 模板 2. 运行 `bash scripts/deploy-k8s.sh -y` 走升级分支(自动保留 Secret) 3. 或手动:`kubectl -n claude-code-hub set env deployment/claude-code-hub KEY=VAL` --- ## 6. cch 管理 CLI 部署完成后,`cch` 用于日常运维。若 `--install-cch` 已启用,直接运行 `cch ...`;否则用 `bash scripts/cch ...`。 ### 生命周期 ```bash cch update # 拉新镜像 + 自动迁移 + 滚动更新,失败自动回滚 cch restart # 滚动重启 (不换镜像) cch rollback # 回滚到上一个 revision cch scale 3 # 调整副本数 ``` ### 观测 ```bash cch status # Pod / HPA / 资源使用 cch logs 500 # 最近 500 行日志 cch logs --since=10m # 透传 kubectl logs 参数 cch follow # 实时 tail cch env # 展示当前 env (JSON) cch info # 访问 URL + Admin Token + 镜像 digest cch doctor # 诊断 (kubectl / 集群 / Pod / 健康) ``` ### 数据 ```bash cch backup # PostgreSQL 备份到 ~/backups/claude-code-hub/(gzip,保留 30 份) cch secret # 输出 Admin Token cch secret dsn # 输出 PostgreSQL 连接串 cch secret redis-url # 输出 Redis 连接串 cch dbshell # psql 交互 cch shell # 应用 Pod 的 sh ``` ### 配置覆盖 (env / config file) 优先级: **CLI 参数 > 环境变量 > `~/.config/cch/config` > 默认值** | 变量 | 默认 | 说明 | |------|------|------| | `CCH_NAMESPACE` | `claude-code-hub` | 目标 namespace | | `CCH_IMAGE` | `ghcr.io/ding113/claude-code-hub:latest` | 目标镜像 | | `CCH_DEPLOY_DIR` | 自动查找 | manifest 所在目录 | | `CCH_RUNTIME` | 自动探测 | 强制设置为 `k3s` 或 `kubectl` | | `CCH_INGRESS_HOST` | — | 用于 `cch info` 的 URL 渲染 | | `CCH_BACKUP_DIR` | `~/backups/claude-code-hub` | 备份路径 | | `CCH_BACKUP_KEEP` | `30` | 保留备份数 | | `NO_COLOR` | — | 设为任意值禁用彩色输出 | 示例 `~/.config/cch/config` (安装时由脚本自动写入): ```bash CCH_NAMESPACE="claude-code-hub" CCH_IMAGE="ghcr.io/ding113/claude-code-hub:latest" CCH_DEPLOY_DIR="/home/user/.config/cch" CCH_RUNTIME="k3s" CCH_INGRESS_HOST="hub.example.com" ``` --- ## 7. 升级、回滚、备份与恢复 ### 升级流程(`cch update`) ```text 1. PostgreSQL 备份 (失败时询问是否继续) 2. k3s: 预拉镜像 / 标准 k8s: 依赖 imagePullPolicy=Always 3. 缩到 1 副本 (减少升级等待时间与排障复杂度) 4. 更新镜像 → 应用启动时通过 AUTO_MIGRATE=true 自动执行 drizzle migrate k3s 路径: 用 digest 固定避免 tag 相同导致 rollout 空跑 标准 k8s 路径: set image(tag 相同则触发 rollout restart) 5. 健康检查 (in-pod fetch /api/health/ready) 6. 健康检查失败 → 自动 rollout undo + 恢复原副本数 7. 健康检查通过 → 恢复到 max(升级前实际副本数, HPA minReplicas) ``` > 说明: 未使用独立的迁移 Job — 应用镜像在启动时通过 `AUTO_MIGRATE=true` 环境变量触发 > drizzle migrate(入口 `src/instrumentation.ts`),避免 Job 与应用并发迁移的竞态,且 > 运行时镜像不再依赖 devDependency 的 drizzle-kit。 > PostgreSQL 默认资源画像: `requests.memory=2Gi`、`limits.memory=4Gi`、`shared_buffers=1GB`、 > `effective_cache_size=3GB`。这套默认值更适合单节点 8GB RAM 起步环境;如业务量继续增长, > 再按节点规格同步上调数据库内存参数和 Pod limit。 ### 回滚 ```bash cch rollback # 回到上一个 revision kubectl -n claude-code-hub rollout history deployment/claude-code-hub # 查看历史 ``` ### 备份 ```bash cch backup # 产物: ~/backups/claude-code-hub/claude_code_hub_YYYYMMDD_HHMMSS.sql.gz # 自动保留最近 30 份,多出的自动删除 ``` 建议把备份目录同步到对象存储(rsync / rclone / aws s3 sync): ```bash # crontab 示例:每天凌晨同步到 S3 0 3 * * * aws s3 sync ~/backups/claude-code-hub s3://my-bucket/cch-backups/ ``` ### 恢复 ```bash # 1. 停 app (避免写入):先删掉 HPA,再直接 kubectl 缩到 0 (cch scale 要求 >=1) # CPU/内存 HPA 不支持直接把 minReplicas 改成 0 kubectl -n claude-code-hub delete hpa claude-code-hub 2>/dev/null || true kubectl -n claude-code-hub scale deployment/claude-code-hub --replicas=0 # 2. 在 postgres pod 内执行恢复 gunzip -c ~/backups/claude-code-hub/claude_code_hub_20260101_030000.sql.gz | \ kubectl -n claude-code-hub exec -i sts/postgres -- \ psql -U claude_code_hub -d claude_code_hub # 3. 用与初次部署一致的 CLI 参数重跑一次完整部署,恢复 HPA / PDB / Service 等资源 # 关键:保留原始的 --replicas / --hpa-min / --hpa-max / --storage-class / --ingress-* 参数 bash scripts/deploy-k8s.sh -n claude-code-hub -y ``` --- ## 8. 卸载与清理 ```bash cch uninstall # 输入 namespace 名称以二次确认;会删除: # - namespace claude-code-hub (含所有 Pod / Service / Ingress / Secret / HPA) # - PVC (Postgres 和 Redis 数据永久丢失) ``` 卸载后 manifest 目录 `~/.config/cch/` 保留,可手动 `rm -rf` 清理。 备份目录 `~/backups/claude-code-hub/` 永不自动删除。 --- ## 9. 故障排查 ### `cch doctor` 检查清单 优先运行: ```bash cch doctor ``` 输出示例: ```console [OK] kubectl installed: v1.31.5 [OK] Cluster reachable (runtime=k3s) [OK] Namespace claude-code-hub exists [OK] Secret claude-code-hub-secrets 存在 [OK] postgres StatefulSet ready [OK] redis StatefulSet ready [OK] App ready replicas: 2 [OK] HPA configured [OK] Ingress resource present [OK] StorageClass local-path (k3s default) [OK] In-pod health check Summary: 10 passed, 0 warnings, 0 failures ``` ### 常见问题 **Pod 卡在 Pending** - 99% 是 PVC 挂载失败。`kubectl -n claude-code-hub describe pvc postgres-data-postgres-0` 看 `Events` - 多数原因:StorageClass 不存在 / 节点磁盘不足 / 权限问题 - 临时方案:`bash scripts/deploy-k8s.sh --storage-class --force-new` **App Pod CrashLoopBackOff** ```bash cch logs 200 # 看应用日志 kubectl -n claude-code-hub describe pod -l app=claude-code-hub # 看 Events ``` - `DSN` / `REDIS_URL` 错:检查 Secret 是否完整 `cch secret dsn` - 迁移失败:`cch logs` 看 drizzle 报错,通常是 Postgres 还没就绪 - 健康探针失败但应用已起:临时 `kubectl -n claude-code-hub port-forward svc/claude-code-hub 13500:80` 再 `curl` **Ingress 域名不通** - `cch info` 确认 Ingress host 和 className 正确 - 检查 Ingress Controller 本身是否就绪:`kubectl -n ingress-nginx get pods` - DNS 是否指向 Ingress Controller 的 LB/IP **升级失败自动回滚了** - 看日志:`cch logs 500` - 常见:DB 迁移脚本冲突 → 从最近备份恢复,回滚后人工 drizzle-kit migrate **镜像拉不下来** - 公网受限:用内网 registry + `-i` - 私有仓库:需 `imagePullSecrets`(脚本暂不自动处理) --- ## 10. 附录 ### manifest 目录结构 ```text deploy/k8s/ ├── namespace.yaml ├── app/ │ ├── deployment.yaml # replicas, env, resources, 3 个 probe, preStop sleep │ ├── service.yaml # ClusterIP / NodePort │ ├── hpa.yaml # CPU 70% / 内存 80%, scaleUp/Down 策略 │ ├── pdb.yaml # maxUnavailable=1 │ └── networkpolicy.yaml # 仅在 Ingress 模式应用 ├── postgres/ │ ├── statefulset.yaml # pg18 + 保守内存参数,50Gi PVC │ ├── service.yaml # ClusterIP,不对外 │ └── networkpolicy.yaml # 仅允许 app 访问 ├── redis/ │ ├── statefulset.yaml # redis:7-alpine,密码保护,AOF │ ├── service.yaml # ClusterIP,不对外 │ └── networkpolicy.yaml # 仅允许 app 访问 └── ingress/ ├── ingress.yaml # 标准 Ingress (nginx-ingress annotations) └── traefik-ingressroute.yaml # Traefik CRD 备选 ``` ### 模板占位符 部署脚本会在渲染时替换以下占位符: | 占位符 | 默认 | |--------|------| | `{{NAMESPACE}}` | `claude-code-hub` | | `{{APP_IMAGE}}` | `ghcr.io/ding113/claude-code-hub:latest` | | `{{APP_REPLICAS}}` | `2` | | `{{APP_HPA_MIN}}` / `{{APP_HPA_MAX}}` | `2` / `6` | | `{{APP_SERVICE_TYPE}}` | `ClusterIP` / `NodePort` | | `{{STORAGE_CLASS}}` | k3s `local-path` / 其他自动或空 | | `{{PG_STORAGE_SIZE}}` | `50Gi` | | `{{REDIS_STORAGE_SIZE}}` | `10Gi` | | `{{TIMEZONE}}` | `Asia/Shanghai` | | `{{INGRESS_HOST}}` | 用户参数 | | `{{INGRESS_CLASS}}` | 自动 / 用户参数 | ### 与 docker-compose.yaml 的对照 | 资源 | docker-compose | K8s | |------|----------------|-----| | App | service `app` | Deployment `claude-code-hub` | | PostgreSQL | service `postgres` | StatefulSet `postgres` + PVC | | Redis | service `redis` | StatefulSet `redis` + PVC | | 网络 | 同一 compose 网络 | ClusterIP Service + NetworkPolicy | | 环境变量 | `env_file: .env` | Secret + Deployment.env | | 端口 | `23000:3000` | Ingress / NodePort | | 持久化 | `./data/postgres`、`./data/redis` | PVC (StorageClass) | ### 进一步阅读 - 主 README: [README.md](../README.md) - Docker 部署: [scripts/deploy.sh](../scripts/deploy.sh) - 架构文档: [docs/architecture-claude-code-hub-2025-11-29.md](./architecture-claude-code-hub-2025-11-29.md) - 源 manifest 目录: [deploy/k8s/README.md](../deploy/k8s/README.md)