Overview
In this tutorial we aim to create a simple Node.js app with Express.js and containerize it with Docker then publish it to Docker Hub.
NOTE: This will be a practical tutorial so I will not be explaining any concepts such as Node.js, containerization or Docker. And I will also assume you already have node
and docker
installed on your machine. But if you want me to write an article explaining those concepts, let me know in the comments.
Sections
- Write the Node.js App
- Containerize the App with Docker
- Publish the Image to Docker Hub
1. Write the Node.js App
We will first create a simple Node js app that we will then work with. Follow these steps to create the app:
1. Create a new directory for the project
Create a new directory for the project with whatever name you like
$ mkdir nodejs_docker_tutorial
and cd
into it
$ cd nodejs_docker_tutorial
2. Initialize the project
I'm gonna be using npm but you can use whatever package manager that suits you. To initialize the project with npm run:
$ npm init
Fill out the information and set entry point
to be src/app.js
the final package.json
should be something like
{
"name": "nodejs_docker_tutorial",
"version": "1.0.0",
"description": "",
"main": "src/app.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}
3. Install the packages
We are going to need these packages:
express, cors and (as an optional dev dependency) nodemon
run:
$ npm i express cors
$ npm i -D nodemon # Optional
4. Create the files and directories
Create the following files and directories so that the project tree should look like:
.
├── package.json
├── package-lock.json
└── src
├── app.js
└── routes
└── home.js
5. Use nodemon to watch for changes (Optional)
Installing and using nodemon
is optional and I've included it in the tutorial just as an example to simulate a real-life scenario.
In the scripts
section in package.json
add the dev
script as follows:
...
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "nodemon src/app.js"
},
...
and now in a terminal session you can run:
$ npm run dev
and it will watch for changes in your source code and rerun the app every time a change is made
6. Write the code
Now we are going to write our actual application.
The goal is to make a simple web server that listens on port 8080
and have 2 endpoints:
GET /
Responses:
200 Hello from Docker!
GET /greetings/:name
Responses:
200 Hello, {name}!
So now in your favorite text editor edit the source code as follows:
The source code for src/app.js
will be:
const express = require('express')
const cors = require('cors')
const app = express()
// Use CORS with default options to allow all origins
app.use(cors())
// Import the home router
const homeRouter = require('./routes/home')
// Use the home router
app.use('/', homeRouter)
// Define the port to listen on
const port = 8080
// Start the server and log a message after it starts
app.listen(port,
() => console.log(`Server listening on port: ${port}`)
)
and for src/routes/home.js
it will be:
const express = require('express')
// Create the router
const router = express.Router()
// Configure the endpoint for the router
router
.get('/', (req, res) => res.send('Hello from Docker!'))
.get('/greetings/:name',
(req, res) => res.send(`Hello, ${req.params.name}`))
// Export the router
module.exports = router
7. Test the app
Now we are going to test if our code works or not.
- If you are using
nodemon
just head tohttp://localhost:8080
- If you are not using
nodemon
just run:
$ node src/app.js
and then head to http://localhost:8080
You should see a web page as follows:
And if you go to http://localhost:8080/greetings/Ahmed
you will see something like:
Congrats! Now the app is done and we can move to the docker stuff!
2. Containerize the App with Docker
Now that our app is ready we can use Docker to create an image of our app.
To create an image for your app follow these steps:
1. Stop the running node app
First thing to avoid port conflicts later on, we need to stop the app for now, use Ctrl+C
on the terminal session where you started your app.
2. Create a Dockerfile
In the root directory of the project create a file named Dockerfile
, for example you can run:
$ touch Dockerfile
3. Dockerfile code
In the Dockerfile
you just created put the following code:
# Base image
FROM node:alpine
# The working directory inside the container
WORKDIR /App
# Copy the package.json file
COPY package.json package.json
# Install the packages for production environment
RUN npm i --production --omit dev
# Copy the source files
COPY src/ src/
# The main entry point of the container
CMD [ "node", "src/app.js" ]
The Dockerfile is divided into steps, each line represents a step (lines starting with #
are comments)
So I'll explain each line/step:
Step 1: Import base image
FROM node:alpine
We select the base image to use for the custom image we want to create, here we are using the Official Node Image with the alpine
tag which will basically import the Alpine Linux
image with Node installed in it. I'm using Alpine
image just because it is lightweight, but you can use any other image you like and you can specify what version of node you want, for example you can use:
FROM node:14.18
To use node version 14.18.
Step 2: Select the working directory
WORKDIR /App
We specify a directory -inside the container- to put our app inside of it, you can use anything you like.
Step 3: Copy package.json to our working directory
COPY package.json package.json
We will copy our package.json
file to the working directory we specified in the above step. Note that you don't need to navigate or write the path of the working directory after you specified it with WORKDIR
instruction.
Step 4: Instal node modules for production
RUN npm i --production --omit dev
This command will basically run npm install
with the --production
and --omit dev
flags. You can use any other flags but this is what I personally use for production apps.
Step 5: Copy the source files to the working directory
COPY src/ src/
Now we will copy out source code files to the working directory we specified on Step 2.
Step 6: Run the node app as the entry point of the image
CMD [ "node", "src/app.js" ]
This is the command that will be run when we spin up a container with our image and we just want to run node src/app.js
.
So that's it, we are done with our Dockerfile.
2. Build the Docker image
Now we want to build the actual image that we will use to spin up containers of our app.
In the terminal run:
$ docker build .
NOTE: You might need to run the docker commands with sudo
if you haven't done the Docker Post-installation Steps
After the command is done you should see something like:
...
Successfully built 33482f9f2921
3. Get the Image ID
We will need the Image ID so we can use it since we didn't specify any tags for it. You can copy the ID from the above docker build
output on your terminal or you can list all the images you have using:
$ docker image list
The output will be something like:
REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> 33482f9f2921 1 minute ago 177MB
...
Now copy the IMAGE ID
.
4. Run a container with the new image
Now we can run a container to test our image, in the terminal run:
$ docker run -d -p 8080:8080 <IMAGE_ID>
Replace <IMAGE_ID>
with the ID of your image.
The flag -d
is used to run the container in Detached Mode
(in the background).
The flag -p
will expose a port from the container on the host machine. It uses the syntax -p hostPort:containerPort
.
You can read more about these flags in the Docker Run Reference.
The output should be a hash, something like:
70f36364143abafd4ce2a4f338b20d97015fda400a0bcfd86fd819e86ee39752
It means that you are up and running. If you go to http://localhost:8080
you should find the app running!
5. Stop the container
You can stop the currently running container using the command:
$ docker stop <CONTAINER_ID>
Replace <CONTAINER_ID>
with the output of the previous step or run:
$ docker ps
To get a list of the running containers and then copy the CONTAINER ID
from the output.
Now we know our image is ready and we can publish it to a Container Registry
to use it from anywhere we want!
3. Publish the Image to Docker Hub
Now we have completed developing our app and we built a Docker image of it, now we need a way to distribute/publish our image either publicly or privately.
Docker Hub is a Container Image Library or a Container Registry where people can push(publish) their images to repositories and make those repositories either public for anyone to view and pull(download) or private where only those authorized can view it or pull it to spin up containers.
We will be pushing our image to a public repository on Docker Hub where we can pull it and use it from anywhere.
To do that, follow these steps:
1. Create a Docker Hub account
If you don't already have an account go to hub.docker.com and create an account.
Note that your username
on Docker Hub will be your namespace for your repositories, for example mine is ahmedwadod
so my images will be ahmedwadod/image_name:tag
2. Create a repository
In your account home page click on Create Repository
Now fill out the details of your repository, we will be setting the visability to Public
, if you set it to Private
you are going to need to login with your credentials on Docker whenever you want to pull the image.
Now click Create
and you will have your repository set to go.
3. Tagging the image
Now we need to rebuild our image with the appropriate tags, the tag for your image will be: YOUR_USERNAME/REPO_NAME:TAG
for the :TAG
we will use latest
as it is the default. The :TAG
can be used to when you want to upload different versions of your app for example it can be :v1.0
or :v2.0
or it can be used for different variants of the base image used, for example :v1.0-alpine
or :v1.0-ubuntu
.
In my case the tag will be: ahmedwadod/nodejs-docker-tutorial:latest
To build the image go to your project's root directory and in the terminal run:
$ docker build -t YOUR_USERNAME/REPO_NAME:TAG .
The output should be something like:
...
Successfully built 33482f9f2921
Successfully tagged ahmedwadod/nodejs-docker-tutorial:latest
4. Login to Docker Hub
Now to publish our image we need to first login into Docker Hub from the terminal, run:
$ docker login -u <YOUR_USERNAME>
Password: # Enter your password and press enter
The output will be:
Login Succeeded
5. Push the image to Docker Hub
Now all we have to do is push the image, run:
$ docker push YOUR_USERNAME/REPO_NAME:TAG
The output will be something like:
The push refers to repository [docker.io/ahmedwadod/nodejs-docker-tutorial]
a62d27597b40: Pushed
c8b55b75e003: Pushed
d6605a78d13e: Pushed
86145b7dbdcb: Pushed
25c4d12b64e7: Mounted from library/node
1d454e07796f: Mounted from library/node
970073eeaee3: Mounted from library/node
8d3ac3489996: Mounted from library/node
latest: digest: sha256:49d70d1032b3389b465db6691c7e402f146d366b71df9f2b2196301af86116c2 size: 1990
Now if you go to the repository in Docker Hub and then to the tags
tab you will find the tag latest
available.
6. Run the image from anywhere!
Now you can go to any server with docker installed on it and run:
$ docker run -d -p 8080:8080 YOUR_USERNAME/REPO_NAME:TAG
And it will spin up a container with your Node js App!
Closure
Now that you have containerized your app, you can deploy it. I will be posting in the future about deploying web apps with Dcoker, so follow me to stay tuned.
You can find this tutorial's code on my Github
If you have faced any problems with these steps, comment down below and I will try to help you fix it.
Thank you for reading.
Top comments (0)