故障排查报告 / 1Panel 容器化部署

KnowFlow / RAGFlow 部署故障排查与解决

三个相互叠加的根因——硬编码 IP 失效、Elasticsearch 内存超限、RBAC 初始化竞态——的定位过程、修复措施与验证结果。

应用KnowFlow v2.1.8(RAGFlow slim)
服务器7.6GB RAM · 无 Swap · 公网
日期2026-08-26
状态已解决 · 登录恢复

00摘要

故障表象是「登录页能正常打开,提交任何凭证都提示网络异常」。实际排查下来,这不是单点故障,而是三个先后叠加、彼此独立的问题:

其一,宿主机内存只有官方要求的一半,Elasticsearch 按默认配置拿到约 4GB 堆,17 小时内被内核 OOM Killer 处决 31 次。其二,早期排障时在 extra_hosts 里写死了 es01 的容器 IP,容器重建后该 IP 变更,ragflow-server 的所有 ES 请求都打进了黑洞地址,127 秒超时后进程退出——且以退出码 0 退出,restart: on-failure 策略判定其为正常退出、不再拉起,服务就此躺平。其三,前两个问题修复、链路全通之后登录仍然失败:knowflow-backend 的 RBAC 初始化有一个 60 秒的等表窗口,而 ragflow-server 首次冷启动建表需要约 75 秒,管理员账号的创建在窗口关闭 13 秒后才轮到执行,从未发生,登录报「邮箱未注册」。

三处全部处理之后,登录恢复正常。

3
叠加根因
网络 / 内存 / 竞态
31
ES 17 小时内
被 OOM 重启次数
13
RBAC 等待窗口与
建表完成的时间差
0.01s
修复后 ES 健康检查耗时
(修复前 127 秒超时)

01环境信息

