Docker Compose容器内调用user.localhost报ENOTFOUND错误
问题原因
触发ENOTFOUND getaddrinfo错误的核心原因是容器内部DNS无法解析user.localhost域名:
- 你在docker-compose里配置的
Host(user.localhost)是Traefik的路由规则,只有当请求经过Traefik的80入口、且携带对应Host头时,Traefik才会把请求转发到user服务,这个规则不会同步到Docker内置DNS服务里。 - Docker Compose默认创建的内网中,内置DNS只能直接解析服务名(也就是你compose里写的
traefik/user/pv这几个名字),根本不识别user.localhost这类自定义域名,所以pv容器里发起请求时第一步解析域名就直接失败了。 - 本地运行能正常通,是因为你本地环境大概率已经把
*.localhost指向了本机127.0.0.1,请求发出去直接打到本地监听80端口的Traefik,自然能匹配上路由规则。
可选解决方案
选任意一种即可:
- 方案1:直接走Docker内网服务名通信(性能最优,推荐内部服务互访用)
同网络下的容器可以直接用compose定义的服务名访问对方,不需要走Traefik代理。你只需要把代码里的请求地址改成user服务的容器内监听地址即可,比如如果你的user服务在容器内监听3000端口,就把地址改成http://user:3000/accessticketing,不需要额外改任何网络配置。
注意:用这个方案要确认user服务的监听地址是0.0.0.0而不是127.0.0.1,否则容器内其他服务无法访问到该端口。 - 方案2:保持原有域名逻辑,请求走Traefik转发(和本地访问链路一致)
如果要保留user.localhost的请求写法,有两种改法:- 改代码:把请求地址改成Traefik的服务地址
http://traefik/accessticketing,在请求头里添加"Host": "user.localhost",Traefik收到请求后会匹配Host规则转发到user服务。 - 不改代码改compose配置:给pv服务添加
extra_hosts配置,把user.localhost指向宿主机地址,让容器内的这个域名请求打到宿主机80端口的Traefik上,配置示例:
- 改代码:把请求地址改成Traefik的服务地址
pv: build: './microservices/pv' container_name: 'pv' volumes: - ./db/Project-vendee:/db labels: - "traefik.http.routers.pv.rule=Host(`pv.localhost`)" extra_hosts: - "user.localhost:host-gateway"
注意:这种方案请求会绕经宿主机网络栈,性能比直接内网通信差,适合需要复用Traefik路由、中间件能力的场景。
相关报错与配置参考
错误日志
FetchError: request to http://user.localhost/accessticketing failed, reason: getaddrinfo ENOTFOUND user.localhost at ClientRequest.<anonymous> (file:///E:/Programming/map/microservices/ticketing/node_modules/node-fetch/src/index.js:108:11) at ClientRequest.emit (node:events:539:35) at Socket.socketErrorListener (node:_http_client:454:9) at Socket.emit (node:events:527:28) at emitErrorNT (node:internal/streams/destroy:157:8) at emitErrorCloseNT (node:internal/streams/destroy:122:3) at processTicksAndRejections (node:internal/process/task_queues:83:21) { type: 'system', errno: 'ENOTFOUND', code: 'ENOTFOUND', erroredSysCall: 'getaddrinfo' }
原docker-compose.yaml配置
version: '3.8' services: traefik: image: traefik:v2.7 container_name: 'Traefik' command: --api.insecure=true --providers.docker ports: - "80:80" - "8080:8080" volumes: - '/var/run/docker.sock:/var/run/docker.sock' user: build: './microservices/User' container_name: 'User' volumes: - ./db/user:/db labels: - "traefik.http.routers.user.rule=Host(`user.localhost`)" pv: build: './microservices/pv' container_name: 'pv' volumes: - ./db/Project-vendee:/db labels: - "traefik.http.routers.pv.rule=Host(`pv.localhost`)"
原业务请求代码
try { const response = await fetch("http://user.localhost/accessticketing", { method: "POST", headers: { "Content-Type": "application/json", }, redirect: "follow", referrerPolicy: "no-referrer", body: JSON.stringify({ Token }), }); const massage: any = await response.json(); return massage; } catch (error: SmartError | undefined | any) { console.log(error); if (error.type === "system" && error.erroredSysCall === "connect") { throw { code: 501, maasage: "Unable to connect to the user section" }; } else { throw error; } }
内容的提问来源于stack exchange,提问作者Mohammad Mahdi Moodi
相关产品推荐
相关产品推荐

