docker使用axios(节点)错误组成网络ENOTFOUND

时间:2018-05-10 00:05:23

标签: node.js docker docker-compose axios

我有一个带有几个容器的docker compose堆栈。这两个问题是一个扩展的python:3-onbuild容器(参见基本图像here),其中运行了一个falcon webserver和一个试图向python webserver发出post请求的基本node:8.11-alpine容器。这是我的撰写文件的简化版本:

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

我知道网络正在运行,因为如果我进入lambda容器

docker exec -it default_lambda_1 /bin/ash

并运行ping app我收到了回复。

$ 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,我会得到一个包含所有表名的回复,完全没问题。

只有当我的节点进程尝试发出帖子请求(使用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根据定义是DNS问题

那么,我研究了使用节点dns packge来查找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 webserver端口更改为80,因此我可以将axios请求发送到普通的http://app,但这会导致ENOTFOUND错误。

我找不到有关如何解决此问题的更多信息。一个想法来自here

  

问题是http.request使用dns.lookup而不是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请求(运行与应用服务相同代码的生产服务器)

编辑:如果我按照建议here从endpointUrl中删除http://,我会Error: Invalid protocol: app

编辑:我认为它可能与使用node-alpine基本图像的this issue相关,但更改为node:8.11node:carbon会导致相同的DNS问题ENOTFOUND

编辑:我确定这不是一个计时问题,因为我等待运行我的测试100秒,每10秒做一次测试请求。如果我跳过测试步骤并让容器像往常一样旋转起来,我可以在app容器内从lambda容器中卷曲Options -MultiViews RewriteEngine On RewriteBase /var/www/cms/public RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^(.+)$ index.php?url=$1 [QSA] 远远超过100秒标记......

1 个答案:

答案 0 :(得分:0)

问题最终与我的测试运动员开玩笑,开玩笑。

  

Jest中的默认环境是通过jsdom的类似浏览器的环境。如果要构建节点服务,则可以使用node选项来改为使用类似节点的环境。

要解决此问题,我必须在我的jest.config.js文件中添加它:

{
  // ...
  testEnvironment: 'node'
  // ...
}

来源:https://github.com/axios/axios/issues/1418