k8s-deployment.md 18 KB

Kubernetes / k3s 部署指南

本文说明如何把 Claude Code Hub 部署到 Kubernetes(含轻量级 k3s)集群。若你只需要单机 Docker 部署,请参考 scripts/deploy.shdocker-compose.yaml


1. 概览

组件架构

              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 <name> 指定
域名(可选) 有 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 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 工具链差异(cpdate 等)已在脚本内做了 POSIX 兼容处理。


3. 一键部署

最简场景(本机 k3s)

# 克隆仓库
git clone https://github.com/ding113/claude-code-hub.git
cd claude-code-hub

# 一键部署 (非交互;无集群时会提示安装 k3s)
bash scripts/deploy-k8s.sh --install-k3s -y

脚本执行完会输出:

+================================================================+
|              Claude Code Hub Deployment Complete!              |
+================================================================+

Access URL:           http://<node-ip>:<nodeport>
Namespace:            claude-code-hub
Image:                ghcr.io/ding113/claude-code-hub:latest

Admin Token (保管好):
    <auto-generated-token>

常用命令 (cch):
    cch status
    cch logs
    cch update
    cch backup
    cch info

标准 K8s + 域名

bash scripts/deploy-k8s.sh \
  --ingress-host hub.example.com \
  --ingress-class nginx \
  --storage-class standard \
  -y

自定义 Namespace + Dev 分支

bash scripts/deploy-k8s.sh -n hub-staging -b dev -y

仅渲染 manifest(离线审阅)

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 <ns> claude-code-hub K8s namespace
--kube-context <ctx> 当前 context kubectl context
--install-k3s off 无集群时自动安装 k3s(需 sudo)
Application -i, --image <ref> ghcr.io/ding113/claude-code-hub:latest 应用镜像
-b, --branch <name> main 分支捷径: main→:latest, dev→:dev
-t, --admin-token <token> 自动 48 位随机 ADMIN_TOKEN
--replicas <n> 2 基线副本数
--hpa-min <n> / --hpa-max <n> 2 / 6 HPA 上下限
--timezone <tz> Asia/Shanghai 容器 TZ
Storage --storage-class <name> 自动探测 PVC storageClassName
--pg-size <size> 50Gi PostgreSQL PVC
--redis-size <size> 10Gi Redis PVC
Ingress --ingress-host <host> 未设置 启用 Ingress 并绑定域名
--ingress-class <cls> 自动探测 Ingress className
--disable-ingress off 跳过 Ingress,使用 NodePort
Network --disable-networkpolicy off 跳过 NetworkPolicy(非标准 Ingress ns 时需要)
Deployment -d, --deploy-dir <path> 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 scripts/deploy-k8s.sh \
  -i harbor.example.com/cch/claude-code-hub:v1.2.3 \
  -y

若 registry 需要认证:先手动创建 docker-registry Secret,再在 Deployment 追加 imagePullSecrets(脚本暂不自动生成,参见 k8s 官方文档)。

云厂商存储类

云厂商 storageClassName
AWS EKS gp3 / gp2
GCP GKE standard-rwo / premium-rwo
Azure AKS managed-csi / managed-premium
k3s (单机) local-path (默认)

传入 --storage-class <name> 覆盖默认,或脚本会自动识别默认 SC。

调整副本数

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 ...

生命周期

cch update            # 拉新镜像 + 自动迁移 + 滚动更新,失败自动回滚
cch restart           # 滚动重启 (不换镜像)
cch rollback          # 回滚到上一个 revision
cch scale 3           # 调整副本数

观测

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 / 健康)

数据

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 自动探测 强制设置为 k3skubectl
CCH_INGRESS_HOST 用于 cch info 的 URL 渲染
CCH_BACKUP_DIR ~/backups/claude-code-hub 备份路径
CCH_BACKUP_KEEP 30 保留备份数
NO_COLOR 设为任意值禁用彩色输出

示例 ~/.config/cch/config (安装时由脚本自动写入):

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)

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=2Gilimits.memory=4Gishared_buffers=1GBeffective_cache_size=3GB。这套默认值更适合单节点 8GB RAM 起步环境;如业务量继续增长, 再按节点规格同步上调数据库内存参数和 Pod limit。

回滚

cch rollback                # 回到上一个 revision
kubectl -n claude-code-hub rollout history deployment/claude-code-hub   # 查看历史

备份

cch backup
# 产物: ~/backups/claude-code-hub/claude_code_hub_YYYYMMDD_HHMMSS.sql.gz
# 自动保留最近 30 份,多出的自动删除

建议把备份目录同步到对象存储(rsync / rclone / aws s3 sync):

# crontab 示例:每天凌晨同步到 S3
0 3 * * * aws s3 sync ~/backups/claude-code-hub s3://my-bucket/cch-backups/

恢复

# 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 <repeat-your-original-cli-args> -y

8. 卸载与清理

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 检查清单

优先运行:

cch doctor

输出示例:

[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-0Events
  • 多数原因:StorageClass 不存在 / 节点磁盘不足 / 权限问题
  • 临时方案:bash scripts/deploy-k8s.sh --storage-class <correct-sc> --force-new

App Pod CrashLoopBackOff

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:80curl

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 目录结构

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)

进一步阅读