[{"data":1,"prerenderedAt":385},["ShallowReactive",2],{"note:\u002Fprojects\u002Frag-knowledge-base\u002F02-环境搭建与启动预热":3},{"id":4,"title":5,"body":6,"date":363,"description":364,"draft":365,"extension":366,"featured":365,"kind":367,"lastmod":363,"meta":368,"navigation":257,"path":372,"planned":373,"section":374,"seo":375,"source":376,"stem":377,"tags":378,"toc":257,"weight":383,"__hash__":384},"notes\u002Fprojects\u002Frag-knowledge-base\u002F02-环境搭建与启动预热.md","RAG 环境搭建：六个容器与启动预热",{"type":7,"value":8,"toc":356},"minimark",[9,14,115,119,136,139,142,186,193,196,203,213,216,222,228,231,288,291,297,312,315,349,352],[10,11,13],"h2",{"id":12},"六个容器各自在管什么","六个容器，各自在管什么",[15,16,17,33],"table",{},[18,19,20],"thead",{},[21,22,23,27,30],"tr",{},[24,25,26],"th",{},"容器",[24,28,29],{},"角色",[24,31,32],{},"不启动会怎样",[34,35,36,51,64,77,90,103],"tbody",{},[21,37,38,45,48],{},[39,40,41],"td",{},[42,43,44],"code",{},"api",[39,46,47],{},"FastAPI 服务本体，跑意图、检索、生成",[39,49,50],{},"什么都没了",[21,52,53,58,61],{},[39,54,55],{},[42,56,57],{},"mysql",[39,59,60],{},"控制面：知识库版本、会话历史、反馈",[39,62,63],{},"版本解析失败，启动直接中断",[21,65,66,71,74],{},[39,67,68],{},[42,69,70],{},"redis",[39,72,73],{},"L2 缓存：检索结果、embedding 向量",[39,75,76],{},"缓存失效，性能掉档但不报错",[21,78,79,84,87],{},[39,80,81],{},[42,82,83],{},"milvus",[39,85,86],{},"向量库：FAQ 集合 + 文档集合",[39,88,89],{},"检索栈预热超时，服务不接流量",[21,91,92,97,100],{},[39,93,94],{},[42,95,96],{},"etcd",[39,98,99],{},"Milvus 的元数据与服务发现",[39,101,102],{},"Milvus 起不来",[21,104,105,110,113],{},[39,106,107],{},[42,108,109],{},"minio",[39,111,112],{},"Milvus 的对象存储（日志、快照、段文件）",[39,114,102],{},[116,117,118],"p",{},"很多人会疑惑：为什么一个 RAG 项目要拖六个容器？因为 Milvus 从来不是单机组件。\n它需要 etcd 存元数据和做服务发现，需要 MinIO 存对象的段文件和日志快照。\n把这两个当成「Milvus 的隐藏依赖」记下来，排障时能省半天。",[116,120,121,125,126,129,130,132,133,135],{},[122,123,124],"strong",{},"依赖链是分层的","：",[42,127,128],{},"etcd + minio -> milvus -> api","，",[42,131,57],{}," 和 ",[42,134,70],{}," 与 api 并行。\n如果 api 起不来但 Milvus 是健康的，问题基本在配置或模型，不在检索基础设施。",[10,137,138],{"id":138},"为什么要做环境隔离",[116,140,141],{},"这套项目默认用「独立环境」的方式部署，而不是覆盖已有服务。手段有三个：",[143,144,145,153,164],"ul",{},[146,147,148,149,152],"li",{},"独立的 Compose 项目名（",[42,150,151],{},"COMPOSE_PROJECT_NAME","）",[146,154,155,156,159,160,163],{},"独立的容器名（",[42,157,158],{},"MYSQL_CONTAINER_NAME","、",[42,161,162],{},"API_CONTAINER_NAME"," 等一整套）",[146,165,166,167,170,171,174,175,178,179,182,183],{},"独立的宿主机端口：API ",[42,168,169],{},"18000","、MySQL ",[42,172,173],{},"13317","、Redis ",[42,176,177],{},"16379","、Milvus ",[42,180,181],{},"29530","、MinIO ",[42,184,185],{},"19000\u002F19001",[116,187,188,189,192],{},"结论很直接：",[122,190,191],{},"新环境和旧部署可以同时存在，互不抢占端口、互不共享数据卷。","\n对「机器上已经有一套在跑的 RAG」这种真实情况，这比「先停旧的再起新的」安全得多。",[10,194,195],{"id":195},"启动时到底校验了什么",[116,197,198,199,202],{},"入口文件很短，",[42,200,201],{},"warmup_runtime()"," 里的顺序就是全部的启动契约：",[204,205,211],"pre",{"className":206,"code":208,"language":209,"meta":210},[207],"language-text","validate_runtime_environment()      # Key、Milvus、MySQL、本地模型、场景配置\nbootstrap_mysql_schema()            # 建表 \u002F 迁移控制面\nvalidate_active_kb_versions()       # 必须存在 active 知识库版本\nwarmup_intent_decision_gateway()    # BERT 意图模型进场\n（后台）refresh_llm_runtime_status() # LLM 供应商连通性，异步探测\nstart_retrieval_warmup_background() # BGE-M3、Reranker、Milvus collection\nwait_for_retrieval_warmup()         # 等它完成，再放行流量\n","text","",[42,212,208],{"__ignoreMap":210},[116,214,215],{},"两个设计点值得抄：",[116,217,218,221],{},[122,219,220],{},"第一，LLM 连通性放到后台。"," 供应商接口响应慢是常态，如果放在主流程里同步探测，\n启动时间就交给了外部服务。所以它异步跑，只写入运行状态。",[116,223,224,227],{},[122,225,226],{},"第二，检索栈预热必须同步等待。"," BGE-M3 和 Reranker 是本地模型，首次加载要几十秒；\nMilvus collection 首次连接也要握手。如果不等，第一个真实用户请求就要和预热竞争资源，\n表现为「首问特别慢」甚至超时。宁可启动慢三十秒，也不要让用户踩这个坑。",[10,229,230],{"id":230},"怎么确认你真的起对了",[204,232,236],{"className":233,"code":234,"language":235,"meta":210,"style":210},"language-powershell shiki shiki-themes github-light github-dark","# 只看本地运行时依赖是否齐全，不启动容器\npython scripts\u002Ftools\u002Fcheck_local_runtime.py\n\n# 启动独立单场景环境\n.\\scripts\\deploy\\deploy_docker_single.ps1\n\n# 验证容器、端口、接口是否都对\n.\\scripts\\deploy\\verify_single_docker.ps1\n","powershell",[42,237,238,246,252,259,265,271,276,282],{"__ignoreMap":210},[239,240,243],"span",{"class":241,"line":242},"line",1,[239,244,245],{},"# 只看本地运行时依赖是否齐全，不启动容器\n",[239,247,249],{"class":241,"line":248},2,[239,250,251],{},"python scripts\u002Ftools\u002Fcheck_local_runtime.py\n",[239,253,255],{"class":241,"line":254},3,[239,256,258],{"emptyLinePlaceholder":257},true,"\n",[239,260,262],{"class":241,"line":261},4,[239,263,264],{},"# 启动独立单场景环境\n",[239,266,268],{"class":241,"line":267},5,[239,269,270],{},".\\scripts\\deploy\\deploy_docker_single.ps1\n",[239,272,274],{"class":241,"line":273},6,[239,275,258],{"emptyLinePlaceholder":257},[239,277,279],{"class":241,"line":278},7,[239,280,281],{},"# 验证容器、端口、接口是否都对\n",[239,283,285],{"class":241,"line":284},8,[239,286,287],{},".\\scripts\\deploy\\verify_single_docker.ps1\n",[116,289,290],{},"启动日志里按顺序出现下面几行，就说明预热链路是完整的：",[204,292,295],{"className":293,"code":294,"language":209,"meta":210},[207],"Runtime preflight passed: ...\nRuntime MySQL schema bootstrap passed: ...\nRuntime active KB version check passed: ...\nRuntime BERT intent model warmup passed: ...\nRuntime retrieval warmup passed: ...\n",[42,296,294],{"__ignoreMap":210},[116,298,299,300,303,304,307,308,311],{},"拿到 ",[42,301,302],{},"http:\u002F\u002F127.0.0.1:18000"," 之后，接口文档在 ",[42,305,306],{},"\u002Fapi\u002Fdocs","，管理端缓存状态在 ",[42,309,310],{},"\u002Fapi\u002Fadmin\u002Fcache\u002Fstatus","。",[10,313,314],{"id":314},"取舍与边界",[143,316,317,323,329,343],{},[146,318,319,322],{},[122,320,321],{},"冷启动换稳定","：预热让首次请求体验一致，代价是启动慢、滚动发布需要就绪探针配合。",[146,324,325,328],{},[122,326,327],{},"单机 Compose 不是生产形态","：这套编排适合本地、演示和单机交付；\n真正多副本部署时，Milvus 和 MySQL 应该走独立集群，api 无状态横向扩。",[146,330,331,334,335,338,339,342],{},[122,332,333],{},"模型要提前放好","：BGE-M3 和 Reranker 体积不小，",[42,336,337],{},"download_models.py"," 支持指定镜像源；\n内网环境建议先把 ",[42,340,341],{},"models\u002F"," 目录整体同步过去，而不是在容器里现下。",[146,344,345,348],{},[122,346,347],{},"失败信息就是排障入口","：preflight 报哪一项，就修哪一项，不要去猜。",[116,350,351],{},"下一篇进入数据侧：企业文档又脏又杂，它是怎么被解析、切分、加指纹，\n最后变成可以增量更新的可检索知识的。",[353,354,355],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":210,"searchDepth":248,"depth":248,"links":357},[358,359,360,361,362],{"id":12,"depth":248,"text":13},{"id":138,"depth":248,"text":138},{"id":195,"depth":248,"text":195},{"id":230,"depth":248,"text":230},{"id":314,"depth":248,"text":314},"2026-06-05","说明 FastAPI、MySQL、Redis、Milvus、etcd 与 MinIO 的职责，以及服务启动时需要完成的预热校验。",false,"md","project-chapter",{"migration":369},{"generator":370,"sourceSha256":371},"nuxt-site\u002Fscripts\u002Fmigrate-content.mjs","3471e86f3acea1e1d2f641b3c94fbfca2899cae6e6628cbe0be62a6cf3938df3","\u002Fprojects\u002Frag-knowledge-base\u002F02-环境搭建与启动预热",null,"projects",{"title":5,"description":364},"content\u002Fprojects\u002Frag-knowledge-base\u002F02-环境搭建与启动预热.md","projects\u002Frag-knowledge-base\u002F02-环境搭建与启动预热",[379,380,381,382],"RAG","Docker","Milvus","FastAPI",32,"Yu6gOybQJXRP6kV81wPaeech2uh-g0eaclTGSrsY-wA",1791279145025]