Excalidraw自动伸缩部署:Kubernetes集群实践
在远程协作日益成为常态的今天,团队对高效、直观的可视化工具需求急剧上升。Excalidraw作为一款开源手绘风格白板工具,凭借其极简设计和强大的实时协作能力,迅速在技术架构图绘制、产品原型讨论和头脑风暴中占据一席之地。更值得称道的是,随着AI辅助绘图功能的引入,用户只需输入“画一个微服务架构图”,系统便能自动生成初步草图,极大提升了创作效率。
但便利的背后,是运维层面的新挑战——当数十人同时进入一个会议房间进行协同编辑时,前端资源加载缓慢、WebSocket连接超时等问题开始浮现。传统的固定服务器部署方式显然难以应对这种突发性、波峰波谷明显的访问模式。如何让这样一个轻量级应用,在高并发下依然保持流畅?答案藏在云原生的弹性哲学里:用Kubernetes实现自动伸缩部署。
我们真正需要的,不是一个永远在线的“大块头”服务,而是一个能随用户行为呼吸起伏的智能系统。早上九点全员开工,它悄然扩容;深夜三点无人使用,它安静缩容。这不仅是技术实现的问题,更是成本与体验之间的精妙平衡。
要达成这一目标,整个体系必须从底层重新设计。核心思路很清晰:容器化封装 + 声明式编排 + 指标驱动伸缩。三者缺一不可。
首先来看Excalidraw本身是否适合作为云原生应用运行。好消息是,它的架构天然契合这一理念。Excalidraw本质上是一个纯前端项目,所有绘图逻辑都在浏览器中完成,后端仅负责静态文件托管和协作状态同步。这意味着它是无状态(stateless)的——任何一个Pod崩溃或重启,都不会影响用户体验。这种特性使得水平扩展变得极其简单:只要多跑几个实例,就能分担流量压力。
官方提供的Docker镜像excalidraw/excalidraw体积控制在100MB以内,基于Alpine Linux构建,启动速度快,非常适合频繁扩缩容场景。如果你有定制需求,比如启用AI功能或更换主题色,也可以通过多阶段构建的方式自定义镜像:
FROM node:16-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm install COPY . . RUN npm run build FROM nginx:alpine COPY --from=builder /app/build /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]这个Dockerfile采用典型的两阶段构建策略:先用Node环境打包前端资源,再交由轻量级Nginx服务器托管。最终产出的镜像不包含任何构建工具链,安全性更高,也更利于快速拉取和启动。其中nginx.conf可进一步优化缓存策略、开启Gzip压缩,甚至配置反向代理以对接内部AI服务。
一旦镜像准备就绪,下一步就是把它交给Kubernetes来管理。K8s的强大之处在于,它不再要求你手动登录服务器、敲命令启停进程,而是让你声明“我希望服务长什么样”——比如“我要2个副本,每个最多占用200m CPU和256Mi内存”,剩下的事情由控制平面自动完成。
以下是最关键的Deployment配置:
apiVersion: apps/v1 kind: Deployment metadata: name: excalidraw-deployment labels: app: excalidraw spec: replicas: 2 selector: matchLabels: app: excalidraw template: metadata: labels: app: excalidraw spec: containers: - name: excalidraw image: excalidraw/excalidraw:latest ports: - containerPort: 80 resources: requests: memory: "128Mi" cpu: "100m" limits: memory: "256Mi" cpu: "200m" env: - name: REACT_APP_ENABLE_AI value: "true" - name: REACT_APP_AI_API_URL value: "http://ai-service.default.svc.cluster.local:5000/generate" livenessProbe: httpGet: path: / port: 80 initialDelaySeconds: 30 periodSeconds: 10 readinessProbe: httpGet: path: / port: 80 initialDelaySeconds: 5 periodSeconds: 5这里有几个细节值得注意。首先是资源request和limit的设置。如果设得太低,可能被调度到资源紧张的节点上导致性能下降;设得太高,则会浪费集群容量。根据实际压测经验,128Mi内存足以支撑单个Pod处理上百个并发连接(主要是静态资源请求),而CPU限制在200m左右可防止某个实例过度占用计算资源。
其次是健康探针的配置。livenessProbe判断容器是否存活,若失败则触发重启;readinessProbe决定Pod是否准备好接收流量。两者都使用HTTP GET/路径检查,虽然简单但有效——只要Nginx能返回首页HTML,说明服务正常。初始延迟时间需略大于容器启动时间,避免误判。
为了让外部用户能够访问,还需要Service和Ingress组件配合:
# Service apiVersion: v1 kind: Service metadata: name: excalidraw-service spec: selector: app: excalidraw ports: - protocol: TCP port: 80 targetPort: 80 type: ClusterIP # Ingress apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: excalidraw-ingress annotations: nginx.ingress.kubernetes.io/rewrite-target: / spec: rules: - host: whiteboard.example.com http: paths: - path: / pathType: Prefix backend: service: name: excalidraw-service port: number: 80Service提供集群内部稳定的虚拟IP和服务发现机制,而Ingress则统一对外暴露域名入口。你可以在此基础上添加TLS证书,启用HTTPS加密传输,确保敏感数据不被窃听。
至此,基础架构已成型。但真正的“智能”体现在自动伸缩能力上。这就是Horizontal Pod Autoscaler(HPA)登场的时候了。
HPA是Kubernetes内置的控制器,它会定期从Metrics Server获取各个Pod的CPU和内存使用率,并根据预设阈值动态调整副本数量。例如,我们可以这样配置:
apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: excalidraw-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: excalidraw-deployment minReplicas: 2 maxReplicas: 10 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70 behavior: scaleDown: stabilizationWindowSeconds: 300 policies: - type: Percent value: 10 periodSeconds: 60 scaleUp: stabilizationWindowSeconds: 60 policies: - type: Percent value: 100 periodSeconds: 60这段配置传递了一个明确的策略:快速扩容,谨慎缩容。
为什么这么做?因为在真实场景中,流量上涨往往意味着一场大型会议即将开始,我们必须尽快增加实例来承接请求,避免用户看到“加载中…”卡顿界面。因此,scaleUp设置为每分钟最多翻倍(100%),且稳定窗口仅60秒,响应非常激进。
而缩容则完全不同。如果刚开完会,负载瞬间回落,立刻删掉一半Pod看似节省资源,但实际上可能会误伤仍在使用的长连接用户(如后台挂着的协作房间)。因此,scaleDown设置了5分钟的稳定期(stabilizationWindowSeconds),并且每次最多只减少10%,逐步释放资源,保证平滑过渡。
当然,仅依赖CPU利用率并不总是最优选择。对于Excalidraw这类I/O密集型应用,真正的瓶颈往往是请求数(QPS)或WebSocket连接数,而非CPU计算。此时可以集成Prometheus + Prometheus Adapter,将自定义指标导入HPA:
metrics: - type: Pods pods: metric: name: websocket_connections target: type: AverageValue averageValue: "50"这样,当每个Pod平均承载超过50个WebSocket连接时,系统就会自动扩容,比单纯看CPU更贴近业务实际。
整个系统的运行流程如下:
- 用户通过
whiteboard.example.com访问页面; - 请求经Ingress路由至Service,转发给后端任一健康的Pod;
- 浏览器加载前端代码,初始化画布;若启用AI功能,则调用内网AI服务生成图形;
- 多人协作时,操作事件通过WebSocket发送至协作后端(如独立部署的Excalidraw Collaboration Server),广播给其他成员;
- Metrics Server每15~30秒采集一次各Pod资源使用情况;
- HPA检测到平均CPU超过70%,触发扩容,新增Pod加入服务池;
- 当负载持续下降,HPA在冷却期后逐步缩容,最终保留最小2个副本维持基本可用性。
这套机制解决了多个现实痛点:
| 实际问题 | 解决方案 |
|---|---|
| 高峰时段卡顿、加载慢 | HPA自动扩容,分散请求压力 |
| 低峰期资源闲置浪费 | 缩容至最小副本,降低计算成本 |
| 单点故障导致服务中断 | 多副本+健康检查,故障自动转移 |
| 版本更新造成服务中断 | 滚动更新策略,实现零停机升级 |
尤其值得一提的是滚动更新。当你发布新版本时,只需要修改Deployment中的镜像标签,Kubernetes就会自动按策略替换旧Pod,始终保持有一定数量的实例在线提供服务。结合ArgoCD等GitOps工具,甚至可以做到“提交代码即上线”,完全自动化。
此外,还可以进一步增强可观测性。通过接入Loki收集日志、Prometheus抓取指标、Grafana绘制仪表盘,你可以清晰看到HPA的伸缩趋势、各Pod的资源使用曲线以及用户活跃时间段分布。这些数据反过来又能指导你优化HPA策略——比如发现每天上午十点都有明显流量高峰,就可以提前预热副本数,而不是被动等待触发。
长远来看,这条技术路径的价值远不止于Excalidraw本身。它验证了一种通用的Web类协作应用部署范式:轻量化前端 + 弹性编排 + 智能伸缩。无论是在线文档、协作文档、低代码平台还是教育直播系统,都可以借鉴这一模型。
未来还有更多可能性值得探索。例如:
- 在集群中引入GPU节点,部署本地大语言模型或图像生成模型,实现私有化AI绘图,避免敏感信息外泄;
- 使用Knative构建Serverless版本,将空闲期的副本数降至零,极致降本;
- 结合Cluster Autoscaler,当节点资源不足时自动扩容Worker节点,形成“全栈弹性”。
这些都不是遥不可及的理想,而是已经在众多企业落地的技术实践。
最终我们会发现,技术演进的方向始终是让基础设施更加“隐形”。开发者不必关心服务器在哪里,运维人员不再熬夜应对突发流量,用户只看到稳定流畅的服务。而这,正是云原生的魅力所在。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考