Dockerizing Our API

Understanding Docker

As a former sysadmin, the concept of containers was a bit hard to grasp at first.

“So you want to virtualize an app, in something like a VM? But it’s not the entire VM? Oh but it could be the entire VM?”

The concept wasn’t fully clear to me until I came across Mike Coleman’s post on Docker’s blog.

I don’t want to summarize it and do any injustice to the original post, so check it out!

If you’re already familiar with Docker..

Dockerizing the Database

Mongo has an official Docker image, so we’ll be using that

$ docker run --name jamstack-db -d -p 27017:27017 mongo:latest mongod --bind_ip_all

Now let’s decrypt all of the arguments:

  • –name // specify a name
  • -d // detach, or run in background
  • -p 27017:27017// Host:Container port syntax. Host will listen on 27017, mapped to the container’s exposed port of 27017
  • mongo:latest // use the mongo image, latest version
  • mongod // command to start the database
  • –bind_ip_all // versions >3.5 of mongod listen to localhost by default

Dockerizing the API [with troubleshooting!]

If you just want to get this API started without troubleshooting, follow the Quick Start section in the README.

1) Ensure our MongoDB container is running

Let’s make sure we don’t start an API without the backend :)

$ docker ps -a
CONTAINER ID        IMAGE                 COMMAND                  CREATED             STATUS                   PORTS               NAMES
b75379169b08        mongo:latest          "docker-entrypoint..."   6 hours ago         Exited (0) 6 hours ago                       jamstack-db

# mine was stopped, so let's start it
$ docker start jamstack-db

2) Create a Dockerfile

3) Also, a .dockerignore file

4) Building our image

After creating those files in the API project directory, we will create a Docker image, then create a container based on that image.

Note: If you haven’t created the JAMstack API yet, read my post, here

# Build image
$ docker build -t nsuave/jamstack-api .
  • -t // tag for the image being built
  • . // current directory
# Create container
$ docker run --name jamstack-api -d -p 8008:8008 nsuave/jamstack-api
  • –name // specify a name
  • -d // detach or run in background
  • -p 8008:8008 // Host will listen on port 8008. Remember that we defined the container’s exposed port of 8008 in server.js
  • running a container based on the “nsuave/jamstack-api” image that we created

It’s running! Kind of.

If you run “docker logs jamstack-api”, you’ll notice an “ECONNREFUSED” error.

$ docker logs jamstack-api

> [email protected] start /usr/src/jamstack-api
> node server.js

{ MongoError: failed to connect to server [localhost:27017] on first connect [MongoError: connect ECONNREFUSED 127.0.0.1:27017]
    at Pool.<anonymous> (/usr/src/jamstack-api/node_modules/mongodb-core/lib/topologies/server.js:336:35)
    at emitOne (events.js:116:13)
    at Pool.emit (events.js:211:7)
    at Connection.<anonymous> (/usr/src/jamstack-api/node_modules/mongodb-core/lib/connection/pool.js:280:12)
    at Object.onceWrapper (events.js:317:30)
    at emitTwo (events.js:126:13)
    at Connection.emit (events.js:214:7)
    at Socket.<anonymous> (/usr/src/jamstack-api/node_modules/mongodb-core/lib/connection/connection.js:187:49)
    at Object.onceWrapper (events.js:315:30)
    at emitOne (events.js:116:13)
  name: 'MongoError',
  message: 'failed to connect to server [localhost:27017] on first connect [MongoError: connect ECONNREFUSED 127.0.0.1:27017]' }

Our database container should be listening on 27017, right?

$ docker run --name jamstack-db -d -p 27017:27017 mongo:latest mongod

And our config/db.js file is trying to connect to that port, right?

url: "mongodb://localhost:27017"

The first problem

This StackOverflow post quickly shows up when you search for that error. Looks like we need to change our config/db.js file!

url: "mongodb://mongo:27017"

When we created the container, it was using the source code that used ‘localhost’ instead of ‘mongo’.

We need to reference the container name of ‘mongo’ instead, and Docker will handle the networking between the containers.

Now that we’ve updated config/db.js to use ‘mongo’ instead of ‘localhost’, let’s update the image with our new file, but hold off on creating a new container:

$ docker stop jamstack-api
$ docker rm jamstack-api
$ docker rmi nsuave/jamstack-api
$ docker build -t nsuave/jamstack-api .

The second problem

Did you notice the reference to –link flag in that StackOverflow post?

According to the official docs, this flag will “Add link to another container”

Sounds like what we want! Syntax is sourceContainerName:containerAliasName

// old
$ docker run --name jamstack-api -d -p 8008:8008 nsuave/jamstack-api

// new
$ docker run --name jamstack-api -d -p 8008:8008 --link jamstack-db:mongo nsuave/jamstack-api

Voila!

We’re back in business! API is now running in a container, and it’s talking to a MongoDB container.

$ docker ps -a
CONTAINER ID        IMAGE                 COMMAND                  CREATED             STATUS                   PORTS               NAMES
e61e2362a5b7        nsuave/jamstack-api   "npm start"              5 seconds ago       Up 4 minutes        0.0.0.0:8008->8008/tcp     jamstack-api
b75379169b08        mongo:latest        "docker-entrypoint..."   3 days ago          Up 9 minutes         0.0.0.0:27017->27017/tcp   jamstack-db