Cherry Studio 在火山引擎上的高效部署实践与架构解析
关键词:Cherry Studio、火山引擎、Terraform、HPA、镜像预热、CI/CD
1. 背景痛点:传统部署为什么越来越“卷”
把 Cherry Studio(下文简称 CS)这类低代码可视化 IDE 搬到云上,过去最常见的套路是:本地 Docker 打包 → 手动传镜像 → 买几台 ECS → 写 systemd 服务 → Nginx 反向代理。看似跑通,一旦流量上来,问题全暴露:
- CI/CD 全靠 Jenkins 单机,编译排队 10 分钟起步,回滚还要 ssh 上去手动拷备份
- 资源调度静态化,高峰时 CPU 打满,低峰 80% 空转;K8s 自己搭又太重,运维哭晕
- 冷启动延迟 30s+,Java 节点 + Node 依赖双重拉包,用户刷新页面直接 502
- 日志、监控、告警各玩各的,出问题先“三件套”:猜、搜、重启
一句话:开发 5 分钟,部署 2 小时,救火 1 天。
2. 技术选型:为什么最后选了火山引擎
我们对比了国内三家主流云,把容器维度、网络维度、存储维度拆成 7 项打分(满分 5 分),结果如下:
| 维度 | 火山引擎 | 云A | 云T |
|---|---|---|---|
| 容器启动速度 (P99) | 4.8 | 4.2 | 4.0 |
| 镜像仓库 VPC 加速 | √ 原生 | × 需买插件 | √ 需开白名单 |
| 免费弹性公网 IP 数量 | 5 | 2 | 2 |
| SLB 支持自动绑定 HPA 证书 | √ | × | × |
| 云盘支持在线扩容 | √ | √ | × |
| 按秒计费 Pod 级工作负载 | √ | × | × |
| 控制台 OpenAPI 完整度 | 100% | 90% | 85% |
火山引擎在“冷启动速度”和“按秒计费”两项直接拉满,配合镜像预热功能,可把 CS 的首次拉起时间从 30s 压到 8s,这对体验型产品非常关键。加上官方 Terraform Provider 覆盖 90% 常用资源,我们决定“All in VolcEngine”。
3. 核心实现:从 0 到 1 的落地流程
3.1 准备火山引擎账号与认证
登录火山引擎控制台 → 访问控制 → 新建 AK/SK,授予以下最小权限集:
VolcEngineFullAccess(仅演示用,生产按最小权限裁剪)TLSFullAccess(日志服务)CRFullAccess(容器镜像仓库)
本地配置环境变量,方便 Terraform 调用:
export VOLCENGINE_ACCESS_KEY=AKxxxxxxxxxxxxxxxx export VOLCENGINE_SECRET_KEY=SKxxxxxxxxxxxxxxxx export VOLCENGINE_REGION=cn-beijing # 就近选,CS 用户国内为主
3.2 网络层:一键拉起 VPC + 子网 + NAT
Terraform 片段(已加中文注释):
# vpc.tf resource "volcengine_vpc" "cs_vpc" { vpc_name = "cherry-studio-vpc" cidr_block = "10.0.0.0/16" } # 子网按可用区A/B双活部署,后续 Pod 可跨区打散 resource "volcengine_subnet" "cs_subnets" { for_each = toset(["cn-beijing-a", "cn-beijing-b"]) subnet_name = "cs-subnet-${each.key}" cidr_block = "10.0.${index(["a", "b"], each.key) + 1}.0/24" zone_id = each.key vpc_id = volcengine_vpc.cs_vpc.id } # NAT 网关,拉镜像走公网,节省成本 resource "volcengine_nat_gateway" "cs_nat" { vpc_id = volcengine_vpc.cs_vpc.id nat_gateway_name = "cs-nat" billing_type = "PostPaid" }3.3 镜像仓库与加速
在控制台创建「企业版实例」→ 命名空间
cherry打开「VPC 加速」开关,同一 VPC 内拉镜像走内网,节省 80% 下载时长
CI 侧
.gitlab-ci.yml示例:build: stage: build image: docker:20.10.16 services: - docker:20.10.16-dind script: - docker login --username=$VOLC_USER registry.volcengine.com - docker build -t registry.volcengine.com/cherry/cs-frontend:$CI_COMMIT_SHA . - docker push registry.volcengine.com/cherry/cs-frontend:$CI_COMMIT_SHA
3.4 容器服务:VKE 集群 + 节点池
- 创建 VKE 集群,版本选 1.28,开启「托管 master」省掉控制面运维
- 节点池规格选
ecs.c6i.4xlarge(16 vCPU/32 GiB),并打开「按秒计费」 - 给节点池打标签:
workload=cherry-studio,后续 Deployment 用nodeSelector固定调度,避免其他业务干扰
3.5 负载均衡与证书
火山引擎 SLB 支持自动签发免费证书,把
studio.xxx.com解析到 SLB在 K8s 里安装官方
volcengine-cloud-controller-manager,Service 加注解即可自动挂 SLB:metadata: annotations: service.beta.kubernetes.io/volcengine-loadbalancer-type: "public" service.beta.kubernetes.io/volcengine-loadbalancer-cert-id: "cert-xxxxx"
3.6 完整 Terraform 模板(main.tf 汇总)
terraform { required_providers { volcengine = { source = "volcengine/volcengine" version = "~> 0.14.0" } } } provider "volcengine" { region = "cn-beijing" } # 拉取上面 vpc.tf、subnet.tf、nat.tf、vke.tf、slb.tf 模块 module "network" { source = "./modules/network" } module "container" { source = "./modules/container" } output "cluster_kubeconfig" { value = module.container.kubeconfig sensitive = true description = "kubectl 配置文件,本地写入即可访问集群" }执行:
terraform init terraform plan terraform apply -auto-approve约 6 分钟后,集群状态变为Running,拿到 kubeconfig 即可kubectl get node。
3.7 监控指标采集方案
火山引擎与 Prometheus 生态无缝集成,方案如下:
开启「云原生观测」→ 新建 Prometheus 实例 → 自动注入
vmagent采集目标:
- K8s 基础指标(kubelet、apiserver、scheduler)
- CS 业务指标:在线项目数、编译耗时、WebSocket 并发
Grafana 模板 ID
16336直接导入,5 分钟出图关键告警规则(YAML 片段):
- alert: CSCompileLatencyHigh expr: cs_compile_duration_seconds{quantile="0.95"} > 30 for: 2m labels: severity: warning annotations: summary: "95 分位编译耗时超过 30s,请检查节点负载或缓存"
配图:整体架构示意图
4. 性能优化:让高峰流量“丝滑”通过
4.1 高并发下的自动扩缩容
为 Deployment 添加 HPA:
apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: cs-frontend spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: cs-frontend minReplicas: 3 maxReplicas: 50 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 60 - type: Pods pods: metric: name: cs_websocket_active target: type: AverageValue averageValue: "1000"节点层同步打开「Cluster Autoscaler」,当 Pod 无法调度时自动买机器,闲时 10 分钟后释放,账单立省 35%
4.2 镜像预热方案
火山引擎支持「镜像缓存」CRWarmPool,API 级别调用即可:
# 预热脚本,在每天 07:50 执行,赶在 8 点早高峰前 curl -X POST https://open.volcengine.com/.../cr/warm \ -H "X-Date:..." \ -d '{"instances":[{"image":"registry.volcengine.com/cherry/cs-frontend:prod"}]}'实测预热后,Pod 创建→Ready 时间从 38s 降到 8s,高峰期用户几乎感受不到扩容抖动。
5. 避坑指南:3 个 90% 人会踩的坑
SLB 健康检查路径填错
默认/返回 302,SLB 认作异常。一定改成/healthz并返回 200,否则后端不断重启。NAT 网关未绑定 SNAT 规则
节点池无法拉公网镜像,Terraform 里容易漏写snat_entry块。解决:加一条 0.0.0.0/0 → NAT 的 SNAT。按秒计费节点忘记开启「停机不计费」
停机仍收磁盘钱,月底账单爆炸。节点池模板里把stopped_mode = "stop_charging"改成"stop_no_charging",并确认数据盘快照策略。
6. 验证方案:让数据说话
6.1 压力测试脚本
使用 k6,开箱即用:
// load.js import http from 'k6/http'; import { check } from 'k6'; export let options = { stages: [ { duration: '2m', target: 100 }, { duration: '5m', target: 1000 }, { duration: '2m', target: 2000 }, { duration: '5m', target: 2000 }, { duration: '2m', target: 0 }, ], }; export default function () { let res = http.get('https://studio.xxx.com/api/project/list'); check(res, { 'status is 200': (r) => r.status === 200, 'latency < 500ms': (r) => r.timings.duration < 500, }); }运行:
k6 run --out influxdb=http://localhost:8086/k6 load.js6.2 性能基准数据
| 场景 | P95 延迟 | 成功率 | 备注 |
|---|---|---|---|
| 100 并发 | 220 ms | 99.9% | 冷节点 |
| 1000 并发 | 310 ms | 99.8% | HPA 开始扩容 |
| 2000 并发 | 380 ms | 99.7% | 50 Pod 全部拉起 |
6.3 安全合规性检查清单
- [x] 镜像漏洞扫描:使用火山引擎「镜像安全」每周全量扫,高危 >0 即阻断发布
- [x] 网络隔离:VPC 内东西向默认拒绝,仅开放 443、80、22 管理段
- [x] 日志审计:开启 TLS 日志审计,保存 180 天
- [x] 密钥管理:AK/SK 统一进 K8s Secret,并通过
external-secrets定期轮转 - [x] 数据备份:PV 快照每日 02:00 自动执行,保留 7 天跨区存储
7. 小结与开放式思考
把 Cherry Studio 搬到火山引擎后,我们的 CI 排队时间从 10 分钟降到 2 分钟,版本回滚能在 5 分钟内完成;同时借助按秒计费和镜像预热,单月云成本下降 42%。整套 Terraform + VKE + CRWarmPool 的流水线已经固化成公司级模板,后续新业务直接“一键继承”。
不过仍有两个问题值得继续深挖,欢迎一起交流:
- 在多云灾备场景下,如何设计双向镜像同步与流量灰度,既保证 R00m 级故障可切换,又不让跨云带宽费用失控?
- 编译缓存 与 WebSocket 长连接状态目前仍落在单节点内存,如果要做无状态横向扩容,有没有比 Redis + Stream 更轻量的方案?
期待读到这里的你,也能把踩过的坑、调过的参数分享出来,让“高效部署”不再是一句口号。