Skip to main content

Containerize the backend

It's now time to create your first container. Packaging a Node.js application is one of the simplest containers you can create, so it's a good place to start.

Let's look at how to do this.

Create a Dockerfile

First, make sure you are in the back folder from the cloned repository. In there, create a new file called Dockerfile.

cd back
touch Dockerfile

In this file, you will list the steps that Docker will need to follow to create the image. The first step is to specify a base image. Each container builds on top of an existing image, and this one is not different. Here, we will use the node image, version 16. This image includes the Node.js runtime, along with npm.

FROM node:16

Now that it has a base image, you can change the working directory to /opt/app. This is where the express server will live. You can then copy the package.json file to the container.

WORKDIR /opt/app
COPY ./package.json .

With the package.json file in place, npm will be able to install all the dependencies. Using the RUN command, you can specify a command to be executed during the build phase for this container.

RUN npm install

All of the dependencies are now installed. You can copy over the source code of your application, which in the case of this specific app, is a single JavaScript file.

COPY ./index.js .
tip

It is a good practice to copy the source code after you installed the dependencies. This is because dependencies change much less often that the source code, and can leverage the previous stages that are cached.

Finally, you need to specify the command that should be executed when the container is started. You will use the same command you used locally here.

CMD node .

Your final Dockerfile will look like this.

FROM node:16
WORKDIR /opt/app
COPY ./package.json .
RUN npm install

COPY ./index.js .
CMD node .
tip

Now would be a good time to stop the local instances you have running, or else you'll run into port issues.

Build the backend image

With this new Dockerfile, you will be able to build your own image to be shared with the world. This image will contain the source code, along with all the required dependencies and runtimes needed to run the application.

The main benefit of creating such an image, is that anyone running this image will have the exact same environment. The versions are always the same, and the runtimes are identical, no matter where this container runs.

This can be useful to share the container with your team mates, or to deploy to a production server.

To build the container, you will use the docker build command.

docker build -t $DOCKER_HUB_USERNAME/mern-k8s-back .

You can then push this new image to your docker hub account. Make sure that you are logged in first.

docker login
docker push $DOCKER_HUB_USERNAME/mern-k8s-back

Start the container

You can now run this new container on your local machine. First, make sure that any existing version of the backend that were running locally. Then, you can start the container by using the following command.

docker run -d --rm --name mern-k8s-back -p 5000:5000 -e PORT=5000 -e CONN_STR="mongodb://user:pass@127.0.0.1:27017" $DOCKER_HUB_USERNAME/mern-k8s-back

You can verify that the application is running with the docker ps command. This command will list any active containers.

CONTAINER ID   IMAGE                    COMMAND                  CREATED         STATUS         PORTS                                           NAMES
4b32f116bf64 joellord/mern-k8s-back "docker-entrypoint.s…" 3 seconds ago Up 2 seconds 0.0.0.0:5000->5000/tcp, :::5000->5000/tcp mern-k8s-back
25edc7acf2b3 mongo "docker-entrypoint.s…" 4 hours ago Up 4 hours 0.0.0.0:27017->27017/tcp, :::27017->27017/tcp mongodb

If you try to curl this new backend on the /healthz route again, you might see an issue.

{"status":"Ok","dbConnected":false}

It looks like the database is not connected anymore. This makes sense because as far as the container goes, there is no running MongoDB instance inside the container. That instance resides on your local machine and currently can be reached by the container.