最近在社群里被问得最多的一个报错就是 Dify 里的PluginInvokeError。不同用户贴出来的日志五花八门有人做知识库流水线时插件调用失败有人在工作流里挂了一个 HTTP 请求节点直接红字有人在把 Dify 接到本地大模型比如 Ollama上时反复报连接超时。表面看是插件执行异常但顺着日志往下查最后十有八九会碰到同一个问题容器 DNS 配置。这篇我就把 Dify 插件调用链路里 DNS 的定位方法、常见故障和修复手段一次讲透。内容适合正用 Docker 部署 Dify、准备做插件二次开发、或者想把 Dify 接进本地大模型服务的同学我会把自己真实排查时的操作命令和判断逻辑全部摊开照着做就能少走弯路。1. 认识 PluginInvokeError报错背后的请求链路1.1 错误信息可能比你想象的更简单Dify 的插件调用机制并不是在主进程里直接执行代码而是交给专门管理插件的容器或进程去跑。当插件内部发起 HTTP 请求时它会先做域名解析再去建立连接、发送请求。如果域名解析失败Dify 会把异常包装成PluginInvokeError返回。所以你在日志里看到的错误往往只有一行plugin_invoke_error: PluginInvokeError Failed to invoke tool http_request很多朋友看到这个错误先怀疑自己的插件代码、API Key 或服务地址这也没错但我建议先冷静一下看一下错误信息里有没有更细的线索。Dify 的日志通常会包含内部异常堆栈里面会出现类似getaddrinfo failed、Name or service not known、connection refused或timeout之类的关键词。不同关键词对应的处理方式完全不同。如果看到的是getaddrinfo failed或Name or service not known那基本可以确定是 DNS 解析问题别急着改业务代码。1.2 为什么 DNS 会成为头号嫌疑对象原因在于 Dify 容器网络是典型的微服务组网。默认的docker-compose.yaml里会拉起 nginx、api、worker、web、plugin_daemon、ssrf_proxy 等多个容器插件运行时如果需要调用 Dify 内部的接口通常写的就是容器名比如http://api:5001。容器名本身不是一个真实域名它需要由 Docker 内置的 DNS 来解析成具体 IP。一旦容器 DNS 配置出问题内部服务名解析失败插件自然报PluginInvokeError。再叠加一个高频场景很多人在宿主机上跑了 Ollama 或其他本地大模型服务Dify 插件里去调http://localhost:11434这同样会失败因为容器里的localhost不是宿主机。这一类问题虽然不是严格意义的 DNS 解析失败但本质上和“容器网络寻址”相关排查思路是同一个。所以遇到PluginInvokeError我的第一个动作永远是查容器网络和 DNS而不是去翻插件源码。2. 容器 DNS 是怎么工作的先摸清 Dify 的网络底牌2.1 Docker 内置 DNS 与 127.0.0.11Docker 为每个用户自定义网络bridge 类型提供了一套内置 DNS 服务地址固定是127.0.0.11。只要容器接入的是自定义 bridge 网络容器内的/etc/resolv.conf就会被 Docker 改写成类似这样nameserver 127.0.0.11 options ndots:0这里可以把它想象成公司前台。容器之间的服务名比如api、worker由这个“前台”直接转接而访问公网域名时“前台”自己不认识就转给外部的总机——也就是宿主机配置的上游 DNS。这个设计本身很高效问题在于上游 DNS 经常配置得不靠谱。特别是宿主机用的是 systemd-resolved 时总机线路会受到干扰导致容器解析外网域名时断时续。2.2 /etc/resolv.conf 的生成逻辑Docker 守护进程dockerd启动时会读取宿主机的/etc/resolv.conf如果没有在启动参数或daemon.json里额外指定 DNS那么宿主机上的 nameserver 就会被当作容器默认的上游 DNS。听起来很合理但坑也在这里。例如 Ubuntu 22.04 默认开启了 systemd-resolved宿主机的/etc/resolv.conf实际指向127.0.0.53。这个地址是一个本地缓存服务本身还需要再去访问真实 DNS。Docker 容器如果直接拿127.0.0.53作为上游 DNS就会遇到“容器访问外部域名失败但容器之间按容器名解析正常”的怪异现象。Dify 插件在这种环境下访问外部工具 API、OpenAI 接口、本地模型服务时就会间歇性出现PluginInvokeError。很多人以为服务不稳定其实只是 DNS 链路太长超时了。2.3 Dify 容器组网里哪些域名最容易被卡以社区版 1.10 左右的默认 compose 为例Dify 相关容器通常有这些角色nginx对外提供 Web 入口。api后端 API 服务内部端口一般是 5001。worker任务执行服务消费队列任务。web前端页面。plugin_daemon插件守护进程负责加载和调用插件。ssrf_proxy防止 SSRF 的代理组件。sandbox执行 Python 代码的沙箱容器。插件调用 Dify 内部接口时很常见的地址就是http://api:5001。如果多个自定义插件都需要访问这个地址只要 Docker DNS 一抖动所有插件都会跟着报错。你还会发现用浏览器访问 Dify 控制台完全正常因为浏览器是走宿主机的 DNS不经过容器网络。这就是为什么很多人第一反应是“Dify 界面好好的插件却一直失败”。3. 一次完整的 PluginInvokeError 排查实录3.1 第一步从日志里把真正有用的错误摘出来排查开始时不要凭感觉改配置先去翻日志。先用下面的命令把 Dify 核心服务最近一段时间的日志捞出来docker compose logs --tail200 dify-api | grep -i plugin docker compose logs --tail200 plugin_daemon如果你用的是较新版本的 Dify服务名可能不叫dify-api而是api-1或docker-api-1建议先用docker compose ps查看当前实际容器名。日志里重点找getaddrinfo、Name or service not known、Temporary failure in name resolution、Connection refused这些关键字。假设你看到类似{error: PluginInvokeError: Failed to invoke tool fetch_url, cause: getaddrinfo failed}这样的内容那排查方向就可以直接锁定 DNS。如果看到的是connection refused说明解析可能没问题而是目标服务端口未监听或者网络策略阻断。先做好这一步后面很多力气才不会白费。3.2 第二步进容器手动验证 DNS 解析日志只能说明“插件执行过程中出了问题”究竟是不是 DNS 还要进容器里实测。Dify 的插件容器通常是精简镜像不一定有nslookup或dig但getent大概率是有的因为它来自 glibc几乎每个容器都会带。docker exec -it plugin_daemon sh cat /etc/resolv.conf getent hosts api getent hosts www.baidu.com如果getent hosts api能返回一个 172.x 之类的内网 IP说明容器名解析正常。如果返回“Name or service not known”说明容器内部服务名解析失败。接着测外网域名比如getent hosts www.baidu.com如果这个也失败说明上游 DNS 配置有问题如果内部服务名解析失败但外网域名正常问题可能出在 Docker 网络的服务发现而不是上游 DNS。另外还可以直接用wget或curl做一次实际请求判断是否是端口问题docker exec plugin_daemon sh -c wget -q -O - http://api:5001/health || echo health_check_fail这一步的目标是把“DNS 解析失败”和“网络不通”分开。不同容器里有没有wget不一定也可以用/dev/tcp这种方式但为了简洁我还是建议优先用wget或curl。3.3 第三步检查 Docker 守护进程与 compose 的 DNS 配置链路手动验证只是看到了现象接下来要找到引发 DNS 异常的配置源头。依次执行下面几条命令cat /etc/docker/daemon.json docker info | grep -A 3 DNS docker inspect plugin_daemon --format {{json .HostConfig.Dns}} grep -n dns docker-compose.yaml如果daemon.json里根本没有 DNS 配置说明 Docker 正在用宿主机resolv.conf中的上游 DNS。继续执行cat /etc/resolv.conf在 Ubuntu 桌面版或启用了 systemd-resolved 的系统上你会看到nameserver 127.0.0.53这个时候要高度怀疑它就是罪魁祸首。再补充一个判断方法对比不同容器的 DNS 配置。执行docker inspect看看 api、worker、plugin_daemon 的HostConfig.Dns字段是否一致。如果你在 compose 里只给某个容器指定了 DNS而其他容器没有那“只有部分插件报错”就解释得通了。3.4 第四步用一个干净容器做交叉验证为了确认“是整个网络的 DNS 配置不对还是只有 Dify 插件容器不对”最好的办法是起一个临时容器接到 Dify 的默认网络里测试。Dify 的 compose 项目名如果保持默认网络名通常是dify_default。docker run --rm --network dify_default curlimages/curl curl -I http://api:5001如果这个临时容器里访问api:5001也失败说明是整个网络的 DNS 配置有问题光改 Dify 的容器配置不彻底。如果临时容器正常只有 plugin_daemon 不行那就需要单独给 plugin_daemon 设置 DNS 或检查它的镜像是否缺少相关组件。这个“干净容器对照法”看着简单但真的能帮你节省很多时间。我处理过好几次问题都发现Dify 本身网络是通的只是某些插件镜像基于更小的运行时解析逻辑受容器内配置影响更大最后给对应容器单独加 DNS 就解决了。4. 容器 DNS 配置的几种修复方式4.1 改全局 daemon.json一劳永逸但影响面最大如果你的宿主机 DNS 本身就乱最直接的办法是在 Docker 守护进程层面统一指定上游 DNS。编辑/etc/docker/daemon.json加入dns字段{ dns: [ 223.5.5.5, 119.29.29.29 ] }选 DNS 服务器时可以优先考虑国内访问稳定的公共 DNS例如阿里云的223.5.5.5和腾讯云的119.29.29.29。如果你所在企业内网有必须解析的内部域名记得把内网 DNS 放在列表最前面否则内部服务名可能解析不了。改完以后需要重启 Docker 守护进程这一步要注意重启会将所有运行中的容器一并重启会影响线上服务。如果你的 Dify 已经跑了好几天容器里有内存态任务最好先docker compose stop停掉 Dify再重启 Docker最后docker compose up -d拉起环境。命令如下sudo systemctl restart docker docker compose up -d之后再用docker inspect验证容器的HostConfig.Dns是否已经变成刚配置的地址。全局配置的好处是所有容器都继承坏处也是影响面大。对于只想快速解决某一类插件问题的情况我更推荐下面这种定向配置。4.2 在 compose 文件里给插件容器单独指定 DNS对于自部署 Dify我更习惯直接修改docker-compose.yaml给plugin_daemon或者你能确认出问题的插件服务加上dns配置services: plugin_daemon: dns: - 223.5.5.5 - 119.29.29.29注意不同版本的 Dify compose 文件里插件守护进程的服务名可能不一样有的老版本叫plugin_daemon新版本可能是plugin-daemon。所以这里建议先执行docker compose ps查看服务列表再决定改哪一项。修改完成后让配置生效不是简单 restart 就行的需要重新创建容器docker compose up -d --force-recreate plugin_daemon这样只会重建 plugin_daemon不会影响 api、worker 和 nginx。如果担心影响其他服务也可以只对plugin_daemon执行。这个方案比改全局 daemon.json 安全得多适合生产环境紧急止血。4.3 用 extra_hosts 把关键域名写死直白且高效有时候 DNS 短期修不好但插件又急着要用可以把关键服务域名直接写到容器的 hosts 文件里。在 compose 的对应服务下加extra_hostsservices: plugin_daemon: extra_hosts: - api:192.168.1.20 - ollama:192.168.1.30这里的api:192.168.1.20表示把api这个名字固定解析到192.168.1.20。这种方式适合目标服务 IP 比较固定的场景比如局域网内部的 Ollama 服务、固定的向量数据库地址。它的优点是不依赖 DNS缺点是没有灵活性一旦服务换了 IP就需要手动同步。如果目标服务在宿主机上还可以用 Docker 提供的host-gateway语法services: plugin_daemon: extra_hosts: - host.docker.internal:host-gateway加了这条之后容器访问host.docker.internal时会被解析到宿主机的真实 IP。这也是让 Dify 插件调用宿主机 Ollama 最稳妥的写法。4.4 Docker Desktop 用户与 host 网络模式的特殊处理如果你是在 Windows 或 macOS 上用 Docker Desktop 部署 DifyDNS 配置入口略有不同。Docker Desktop 的 Settings 里可以设置 Docker daemon 的 JSON 配置也支持全局 DNS但实测下来Docker Desktop 在某些网络环境下自身 DNS 解析不够稳定插件报PluginInvokeError的频率明显比 Linux 服务器高。另一个常见做法是把某个容器直接改成network_mode: host让容器共享宿主机网络栈。这在 Linux 上很顺手但在 Docker Desktop尤其 macOS上支持并不完整容器虽然能联网但端口映射和容器名解析行为跟预期差别很大。所以我的建议是部署 Dify 尽量用 Linux 服务器插件调用本地服务优先用extra_hosts不要为了省事切换到 host 模式否则后续很多容器间服务名的解析都会出问题。5. 排查过程中常踩的坑一组避雷对照表5.1 容器里用 127.0.0.1 调宿主机注定失败这个操作我在社群里见得太多了。很多人在宿主机上启动了 Ollama然后在 Dify 插件里填的 Base URL 是http://127.0.0.1:11434结果必报PluginInvokeError。原因很简单插件运行在容器里容器内的127.0.0.1指向容器自己而不是宿主机。解决办法上面已经提到给容器加extra_hosts映射host.docker.internal:host-gateway然后把地址改成http://host.docker.internal:11434。建议的 compose 配置services: plugin_daemon: extra_hosts: - host.docker.internal:host-gateway dns: - 223.5.5.5改完以后重建容器再测试。如果 Ollama 也是用 Docker 启动的并且和 Dify 在同一个自定义 network 里那更简单插件地址直接写http://ollama:11434即可。5.2 systemd-resolved 造成的“时好时坏”这个坑会表现得非常隐蔽。早上插件还能用下午突然不行了过几分钟又自行恢复。你翻日志找不到固定规律容器重建之后依然时好时坏。这时候大概率是宿主机启用了 systemd-resolved容器继承的上游 DNS 指针不稳定。判断方法仍然是进容器看/etc/resolv.conf如果 nameserver 是127.0.0.53就可以确认了。修复方案优先在daemon.json里固定公共 DNS或者直接关闭桌面系统的 systemd-resolved 服务但这会影响系统自身的域名解析建议慎重。最省事的就是我前面写的daemon.json指定 DNS。这里有个细节值得注意即使你改完daemon.json并重启 Docker旧容器也需要重建才会用到新的 DNS所以docker compose up -d --force-recreate是必须的。5.3 看到 SSL 错误别急着改 DNS很多用户把 Dify 接入公司内部系统时看到SSL: CERTIFICATE_VERIFY_FAILED就以为是域名解析问题其实是证书链路没配好或者容器时间不同步。DNS 解析是“找到服务器在哪”SSL 是“确认这个服务器确实可信”两个环节不要混在一起。排查时可以先用getent hosts确认域名能解析出 IP然后看报错关键字。如果域名能解析但报证书错误下一步检查容器系统时间docker exec plugin_daemon date如果时间明显不对会导致 HTTPS 证书有效性校验失败。关于 Dify 接入内网 HTTPS 服务时的证书处理通常要把自签 CA 证书挂载到插件容器里并设置SSL_CERT_FILE环境变量而不是盲目关掉校验。热搜里经常出现“dify ssl错误”这类词其实很多都是时间同步问题。这一块和 DNS 是两个方向不能混为一谈。5.4 PluginInvokeError 并不全等于 DNS 问题为了行文严谨这里把常见的几类报错放在一起对照。通过错误关键字快速判断方向是我认为排查效率最高的做法。日志关键字可能原因优先排查方向getaddrinfo/Name or service not knownDNS 解析失败容器 DNS 配置、上游 DNS、extra_hostsconnection refused端口没监听或防火阻断被调服务状态、端口映射、服务协议timed out网络不通、防火墙丢包网络策略、路由、防火墙CERTIFICATE_VERIFY_FAILED证书不受信任或时间偏差容器时间、CA 证书、SSL 配置JSONDecodeError返回内容不是预期 JSON服务方响应格式、插件解析逻辑日志里经常还会看到 Dify 工作流整体报错比如提示“上下文超长”这种问题就不是出在插件调用链路而是模型的 token 限制。因为 Dify 会把工作流上下文传给模型当文本量超过模型限制时会报长度相关错误和容器 DNS 完全没有关系。一旦把问题归错了类就会在错误的维度上做无效排查。所以我每次都会强调先定位异常关键字再下结论。5.5 给常用排查场景准备一个检查脚本排查 Dify 容器 DNS 问题时与其一遍遍手敲命令不如准备一个小脚本放在宿主机上出问题时一键跑快速定位方向。# dify-dns-check.sh echo 1. resolv.conf docker exec plugin_daemon cat /etc/resolv.conf echo 2. resolve api container docker exec plugin_daemon getent hosts api || echo FAIL: api echo 3. resolve external domain docker exec plugin_daemon getent hosts www.baidu.com || echo FAIL: external echo 4. test api port docker exec plugin_daemon sh -c wget -q -O - http://api:5001/health || echo FAIL || echo FAIL: http api echo 5. docker daemon dns cat /etc/docker/daemon.json | grep -A 4 dns echo 6. host config dns docker inspect plugin_daemon --format {{json .HostConfig.Dns}}脚本不复杂但很实用。每次插件报PluginInvokeError我会先跑一遍这个脚本根据输出直接判断是上游 DNS、容器名解析、还是服务端口的问题省掉很多纠结。6. 最后想分享的一点体会踩过几次坑之后我现在处理PluginInvokeError的习惯已经固定下来先看异常关键字再进容器验证 DNS确认后再决定改全局配置还是局部配置。说实话Dify 本身的设计让插件调用链路比普通单体应用复杂不少而 DNS 又是这条链路上最不起眼、最容易出错的一环。我见过有人折腾了几天最后发现只是/etc/docker/daemon.json里少了一行 DNS 配置。这种问题一旦理解原理解决起来就是几分钟的事。最后再送一个小技巧改完 DNS 配置后不要只执行docker compose restart因为很多配置尤其 DNS 和 extra_hosts在重新 create 容器时才会生效用docker compose up -d --force-recreate才是稳妥做法。容器重建后 IP 可能会变但 Dify 内部服务名解析会自动适配如果插件里写了硬编码 IP那就要特别注意了。希望这篇排查攻略能帮你省下接下来几天的排查时间下次再看到PluginInvokeError第一反应不再是翻代码而是直接想到容器里的那个resolv.conf。