项目内容
管理面板1Panel
应用版本KnowFlow v2.1.8(镜像 zxwei/knowflow:v2.1.8,内含 RAGFlow v2.1.2-189-gdd54c95f slim
部署路径/home/1panel/KnowFlow/docker(docker compose 管理)
文档引擎Elasticsearch 8.11.3(容器 ragflow-es-01,服务名 es01
其他容器MySQL 8.0.39 · Valkey 8 · MinIO · Gotenberg 8 · knowflow-backend · ragflow-server
容器网络docker_ragflow(bridge)

端口映射

宿主机端口容器端口用途
8088(原 80)ragflow-server:80Web 入口(nginx,登录页在此)
8443(原 443)ragflow-server:443HTTPS
1200es01:9200Elasticsearch API(外部访问)
5455mysql:3306MySQL
5000knowflow-backend:5000RBAC 后端(登录接口)
3000 / 6379 / 9000-9001gotenberg / redis / minio文档转换 / 缓存 / 对象存储

硬件与官方要求对照

项目本机实际官方要求[1]
内存7.6GB,无 Swap≥ 16GB
磁盘 / CPU未核查磁盘 ≥ 50GB,CPU ≥ 4 核

伏笔内存一项差了一半,是后面所有问题的物质基础。官方 README 对硬件要求有明确说明[1],部署前应当先对齐。

02故障现象

  1. 浏览器能正常打开 http://IP:8088/login——说明 nginx 与前端静态资源本身正常。
  2. 提交任意账号密码,页面统一提示「网络异常,您的网络发生异常,无法连接服务器」。
  3. 1Panel 界面显示各服务「启动中」,看起来一切都在跑。
  4. 事实:docker compose ps 只列出 6 个容器,ragflow-server 不在其中;加 -a 后显示 Exited (0)

前端这个「网络异常」提示对应的场景是请求超时或代理层无响应,与凭证对错无关——密码错误会返回明确的文案。而「容器在跑」的错觉来自两点:compose 默认不显示已退出容器,以及 RAGFlow 官方 FAQ 强调的——容器状态 Up 不等于服务健康[5]

03故障因果链总览

图 1三个根因如何层层传导为「登录失败」
flowchart TD
    subgraph R1["根因一 · Elasticsearch 反复 OOM"]
        A1["宿主机内存 7.6GB,无 Swap
低于官方要求的 16GB"] A2[".env 默认 MEM_LIMIT = 8GB
ES 堆自动取一半 ≈ 4GB"] A3["ES 被 OOM Killer 反复处决
17 小时内重启 31 次"] A1 --> A2 --> A3 end subgraph R2["根因二 · extra_hosts 硬编码 IP 失效"] B1["早期排障在 extra_hosts
写死 es01:172.19.0.4"] B2["容器重建后 es01 实际 IP
变为 172.20.0.6"] B3["ragflow-server 的 ES 请求
全部打入黑洞,127 秒超时"] B1 --> B2 --> B3 end subgraph R3["根因三 · RBAC 初始化竞态"] C1["knowflow-backend 等待建表
上限 30 次 × 2 秒 = 60 秒"] C2["ragflow-server 加载 deepdoc 模型
约 75 秒后才建好三张表"] C3["RBAC 初始化放弃执行
user 表始终为空"] C1 --> C3 C2 --> C3 end A3 --> E1 B3 --> E1["ragflow-server 报
ES unhealthy in 120s 后退出"] E1 --> E2["退出码为 0,restart: on-failure
判定正常退出,不再拉起"] E2 --> E3["ragflow-server 长期 Exited
8088 端口无人应答"] E3 --> E4["登录页提示「网络异常」"] C3 --> E5["链路修复后登录
报 109 邮箱未注册"]

三个根因相互独立:只修任何一个,登录都不会成功。这也是本次排障反复「修完还是不行」的原因。

04根因分析

4.1 根因一:extra_hosts 硬编码 IP,随容器重建失效

来龙去脉。初次部署时 ragflow-server 反复退出,日志报 Failed to resolve 'es01'。当时为快速恢复,在 ragflow 服务的 extra_hosts 中写入了 es01 当时的容器 IP(172.19.0.4),服务一度恢复。随后一次 docker compose down && docker compose rm -f && docker compose up -d 重建了全部容器——bridge 网络的 IP 由 Docker 按启动顺序分配,重建后 es01 拿到的是 172.20.0.6,而旧映射没有清除。

证据。两条 inspect 输出直接对比:

# ragflow-server 容器实际生效的 extra_hosts
["es01:172.19.0.4","host.docker.internal:host-gateway"]

# es01 容器当前 IP
es01 当前IP: docker_ragflow = 172.20.0.6

识别特征。黑洞 IP 的行为是「包发出去,永远等不到回应」——超时发生在 127 秒量级,而不是「连接拒绝」。ragflow-server 日志里整齐划一的 GET http://es01:9200/ [status:N/A duration:127.2s] 就是这个特征。

最容易误判的一处当时用临时容器 curl http://es01:9200 返回 401,得出「网络是通的、只缺认证」的结论——这个结论对临时容器成立,对 ragflow-server 不成立:临时容器没有 extra_hosts 覆盖,走的是 Docker 内置 DNS;ragflow-server 的解析被 hosts 劫持了。用「别的容器能通」验证「这个容器能通」,在存在 hosts 覆盖时不成立。

正解。同一 compose 网络内,服务名本来就能直接解析,extra_hosts 只在跨网络访问宿主机等场景需要。修复方式是删除这一行,而不是把 IP 更新成新值——下次重建它还会变。

4.2 根因二:内存超限,ES 反复被 OOM 处决

证据链。

# 宿主机内存
Mem:  total 7.6G   used 6.9G   free 128M   available 57M
Swap: 0B 0B 0B

# ES 容器重启计数(容器创建约 17 小时)
RestartCount=31

# 内核 OOM 记录(两次选中 RSS 最大的 java 进程,即 ES)
Killed process 5246 (java) ... anon-rss:4426688kB   ≈ 4.2GB
Killed process 19456 (java) ... anon-rss:4096060kB  ≈ 3.9GB

机理。官方 .env 默认 MEM_LIMIT=8073741824(8GB),注释写明需按宿主机内存调整[3];compose 将它作为 es01 的 mem_limit[4]。ES 8 在未显式设置 ES_JAVA_OPTS 时,堆自动取容器内存限制的一半,即约 4GB。7.6GB 的机器上,MySQL、MinIO、Redis、knowflow-backend、ragflow-server 各占一块之后,剩余内存兜不住 4GB 堆加 JVM 非堆开销,内核 OOM Killer 每次都选中 RSS 最大的 java 进程——这正是 dmesg 里两处 Killed process (java) 的来历。

处理。MEM_LIMIT 降为 2147483648(2GB,堆约 1GB)。代价是文档量大、并发高的场景下 ES 会吃紧;长期方案仍是把内存补到 16GB 以上,或显式设置更小的堆并接受性能折损。

4.3 放大因素:Exited(0) × restart: on-failure

ragflow-server 因 ES 不健康抛出异常,但入口脚本最终以退出码 0 结束。compose 里的重启策略是 restart: on-failure[2]——只重启「失败」(非零退出码)的容器。退出码 0 被判定为正常退出,哪怕 ES 后来恢复 healthy,ragflow-server 也不会自己起来。所以现场呈现的是:6 个依赖容器 Up、主角 Exited、登录页报网络异常,且没有任何自动恢复的迹象。

排查上的两个对应教训:docker compose ps 不加 -a 看不到退出的容器;看到 Exited (0) 不要想当然认为「正常退出、没有问题」。

4.4 根因三:RBAC 初始化竞态——60 秒窗口 vs 75 秒建表

现象。前两个根因修复后,ES 健康检查 200、ragflow-server 横幅出现、登录接口返回 200,链路明明全通了——但页面报 109 Email: admin@gmail.com is not registered。换官方默认凭证 admin / 12345678 登录,同样报未注册。

查证。三条证据指向同一个事实——管理员账号从未被创建:

# .env 中没有覆盖管理员定义(走 compose 默认值)
$ grep -E 'MANAGEMENT_ADMIN' .env
(无输出)

# rag_flow 库的 user 表是空的——「未注册」是真话
$ SELECT email, nickname FROM `user`;
Empty set

# knowflow-backend 日志:等表等了 30 次,放弃
等待以下表创建: user, tenant, user_tenant
等待数据库就绪... (尝试 30/30)
ERROR - RBAC初始化最终失败,已达到最大重试次数
图 2竞态时间线:两个进程的窗口只差 13 秒
knowflow-backend · 等待 user / tenant / user_tenant 建表 09:52:02 → 09:53:04 ragflow-server · CPU 加载 deepdoc 模型并初始化 web 数据 09:52:12 → 09:53:17 09:53:04 RBAC 初始化放弃 09:53:17 三张表建好 13 秒之差 09:52:00 09:52:30 09:53:00 09:53:30 整栈冷启动实测:表建好时,knowflow-backend 已在 13 秒前放弃等待——初始化逻辑不会再来第二次。

机理。knowflow-backend 启动时等待 user / tenant / user_tenant 三张表就绪,重试上限 30 次 × 2 秒 = 60 秒;而 ragflow-server 首次冷启动要在 CPU 上加载 deepdoc 的 det.onnx / rec.onnx 模型并初始化 web 数据,本机耗时约 75 秒。竞态只在整栈同时冷启动时触发——一旦错过窗口,RBAC 初始化不会重试,user 表从此为空。

处理。此时三张表早已存在,单独 docker restart knowflow-backend 即可,初始化立即成功、管理员账号落库。管理员凭证取 compose 中 MANAGEMENT_ADMIN_* 环境变量的默认值(admin / 12345678),本机 .env 未覆盖[2]

复发风险每次整栈冷启动(服务器重启、compose down/up)都可能再次触发该竞态。若重启后登录报「邮箱未注册」,先执行 docker restart knowflow-backend,不要急着怀疑密码。

05排查过程复盘

阶段动作发现 / 结果
初次部署docker compose up -dragflow-server 反复退出,日志报 Failed to resolve 'es01'
初排 ①extra_hosts 写入 es01:172.19.0.4服务一度恢复——同时埋下根因二(IP 随重建失效)
初排 ②docker compose config发现 extra_hosts 重复定义导致 YAML 解析错误,删除重复项
初排 ③down && rm -f && up -d容器全部启动,但登录仍报网络异常(此时旧 IP 已失效、ES 仍在被 OOM)
本次 ①docker compose ps6 个容器在跑,ragflow-server 缺席
本次 ②docker compose ps -aragflow-server Exited (0)
本次 ③docker logs ragflow-serverES 检查全部 status:N/A duration:127sunhealthy in 120s → 退出
本次 ④inspect es01 / free -h / dmesgRestartCount=31;内存 7.6G 可用 57M;OOM 处决 java 两次
本次 ⑤inspect 对比 extra_hosts 与 es01 IP172.19.0.4 ≠ 172.20.0.6,坐实黑洞映射
修复 ①删除 extra_hosts 中 es01 硬编码行仅剩 host.docker.internal:host-gateway
修复 ②MEM_LIMIT 改为 2147483648es01 重建,Memory=2147483648,RestartCount 归零
修复 ③docker compose up -d7 个容器全部启动,ES healthy,ragflow-server 横幅出现,POST /v1/user/login 200 ×3
新现象页面登录改报 109 邮箱未注册——链路问题已清,转入账号问题
查证grep .env / 查 user 表 / backend 日志user 表为空;RBAC 30 次等待失败,确认初始化从未执行
修复 ④docker restart knowflow-backend管理员账号创建,登录成功

06修复措施

所有改动前先备份:docker-compose.yml.bak.0826.env.bak.0826(与原文件同目录)。

1删除 extra_hosts 中硬编码的 es01 映射
cd /home/1panel/KnowFlow/docker
cp docker-compose.yml docker-compose.yml.bak.$(date +%m%d)
sed -i '/es01:172.19.0.4/d' docker-compose.yml
grep -n -A3 'extra_hosts' docker-compose.yml

验证点:ragflow 服务下 extra_hosts 仅剩 host.docker.internal:host-gateway。同网络内服务名交给 Docker DNS 解析。

2ES 内存限制降为 2GB
cp .env .env.bak.$(date +%m%d)
sed -i 's/^MEM_LIMIT=.*/MEM_LIMIT=2147483648/' .env
grep -n 'MEM_LIMIT' .env

验证点:输出 MEM_LIMIT=2147483648。ES 堆随之降为约 1GB,退出被反复 OOM 的循环。

3重建容器
docker compose up -d

# 验证两项改动均已生效
docker inspect ragflow-server --format '{{json .HostConfig.ExtraHosts}}'
docker inspect ragflow-es-01 --format 'Memory={{.HostConfig.Memory}}'

预期:ExtraHosts 只剩 host-gateway;Memory=2147483648。随后 docker logs -f ragflow-server,等到 ASCII 横幅再试登录——官方文档明确说明横幅出现前登录会报网络异常[1]

4补做 RBAC 初始化(针对竞态)
docker restart knowflow-backend
docker logs knowflow-backend --tail 40

适用条件:链路已通但登录报「邮箱未注册」。表已存在时重启即完成管理员创建。整栈冷启动后若复发,重复本步即可。

07修复验证

指标修复前修复后
ES 健康检查status:N/A,duration 127 秒超时status:200,duration 0.01 秒
ES 稳定性17 小时重启 31 次(OOM)RestartCount 0(限内存 2GB 后)
ragflow-serverExited (0),无自动拉起Up,横幅 + HTTP server start (0.0.0.0)
登录接口前端拿不到响应(网络异常)POST /v1/user/login → 200
登录结果成功进入系统

08常用命令速查

目的命令备注
查看所有容器(含已退出)docker compose ps -a不加 -a 看不到 Exited 的容器
跟踪 ragflow 启动docker logs -f ragflow-server看到 ASCII 横幅才算就绪[1]
看 backend 日志docker compose logs knowflow-backend --tail 50RBAC 初始化结果在这里
查容器当前 IPdocker inspect ragflow-es-01 --format '{{range $k,$v := .NetworkSettings.Networks}}{{$k}} = {{$v.IPAddress}}{{end}}'重建后会变化
查容器生效的 extra_hostsdocker inspect ragflow-server --format '{{json .HostConfig.ExtraHosts}}'排查 hosts 劫持
查重启次数 / 内存限制docker inspect ragflow-es-01 --format 'RestartCount={{.RestartCount}}'RestartCount 持续增长 = 异常
容器内测 ESdocker exec ragflow-server curl -s -u elastic:<密码> http://es01:9200401 也说明网络通、只缺认证
查用户表docker exec ragflow-mysql mysql -uroot -p<密码> -e "USE rag_flow; SELECT email FROM \`user\`;"空表 = 管理员未创建
验证 compose 配置docker compose configYAML 语法与变量检查
查 OOM 记录dmesg | grep -i "killed process"配合 free -h 使用
整栈重启docker compose down && docker compose up -d冷启动后留意第 4 步竞态

09经验与注意事项

  1. 不要把容器 IP 写进 extra_hosts。bridge 网络的 IP 由 Docker 分配,容器重建即变。同网络内服务名解析是 Docker DNS 的本职;确需跨网络访问时,建共享 external network,而不是维护 IP 清单。
  2. 官方硬件要求要当真。7.6GB 跑一个要求 16GB 的栈,ES 默认 8GB 的 MEM_LIMIT 就是定时炸弹。部署前先对齐 .env 中 MEM_LIMIT 与实际内存[3]
  3. docker compose ps 默认不显示退出容器,排查第一步先加 -a;同时记住 Exited (0) 不等于「正常」——on-failure 策略不会拉起退出码为 0 的容器[2]
  4. 「临时容器能通」不能证明「目标容器能通」。hosts 覆盖只作用于被注入的容器,验证连通性要在目标容器内部执行。
  5. 复合故障按证据链逐层剥。本次网络、内存、竞态三个问题独立存在,修任何一个都不够;每修一项就验证一项,不要多变量混着改。
  6. 整栈冷启动后登录报「未注册」,先 docker restart knowflow-backend(60 秒等表窗口小于首次建表耗时),排除初始化竞态后再怀疑密码。
  7. 判断服务真就绪看日志横幅,不是容器状态 Up——官方文档对登录时机有同样的说明[1][5]
安全提醒(公网部署必读) 本机为公网 IP 直连部署。默认管理员凭证为 compose 默认值 admin / 12345678[2],必须立即通过页面或 .env 修改;3306、6379、9200、5000、9000 等容器端口已映射到公网网卡,建议用防火墙 / 安全组收敛到仅 8088 / 8443 可达,其余端口限制来源 IP 或改为仅监听内网。