article

Micro Services Part I: Node, Docker, and Docker Compose

Node.js and Docker logo

This is Part I of a two part series of how to build and deploy Dockerized applications. In this article you will see a breakdown of the Dockerfile and how to use Docker Compose on a simple application. We will run the app locally and in Part II, we'll stand it up in the cloud using AWS EC2.

Before we get started:

It assumes that you have Docker installed on your machine, if you don't check here. You can follow this tutorial with only Docker installed but for the first step you will need Node.js. If you don't have it get it here. You also should know a little about Git and Github so that you can clone the repo.

I have created a repo of this app on Github. Clone it from here and follow along!

Now that we got that stuff out of the way, let's get started!

Start with Node

Let's make a regular Node.js Express app

My folder structure looks like this:

/app
  docker.compose.yml
  Dockerfile
  index.html
  package.json
  server.js
  README.md

The Challenges of Nanoservices

If you have cloned the repo, you can simply run the command npm i && npm start from the root directory and then in your browser visithttp://localhost:8080 and see the app run using Node.

You should see this in your browser:

Browser image of app running locally

This is a super simple node app — I can't stress that enough. Not much going on here, just a simple express server that serves up an HTML file. There is also one api type route /doc.

Now that we know that the app works, let's get to the fun stuff with Docker.

Start container with Docker

Stop the app using cmd + c or control + c.

Now we can start the app in a container using Docker by running in our terminal, the command docker run node-test. We will run it from the root directory as well. In your terminal you should see something like this:

View of CLI, terminal

Return to the browser and you should see the same HTML vialocalhost:8080 as you did before. Excellent!

Let's talk about what is happening.

First, let's look at our Dockerfile. This is the file that Docker will use to build our image automatically.

Our Dockerfile is really simple but let's start from the top, as does Docker.

FROM node:11.3.0
RUN mkdir -p /app
WORKDIR /app
COPY . /app
RUN npm i
ENTRYPOINT [ "npm" ]
CMD ["start"]

Let's look at it line by line:

FROM node:11.3.0

The first line is our base image that we will build upon. FROM will get that image from the Docker registry. It could be any image but we'll use the official image from Node. It has everything that we will need to build on for this service.

RUN mkdir -p /app

The RUN command will allow us to run Linux commands on this image. In this step, we are telling it to make a new folder and if that folder already exist we will override/overwrite it and create the folder anyway using the -p flag. Click here for a Linux command cheat sheet — very helpful.

WORKDIR /app

We will assign a working directory of /app using WORKDIR. Any command that we run after this will be ran in the /app directory.

COPY . /app

Next, we COPY everything (file/directories) from the place . (root) that we first ran the docker run node-test command. We use this command to copy everything to the /app directory of our image.

RUN npm i

RUN executes the command npm i to install the necessary dependencies.

ENTRYPOINT [ "npm" ]

Create an ENTRYPOINT that will “allow you to configure a container that will run as an executable”. For example, we could run docker run node-test update and update the version of NPM in our container.

CMD ["start"]

Lastly, we set CMD which is what will follow the ENTRYPOINT. So now if we run docker run node-test the command that will be ran inside the container is npm start.

Magic 🎉 our app is being served on port 8080!

Start container with Docker Compose

As an added, I have created a docker-compose.yml file. Docker-compose is necessary when you have a multi-container app but can work fine with single container apps as well. Let's take a look at a very simple docker-compose.yml.

version: '3'
services:
  node-test:
    build:
      context: .
    image: node-test
    container_name: node-test
    ports:
      - "8080:8080"

The first line of this YAML file is specifying the version of the file format. At the time of this post the current version 3.7 but we only need to specify 3 for this post. See more about the versions here.

Next we begin to define our various services. Our first and only service will be node-test.

Next, we setup our build. We will give it a context which we have set to be the root, indicated by the dot. Context is about telling Docker where to find the Dockerfile for this service.

By specifying an image, we tell Docker Compose to name this image node-test.

The container_name is to give our container a name of our choosing. If we don't specify it here one will be generated for us.

Lastly, we will indicate our port bindings. Here we can make as many bindings as we want but for the purpose of this we'll only use “8080:8080”. This is the short form of assigning port bindings the long form can be seen here. The first 8080 is for the host machine (your machine). That represents the port you can visit to see your app in the browser. The second port is the port that the app runs on inside of the container. Play around with it by changing the numbers to get a better understanding of how it works. Just make sure to keep you port numbers above 60 as suggested by the docs.

And that's about all there is to it! Granted, this is a simple application and there are many other options/steps that are available to us when using Docker but this is a great starting point.

Next

In my next post, that will be available on March 17, 2019, I will be demonstrating how to deploy this app on AWS using EC2.

Please clap, comment, and follow! And contact me for help if you need it concerning Docker or any of the technologies in this post or my others. I will be happy to consult.

Microservices
Docer
Containers
Backend Development
Software Engineering
DevOps