Dockers and Dad Jokes: How to Containerize a NodeJS Application Using Docker
Tags: node, docker, containers
Published: 2021-03-02 21:35:57

{% youtube Sffpk4aKcsU %}

In this blog post, I will be diving into the "meat and potatoes" of containerization. We will now get the opportunity to containerize an existing NodeJs application. This will allow us to deploy our application to any location that supports docker with almost no code re-work or changes. I cannot emphasize how big of a deal that is.

But before we get started, there are a few pre-requisites to doing this:

Pre-Requisites:
* Basic understanding of containers
* Docker installed on local machine
* Download the Dad Jokes NodeJS Server Application from [this github](https://github.com/bradstondevcode/dad-jokes-apiv1)

If you are unfamiliar with containers and what they are, check out this blog for a quick overview of the topic: [What are Containers: How Video Games Have Inspired My Containerization Journey](https://dev.to/bradstondev/what-are-containers-how-video-games-have-inspired-my-containerization-journey-222)

If you have not installed Docker on your local machine, check out this blog for a walkthrough of how to install Docker on your local machine: [Setting Up Docker On Your Local Machine Using Docker Desktop ](https://dev.to/bradstondev/setting-up-docker-on-your-local-machine-using-docker-desktop-mac-3ckp)


If you have fulfilled those two pre-requisites, then you are ready to go, so let's jump right in.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q3pwabyxv7p1h5mr1zae.png)

# Dad Jokes API

So today we will be containerizing a simple NodeJS application called the "Dad Jokes API" (we will reference it as DJA for short). The DJA service has only one use; Provide high quality dad jokes to whomever desires them. So simply, a user will access our DJA endpoint and will be presented with a dad joke within a JSON Object. So if you haven't already, go ahead and download the source code for the DJA from [my github](https://github.com/bradstondevcode/dad-jokes-apiv1). I will be basing all of my following steps off of this project setup.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q3pwabyxv7p1h5mr1zae.png)

## Step 1: Create a Dockerfile

Open up a terminal on your local machine and change directory (cd) into DJA directory:

```bash
cd <location-path>/docker-jokes-api
```

Once in directory, create a Dockerfile using the "touch" command:

```bash
touch Dockerfile
```

Next, we will be inputting some commands into the Dockerfile. Below you will be able to view the commands we will input. Each line will execute in order setting up our container image and allowing our DJA application to run in our Docker environment. I will explain what each line does separately.

```bash
FROM node:alpine3.10

RUN mkdir /app
WORKDIR /app

COPY package.json /app
RUN npm install
COPY . /app 

EXPOSE 8080 

CMD ["node", "server.js"]
```

First we will be grabbing a NodeJS image to put in our container. This will place all needed dependencies we need to run a NodeJS application. You can check out [this Docker Hub](https://hub.docker.com/_/node/) to see the all the possible NodeJS images you could use based on their tags. 
```bash
FROM node:alpine3.10
```

Next, we are creating the directory where our application will be running out of in our container:
```bash
#Make app directory in container
RUN mkdir /app
```
Once the directory is created, we are identifying to the container that this will be our working directory, or where are application files will run from:
```bash
#Identify working directory
WORKDIR /app
```
After that, we are going to copy over our package.json file to our app folder. This holds all of our Node framework dependencies for our project:
```bash
#Copy package
COPY package.json /app
```

We will then install all of our node module packages by running the _npm install_ command in the container:
```bash
#Install rpm packages from package.json
RUN npm install
```
Once we have installed all of our dependencies, we are going to copy over all of our remaining project files into our app directory using the copy command. NOTE: The "." here just tells the code to copy all files in our folder:
```bash
#Copy over app to app folder
COPY . /app 
```

Next, we need to allow the port that will serve our API to be accessible outside of the container. This will allow for other containers to access this API within our Docker environment. Technically, this does nothing until we actually  expose the port via our _docker run_ command. It acts more as metadata for our container image. NOTE: There is an additional step we will do later to allow this port to be accessible **outside** of the Docker environment itself, when hosted somewhere.
```bash
#Expose server at port ( accessible outside of container)
EXPOSE 8080 
```
Once we have the dependencies installed and our application folder in our Working directory, we will initiate the node server start command: 
```bash
#Start app 
CMD ["node", "server.js"]
```

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q3pwabyxv7p1h5mr1zae.png)

## Step 2: Create a .dockerignore

Next we will create a _.dockerignore_ file. This file will allow us to "ignore" specific files when building our docker image, which will help us save us build time and ensure we don't accidentally overwrite any installed files in our image.

```bash
node_modules
build
.dockerignore
Dockerfile
Dockerfile.prod
```

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q3pwabyxv7p1h5mr1zae.png)

## Step 3: Build Docker Image

Our next step will be to build our Docker Image that we will want to run in our container. Here is the format of the command we will be using in our terminal for creating our Docker Image. 

```bash
docker build -t <image-name>:<tag> .
```
So what's happening here:
* _docker build_ initiates the Docker Image building process
* _-t_ Flag used for tagging build 'name:tag' format
* _image-name_ is our desired image name
* _tag_ is our desired tag for this version of the image. Used for deploying different versions of images
* _._ signifies the path that we will be building from. NOTE: This is **absolutely** necessary for the build command to work

If you are interested in learning more about _docker build_ commands and flags, check out the [Docker Build Documentation](https://docs.docker.com/engine/reference/commandline/build/) for more info.

So in my case this is what my build command will look like (feel free to use this command as well). 

```bash
docker build -t dad-joke-api:version1 .
```

Once you've inputted the build command, press return and the docker should begin to build. You should see something like this appear in your terminal window:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j46xaflpv8ra2b9b0g33.png)

One thing to note, is that if you had not added the tag to the build command, it would have automatically tagged this build as _latest_.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q3pwabyxv7p1h5mr1zae.png)

## Step 4: Run Docker

Now let's run our Docker and see it in action.

The next command we will run in the terminal will look like this:

```bash
docker run -p 8000:8080 -d <image-name>:<tag>
```

So what's happening here:
* _docker run_ runs our Docker image within our container
* _-p_ is used to set the port we want to expose outside of our container to our host
* _8000:8080_ exposes the 8080 port to other containers in our Docker for inter-container communication and exposes 8000 to our host. For more information on this, check out this great explanation on [StackOverflow](https://stackoverflow.com/questions/22111060/what-is-the-difference-between-expose-and-publish-in-docker)
* _-d_ allows our container to run in the background, allowing us to continue to use our terminal.

In my case, the command will look like this:

```bash
docker run -p 8000:8080 -d dad-joke-api:version1
```

Once you've inputted the run command, press return and the docker should begin to run the Docker Image on your local machine. You should see something like this appear in your terminal window:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jth203vb62tiw5m9wu5k.png)

This is your Contianer ID for your newly running docker image.

If you are interested in learning more about _docker run_ commands and flags, check out the[Docker Run Documentation](https://docs.docker.com/engine/reference/commandline/run/)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q3pwabyxv7p1h5mr1zae.png)

## Step 5: Verify Docker Running and Expose Port

Once we have our DJA Image running in our Docker container, we should verify that it is not only running but that we know which port the image is being exposed at on our local machine. In order to verify, we will need to run this command in our terminal:

```bash
docker ps
```

When you run this command you should see something similar to this:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bmhsxjkzfkpr5qlx8afw.png)

Essentially, the _docker ps_ command lists all available containers in our environment. If we do not see our docker image listed in the list of available containers, then we did something incorrectly in a previous step and must fix that error. In my particular case, you should be able to see that the container port 8080 is now being exposed at the port 8000 on my local machine (outside of the containerized environment).

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q3pwabyxv7p1h5mr1zae.png)

## Step 6: Use Dad Joke API in Docker

Now that we have have verified that our DJA application image is running, it's finally time to use it.

So now we will open the browser of our choice and we will enter in the address bar, this web address:

```bash
http://localhost:8000/getdadjoke
```

If everything is working correctly, we should be presented with a JSON object presenting us with one of the **funniest** Dad jokes you have ever seen. 😅 NOTE: I am using the Firefox browser and seeing something like this:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7qy2vcok61il6y8g9lk0.png)

Viola! You successfully containerized your first NodeJS Application. The great thing is that this is just the beginning.

If you'd like to check out the completed Docker version of DJA application, check it out [here](https://github.com/bradstondevcode/dad-jokes-apiv1-docker). 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q3pwabyxv7p1h5mr1zae.png)

==== BONUS LEARNING ====

Containerization: Starting with Docker
https://developer.ibm.com/technologies/containers/tutorials/building-docker-images-locally-and-in-cloud/

Learn more about Containers: https://developer.ibm.com/technologies/containers/

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q3pwabyxv7p1h5mr1zae.png)

==== FOLLOW ME ON SOCIAL MEDIA ====
Twitter: [Bradston Dev](https://twitter.com/BradstonDev)
Dev.to: @BradstonDev 
Youtube: [Bradston YT](https://www.youtube.com/channel/UC6Ky8s71RP65akLb_XV1_OA)