我有一个包含一些容器的 docker compose 堆栈。有问题的两个容器是一个扩展的 python:3-onbuild 容器(参见基础镜像 here ),其中运行着 falcon Web 服务器,还有一个基本的 Node:8.11-alpine 容器,它试图向 python Web 服务器发出 post 请求。这是我的撰写文件的简化版本:
version: '3.6'
services:
app: # python:3-onbuild
ports:
- 5000
build:
context: ../../
dockerfile: infra/docker/app.Dockerfile
lambda: # node:8.11-alpine
ports:
- 10000
build:
context: ../../
dockerfile: infra/docker/lambda.Dockerfile
depends_on:
- app
我知道网络正在工作,因为如果我 ssh 进入 lambda 容器
docker exec -it default_lambda_1 /bin/ash
并运行ping应用程序
我得到了响应。
$ ping app
PING app (172.18.0.4): 56 data bytes
64 bytes from 172.18.0.4: seq=0 ttl=64 time=0.184 ms
64 bytes from 172.18.0.4: seq=1 ttl=64 time=0.141 ms
我什至可以运行 ping app:5000
ping app:5000
PING app:5000 (172.18.0.3): 56 data bytes
64 bytes from 172.18.0.3: seq=0 ttl=64 time=0.105 ms
64 bytes from 172.18.0.3: seq=1 ttl=64 time=0.176 ms
我什至在我的 api 上创建了一个 /status
端点来尝试解决此问题。它查询数据库中所有表的名称,因此如果数据库未准备好,它应该失败。但是,如果我运行curl http://app:5000/status
,我会收到包含所有表名称的响应,完全没有问题。
只有当我的 Node 进程尝试发出 post 请求(使用 axios)时才会出现问题
this.endpointUrl = 'http://app:5000';
const options: AxiosRequestConfig = {
method: 'POST',
url: `${this.endpointUrl}/session/get`,
data: {
'client': 'alexa'
},
responseType: 'json'
};
axios(options)
.then((response: AxiosResponse<any>) => {
console.log(`[SessionService][getSession]: "/session/get" responseData = ${JSON.stringify(response.data)}`);
})
.catch(err => {
console.error(`[SessionService][getSession]: error = ${err}`);
});
我收到以下错误:
console.error src/index.ts:111
VirtualConsole.on.e at project/node_modules/jsdom/lib/jsdom/virtual-console.js:29:45
Error: Error: getaddrinfo ENOTFOUND app app:5000
at Object.dispatchError (/project/node_modules/jsdom/lib/jsdom/living/xhr-utils.js:65:19)
at Request.client.on.err (/project/node_modules/jsdom/lib/jsdom/living/xmlhttprequest.js:676:20)
at emitOne (events.js:121:20)
at Request.emit (events.js:211:7)
at Request.onRequestError (/project/node_modules/request/request.js:878:8)
at emitOne (events.js:116:13)
at ClientRequest.emit (events.js:211:7)
at Socket.socketErrorListener (_http_client.js:387:9)
at emitOne (events.js:116:13)
at Socket.emit (events.js:211:7) undefined
console.error src/index.ts:111
axios_1.default.then.catch.err at services/Service.ts:94:25
[SessionService][getSession]: error = Error: Network Error
我从 here 了解了有关此 ENOTFOUND
错误消息的更多信息
getaddrinfo is by definition a DNS issue
然后,我考虑使用 Node dns
包来查找app
> dns.lookup('app', console.log)
GetAddrInfoReqWrap {
callback: [Function: bound consoleCall],
family: 0,
hostname: 'app',
oncomplete: [Function: onlookup],
domain:
Domain {
domain: null,
_events: { error: [Function: debugDomainError] },
_eventsCount: 1,
_maxListeners: undefined,
members: [] } }
> null '172.18.0.3' 4
> dns.lookup('app:5000', console.log)
GetAddrInfoReqWrap {
callback: [Function: bound consoleCall],
family: 0,
hostname: 'app:5000',
oncomplete: [Function: onlookup],
domain:
Domain {
domain: null,
_events: { error: [Function: debugDomainError] },
_eventsCount: 1,
_maxListeners: undefined,
members: [] } }
> { Error: getaddrinfo ENOTFOUND app:5000
at errnoException (dns.js:50:10)
at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:92:26)
code: 'ENOTFOUND',
errno: 'ENOTFOUND',
syscall: 'getaddrinfo',
hostname: 'app:5000' }
看起来它可以找到 app
但找不到 app:5000
?诡异的。发现这一点后,我尝试将 python Web 服务器端口更改为 80,以便我可以向普通的 http://app
发出 axios 请求,但这导致了相同的 ENOTFOUND
错误。
我找不到太多有关如何解决此问题的信息。一个想法来自here
the problem is that http.request uses dns.lookup instead of dns.resolve
答案建议使用覆盖网络。我不太明白那是什么,但如果需要的话,我会继续这样做。但是,我想知道现在我使用 compose yml 版本 3.6 是否有更简单的解决方案。无论如何,dns.resolve
都会为我提供与 dns.lookup
> dns.resolve('app', console.log)
QueryReqWrap {
bindingName: 'queryA',
callback: [Function: bound consoleCall],
hostname: 'app',
oncomplete: [Function: onresolve],
ttl: false,
domain:
Domain {
domain: null,
_events: { error: [Function: debugDomainError] },
_eventsCount: 1,
_maxListeners: undefined,
members: [] },
channel: ChannelWrap {} }
> null [ '172.18.0.3' ]
> dns.resolve('app:5000', console.log)
QueryReqWrap {
bindingName: 'queryA',
callback: [Function: bound consoleCall],
hostname: 'app:5000',
oncomplete: [Function: onresolve],
ttl: false,
domain:
Domain {
domain: null,
_events: { error: [Function: debugDomainError] },
_eventsCount: 1,
_maxListeners: undefined,
members: [] },
channel: ChannelWrap {} }
> { Error: queryA ENOTFOUND app:5000
at errnoException (dns.js:50:10)
at QueryReqWrap.onresolve [as oncomplete] (dns.js:238:19)
code: 'ENOTFOUND',
errno: 'ENOTFOUND',
syscall: 'queryA',
hostname: 'app:5000' }
编辑:顺便说一句,我可以通过 https 从 lambda 容器向外界发出相同的 POST 请求(与应用程序服务运行相同代码的生产服务器)
编辑:如果我按照建议从端点Url中删除http://
here ,我收到错误:协议(protocol)无效:应用程序
编辑:我认为这可能与this issue有关使用 node-alpine 基础镜像,但更改为 node:8.11
或 node:carbon
导致相同的 DNS 问题 ENOTFOUND
编辑:我确定这不是计时问题,因为我正在等待运行测试 100 秒,每 10 秒发出一次测试请求。如果我跳过测试步骤并让容器像平常一样旋转,我就可以在 100 秒标记之前从 lambda
容器内部 curl app
...
最佳答案
问题最终出在我的测试运行程序上,开玩笑。
The default environment in Jest is a browser-like environment through jsdom. If you are building a node service, you can use the node option to use a node-like environment instead.
要解决此问题,我必须将其添加到我的 jest.config.js 文件中:
{
// ...
testEnvironment: 'node'
// ...
}
关于node.js - docker 与 axios (node) 组成网络错误 ENOTFOUND,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50263684/