一个位于 Docker 容器内的应用为什么无法访问其自己的 API 终端点?

huangapple go评论89阅读模式
英文:

Why can't an app inside a docker container access it's own API endpoint?

问题

我在Windows上使用Docker桌面应用程序。在Ubuntu WSL2中,我有一个运行着Meteor / Node.js服务器的Docker容器。从WSL内部使用docker-compose启动。

docker-compose.yaml
  ...
  environment:
    - ROOT_URL=http://localhost:8080
    - PORT=8080
  ports:
    - "8080:8080"

在服务器上调用Meteor-Files,它将处理请求:

return new Promise((resolve, reject) => {
  UserFiles.load(url, options,
    (loadError, fileRef) => {
      if (loadError) {
        return reject(loadError);
      } else {
        return resolve(fileRef);
      }
    }, true);
});

现在,Docker容器内的应用程序必须访问自身的HTTP端点,如 http://127.0.0.1:8080/api

当我从容器外部调用此端点时,我得到了正确的结果。

当我通过终端连接到Docker容器并使用curl访问该端点时,我也得到了正确的结果。

更新:当在真实的Linux机器上运行Docker而不是Windows上的WSL2 Ubuntu时,它也可以工作。

但是,当运行在容器内的应用程序/服务尝试通过相同的端点和相同的URL调用自身时,我会收到 ECONNREFUSED - CONNECTION REFUSED 错误。

完整错误消息:

FetchError: request to http://127.0.0.1:8080/api failed, reason: connect ECONNREFUSED 127.0.0.1:8080
at ClientRequest.<anonymous> (/app/bundle/programs/server/npm/node_modules/meteor/fetch/node_modules/node-fetch/lib/index.js:1444:11)
at ClientRequest.emit (events.js:315:20)
at ClientRequest.EventEmitter.emit (domain.js:483:12)
at Socket.socketErrorListener (_http_client.js:426:9)
at Socket.emit (events.js:315:20)
at Socket.EventEmitter.emit (domain.js:483:12)
at emitErrorNT (internal/streams/destroy.js:92:8)
at emitErrorAndCloseNT (internal/streams/destroy.js:60:3)
at processTicksAndRejections (internal/process/task_queues.js:84:21)
=> awaited here:
     at Function.Promise.await (/app/bundle/programs/server/npm/node_modules/meteor/promise/node_modules/meteor-promise/promise_server.js:56:12)
     at imports/startup/filemigration.js:273:20
     at /app/bundle/programs/server/npm/node_modules/meteor/promise/node_modules/meteor-promise/fiber_pool.js:43:40 {
   type: 'system',
   errno: 'ECONNREFUSED',
   code: 'ECONNREFUSED'
 }

这可能是什么问题?也许是WSL2的安全性问题?

英文:

I use the docker desktop app on Windows.
Inside a Ubuntu WSL2 I have a docker container with a meteor / nodejs server running.
Started via docker-compose from inside the WSL.

docker-compose.yaml
  ...
  environment:
    - ROOT_URL=http://localhost:8080
    - PORT=8080
  ports:
    - &quot;8080:8080&quot;

Call to Meteor-Files on the server, which will do the request:

  return new Promise((resolve, reject) =&gt; {
    UserFiles.load(url, options,
      (loadError, fileRef) =&gt; {
        if (loadError) {
          return reject(loadError);
        } else {
          return resolve(fileRef);
        }
      }, true);
  });
}

Now the app inside the docker container must access an HTTP endpoint of itself like http://127.0.0.1:8080/api

When I call the this endpoint from outside the docker, I get the correct result.

When I connect into the docker container with a terminal and curl the endpoint, I get the correct result.

Update: Also when running a Docker on a real Linux machine and not the WSL2 Ubuntu from Windows, it's working.

But, when the running app / service running inside the container tries to call itself by the same endpoint with the same URL, I get a ECONNREFUSED - CONNECTION REFUSED error.

Full Error Message:

FetchError: request to http://127.0.0.1:8080/api failed, reason: connect ECONNREFUSED 127.0.0.1:8080
at ClientRequest.&lt;anonymous&gt; (/app/bundle/programs/server/npm/node_modules/meteor/fetch/node_modules/node-fetch/lib/index.js:1444:11)
at ClientRequest.emit (events.js:315:20)
at ClientRequest.EventEmitter.emit (domain.js:483:12)
at Socket.socketErrorListener (_http_client.js:426:9)
at Socket.emit (events.js:315:20)
at Socket.EventEmitter.emit (domain.js:483:12)
at emitErrorNT (internal/streams/destroy.js:92:8)
at emitErrorAndCloseNT (internal/streams/destroy.js:60:3)
at processTicksAndRejections (internal/process/task_queues.js:84:21)
 =&gt; awaited here:
     at Function.Promise.await (/app/bundle/programs/server/npm/node_modules/meteor/promise/node_modules/meteor-promise/promise_server.js:56:12)
     at imports/startup/filemigration.js:273:20
     at /app/bundle/programs/server/npm/node_modules/meteor/promise/node_modules/meteor-promise/fiber_pool.js:43:40 {
   type: &#39;system&#39;,
   errno: &#39;ECONNREFUSED&#39;,
   code: &#39;ECONNREFUSED&#39;
 }

What could be the problem of this? Maybe a WSL2 security problem?

答案1

得分: 1

从错误消息来看,最终需要访问应用程序本身的HTTP端点的代码位于名为imports/startup/filemigration.js的文件中。

从其名称来看,我假设它在应用程序启动时运行。但是,Meteor应用程序可能还没有启动其http Node服务器,因此出现了“连接被拒绝”的错误消息。

甚至可能存在不同启动任务之间的竞争条件,这会导致在不同的环境下出现不同的行为。

如果可行的话,您可以尝试稍微延迟启动/迁移任务,以确保Meteor应用程序已经正确启动其服务器并侦听端口。

通常,您可以使用Meteor.startup(func)来实现:

> 当客户端或服务器启动时运行[func]代码。
>
> [...]
>
> 在服务器上,该函数将在服务器进程完成启动时立即运行。

英文:

From the error message, the code that ultimately needs to access an HTTP endpoint of the app itself, resides in a file called: imports/startup/filemigration.js

From its name, I assume it runs when the app starts. However, there is a possibility that the Meteor app has not started its http Node server yet, hence the "connection refused" error message.

There may even be a race condition between the different starting tasks, leading to different behaviors depending on the exact environments.

You can try delaying your startup / migration task a bit, if feasible, to ensure the Meteor app has correctly started its server and listens on the port.

You can typically use Meteor.startup(func) for that:

> Run [func] code when a client or a server starts.
>
> [...]
>
> On a server, the function will run as soon as the server process is finished starting.

答案2

得分: 0

我不能确定任何事情,除非查看实际的代码或看到完整的错误消息,但尝试将您的以下部分替换:

- ROOT_URL=http://localhost:8080

替换为:

- ROOT_URL=http://127.0.0.1:8080

如果您在其他地方也有本地主机URL,请进行替换。

英文:

I can't say anything for sure without looking at the actual code or seeing the full error message, but try to replace your

- ROOT_URL=http://localhost:8080

with

- ROOT_URL=http://127.0.0.1:8080

And if you have any other places with localhost url, replace them as well.

答案3

得分: -1

我也不能确定地说什么,没有查看代码或错误的情况下,但您尝试将localhost替换为Compose文件中容器的名称了吗?

英文:

I also can't say anything certainly without looking at the code or error, but have you tried replacing the localhost with the name of the container within the compose file?

huangapple
  • 本文由 发表于 2023年7月31日 22:57:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/76804838.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定