RAGFlow v0.25.0 源码镜像构建:三层容器化与生产级部署详解
发布时间:2026/9/21 0:54:50 作者:尧图编辑部 阅读量:1,286

1. 项目概述为什么需要亲手构建 RAGFlow v0.25.0 的源码镜像RAGFlow 是当前中文 RAG检索增强生成领域里少有的、真正把“开箱即用”和“深度可控”同时做扎实的开源项目。它不像某些框架只提供抽象接口也不像部分工具堆砌功能却难以调试——RAGFlow 的核心价值恰恰在于所有能力都扎根于可读、可改、可追踪的 Python 源码中而它的部署形态又高度依赖容器化封装。所以当你看到标题里“从源码构架 ragflow v0.25.0 镜像”这不是一个简单的docker build命令复刻而是一次对 RAGFlow 工程底座的系统性解剖。我去年在给三家客户做本地知识库平台选型时反复验证过直接拉取官方 Docker Hub 上的ragflow/ragflow:0.25.0镜像虽然启动快但一旦涉及模型替换比如换成 Qwen2-7B-Int4、向量库迁移从默认的 Milvus 切到 Weaviate、或自定义 OCR 后处理逻辑就会卡在“不知道哪一行代码控制了 Redis 连接超时”“不清楚 embedding 模型加载路径是否被硬编码”这类问题上。这时候你手里必须有一份完全透明、可断点、可 patch 的镜像——它不是从 Dockerfile 一层层 COPY 进去的黑盒而是从git clone开始把requirements.txt的每个包版本、Dockerfile的每条指令、docker-compose.yml里每个服务的启动顺序全部纳入你的掌控范围。v0.25.0 这个版本尤其关键它首次将 Web UI 的构建流程从 Node.js 构建阶段彻底解耦前端资源不再打包进后端镜像而是通过 Nginx 静态服务独立提供同时后端 API 层引入了更细粒度的异步任务队列管理这对 Redis 和 Celery 的配置耦合度提出了新要求。如果你正在评估 RAGFlow 是否适配企业级私有化部署场景或者需要为合规审计准备完整的软件物料清单SBOM那么亲手构建这个镜像不是“可选项”而是上线前的必经工序。它解决的不是“能不能跑起来”的问题而是“出问题时能不能三分钟内定位到源码第 387 行”的问题。2. 整体架构拆解RAGFlow v0.25.0 的三层容器化分层逻辑RAGFlow v0.25.0 的镜像构建绝非单体式打包它遵循清晰的“职责分离依赖收敛”原则将整个系统拆解为三个逻辑层每一层对应一个独立构建的镜像最终通过docker-compose.yml协同运行。这种设计不是为了炫技而是为了解决 RAG 场景下最典型的工程矛盾模型推理、文档解析、Web 交互这三类任务对计算资源、依赖环境、更新频率的需求天差地别。比如 OCR 引擎如 PaddleOCR需要 CUDA 11.8 cuDNN 8.6而 Web 前端只需要 Nginx 的静态文件服务再比如 embedding 模型升级可能每周一次但数据库 schema 变更一年才一次。如果强行塞进一个镜像每次微调模型都要重跑整个 CI 流程构建时间从 2 分钟飙升到 18 分钟CI 资源浪费严重。v0.25.0 的三层结构正是对这一痛点的精准回应。2.1 第一层ragflow-backend—— 核心业务逻辑与 API 网关这是整个系统的“大脑”基于 FastAPI 构建承载所有 RAG 核心能力知识库创建、文档上传解析、向量索引构建、检索查询、LLM 调用编排。它的 Dockerfile 位于项目根目录docker/backend/Dockerfile采用多阶段构建multi-stage build第一阶段用python:3.10-slim-bookworm作为构建环境安装poetry并执行poetry install --no-dev确保只保留生产依赖第二阶段则切换到更轻量的python:3.10-slim-bookworm基础镜像仅 COPY 第一阶段生成的.venv虚拟环境中的site-packages目录。这种设计让最终镜像体积从 1.2GB 压缩到 480MB关键在于它剥离了所有构建时依赖如gcc、cmake只留下运行时必需的.so文件和 Python 字节码。特别注意RUN pip install --no-cache-dir -r requirements.txt这行已被移除因为 v0.25.0 全面迁移到 Poetry 管理依赖pyproject.toml中明确锁定了pymilvus2.4.5、xinference0.14.0等关键版本避免了 pip 安装时因网络波动导致的版本漂移。2.2 第二层ragflow-web—— 独立前端服务v0.25.0 最大的架构变更就发生在这里。旧版本v0.23.x的前端代码是通过npm run build生成dist/目录然后 COPY 进ragflow-backend镜像由 Uvicorn 的静态文件中间件提供服务。这种方式导致后端镜像臃肿且前端热更新需重启整个服务。新版本彻底解耦ragflow-web镜像基于nginx:alpine其Dockerfile位于docker/web/Dockerfile只做一件事——把web/dist/目录下的 HTML、JS、CSS 文件 COPY 到 Nginx 的/usr/share/nginx/html/路径并覆盖默认的nginx.conf添加反向代理规则将/api/前缀的请求转发给backend服务。这意味着你可以单独更新前端 UI比如修复一个按钮点击无响应的 bug只需重新构建并推送ragflow-web镜像后端服务完全不受影响。我在某金融客户现场就利用这点在不中断知识库查询服务的前提下20 分钟内完成了管理员后台权限面板的灰度发布。2.3 第三层ragflow-init—— 初始化与数据迁移工具这是一个常被忽略但极其关键的“隐形层”。它不是一个长期运行的服务而是一个一次性执行的初始化容器负责在系统首次启动时完成数据库表结构创建、默认用户插入、初始配置写入等操作。其镜像基于python:3.10-slim-bookworm核心逻辑在scripts/init_db.py中它会连接 PostgreSQL执行alembic upgrade head进行数据库迁移并检查settings.py中定义的DEFAULT_EMBEDDING_MODEL是否已存在于embedding_model表中若不存在则自动插入一条记录。这个设计的价值在于它把“环境准备”从运维脚本如 shell 脚本提升到了应用代码层面确保初始化逻辑与后端代码版本严格一致。试想一下如果某次升级新增了一个knowledgebox_metadata字段而你用旧版 shell 脚本初始化数据库新字段缺失会导致后续所有知识库创建失败——ragflow-init容器则天然规避了这种版本错配风险。3. 核心细节解析构建过程中的五个关键决策点与原理构建 RAGFlow v0.25.0 镜像不是机械执行docker build而是在每个环节做出符合生产环境要求的技术决策。这些决策背后都有明确的工程权衡理解它们才能避免“构建成功但线上崩盘”的尴尬。以下是我在实际构建中反复验证、必须重点关注的五个核心点。3.1 决策一Python 基础镜像选择 ——slim-bookworm而非alpineDockerfile中明确指定FROM python:3.10-slim-bookworm而非更小的python:3.10-alpine。原因很实在RAGFlow 重度依赖pymilvusMilvus 客户端和xinference分布式推理框架这两个包的 wheel 文件在 PyPI 上只提供了manylinux构建的版本它们链接的是 glibc 而非 musl libc。Alpine Linux 使用 musl libc强行安装会导致ImportError: Error loading shared library libstdc.so.6这类符号链接错误。slim-bookworm基于 Debian 12使用标准 glibc兼容性完美。虽然镜像体积比 Alpine 大约 80MB但换来的是 100% 的依赖稳定性。我曾尝试用apk add gcompat强行桥接结果在 GPU 推理场景下触发了 CUDA 驱动的 ABI 不兼容最终回滚。记住在 AI 工程中“小”永远不该以“不可靠”为代价。3.2 决策二依赖安装方式 —— Poetry 锁定而非 Pip 直装pyproject.toml是 v0.25.0 的依赖权威来源。执行poetry export -f requirements.txt --without-hashes requirements.txt生成的requirements.txt文件其每一行都包含精确的版本号如fastapi0.111.0而非或~。这是为了杜绝“依赖地狱”假设某天uvicorn发布了 0.30.0 版本它内部重构了Lifespan协议而 RAGFlow 的main.py中app FastAPI(lifespanlifespan)的写法恰好与新协议不兼容。如果requirements.txt写的是uvicorn0.29.0CI 流程会自动拉取 0.30.0导致服务启动失败。Poetry 的锁定机制强制所有环境使用完全一致的依赖树这是企业级部署的生命线。实操中我在Dockerfile的构建阶段加入RUN poetry export -f requirements.txt --without-hashes | grep -v ^\s*# | xargs pip install --no-cache-dir确保即使requirements.txt文件被意外修改也能从pyproject.toml动态生成最新锁定列表。3.3 决策三Redis 连接池配置 ——max_connections100的量化依据settings.py中REDIS_URL默认值为redis://redis:6379/0但真正决定性能的是redis-py客户端的连接池参数。RAGFlow 在core/redis_client.py中初始化连接池时硬编码了max_connections100。这个数字不是拍脑袋定的而是基于典型 RAG 场景的并发压力测算一个知识库查询请求会触发 3-5 次 Redis 操作缓存 key 生成、向量 ID 查询、元数据读取、会话状态更新。假设你的 API 网关如 Nginx配置了worker_connections 1024理论最大并发为 1024那么 100 的连接池上限能支撑约 20-30 个并发查询请求。如果压测发现redis.exceptions.ConnectionError: Error 113 connecting to redis:6379. No route to host.频发说明连接池耗尽此时应优先检查ulimit -n文件描述符限制是否足够而非盲目调高max_connections——因为 Redis 服务端本身也有maxclients限制默认 10000100 的客户端连接池是安全冗余的合理选择。3.4 决策四PostgreSQL 初始化 ——initdb与pg_hba.conf的协同docker-compose.yml中postgres服务的volumes挂载了./docker/postgres/init.sql:/docker-entrypoint-initdb.d/init.sql。这个init.sql文件内容极简只有CREATE DATABASE ragflow;。但真正的初始化魔法发生在postgres镜像的入口脚本中。当检测到/var/lib/postgresql/data目录为空时它会自动执行initdb命令创建集群并生成默认的pg_hba.conf文件。该文件定义了客户端连接认证规则其中关键一行是host all all 0.0.0.0/0 md5允许任何 IP 通过密码认证连接。RAGFlow 的settings.py中DATABASE_URL使用postgresql://postgres:postgrespostgres:5432/ragflow这里的postgres用户密码必须与docker-compose.yml中postgres服务的POSTGRES_PASSWORD环境变量严格一致。我曾在一个离线环境中因POSTGRES_PASSWORD被误设为空字符串导致ragflow-init容器连接 PostgreSQL 失败日志里只显示psycopg2.OperationalError: FATAL: password authentication failed for user postgres排查了两小时才发现是环境变量拼写错误。3.5 决策五Xinference 模型注册 ——--model-source huggingface的隐含约束RAGFlow v0.25.0 默认集成 Xinference 作为 LLM 和 Embedding 模型的统一调度层。docker-compose.yml中xinference服务的启动命令为xinference-local --host 0.0.0.0 --port 9997 --model-source huggingface。这个--model-source huggingface参数至关重要它告诉 Xinference 所有模型都从 Hugging Face Hub 下载而非本地路径。因此当你在 RAGFlow Web UI 的“模型管理”页面注册一个新 embedding 模型如BAAI/bge-m3时Xinference 会自动执行huggingface-cli download BAAI/bge-m3 --local-dir /root/.xinference/models/BAAI/bge-m3。这意味着你的构建环境必须能访问 Hugging Face国内需配置镜像源且xinference容器的/root/.xinference目录必须挂载为持久化卷否则模型下载后重启容器就会丢失。我在测试环境曾忘记挂载该卷结果每次重启xinference所有已注册模型都消失UI 上显示“Model not found”根源就在这里。4. 实操过程详解从零开始构建 v0.25.0 镜像的完整步骤与参数说明现在我们进入最硬核的部分手把手构建 RAGFlow v0.25.0 的三个镜像。这不是复制粘贴教程而是每一步都解释“为什么这么做”以及“不做会怎样”。整个过程在一台 16GB 内存、Ubuntu 22.04 的开发机上完成全程联网需确保能访问 GitHub 和 PyPI。4.1 步骤一环境准备与源码获取首先确保系统已安装git、docker、docker-composev2.20和poetryv1.7。执行以下命令克隆指定版本源码git clone https://github.com/infiniflow/ragflow.git cd ragflow git checkout v0.25.0提示务必使用git checkout v0.25.0而非git pull因为主干分支main可能已合并了未发布的 PR导致依赖版本不匹配。我曾因跳过这步在构建时遇到ModuleNotFoundError: No module named xinference追查发现是pyproject.toml中xinference的版本号被临时改为0.15.0-dev而 PyPI 上尚未发布。接着为加速国内构建需配置国内镜像源。编辑pyproject.toml在[tool.poetry.source]下添加[[tool.poetry.source]] name tsinghua url https://pypi.tuna.tsinghua.edu.cn/simple/ priority primary同时为xinference的模型下载提速在docker-compose.yml的xinference服务下添加环境变量environment: - HF_ENDPOINThttps://hf-mirror.comHF_ENDPOINT是 Hugging Face 官方支持的镜像源配置项比手动替换transformers库中的 URL 更可靠。4.2 步骤二构建ragflow-backend镜像进入docker/backend/目录执行构建命令docker build -t ragflow-backend:v0.25.0 \ --build-arg POETRY_VERSION1.7.1 \ --build-arg PYTHON_VERSION3.10 \ -f Dockerfile .这里--build-arg传递了两个构建参数POETRY_VERSION确保构建环境使用指定版本的 Poetry避免因 Poetry 自身 bug 导致依赖解析失败PYTHON_VERSION与基础镜像保持一致。构建过程约需 8-12 分钟关键观察点是日志中Installing dependencies from lock file这一行它确认 Poetry 正在从poetry.lock文件精确还原依赖。构建完成后用docker images | grep ragflow-backend验证镜像存在大小应在 480MB 左右。若远大于此如 1.1GB大概率是构建阶段未正确使用多阶段构建gcc等编译工具被 COPY 进了最终镜像。4.3 步骤三构建ragflow-web镜像前端构建需先生成静态资源。回到项目根目录执行cd web npm install --registry https://registry.npmmirror.com npm run build--registry参数指向 CNPM 镜像避免node_modules安装失败。npm run build会在web/dist/下生成压缩后的 HTML/JS/CSS 文件。然后进入docker/web/目录cd ../docker/web docker build -t ragflow-web:v0.25.0 -f Dockerfile .此构建极快30 秒因为它只是把dist/目录 COPY 进 Nginx 镜像。验证方法运行临时容器并检查文件docker run --rm -it ragflow-web:v0.25.0 ls -la /usr/share/nginx/html/应看到index.html、static/等文件。若报错No such file or directory说明Dockerfile中的COPY ../web/dist/ /usr/share/nginx/html/路径错误需检查相对路径。4.4 步骤四构建ragflow-init镜像此镜像构建最简单但最容易被忽略。进入docker/init/目录cd ../../docker/init docker build -t ragflow-init:v0.25.0 -f Dockerfile .其Dockerfile只有 5 行基于 Python 镜像COPYscripts/init_db.py设置入口点。构建后可通过docker run --rm -it --network ragflow_default ragflow-init:v0.25.0 python scripts/init_db.py手动测试初始化脚本是否能连通postgres服务需提前启动postgres容器。若报错psycopg2.OperationalError: could not translate host name postgres to address说明--network ragflow_default参数错误应替换为你的docker-compose定义的实际网络名。4.5 步骤五启动与验证全栈服务将docker-compose.yml中的镜像标签统一改为v0.25.0services: backend: image: ragflow-backend:v0.25.0 web: image: ragflow-web:v0.25.0 init: image: ragflow-init:v0.25.0然后执行docker-compose up -d postgres redis xinference # 等待 30 秒确保基础服务就绪 docker-compose up -d init # 等待 10 秒确保初始化完成 docker-compose up -d backend web注意必须按此顺序启动init依赖postgresbackend依赖init和redisweb依赖backend。直接docker-compose up -d可能因启动竞态导致初始化失败。启动后用docker-compose logs -f backend查看后端日志关键成功标志是INFO: Application startup complete和INFO: Uvicorn running on http://0.0.0.0:8000。若出现Connection refused错误90% 是redis或postgres服务未完全就绪需检查docker-compose ps状态。5. 常见问题与排查技巧实录我在 12 个真实部署中踩过的坑构建 RAGFlow 镜像的过程看似线性实则充满隐蔽陷阱。以下是我过去半年在 12 个不同客户环境从 8GB 内存的边缘设备到 128GB 的 GPU 服务器中总结的高频问题、根本原因及独家排查技巧。这些问题在官方文档中几乎找不到答案全是血泪经验。5.1 问题一backend容器反复重启日志显示OSError: [Errno 24] Too many open files现象docker-compose ps显示backend状态为Restartingdocker-compose logs backend滚动输出大量OSError: [Errno 24]。根本原因Linux 系统对单个进程打开文件描述符file descriptor, FD数量有限制默认通常为 1024。RAGFlow 后端使用asyncio处理大量并发请求每个 HTTP 连接、Redis 连接、数据库连接都会占用一个 FD。当并发请求数超过 1024新连接就会失败。排查技巧进入backend容器内部执行ulimit -n查看当前限制再执行ls /proc/$(cat /tmp/pid)/fd | wc -l/tmp/pid是 Uvicorn 主进程 PID 文件路径统计实际使用 FD 数。若后者接近前者即为瓶颈。解决方案在docker-compose.yml的backend服务下添加ulimits配置ulimits: nofile: soft: 65536 hard: 65536同时在宿主机上执行echo * soft nofile 65536 | sudo tee -a /etc/security/limits.conf永久生效。切记ulimits必须在docker-compose.yml中声明仅改宿主机 limits.conf 对容器无效。5.2 问题二Web UI 加载空白浏览器控制台报Failed to load resource: the server responded with a status of 404 (Not Found)现象http://localhost:3000打开后是白屏F12 控制台 Network 标签页显示main.123456.js返回 404。根本原因ragflow-web镜像中的 Nginx 配置未正确映射静态资源路径。v0.25.0 的nginx.conf中location / { root /usr/share/nginx/html; }是正确的但若web/dist/目录在构建时为空npm run build失败则COPY进去的就是空目录。排查技巧运行docker run --rm -it ragflow-web:v0.25.0 ls -la /usr/share/nginx/html/若输出只有index.html且大小为 0 字节说明构建失败。解决方案重新执行cd web npm run build并检查web/dist/目录是否包含index.html和static/子目录。若npm run build报错Cannot find module vue说明package.json中devDependencies未安装需补npm install。5.3 问题三xinference服务启动后RAGFlow UI 中模型列表为空日志显示xinference.exceptions.XinferenceRequestFailed: Request failed with status code 404现象http://localhost:9997Xinference UI可访问但http://localhost:3000的模型管理页无任何模型。根本原因xinference容器的--model-source huggingface参数生效但ragflow-backend容器无法访问xinference服务。docker-compose.yml中backend服务的environment下XINFERENCE_ENDPOINThttp://xinference:9997是正确的但若xinference服务的ports配置为9997:9997则外部可访问但容器间通信应使用内部端口9997无需映射。排查技巧进入backend容器执行curl -v http://xinference:9997/v1/models若返回Connection refused说明网络不通若返回{data:[]}说明通信正常但无模型。解决方案确认xinference服务的network_mode为default即与backend同属一个docker network并确保backend的depends_on包含xinference。若仍失败临时在xinference服务下添加command: [xinference-local, --host, 0.0.0.0, --port, 9997, --model-source, huggingface, --log-level, DEBUG]查看详细日志。5.4 问题四ragflow-init容器执行完毕后退出但postgres数据库中ragflow库为空alembic_version表不存在现象docker-compose logs init显示Database initialized successfully但docker exec -it ragflow_postgres_1 psql -U postgres -d ragflow -c \dt返回Did not find any relations.。根本原因init_db.py脚本中的alembic upgrade head命令失败但脚本未捕获异常导致静默退出。常见原因是alembic.ini中sqlalchemy.url配置错误或postgres服务未完全就绪init启动太快。排查技巧修改init_db.py在alembic.command.upgrade(config, head)前后添加print(Before upgrade)和print(After upgrade)然后重新构建ragflow-init镜像并运行观察日志是否打印After upgrade。解决方案在docker-compose.yml的init服务下添加健康检查healthcheck等待postgres就绪healthcheck: test: [CMD-SHELL, pg_isready -U postgres -d ragflow] interval: 30s timeout: 10s retries: 5并在backend服务的depends_on中引用该健康检查depends_on: postgres: condition: service_healthy5.5 问题五GPU 版本xinference启动失败日志显示CUDA error: no kernel image is available for execution on the device现象在docker-compose.yml中将xinference服务的image改为xinference/xinference:latest-gpu启动后容器立即退出。根本原因CUDA 驱动版本与xinference镜像中预装的 CUDA Toolkit 版本不兼容。xinference:latest-gpu基于 CUDA 12.1而宿主机 NVIDIA 驱动版本过低如 470.x不支持 CUDA 12.1。排查技巧在宿主机执行nvidia-smi查看右上角显示的CUDA Version: xx.x再执行docker run --rm --gpus all nvidia/cuda:12.1.1-runtime-ubuntu22.04 nvidia-smi若报错NVIDIA driver version is insufficient即为版本不匹配。解决方案根据nvidia-smi显示的 CUDA 版本选择对应xinference镜像。例如若宿主机 CUDA Version 为 11.8则使用xinference/xinference:0.14.0-gpu-cu118。黄金法则宿主机驱动版本 ≥ 镜像所需驱动版本具体对应关系查 NVIDIA 官网 CUDA Toolkit 文档。6. 进阶实践如何基于此镜像实现企业级定制与安全加固构建出v0.25.0镜像是起点而非终点。在真实企业环境中你需要在此基础上进行深度定制以满足合规、安全、可观测性等硬性要求。以下是我在金融、政务客户项目中落地的三项关键实践每项都附带可直接复用的代码片段。6.1 实践一嵌入式审计日志 —— 记录每一次知识库操作的完整上下文RAGFlow 默认日志只记录 API 请求路径和状态码无法满足等保三级对“操作留痕”的要求。我们需要在core/rag_service.py的create_knowledgebase方法中注入审计逻辑。在函数开头添加import logging from datetime import datetime from core.model_manager import ModelManager logger logging.getLogger(audit) def create_knowledgebase(...): # ... 原有代码 ... audit_data { timestamp: datetime.utcnow().isoformat(), user_id: current_user.id, action: create_knowledgebase, kb_name: req.name, kb_description: req.description, embedding_model: ModelManager.get_instance().get_model_info(req.embedding_model).name, ip_address: request.client.host } logger.info(fAUDIT: {json.dumps(audit_data)}) # ... 后续代码 ...然后在docker/backend/Dockerfile中于COPY . /app后添加RUN pip install --no-cache-dir python-json-logger并在settings.py中配置审计日志处理器LOGGING { version: 1, disable_existing_loggers: False, formatters: { json: { (): pythonjsonlogger.jsonlogger.JsonFormatter, fmt: %(asctime)s %(name)s %(levelname)s %(message)s } }, handlers: { audit_file: { class: logging.handlers.RotatingFileHandler, filename: /var/log/ragflow/audit.log, maxBytes: 10485760, backupCount: 5, formatter: json } }, loggers: { audit: { handlers: [audit_file], level: INFO, propagate: False } } }最后在docker-compose.yml中挂载日志卷volumes: - ./logs:/var/log/ragflow这样所有知识库创建、删除、文档上传操作都会以 JSON 格式写入./logs/audit.log可直接对接 SIEM 系统。6.2 实践二模型签名验证 —— 防止恶意模型注入RAGFlow 允许用户通过 UI 注册任意 Hugging Face 模型这带来供应链风险。我们需在模型注册 API 中增加签名验证。首先在core/model_management.py的register_model函数中添加对model_url的校验import hashlib import requests def register_model(...): # ... 原有代码 ... if model_source huggingface: # 从 Hugging Face Hub 获取模型 card card_url fhttps://huggingface.co/{model_id}/raw/main/README.md try: response requests.get(card_url, timeout10) response.raise_for_status() # 计算 README.md 的 SHA256 card_hash hashlib.sha256(response.content).hexdigest() # 检查是否在白名单中 if card_hash not in get_trusted_model_hashes(): raise ValueError(fModel {model_id} not trusted. Card hash: {card_hash}) except Exception as e: logger.error(fFailed to verify model {model_id}: {e}) raise # ... 后续代码 ...get_trusted_model_hashes()是一个从配置文件读取的哈希白名单。将白名单文件trusted_models.json放入config/目录并在Dockerfile中 COPY 进镜像。此举确保只有经过安全团队审核的模型才能被注册。6.3 实践三内存与 CPU 用量硬限制 —— 防止 OOM Kill 影响其他服务在 Kubernetes 环境中backend容器若无资源限制可能因突发流量耗尽节点内存导致 kubelet 杀死其他关键 Pod。我们在docker-compose.yml中为backend添加硬性限制deploy: resources: limits: memory: 4G cpus: 2.0 reservations: memory: 2