Initiate NPM:
mkdir node-docker-example && cd node-docker-example
npm init -y
Install Express, Morgan and Esm packages. Morgan is a request logger library and esm
will let you use es6 syntax while including modules.
yarn add express morgan esm
Create files:
mkdir src
touch src/index.html src/server.js
Create an Express server:
// Server.js
import express from "express"
import path from "path"
import morgan from "morgan"
const PORT = 8082
const HOST = "0.0.0.0"
// App
const app = express()
// Middlewares
app.use(morgan("combined"))
// Routes
app.get("/", (req, res) => {
res.sendFile(path.join(__dirname + "/index.html"))
})
app.listen(PORT, HOST)
console.log(`Running on http://${HOST}:${PORT}`)
The server will listen to requests on 8082 port and it will return index.html
for GET http://0.0.0.0:8082
request.
Create index.html
file. I've included Bulma library here to make it look a bit more interesting.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/[email protected]/css/bulma.min.css"
/>
</head>
<body>
<div class="container">
<section class="section">
<section class="hero is-info">
<div class="hero-body">
<div class="container">
<h1 class="title">
Hello ExpressJS š„³ššš
</h1>
<h2 class="subtitle">
with Docker
</h2>
</div>
</div>
</section>
</section>
<div class="tile is-ancestor">
<div class="tile is-4 is-vertical is-parent">
<div class="tile is-child box">
<p class="title">One</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin
ornare magna eros, eu pellentesque tortor vestibulum ut. Maecenas
non massa sem. Etiam finibus odio quis feugiat facilisis.
</p>
</div>
<div class="tile is-child box">
<p class="title">Two</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin
ornare magna eros, eu pellentesque tortor vestibulum ut. Maecenas
non massa sem. Etiam finibus odio quis feugiat facilisis.
</p>
</div>
</div>
<div class="tile is-parent">
<div class="tile is-child box">
<p class="title">Three</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam
semper diam at erat pulvinar, at pulvinar felis blandit.
Vestibulum volutpat tellus diam, consequat gravida libero rhoncus
ut. Morbi maximus, leo sit amet vehicula eleifend, nunc dui porta
orci, quis semper odio felis ut quam.
</p>
<p>
Suspendisse varius ligula in molestie lacinia. Maecenas varius
eget ligula a sagittis. Pellentesque interdum, nisl nec interdum
maximus, augue diam porttitor lorem, et sollicitudin felis neque
sit amet erat. Maecenas imperdiet felis nisi, fringilla luctus
felis hendrerit sit amet. Aenean vitae gravida diam, finibus
dignissim turpis. Sed eget varius ligula, at volutpat tortor.
</p>
<p>
Integer sollicitudin, tortor a mattis commodo, velit urna rhoncus
erat, vitae congue lectus dolor consequat libero. Donec leo
ligula, maximus et pellentesque sed, gravida a metus. Cras
ullamcorper a nunc ac porta. Aliquam ut aliquet lacus, quis
faucibus libero. Quisque non semper leo.
</p>
</div>
</div>
</div>
</div>
</body>
</html>
Let's confirm, everything works fine.
Add "start": "node -r esm ./src/server.js"
to package.json and run npm start
.
~/punched-card-examples/01-express-docker-example į
yarn start
yarn run v1.16.0
$ node -r esm ./src/server.js
Running on http://0.0.0.0:8082
Go to http://0.0.0.0:8082
on your browser to check it out.
Create a file named Dockerfile
. Dockerfile will contains instructions to run to create a docker image.
The image is like a blueprint of application. It will be used the create a docker container.
Docker Hub has images many images for open source projects. So the first step is to pull existing node 12 alpine image.
After that, you set the working directory to app
but you can name this anything you like.
Next step is copying package.json
file across. This is quick and it will allow you to install packages in the image.
Copy the rest of the code into the image with COPY . .
.
And lastly, set yarn start
as a command to run when container boots up.
FROM node:12.16.1-alpine3.11
WORKDIR /app
COPY package.json ./
RUN yarn install
COPY . .
CMD [ "yarn", "start" ]
Build the image:
docker build -t node-docker-example .
Run the container:
docker run -d -p 8082:8082 node-docker-example
This command will start the container and bind the container port 8082 to host port 8082.
You can go to the browser and type http://localhost:8082/
but this time you are seeing application run in docker.
Check running docker containers:
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a39d1c3f4323 node-docker-example "docker-entrypoint.sā¦" 2 minutes ago Up 2 minutes 0.0.0.0:8082->8082/tcp angry_beaver
To stop container use the printed container ID:
docker stop <container-id>
While you are developing your code, stopping and restarting the server is tedious. Instead we will add Nodemon to watch your files and do this automatically.
Install nodemon:
yarn add nodemon -D
Add watch task to package.json
"watch": "nodemon -r esm ./src/server.js"
This task will let you watch changes when you are running application in local. But you can also make Docker use it. Watching for file changes are only needed in the development environment. When you run your Docker container in the server, you don't need to run it with watch task. In fact, it will make it less performant. So it makes more sense to add watch task in docker compose rather than dockerfile.
Docker compose allows you to run multiple containers. This is usually only used in the development environment. Cloud providers such as AWS provide their own container orchestration tools such as ECS.
Create a docker-compose.yml
file:
version: "3.7"
services:
node-docker-example:
build: .
ports:
- "8082:8082"
volumes:
- ./src:/app/src
command: yarn watch
Src folder included as volumes so that when it changes in the host environment, it also updates in the docker container.
Run the container:
docker-compose up
This command will create the image using the DockerFile and then start a container.
Add console.log("alive");
to server.js. You should see this console log on bash.
Change Hello ExpressJS to Hello Docker in index.html
and refresh the page. You should see that it has updated.
Stop container:
docker-compose down
The code examples can be also found at Github.