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:
- Basic understanding of containers
- Docker installed on local machine
- Download the Dad Jokes NodeJS Server Application from this github
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
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
If you have fulfilled those two pre-requisites, then you are ready to go, so let's jump right in.
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. I will be basing all of my following steps off of this project setup.
Open up a terminal on your local machine and change directory (cd) into DJA directory:
Once in directory, create a Dockerfile using the "touch" command:
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.
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 to see the all the possible NodeJS images you could use based on their tags.
Next, we are creating the directory where our application will be running out of in our container:
#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:
#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:
#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:
#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:
#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.
#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:
#Start app CMD ["node", "server.js"]
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.
node_modules build .dockerignore Dockerfile Dockerfile.prod
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.
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 for more info.
So in my case this is what my build command will look like (feel free to use this command as well).
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:
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.
Now let's run our Docker and see it in action.
The next command we will run in the terminal will look like this:
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
- -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:
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:
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 theDocker Run Documentation
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:
When you run this command you should see something similar to this:
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).
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:
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:
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.
==== BONUS LEARNING ====
Containerization: Starting with Docker
Learn more about Containers: https://developer.ibm.com/technologies/containers/