Docker - 为啥这个 express.js 容器带有暴露/发布的端口拒绝连接? (使用 boot2docker)

Posted

技术标签:

【中文标题】Docker - 为啥这个 express.js 容器带有暴露/发布的端口拒绝连接? (使用 boot2docker)【英文标题】:Docker - Why is this express.js container with an exposed/published port reject connections? (using boot2docker)Docker - 为什么这个 express.js 容器带有暴露/发布的端口拒绝连接? (使用 boot2docker) 【发布时间】:2014-09-26 13:03:48 【问题描述】:

我在 docker 容器内有一个简单的 hello world express.js 应用程序。它设置为在端口 8080 上运行,并且 docker 文件在映像中公开了此端口。此外,我在运行映像时发布了端口。然而,当我尝试发出简单的 curl 请求时,连接被拒绝。以下是我设置此测试的方式:

我的 Dockerfile 非常简单:

FROM node

ADD ./src /src
WORKDIR /src

# install your application's dependencies
RUN npm install

# replace this with your application's default port
EXPOSE 8080

# replace this with your main "server" script file
CMD [ "node", "server.js" ]

在我的 ./src 目录中,我有一个 server.js 文件,如下所示:

var express = require('express');
var app = express();

app.get('/', function(req, res)
  res.send('Hello World');
);

var server = app.listen(8080, function() 
    console.log('Listening on port %d', server.address().port);
);

还有一个基本的 package.json,如下所示:


  "name": "hello-world",
  "description": "hello world test app",
  "version": "0.0.1",
  "private": true,
  "dependencies": 
    "express": "4.7.2"
  

图像构建得很好:

→ docker build -t jimjeffers/hello-world .
Sending build context to Docker daemon 1.126 MB
Sending build context to Docker daemon 
Step 0 : FROM node
 ---> 6a8a9894567d
Step 1 : ADD ./src /src
 ---> 753466503fbf
Removing intermediate container 135dab70dfff
Step 2 : WORKDIR /src
 ---> Running in 12257ff3f990
 ---> 010ce4140cdc
Removing intermediate container 12257ff3f990
Step 3 : RUN npm install
 ---> Running in 1a9a0eb9d188
 ---> 5dc97c79281e
Removing intermediate container 1a9a0eb9d188
Step 4 : EXPOSE 8080
 ---> Running in abbaadf8709d
 ---> 9ed540098ed2
Removing intermediate container abbaadf8709d
Step 5 : CMD [ "node", "server.js" ]
 ---> Running in 63b14b5581cd
 ---> eababd51b50e
Removing intermediate container 63b14b5581cd
Successfully built eababd51b50e

开始就好了:

→ docker run -P -d jimjeffers/hello-world
ee5024d16a679c10131d23c1c336c163e9a6f4c4ebed94ad4d2a5a66a64bde1d

→ docker ps
CONTAINER ID        IMAGE                           COMMAND                CREATED             STATUS              PORTS                     NAMES
ee5024d16a67        jimjeffers/hello-world:latest   node server.js         About an hour ago   Up 11 seconds       0.0.0.0:49158->8080/tcp   jovial_engelbart    
5d43b2dee28d        mongo:2.6                       /usr/src/mongo/docke   5 hours ago         Up 3 hours          27017/tcp                 some-mongo          

我可以确认服务器正在容器内运行:

→ docker logs ee5024d16a67
Listening on port 8080

但如果我尝试发出请求,连接将被拒绝。

→ curl -i 0.0.0.0:49158
curl: (7) Failed connect to 0.0.0.0:49158; Connection refused

这里有什么我遗漏的吗?如果我在不使用 docker 的情况下运行应用程序,它会按预期工作:

→ node src/server.js 
Listening on port 8080

→ curl -i 0.0.0.0:8080
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/html; charset=utf-8
Content-Length: 11
ETag: W/"b-1243066710"
Date: Mon, 04 Aug 2014 05:11:58 GMT
Connection: keep-alive

Hello World

【问题讨论】:

您的主机系统的防火墙是否阻止了它? 【参考方案1】:

我发现了混乱的根源。我的机器在 Mac OSX 上运行,因此我使用boot2docker 安装了 docker。

所以再次重复这个过程:

→ docker run -P -d jimjeffers/hello-world
28431b32b93dbecaa2a8a5b129cbd36eebe8a90f4c20ab10735455d82fa9de37

→ docker ps
CONTAINER ID        IMAGE                           COMMAND                CREATED             STATUS              PORTS                     NAMES
28431b32b93d        jimjeffers/hello-world:latest   node server.js         2 hours ago         Up 9 minutes        0.0.0.0:49159->8080/tcp   stoic_franklin      
5d43b2dee28d        mongo:2.6                       /usr/src/mongo/docke   6 hours ago         Up 4 hours          27017/tcp                 some-mongo   

最后,诀窍不是连接到我自己的机器,而是从 VM 的 IP 地址 curl:

→ boot2docker ip

The VM's Host only interface IP address is: 192.168.59.103

所以我在 curl 虚拟机时终于取得了成功:

→ curl -i 192.168.59.103:49159
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/html; charset=utf-8
Content-Length: 11
ETag: W/"b-1243066710"
Date: Mon, 04 Aug 2014 04:32:37 GMT
Connection: keep-alive

这一切都在 Docker 的 installation guide 上进行了详细解释,但我错过了它,因为它在文档的末尾。

【讨论】:

偶然发现了这一点,然后意识到我已经阅读了 boot2docker 文档说明了这一点。谢谢吉姆。 +1 使用新工具集,这里是docker-machine ip,用于获取 docker VM 的 IP 地址。【参考方案2】:

在您的 dockerfile 中公开端口后,您还必须告诉 Docker 如何从您的主机系统映射到您的容器。

尝试将 Docker 运行命令更改为如下内容:

docker run -p 127.0.0.1:49158:8080 -d jimjeffers/hello-world

然后尝试:curl'ing:

curl -i 127.0.0.1:49158

【讨论】:

您好,感谢乔纳森的回复。使用该显式 IP 映射重新启动容器仍会导致响应被拒绝。会不会有别的东西挡住它? 如果您没有以 root 身份运行命令,那么它将无法绑定到端口。您是否每次都使用sudo 运行命令? 谢谢乔纳森,你没有回答我的问题,但你指出了我正确的方向。因为我在 OSX 上使用 boot2docker,所以我无法以 sudo 运行 docker。 Boot2Docker 运行一个轻量级的虚拟机。所以我将我暴露的端口发布到虚拟机而不是我自己的机器上。 不错。很高兴你知道了!

以上是关于Docker - 为啥这个 express.js 容器带有暴露/发布的端口拒绝连接? (使用 boot2docker)的主要内容,如果未能解决你的问题,请参考以下文章

使用 Express.js 和 docker 理解微服务

为啥 localhost:5000 在 Express.js 中被拒绝?

为啥我的 Express.js 后端的 CORS 设置不起作用?

为啥我不能像传递其他变量一样将函数从 Express.js 传递给 EJS?

在 Express.js 中为啥 res.json() 之后的代码仍然执行?

为啥我不能在 Socket.io http 服务器上使用 express.js 获取方法