Docker is a great way to accelerate new developer onboarding and reduce overall operational complexity in production by creating a consistent experience for everyone and everywhere an app needs to run.
Containerizing your JavaScript/Node API with Docker to create a cloud-ready application is accomplished by:
-
Writing a
Dockerfile
— a file that defines running instructions on how to build your application. -
Creating a
docker-compose.yml
— a file that defines how to run multiple docker containers and set environment variables. You can use Docker without Docker Compose, but it is far easier to build and run containers with Compose.
If you haven’t already, go ahead and install docker. It’s the last cli tool you’ll ever need.
Create Your JavaScript App
For this example, you’re going to create a two-file app. Although this app is only two JavaScript files, this pattern will work for containerizing applications for any programming language of any size! Both of these files exist in the same directory:
├── index.js
└── pacakge.json
index.js
const express = require('express')
const app = express()
const port = process.env.PORT || 3000
app.get('/', (req, res) => {
res.send('Hello World!')
})
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})
package.json
{
"name": "bitovi-blog-app",
"version": "1.0.0",
"scripts": {
"start": "node index.js"
},
"dependencies": {
"express": "^4.17.1"
}
}
If you have a compatible version of node and npm installed, you can run this locally with npm install
then npm run start
, but if not, that’s okay because Docker will handle that for you!
Writing a
Dockerfile
Now you’re going to add a Dockerfile
to your root directory. A Dockerfile
contains a set of instructions on what dependencies, files, config, and packages we need to run an app.
├── Dockerfile
├── index.js
└── pacakge.json
Dockerfile
# Use node version 15
FROM node:15
# Set an environment variable PORT with a default value of 8000
ARG PORT=8000
ENV PORT=$PORT
# Copy everything (index.js and package.json) from local working dir in to the docker image
COPY . .
# Run npm install within the image to download node dependencies
RUN npm install
# On startup, run npm start
CMD npm run start
This is a simple Dockerfile
that will build and run the app on port 8000. You can optimize this process by leveraging multi-stage builds among other neat Docker tricks, but this file is sufficient for demonstrating basic Docker capabilities.
Creating a docker-compose.yml
Without Docker Compose, you could build and run your docker container with docker build -t my-node-app . && docker run my-node-app -p 9000:9000 -e PORT=9000
. Instead, you’re going to introduce a .env
and docker-compose.yml
file to codify these in to a simple docker-compose up
.
Add .env
and docker-compose.yml
to your directory:
├── .env
├── Dockerfile
├── docker-compose.yml
├── index.js
└── package.json
.env
This file defines environment variables that will be automatically read by docker-compose.yml
. In this case, it will inject MY_PORT
wherever it is referenced.
MY_PORT=9000
docker-compose.yml
# docker compose api version - do not edit
version: "3.8"
# A list of containers we want to run. We're just running 1 here
services:
# Our service is called "my-app"
my-app:
# "my-app" is a docker image that will be built on demand
build:
# The `Dockerfile` exists in the same directory
context: .
# Will create a Docker image called "my-node-app"
image: my-node-app
# Will expose the running container on localhost:9000 regardless of what port the app is actually listening on (controlled by MY_PORT env var).
ports:
- "9000:${MY_PORT}"
# Pass in env var PORT to the running container
environment:
PORT: ${MY_PORT}
Testing
Now that your app is built and Dockerized, it’s time to test! Go ahead and run docker-compose up
, then open your browser to http://localhost:9000.
$ docker-compose up
Creating network "blog_default" with the default driver
Creating blog_my-app_1 ... done
Attaching to blog_my-app_1
my-app_1 |
my-app_1 | > bitovi-blog-app@1.0.0 start
my-app_1 | > node index.js
my-app_1 |
my-app_1 | Example app listening at http://localhost:9000
$ curl localhost:9000
Hello World!
You can stop your container with ctrl+c
or docker-compose down
in another tab and rebuild your container after making code changes with docker-compose build
. Cool!
Hungry for More?
This blog post has been a condensed version of a free Learn Docker course. Check it out if you want to learn more about what is happening behind the scenes or to optimize your containers for both local development and production deployments.
Need Help?
Bitovi has consultants that can help. Drop into Bitovi's Community Discord, and talk to us in the #devops
channel!
Need DevOps Consulting Services? Head over to DevOps Consulting - Bitovi , and book a free consultation.