ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

第29章:RAGFlow Kubernetes/Helm 部署与弹性扩缩容

第29章:RAGFlow Kubernetes/Helm 部署与弹性扩缩容 1 项目背景业务场景「云帆科技」的 Docker Compose 单机部署已经支撑了 500 人三个月的使用。但随着业务量增长单机的资源瓶颈逐渐显现——32GB 内存已被占满 85%而且每逢周一早高峰全员集中问答CPU 飙到 95%P95 延迟从 4 秒飙升到 18 秒。更麻烦的是运维层面——某次 API Server 进程 OOM 被内核杀掉后需要人工 SSH 登录重启。CTO 要求达到99.9% 可用性每月停机不超过 43 分钟单机 Docker 根本无法满足——没有故障自愈、没有自动扩缩容、没有滚动升级。CTO 拍板将 RAGFlow 迁移到 Kubernetes利用 K8s 的自动重启、HPA 弹性扩缩容、滚动更新能力实现生产级部署。痛点Docker Compose 单机部署的生产化局限无故障自愈进程挂了不会自动重启——需要人工docker restart。无弹性扩缩容周一早高峰 500 人同时用下午只有 50 人——但资源全天不变要么高峰不够用要么低谷浪费。无滚动升级升级版本必须先停掉旧容器再启新容器——有停机窗口。无资源隔离所有进程共享一台机器——Task Executor 的 OOM 可能连带杀死 API Server。无多副本负载均衡单点故障——唯一的 API Server 挂了全部不可用。Docker Compose vs K8s 的生产化差异 Docker Compose: Kubernetes: manual restart → auto-restart (liveness probe) fixed replicas → HPA auto-scaling stop→upgrade→start → rolling update (zero downtime) shared resources → resource limits QoS classes single instance → multi-replica Service LB docker logs → Prometheus Grafana2 项目设计小胖打开 K8s 文档满屏 YAML“大师Kubernetes 也太复杂了——Deployment、StatefulSet、Service、Ingress、ConfigMap、Secret、PVC……我感觉我的人生被 YAML 填满了。RAGFlow 有没有 Helm Chart 一键部署”大师“K8s 确实学习曲线陡峭但一旦配好运维成本反而大幅下降——以前人工手动做的事现在全自动化。RAGFlow 提供了基础的 K8s 部署清单你可以基于它构建 Helm Chart。先理解 RAGFlow 在 K8s 中怎么拆”RAGFlow in Kubernetes 架构 ┌─────────────────────────────────────────────────┐ │ Ingress │ │ (nginx-ingress / traefik) │ │ /api/* → api-service, /* → web │ └──────────────────────┬──────────────────────────┘ │ ┌─────────────┼─────────────┐ ▼ ▼ ▼ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ API Server │ │ API Server │ │ API Server │ ← Deployment (replicas: 3) │ (pod-1) │ │ (pod-2) │ │ (pod-3) │ HPA: 3-10 └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ └─────────────┼─────────────┘ │ ┌─────────────┼─────────────┐ ▼ ▼ ▼ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ Redis │ │ MySQL │ │ MinIO │ ← StatefulSet (各1-3副本) │ (Stateful) │ │ (Stateful) │ │ (Stateful) │ └─────────────┘ └─────────────┘ └─────────────┘ ┌─────────────────────────────────────┐ │ Task Executor (Deployment) │ │ replicas: 2-5 (按队列积压 HPA) │ │ 每个 Pod 拉取 Redis Stream 任务 │ └─────────────────────────────────────┘ ┌─────────────────────────────────────┐ │ Infinity / ES (StatefulSet) │ │ replicas: 1-3 │ │ PVC 持久化向量索引数据 │ └─────────────────────────────────────┘小胖“为什么 API Server 用 Deployment而 MySQL 和 Redis 用 StatefulSet”大师“核心区别Deployment 是无状态服务——Pod 随时可以死掉、新建、漂移到别的节点。StatefulSet 是有状态服务——每个 Pod 有固定的身份如 redis-0, redis-1和固定的持久化存储PVC 不随 Pod 死亡而丢失。”组件K8s 资源类型原因API ServerDeployment无状态——可以随时扩缩、漂移Task ExecutorDeployment无状态——任务从 Redis 恢复Web 前端Deployment无状态静态资源MySQLStatefulSet有状态——数据必须持久化RedisStatefulSet有状态——AOF/RDB 持久化MinIOStatefulSet有状态——对象存储持久化ES/InfinityStatefulSet有状态——索引数据持久化技术映射Deployment 共享单车的停车点车可以随时换StatefulSet 你家楼下的固定车位车牌号对应车位换了车车位还是你的。小白“弹性扩缩容具体怎么做HPA 根据什么指标扩缩”大师“两个维度API Server 按 CPU/内存 QPSTask Executor 按 Redis 队列长度。”# HPA: API Server 弹性扩缩容apiVersion:autoscaling/v2kind:HorizontalPodAutoscalermetadata:name:ragflow-api-hpaspec:scaleTargetRef:apiVersion:apps/v1kind:Deploymentname:ragflow-apiminReplicas:3maxReplicas:10metrics:-type:Resourceresource:name:cputarget:type:UtilizationaverageUtilization:70# CPU 超过 70% 触发扩容-type:Resourceresource:name:memorytarget:type:UtilizationaverageUtilization:80# 内存超过 80% 触发扩容---# 基于自定义指标的 Task Executor HPAapiVersion:autoscaling/v2kind:HorizontalPodAutoscalermetadata:name:ragflow-task-executor-hpaspec:scaleTargetRef:apiVersion:apps/v1kind:Deploymentname:ragflow-task-executorminReplicas:2maxReplicas:5metrics:-type:Podspods:metric:name:redis_queue_length# Prometheus 采集的队列长度target:type:AverageValueaverageValue:50# 队列 50 触发扩容小胖“那滚动升级怎么做升级过程中会不会影响用户”大师“Deployment 的strategy: RollingUpdate保证零停机升级——新 Pod 就绪后旧 Pod 才被终止。”apiVersion:apps/v1kind:Deploymentmetadata:name:ragflow-apispec:replicas:3strategy:type:RollingUpdaterollingUpdate:maxUnavailable:1# 升级期间最多 1 个 Pod 不可用maxSurge:1# 升级期间允许超出 1 个 Podtemplate:spec:containers:-name:ragflow-apiimage:infiniflow/ragflow:v0.27.0# 改这个版本号触发滚动升级# readiness/liveness probes...滚动升级过程3副本 → 新版本 Step 1: [v0.26] [v0.26] [v0.26] → 正常运行 Step 2: [v0.26] [v0.26] [v0.27] → 新 Pod 启动等待 Readiness Step 3: [v0.26] [v0.27] [v0.27] → 逐个替换 Step 4: [v0.27] [v0.27] [v0.27] → 全部升级完成0 停机3 项目实战环境准备目标在 Minikube 或真实 K8s 集群中部署 RAGFlow。# Minikube 本地测试minikube start--cpus4--memory8192--disk-size50g# 或连接现有 K8s 集群kubectl cluster-info kubectl get nodes分步实现步骤1创建 Namespace 和 ConfigMap/Secret# 1. 创建 Namespacekubectl create namespace ragflow# 2. 创建 Secret数据库密码等kubectl create secret generic ragflow-secrets-nragflow\--from-literalmysql-passwordRagflow2024!ProdK8s\--from-literalredis-passwordRagflow2024!ProdK8s\--from-literalminio-root-userragflow_admin\--from-literalminio-root-passwordRagflow2024!ProdK8s# 3. 创建 ConfigMapkubectl apply-f-EOF-nragflowapiVersion: v1 kind: ConfigMap metadata: name: ragflow-config data: DOC_ENGINE: infinity LOG_LEVEL: INFO WS: 2 TZ: Asia/Shanghai EOF步骤2部署有状态服务MySQL、Redis、MinIO、Infinity# ragflow-stateful.yaml---# MySQL StatefulSetapiVersion:apps/v1kind:StatefulSetmetadata:name:mysqlnamespace:ragflowspec:serviceName:mysqlreplicas:1selector:matchLabels:app:mysqltemplate:metadata:labels:app:mysqlspec:containers:-name:mysqlimage:mysql:8.0env:-name:MYSQL_ROOT_PASSWORDvalueFrom:secretKeyRef:name:ragflow-secretskey:mysql-password-name:MYSQL_DATABASEvalue:ragflowports:-containerPort:3306volumeMounts:-name:mysql-datamountPath:/var/lib/mysqlresources:requests:memory:2Gicpu:500mlimits:memory:4Gicpu:2livenessProbe:exec:command:[mysqladmin,ping,-uroot,-p$(MYSQL_ROOT_PASSWORD)]initialDelaySeconds:30periodSeconds:10volumeClaimTemplates:-metadata:name:mysql-dataspec:accessModes:[ReadWriteOnce]resources:requests:storage:50Gi---# Redis StatefulSet类似结构# MinIO StatefulSet类似结构# Infinity StatefulSet类似结构步骤3部署无状态服务API Server Task Executor HPA# ragflow-api.yamlapiVersion:apps/v1kind:Deploymentmetadata:name:ragflow-apinamespace:ragflowspec:replicas:3selector:matchLabels:app:ragflow-apitemplate:metadata:labels:app:ragflow-apispec:containers:-name:api-serverimage:infiniflow/ragflow:v0.26.0env:-name:LIGHTENvalue:1-name:MYSQL_HOSTvalue:mysql.ragflow.svc.cluster.local-name:REDIS_HOSTvalue:redis.ragflow.svc.cluster.local-name:MINIO_HOSTvalue:minio.ragflow.svc.cluster.local-name:DOC_ENGINEvalueFrom:configMapKeyRef:name:ragflow-configkey:DOC_ENGINE-name:MYSQL_PASSWORDvalueFrom:secretKeyRef:name:ragflow-secretskey:mysql-passwordports:-containerPort:9380resources:requests:memory:1Gicpu:500mlimits:memory:2Gicpu:2livenessProbe:httpGet:path:/api/v1/versionport:9380initialDelaySeconds:30periodSeconds:15readinessProbe:httpGet:path:/api/v1/versionport:9380initialDelaySeconds:10periodSeconds:5---apiVersion:v1kind:Servicemetadata:name:ragflow-apinamespace:ragflowspec:selector:app:ragflow-apiports:-port:9380targetPort:9380type:ClusterIP---# HPA for API ServerapiVersion:autoscaling/v2kind:HorizontalPodAutoscalermetadata:name:ragflow-api-hpanamespace:ragflowspec:scaleTargetRef:apiVersion:apps/v1kind:Deploymentname:ragflow-apiminReplicas:3maxReplicas:10metrics:-type:Resourceresource:name:cputarget:type:UtilizationaverageUtilization:70步骤4Ingress 配置# ragflow-ingress.yamlapiVersion:networking.k8s.io/v1kind:Ingressmetadata:name:ragflow-ingressnamespace:ragflowannotations:nginx.ingress.kubernetes.io/proxy-body-size:100mnginx.ingress.kubernetes.io/proxy-read-timeout:120cert-manager.io/cluster-issuer:letsencrypt-prodspec:ingressClassName:nginxtls:-hosts:-ragflow.yunfan.comsecretName:ragflow-tlsrules:-host:ragflow.yunfan.comhttp:paths:-path:/api/pathType:Prefixbackend:service:name:ragflow-apiport:number:9380-path:/pathType:Prefixbackend:service:name:ragflow-webport:number:80步骤5滚动升级与回滚# 升级到新版本kubectlsetimage deployment/ragflow-api\api-serverinfiniflow/ragflow:v0.27.0\-nragflow# 观察滚动升级进度kubectl rollout status deployment/ragflow-api-nragflow# 输出: Waiting for rollout to finish: 1 old replicas pending termination...# deployment ragflow-api successfully rolled out# 如果升级出问题立即回滚kubectl rollout undo deployment/ragflow-api-nragflow# 查看升级历史kubectl rollouthistorydeployment/ragflow-api-nragflow# 验证 HPA 自动扩缩# 模拟高负载在另一个终端kubectl run-i--ttyload-generator--rm--imagebusybox--restartNever-nragflow -- /bin/sh-c\while true; do wget -q -O- http://ragflow-api:9380/api/v1/version; done# 观察 HPA 变化kubectl get hpa ragflow-api-hpa-nragflow-w# NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS# ragflow-api-hpa Deployment/ragflow-api 85%/70% 3 10 3# ragflow-api-hpa Deployment/ragflow-api 92%/70% 3 10 5 ← 自动扩容# ragflow-api-hpa Deployment/ragflow-api 55%/70% 3 10 4 ← 自动缩容测试验证# test_k8s_deployment.pyimportsubprocessdeftest_all_pods_running():验证所有 Pod 都在运行resultsubprocess.run([kubectl,get,pods,-n,ragflow,-o,json],capture_outputTrue,textTrue)podsjson.loads(result.stdout)[items]not_ready[pforpinpodsifp[status][phase]!Running]assertlen(not_ready)0,f未运行 Pod:{[p[metadata][name]forpinnot_ready]}deftest_api_accessible():验证通过 Service 可访问 API# Port-forward 到 API Servicesubprocess.Popen([kubectl,port-forward,svc/ragflow-api,8080:9380,-n,ragflow])time.sleep(3)rrequests.get(http://localhost:8080/api/v1/version)assertr.status_code200deftest_hpa_scaling():验证 HPA 配置存在且正常resultsubprocess.run([kubectl,get,hpa,-n,ragflow,-o,json],capture_outputTrue,textTrue)hpasjson.loads(result.stdout)[items]assertlen(hpas)0,HPA 未配置deftest_rolling_update():验证滚动升级不会导致服务中断# 持续发请求同时在另一线程触发升级# 检查是否有任何请求失败pass完整代码清单路径说明column/chapter29/本章 K8s 部署清单docker/Docker 部署含部分 K8s 清单helm/Helm Chart如果社区提供4 项目总结优点 缺点维度K8s 部署Docker Compose裸机部署云托管 K8s (EKS/GKE)故障自愈★★★ 自动重启★☆☆ 手动★☆☆ 手动★★★ 自动弹性扩缩容★★★ HPA★☆☆ 不支持★☆☆ 不支持★★★ HPA Cluster Autoscaler滚动升级★★★ 零停机★☆☆ 需停机★☆☆ 需停机★★★ 零停机部署复杂度★☆☆ 较高★★★ 简单★★☆ 中等★★☆ 托管简化运维成本★★☆ 需 K8s 知识★★★ 低★★☆ 中等★★★ 平台减负资源开销★★☆ K8s 本身吃 1-2GB★★★ 开销小★★★ 开销小★★☆ 管理费适用场景生产环境多副本需要 99.9% 可用性自动故障恢复。弹性流量有明显的高峰低谷如办公时间 vs 夜间HPA 自动调整资源。多团队多环境通过 Namespace 隔离开发/测试/生产环境。灰度发布通过 Ingress 的 canary 注解做金丝雀发布。已有 K8s 基础设施公司其他服务已在 K8s 上统一技术栈降低运维成本。不适用场景小规模单机 100 人在用、 500 份文档——K8s 的复杂度不值得。无 K8s 运维能力团队没人懂 K8s——硬上等于自找麻烦。先用 Docker Compose等规模够了再迁移。注意事项持久化存储StatefulSet 的 PVC 一定要正确配置 StorageClass云上用 gp3/managed-premium自建用 local-path/nfs。Resource Limits 必须设不给 Pod 设 limits一个 OOM 可能拖垮整个节点。——K8s 的 QoS 机制会优先驱逐无 limits 的 Pod。HPA 冷却期默认缩容冷却 5 分钟——防止频繁扩缩flapping。短时尖峰流量可能来不及响应。Pod Disruption BudgetPDB设minAvailable: 2防止节点维护时 API Server 全部下线。Secret 加密K8s Secret 默认是 base64 编码非加密。生产环境建议启用 etcd 加密或使用 External Secrets Operator。常见踩坑经验故障现象根因解决方法Pod 一直 CrashLoopBackOff容器启动时 MySQL/Redis 还未就绪加 initContainers 等待依赖服务就绪HPA 不自动扩容metrics-server 未安装或未采集自定义指标kubectl get apiservice确认 metrics 可用PVC 绑定失败StorageClass 未设置或 PV 不足检查 StorageClass 和 PV 状态滚动升级后服务不可用新旧版本 API 不兼容或 readinessProbe 太宽松设合理的 readinessProbe配合 PDBStatefulSet Pod 删除后数据丢失PVC 的persistentVolumeReclaimPolicy为 Delete改为 Retain手动清理思考题当前的 HPA 基于 CPU 和内存指标但 CPU 和内存不直接等于用户体验——CPU 高但延迟正常是没问题的。请设计一个基于P95 聊天延迟的 HPA 自定义指标配置——当 P95 延迟超过 8 秒时触发扩容。公司在中国和欧洲各有业务用户需要通过最近的节点访问 RAGFlow 以降低延迟。请设计一个多地域 K8s 部署方案——包括数据同步策略MySQL 主从、MinIO 跨区域复制、流量路由GeoDNS和故障切换Failover。答案提示见第30章末尾或附录 D。延伸阅读与资源10倍开发者的 Dify 魔法书从零构建全栈 AI 应用后端工程师转型AI第一课-Ollama 与私有化大模型实战大型语言模型(LLM) vLLM 高性能推理落地实战Agent开发之LlamaIndex 实战修炼与源码进阶大语言模型Transformers 实战修炼与源码剖析
返回列表