ARTICLE DETAIL

资讯详情

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

OpenWebUI整合SearXNG实战:容器网络与搜索配置避坑指南

OpenWebUI整合SearXNG实战:容器网络与搜索配置避坑指南 先把结论放在前面OpenWebUI 和 SearXNG 是目前自建 AI 服务里非常经典的一对组合。OpenWebUI 负责把本地模型包装成一个能对话、能联网检索的聊天界面SearXNG 则是一个聚合搜索引擎通过它模型才能在回答问题时拿到实时信息。这个组合的部署门槛不高真正劝退多数人的是接入过程中的一堆“软配置”。最近我自己在一个新环境里重新搭了一套又踩了一遍当年踩过的坑索性把它们整理成这篇避坑指南。如果你正准备在 Docker 里部署 OpenWebUI SearXNG或者已经接上了但搜索总是不稳定这篇文章应该能帮你省下至少一个晚上的排查时间。1. 两个容器互相访问不了Docker网络配置才是第一道坎1.1 现象OpenWebUI日志里的Connection refused不少人照着网上教程用 docker run 分别启动 OpenWebUI 和 SearXNG启动都成功浏览器也能打开两个独立页面但 OpenWebUI 的联网搜索就是报错。打开 OpenWebUI 容器日志常见的错误是httpx.ConnectError: [Errno 111] Connection refused我当时第一次遇到这个报错时第一反应是 SearXNG 没启动于是去浏览器里开了 SearXNG 页面发现明明能打开。这就形成了第一个认知冲突服务明明活着为什么另一个容器说连不上1.2 根因容器网络命名空间隔离Docker 容器的网络和宿主机不是同一套网络栈。每个容器默认有自己独立的网络命名空间localhost 在容器内部指的是“这个容器自己”不是宿主机也不是其他容器。如果你在 OpenWebUI 容器里配置 SearXNG 地址为http://localhost:8080那 OpenWebUI 容器会尝试连接自己容器内的 8080 端口而它自己根本没有监听这个端口所以立刻 Connection refused。这类问题的根源不是你配置的端口错了而是你默认了两个容器可以通过 localhost 互通。这是容器网络隔离开带来的必然结果几乎所有刚开始用 Docker 部署多容器服务的人都会在这里栽一次。1.3 完整排查链路从docker inspect到network列表遇到连接问题别急着改配置先按下面顺序快速排查确认两个容器都在运行docker ps查看 OpenWebUI 和 SearXNG 分别挂在哪个网络下docker inspect openwebui-container --format {{json .NetworkSettings.Networks}}SearXNG 同理。对比 networks 的 key 是否一致。查看当前宿主机上有哪些自定义网络docker network ls。看到bridge是 Docker 默认网络openwebui_default、searxng_default这类是各自 compose 项目创建的网络。在一个容器里测试另一个容器的网络连通性docker exec openwebui-container ping searxng-container如果容器里没有 ping 命令可以用docker exec openwebui-container wget -qO- http://searxng-container:8080/search?qtestformatjson看有没有响应。排查到这里十有八九你会发现两个容器根本不在同一个网络里。它们各自使用了自己的默认 bridge 网络彼此之间没有通信通道。1.4 修复方案显式声明共享网络我用的是 docker-compose把 OpenWebUI 和 SearXNG 放在同一个 compose 文件里并显式声明网络。下面是精简后的示例services: searxng: image: searxng/searxng:latest container_name: searxng networks: - ai-net volumes: - ./searxng:/etc/searxng ports: - 8080:8080 openwebui: image: ghcr.io/open-webui/open-webui:main container_name: openwebui networks: - ai-net ports: - 3000:8080 environment: - SEARXNG_QUERY_URLhttp://searxng:8080/search?qquery depends_on: - searxng volumes: - ./openwebui:/app/backend/data networks: ai-net: driver: bridge关键点就是在 compose 文件底部声明一个名为ai-net的自定义网络然后两个服务都挂到这个网络下。这样 OpenWebUI 容器访问 SearXNG 时直接用服务名http://searxng:8080就能解析到 SearXNG 容器的 IP不再需要关心容器的具体 IP 是否变化。如果你已经用两个独立的 compose 文件管理服务就需要把一个网络声明为 external外部网络另一个服务加入这个外部网络。例如在 OpenWebUI 的 compose 文件里加networks: default: external: name: searxng_default这种做法的好处是网络由 SearXNG 侧管理OpenWebUI 作为客户端加入职责更清晰。1.5 一个容易被忽略的连带问题宿主机防火墙容器网络打通之后还有一层网络关口是宿主机防火墙。尤其是 SearXNG 部署在同一台服务器上、你在浏览器里直接访问http://宿主机IP:8080时如果发现一直超时大概率是防火墙没放行 8080 端口。Ubuntu 上用 ufw 的话记得执行sudo ufw allow 8080/tcpCentOS/RHEL 系用 firewall-cmd 的话对应命令是sudo firewall-cmd --permanent --add-port8080/tcp sudo firewall-cmd --reload这里有个容易混淆的地方容器端口映射到宿主机端口后从宿主机访问需要防火墙放行但从 OpenWebUI 容器内部访问 SearXNG 走的是 Docker 网络不受宿主机防火墙策略约束。所以可能出现“OpenWebUI 日志正常但浏览器打不开 SearXNG”这种割裂现象排查时不要只盯着一个方向。2. SearXNG的JSON输出被关着OpenWebUI一直提示找不到搜索接口2.1 现象Web Search功能显示异常或返回404容器网络通了之后OpenWebUI 已经能访问到 SearXNG但日志里又开始报新的错误。一种情况是直接报 404另一种情况是 OpenWebUI 界面上搜索开关打开但每次问答都提示搜索失败返回内容里看不到任何联网检索的结果。这个阶段很多人会怀疑 OpenWebUI 的配置模板写错了于是反复修改 URL 模板但问题其实根本不在 OpenWebUI 这一侧。2.2 根因formats里没开jsonSearXNG 默认不提供 API 结构化输出SearXNG 本质上是一个网页搜索引擎默认情况下它会把搜到的结果渲染成 HTML 页面供你在浏览器里直接浏览。但 OpenWebUI 需要的是结构化的 JSON 数据而不是一个 HTML 页面。SearXNG 的配置文件settings.yml中有一个关键节点search: formats: - html如果 formats 里只有 html没有 json那么 SearXNG 收到formatjson的请求时不会返回 JSON 结果而是返回 404 或者直接把 HTML 页面丢给你。OpenWebUI 解析不到目标数据自然就会显示搜索失败。2.3 用一句话验证问题是否在这个环节在你桌面的浏览器里手动访问http://localhost:8080/search?qtestformatjson如果你看到的是一个正常的 HTML 搜索页面或者页面上出现 “JSON not supported” 之类的提示那就说明 formats 里缺了 json如果你看到一长串带results字段的 JSON 内容说明这一关你已经过了。这个方法整个排查链路里非常实用十五秒就能定位问题是不是出在这里。2.4 修复settings.yml调整与容器重启修改你挂载到容器里的 SearXNG 配置目录下的settings.yml把 formats 改成search: formats: - html - json这里建议不要把 html 删掉。因为你会遇到需要直接在浏览器里打开 SearXNG 调试搜索结果的场景如果只剩 json 格式浏览器访问会非常难用。两者保留是兼容性最好的选择。改完配置后重启 SearXNG 容器docker restart searxng重启后再执行一次上面的验证 URL能正常输出 JSON 就说明修改生效了。要特别留意一点如果容器里的配置文件是通过 volume 挂载的改的是宿主机上的文件重启容器后会重新读取没问题但如果你之前是直接 docker exec 进容器改的文件容器一重建改动就没了重新部署前要注意把改动同步到宿主机挂载目录中。2.5 版本差异不同OpenWebUI版本搜索设置入口OpenWebUI 的搜索配置入口在不同版本里变化比较大。旧版本中你需要在环境变量里配置SEARXNG_QUERY_URL很多教程也会让你这么做。但新版 OpenWebUI 改成了在管理面板里通过界面配置Admin Panel - Settings - Search界面里需要填的内容包括搜索提供方可以选择 SearXNG、API 地址比如http://searxng:8080/search?qquery。填写完之后还要在 “Web Search” 开关处启用联网搜索。如果你用的是旧版环境变量方式却一直不生效优先去界面里看看到底配置的是什么。新版 WebUI 的配置优先级通常高于环境变量两处配置互相冲突时界面配置会覆盖环境变量这也是一个容易让人误判的点。3. 浏览器跨域拦截CORS配置里最容易被照抄翻车的细节3.1 现象接口正常但浏览器拒绝读取JSON 输出打开后OpenWebUI 和 SearXNG 之间看似通了但如果你在本地开发时用的是“OpenWebUI 页面在一个端口SearXNG 直接暴露在另一个端口”的架构浏览器里会报出这样一条错误Access to XMLHttpRequest at http://localhost:8080/search?qtestformatjson from origin http://localhost:3000 has been blocked by CORS policy: No Access-Control-Allow-Origin header is present on the requested resource.注意这个问题的特殊性服务端接口完全正常你直接用 curl 或浏览器地址栏访问都拿到了 JSON 数据但前端页面里的 JS 代码就是读不到数据。这是因为浏览器强制执行同源策略来自http://localhost:3000的页面去请求http://localhost:8080的接口时必须确认对方允许跨域访问否则浏览器会在中间层把响应拦截掉。3.2 根因同源策略和SearXNG默认CORS策略同源策略可以理解为浏览器给你上的一道保险只有协议、域名、端口完全一致的请求才能直接共享数据。OpenWebUI 默认跑在 3000 端口SearXNG 跑在 8080 端口两者端口不同天然跨域。SearXNG 的默认配置里CORS 相关的设置不一定满足 OpenWebUI 的跨域需求需要你主动在settings.yml中开启并声明允许的来源。很多部署教程对这一块一笔带过导致不少人的 SearXNG 一直没返回 CORS 头浏览器自然拦截。3.3 修复server.cors的具体配置在 SearXNG 的settings.yml中添加或修改以下内容server: cors: allow_origin: - http://localhost:3000 - http://192.168.1.100:3000allow_origin是一个列表里面可以写多个来源地址。需要注意这里不能简单写成*通配符。原因是如果你同时开启了allow_credentials: true允许请求携带凭据、Cookie 等浏览器会直接拒绝通配符形式的 CORS 头。修改完成后同样需要重启 SearXNG 容器。重启后可以在浏览器里打开 OpenWebUI 的页面重新触发一次联网搜索再按 F12 找到对应请求查看响应头里是否出现了Access-Control-Allow-Origin: http://localhost:3000如果出现了这个头说明 CORS 已经放行。3.4 风险点反射Origin credentialstrue为什么是错的网上不少教程为了省事把 CORS 配置简化成“把请求里的 Origin 原样返回”再配合Access-Control-Allow-Credentials: true使用。这种配置危险在哪儿呢我在实际排查过一个朋友的项目后发现他对“反射 Origin credentialstrue”这个组合完全没有风险意识。如果服务器无条件反射任意 Origin同时允许携带凭据那么任何恶意网站都可以在你的登录态下向该服务发起跨域请求。恶意网站只需要在自己的页面里发起一个指向你服务的请求浏览器会因为 CORS 配置而允许这次跨域读取攻击者就能拿到你的会话数据。SearXNG 本身的凭据体系可能不那么敏感但这个错误思路一旦沿用到同一个配置文件里的其他服务上风险就会被放大。稳妥的做法就是像上面那样明确列出允许的来源域名不要用反射也不要随便开 credentials。如果你确实需要携带凭据那更要严格控制 allow_origin 列表只添加你信任的站点。3.5 我在实际配置中踩过的小坑改完配置文件但忘记重启容器这一点看似低级但真的很容易犯。我之前在一台长期运行的服务器上改完 CORS 配置后顺手用 OpenWebUI 测了一下发现还是跨域错误。当时第一反应是“配置没写对”于是反复检查和调整 allow_origin 的格式折腾了半个多小时。最后才想起来是容器根本没重启配置没有被重新加载。CORS 配置不像有些应用支持热更新SearXNG 的 settings.yml 是在容器启动时加载的。所以记住一个流程改配置 - 重启容器 - 验证响应头。不要先怀疑配置语法先确认你有没有重启。4. BASE_URL写成localhost镜像环境里的网络指向偏差4.1 现象宿主机访问一切正常OpenWebUI就是连不上这个问题和第一个容器网络问题非常像但场景更隐蔽。有些人的 SearXNG 不是跑在容器里而是直接装在宿主机上OpenWebUI 仍然跑在容器里。这时候按照本机部署的习惯你可能会在 OpenWebUI 的搜索配置中填http://localhost:8080/search?qquery填完之后发现宿主机浏览器能打开 SearXNGOpenWebUI 却始终搜索失败。这种现象特别有迷惑性因为你的第一反应通常是“SearXNG 是不是有问题”但事实是 Searcher 在宿主机上跑得好好的。4.2 根因localhost 在容器内的含义问题的核心还是容器网络命名空间。当 OpenWebUI 运行在 Docker 容器里时它访问localhost:8080实际上访问的是容器自身的回环地址而不是宿主机的回环地址。容器自己并没有监听 8080 端口所以请求必然失败。这就好比你在一个小区里每家每户都有自己的门牌号但你在邻居家喊“我家的门牌号”邻居听到的是他家自己的门牌号。localhost 在容器环境里就是最典型的“各喊各的门牌号”。4.3 修复三种可行写法及适用场景根据 SearXNG 部署方式的不同BASE_URL 有三种正确写法部署方式正确写法说明SearXNG 也跑在 Docker且与 OpenWebUI 同网络http://searxng:8080/search?qquery使用 Docker 服务名最稳定IP 变化不影响SearXNG 跑在宿主机上非容器http://host.docker.internal:8080/search?qqueryDocker Desktop 平台特有能访问宿主机回环地址SearXNG 跑在另一台机器上http://192.168.1.50:8080/search?qquery使用对方机器的实际 IP 地址第三种写法最直观但要注意 IP 地址写死之后如果 SearXNG 所在机器的 IP 变化了配置也需要同步更新。第一种写法是生产环境里最推荐的因为 Docker 内置的 DNS 解析会自动把服务名解析成正确的容器 IP不怕 IP 漂移。4.4 附赠host.docker.internal 在不同平台的差异我在 macOS 和 Windows 上用 Docker Desktop 时host.docker.internal是开箱即用的。但如果你在 Linux 服务器上部署就有个坑原生 Docker Engine 在 Linux 上默认不支持host.docker.internal。需要在启动容器时额外加参数docker run --add-hosthost.docker.internal:host-gateway ...或者在 compose 文件里这样写extra_hosts: - host.docker.internal:host-gateway加上这个参数之后容器里的host.docker.internal才能正确解析到宿主机。这个问题在 Linux 上特别常见我见过不少人在 Linux 服务器上配完发现 host.docker.internal 不生效然后开始怀疑防火墙配置不对白白浪费了不少时间。5. 搜得到搜索页却搜不到结果上游引擎限流、超时与引擎清单5.1 现象SearXNG 能打开搜索返回0条结果前面几关全都过了OpenWebUI 也能正常调用 SearXNG 了但搜索结果还是不如预期。常见现象是手动访问 SearXNG 首页页面正常输入关键词搜索页面顶部确实有分类栏但下面一条结果都没有或者显示超时错误。在 OpenWebUI 里表现为模型回答“无法获取搜索结果”。5.2 根因上游引擎的429/403与SearXNG并发策略SearXNG 自己不抓取网页内容它只是把请求转发给上游搜索引擎Google、Bing、DuckDuckGo 等再把各个来源的结果聚合后返回。如果上游引擎拒绝或限流了 SearXNG 的请求SearXNG 能做的只是把错误状态透传给你。上游引擎拒绝的原因主要有几类服务器 IP 被识别为数据中心流量搜索引擎出于反爬策略直接返回 403。搜索请求太频繁触发了 429 限流。搜索请求携带的 User-Agent 被识别为非常规浏览器。SearXNG 默认会对多个底层引擎发起并发请求这个并发行为如果没控制好更容易触发上游限流机制。5.3 排查链路先看SearXNG自身的网络日志遇到搜索结果为空先在 SearXNG 日志里找线索docker logs searxng --tail 200日志里如果大面积出现429 Too Many Requests、403 Forbidden就说明上游限制了这个出口 IP 的访问如果出现超时类错误说明上游地址本身不可达或响应太慢。你还可以配合 SearXNG 自带的调试入口在浏览器打开http://localhost:8080/search?qtestformatjson返回的 JSON 里如果results数组是空的但unresponsive_engines里列出了一堆引擎名基本可以确定问题出在上游引擎这一层。5.4 修复精简引擎清单、调整超时与连接池SearXNG 的配置文件中有一个engines列表里面默认启用了不少引擎。这些引擎在你所在的网络环境下不一定都可用建议只保留真正能出结果的几类。在settings.yml的engines部分你可以通过把某个引擎的disabled: true来关闭它。举例engines: - name: google disabled: false - name: duckduckgo disabled: false - name: bing disabled: true如果你不想一个一个调整引擎也可以在 SearXNG 的界面右上角选择“搜索引擎分类”或者直接通过配置把不可达的引擎标记为禁用。同时在outgoing节点下合理设置超时时间和连接池参数outgoing: request_timeout: 5.0 pool_connections: 100 pool_maxsize: 20request_timeout设置单次请求的最大等待时间pool_connections和pool_maxsize控制连接池的数量。建议不要一次性把并发调得过大否则更容易触发对端限流。参数改完后同样是重启容器生效。如果你探测到某个引擎总是超时或者被限制就把它禁用掉换另一个可用的。这个方法成本低、效果直接比反复调请求头的体验好太多。实际上在普通网络环境下一个可选引擎只要有一个稳定返回结果就已经足够支撑 OpenWebUI 的日常联网搜索了。5.5 关于出站代理合规HTTP代理的配置位置如果你所在的企业内网或实验室网络有合规的出站 HTTP 代理用于访问外网SearXNG 也支持在settings.yml中配置代理出口outgoing: proxies: http: http://your-proxy-server:port https: http://your-proxy-server:port请注意这里的前提是你使用的是你所在网络环境提供或认可的正规代理服务而不是来路不明的公共代理。把搜索流量交给一个不可信的代理等于把你的搜索关键词和 IP 关联信息全部暴露给第三方风险远大于收益。如果没有这类代理就跳过这个配置直接把不可达的引擎禁用掉不要为了追求“引擎全开”而去做冒险的操作。5.6 经验把SearXNG当独立服务先跑通最后分享一个我自己固定的调试经验。接入 OpenWebUI 之前先把 SearXNG 当独立服务跑起来并且在浏览器里完成一次完整搜索确认能正常出结果。这个步骤看着简单但能帮你把问题边界划分清楚如果 SearXNG 页面本身能搜索出结果说明上游引擎、网络、配置都没问题。如果 SearXNG 页面本身都没有结果先解决搜索源问题再回来折腾 OpenWebUI 的接入。这样排错时永远只有一个变量。很多人习惯直接改 OpenWebUI 的配置来回试了好几种写法都不行最后才发现 SearXNG 压根搜不到东西既浪费了时间又把问题搞得一团糟。我自己的习惯是SearXNG 单独跑通后再到 OpenWebUI 里配置搜索提供方然后用一个冷门但具体的关键词测试比如“某个软件的官方文档地址”这样能明确判断搜索结果是不是真的来自实时互联网而不是模型靠训练数据现编的。联网功能真正跑通是一个很爽的瞬间因为模型终于从“记忆问答”变成了“检索回答”。最后再补充一个实际运维中的建议SearXNG 的配置改动虽然不频繁但每次改动前建议先备份一份settings.yml。这个文件里已经踩过的坑、调好的引擎清单、CORS 白名单都是你花时间调出来的成果容器重建时如果没有备份这些配置很可能在参数组合上漏一项又要重新排查一轮。留好备份部署和迁移的效率会明显不一样。
返回列表