Kubernetes 部署
使用 Helm 或单文件 manifest 在 Kubernetes 中运行 Clouisle
Kubernetes 部署需要 Kubernetes 1.25+、配置好的 kubectl、Ingress Controller 和可访问镜像仓库;Helm 方式需要 Helm 3。使用本地上传存储扩展 API 副本时需要支持 ReadWriteMany 的 StorageClass。
外部服务要求:PostgreSQL 17+ 且预载 pg_search(0.24.3)与 pg_stat_statements、Redis 6+、Qdrant 1.7+。
存储与默认副本
单文件 manifest 默认创建三个 PVC:postgres-data(10Gi,RWO)、qdrant-data(10Gi,RWO)、uploads-data(10Gi,RWX,仅 API 挂载 /app/uploads)。Redis 差异:单文件 manifest 用 emptyDir(缓存可恢复),Helm 默认 5Gi PVC。
沙箱数据不放在 PVC 上:sandbox-worker 是 DaemonSet,每个节点通过 hostPath 挂载 /var/lib/clouisle/clouisle/sandbox(type: DirectoryOrCreate)到 /var/lib/clouisle/sandbox。Helm 用 sandboxWorker.localDataPath 覆盖,默认 /var/lib/clouisle/<namespace>/<release>/sandbox;同一路径绝不能被多个 release 或不同安装共用。节点丢失、磁盘损坏或该目录被清理后,节点上的沙箱会话与检查点无法在别的节点恢复。
默认副本数:api 2、worker 2、sandbox-worker 1(每节点)、beat 1(Recreate 策略)、frontend 2。manifest 不包含 HPA,副本数静态,需用 kubectl scale 调整。
ConfigMap 与 Secret
单文件 manifest 的资源顺序为:Namespace → ConfigMap → Secret → PostgreSQL(StatefulSet + Headless Service + PVC)→ Redis(Deployment + Service)→ Qdrant(StatefulSet + Headless Service + PVC)→ uploads PVC → API → Worker → Sandbox Worker → Beat → Frontend → Ingress,共 13 段。
clouisle-config 的键必须与 manifest 一致(Pod 通过 envFrom.configMapRef 整体注入):
| 键 | 值 | 说明 |
|---|---|---|
PROJECT_NAME / TIMEZONE | Clouisle / Asia/Shanghai | 应用名与时区 |
API_BASE_URL / API_INTERNAL_BASE_URL | http://api:8000 | 服务端与 Worker 内部地址 |
PUBLIC_API_URL | "" | 部署到公网域名时填写 |
SANDBOX_ARTIFACT_UPLOAD_BASE_URL | http://api:8000 | Sandbox 产物上传 |
SANDBOX_WORKER_CONCURRENCY | "1" | Sandbox Worker 并发(被启动命令的 ${SANDBOX_WORKER_CONCURRENCY:-1} 消费) |
SANDBOX_WORKSPACE_ROOT / SANDBOX_CHECKPOINT_ROOT | /var/lib/clouisle/sandbox/jobs / /var/lib/clouisle/sandbox/checkpoints | API 与 Worker 看到的共享虚拟路径;物理磁盘只挂在 sandbox-worker 上 |
WECHAT_POLL_BATCH_SIZE / WECHAT_POLL_CONCURRENCY / WECHAT_POLL_MAX_BATCHES | "64" / "16" / "8" | 微信轮询批次、并发与在途批次上限 |
SANDBOX_FILESYSTEM_ISOLATION_ENABLED | "true" | 开启 bwrap 文件系统隔离 |
SANDBOX_FILESYSTEM_ISOLATION_BINARY | "/usr/bin/bwrap" | 镜像内 bubblewrap 绝对路径 |
FRONTEND_URL | http://frontend:3000 | SSO 回调基址 |
POSTGRES_SERVER / POSTGRES_PORT / POSTGRES_USER / POSTGRES_DB | postgres / 5432 / postgres / clouisle | 数据库(密码在 Secret) |
REDIS_HOST / REDIS_PORT | redis / 6379 | Redis |
VECTOR_BACKEND / QDRANT_URL / QDRANT_COLLECTION_PREFIX / QDRANT_DISTANCE | qdrant / http://qdrant:6333 / kb_dim / Cosine | 向量库 |
RETRIEVAL_HYBRID_KILL_SWITCH / RETRIEVAL_SHADOW_ENABLED | "false" / "false" | 检索开关 |
BACKEND_CORS_ORIGINS | '["http://localhost:3000"]' | 换成真实前端域名 |
clouisle-secret 有 6 个键,必须替换:SECRET_KEY、POSTGRES_PASSWORD、REDIS_PASSWORD、QDRANT_API_KEY、INTERNAL_API_TOKEN、SANDBOX_ARTIFACT_UPLOAD_API_KEY。模板里的值是 Base64 占位符;手动编辑时用 echo -n 'value' | base64 生成。
API、Worker、Sandbox Worker、Beat 都带 wait-for-postgres initContainer,用 pg_isready 等到数据库可连;API 的 readiness/liveness 探针是 GET /api/v1/health:8000(initialDelaySeconds 10/20),Frontend 探针是 GET /:3000。Worker 与 Sandbox Worker 通过 INTERNAL_API_TOKEN_FILE=/var/run/secrets/clouisle/internal-api-token 读取内部 Token(由 Secret 挂载),并设 UPLOAD_STORAGE_MODE=remote。
sandbox-worker 以 DaemonSet 运行,terminationGracePeriodSeconds: 35,securityContext 为 runAsUser: 0、allowPrivilegeEscalation: false、capabilities.drop: [ALL] + add: [SYS_ADMIN, SETFCAP, NET_ADMIN]、seccompProfile: Unconfined,资源默认 requests 250m/512Mi、limits 2 CPU/2Gi(Helm 用 sandboxWorker.securityContext 与 sandboxWorker.resources 覆盖)。删除这些能力或改回非 root 会让 bwrap 无法创建命名空间。SANDBOX_NODE_ID 由 spec.nodeName 注入,SANDBOX_WORKER_ID 留空以便首次启动在本地磁盘上生成。
K8s 部署同样没有 wechat-receiver 工作负载:微信轮询任务路由到 default 队列,由 worker 消费;飞书长连接由 worker 的 agent 队列消费者托管。只有 clouisle-backend 镜像显式安装 ffmpeg/ffprobe,微信音视频采集与转码运行在 api/worker 容器内;自建镜像时不要漏装。
frontend Deployment 在单文件 manifest(以及 Helm 默认值)中不设置任何环境变量。浏览器流量走 Ingress 的 /api 前缀不受影响,但 Next.js 服务端取数会回退到代码默认 BACKEND_INTERNAL_URL=http://localhost:8000,在 Pod 内不可达。需要 SSR 取数时给 frontend 显式加 BACKEND_INTERNAL_URL=http://api:8000。
Ingress 使用 nginx class,注解 proxy-body-size: 100m、proxy-read-timeout: 1800、proxy-send-timeout: 1800、proxy-http-version: 1.1、use-forwarded-headers: true;路径 /api → api:8000,/ → frontend:3000。host 默认 clouisle.example.com,必须替换为真实域名。
Helm(推荐)
helm lint deploy/helm/clouisle \
--set-string secrets.values.INTERNAL_API_TOKEN=lint-only-token
helm upgrade --install clouisle deploy/helm/clouisle \
--namespace clouisle --create-namespace \
--set-string secrets.values.INTERNAL_API_TOKEN="$(openssl rand -hex 32)"生产环境将 SECRET_KEY、数据库、Redis、Qdrant、Sandbox 和内部 Token 放入 Kubernetes Secret,并使用 values-production.yaml。
生成单文件 manifest
curl -fsSL https://raw.githubusercontent.com/clouisle/Clouisle/main/deploy/install.sh | \
CLOUISLE_DEPLOYMENT=k8s CLOUISLE_K8S_MANIFEST="$PWD/clouisle-k8s.yaml" \
CLOUISLE_YES=1 bash
kubectl apply -f ./clouisle-k8s.yaml生成文件权限为 0600,包含 Base64 编码 Secret;安装器不会自动 apply,也不会修改模板。审阅 image、域名、Ingress、存储和 Secret 后再应用。
扩展与限制
kubectl -n clouisle scale deployment worker --replicas=4
kubectl -n clouisle scale deployment api --replicas=3sandbox-worker 是 DaemonSet(每节点一 Pod),不能 scale 改副本——扩容即加节点。每个 Pod 使用各自节点的 hostPath,不能共享同一目录;节点上的会话与检查点随节点消失,无法迁移到其他节点。
beat 始终保持一个副本。使用 ReadWriteOnce 本地上传卷时,API 保持单副本;生产多副本应配置共享卷或对象存储。
worker 默认消费 default,agent,knowledge,workflow 4 个队列(Helm worker.command),整体扩副本会同时扩展这 4 类任务。只扩容某一类时,为其单独部署一个 Worker,命令末尾只保留一个队列(如 -Q agent);sandbox 队列由独立的 sandbox-worker 消费。队列与任务的对应关系见部署架构 → Celery 队列划分。
性能目标与自动扩缩
以下目标、规格与 HPA 示例来自产品 deployment/scaling.md,属于通用调优起点,不是交付配置的保证;请按自身负载做代表性压测后验证。
性能基线目标:API 端点 p50 < 100ms、p95 < 500ms、p99 < 1000ms;聊天首 Token < 2s、流式输出每 Token < 100ms;简单工作流 < 5s、复杂工作流 < 30s;向量检索 < 500ms、混合检索 < 1s。
垂直规格(API Deployment 示例):requests 内存 4Gi、CPU 2000m;limits 内存 8Gi、CPU 4000m。生产模式用 python main.py server --no-reload -w 8 增加 API Worker,用 python main.py worker -c 8 -Q default,agent,knowledge,workflow 提高 Celery 并发。
自动扩缩(HPA 示例,CPU 70%、内存 80%,3–20 副本):
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api
minReplicas: 3
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
behavior:
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 50
periodSeconds: 60
scaleUp:
stabilizationWindowSeconds: 0
policies:
- type: Percent
value: 100
periodSeconds: 30
- type: Pods
value: 2
periodSeconds: 30
selectPolicy: Max官方 manifest 默认不带 HPA,副本数静态;启用 HPA 前先给 API Deployment 设置 resources.requests,否则 CPU/内存利用率无从计算。
这篇文章对你有帮助吗?