Docker - Why is this express.js container with an exposed/published port reject connections? (using boot2docker)
Asked Answered
S

2

7

I have a simple hello world express.js application inside of a docker container. It's set to run on port 8080 and the docker file exposes this port in the image. Additionally, I publish the port when I run the image. Yet when I try to make a simple curl request, the connection is rejected. Here's how I've setup this test:

My Dockerfile is pretty simple:

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" ]

And inside my ./src directory I have a server.js file that looks like this:

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);
});

as well as a basic package.json which looks like this:

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

The image builds just fine:

→ 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

And starts just fine:

→ 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          

I can confirm the server is running inside the container:

→ docker logs ee5024d16a67
Listening on port 8080

But if I attempt to make a request the connection is refused.

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

Is there something I'm missing here? If I run the application without using docker it works as expected:

→ 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
Sanguine answered 4/8, 2014 at 5:21 Comment(1)
Is your host system's firewall blocking it?Putput
S
12

I found out the source of confusion. My machine is running on Mac OSX and thus I installed docker with boot2docker.

So again repeating the process:

→ 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   

Finally, the trick was not to connect to my own machine but to curl from the VM's IP address:

→ boot2docker ip

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

So I had finally achieved success when I curled the VM:

→ 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

This is all explained well in detail on Docker's installation guide but I missed it as it was towards the end of the document.

Sanguine answered 4/8, 2014 at 6:51 Comment(2)
Stumbled on this and then slapped my head with the realisation that I'd already read the boot2docker documentation stating just that. Thanks Jim. +1Planchette
with the new toolset, this is docker-machine ip to get the IP address of the docker VM.Apothem
P
0

After exposing the port in your dockerfile you also have to tell Docker how to map from your host system to your container.

Try changing your Docker run command to something like this:

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

Then try: curl'ing:

curl -i 127.0.0.1:49158

Putput answered 4/8, 2014 at 5:41 Comment(4)
Hey there, thanks for your response Jonathan. Restarting the container with that explicit IP mapping still leads to a rejected response. Could there be anything else blocking it?Sanguine
If you aren't running the commands as root, then it will not be able to bind to the ports. Are you running the commands with sudo each time?Putput
Thanks Jonathan, you didn't answer my question but you pointed me in the right direction. I can't run docker as sudo since I'm using boot2docker on OSX. Boot2Docker runs a lightweight VM. So I was publishing my exposed ports to the VM and not my own machine.Sanguine
Nice. Glad you figured it out!Putput

© 2022 - 2024 — McMap. All rights reserved.