Technologies


Use the following files to dockerize a node application:

server.mjs

import express from "express";
 
const app = express();
 
app.get("/", (req, res) => {
  res.send("<h2>Hi there! You hey</h2>");
});
 
app.listen(process.env.PORT);

package.json

{
  "name": "data-volume-example",
  "version": "1.0.0",
  "description": "",
  "main": "server.js",
  "license": "ISC",
  "scripts": {
    "start": "nodemon server.mjs"
  },
  "dependencies": {
    "body-parser": "^1.19.0",
    "express": "^4.17.1"
  },
  "devDependencies": {
    "nodemon": "2.0.4"
  }
}

Dockerfile

FROM node:14
 
WORKDIR /app
 
COPY package.json .
 
RUN npm install
 
COPY . .
 
ARG DEFAULT_PORT=80
 
ENV PORT $DEFAULT_PORT
 
EXPOSE $PORT
 
CMD [ "npm", "start" ]

.dockerignore

node_modules
Dockerfile
.git

.env

PORT=8000

# Instructions

Your goal is to have a docker container running that uses your most recent files on your host system as the application code that will be executed when you visit the web page running in the container. This way you can have a well-defined execution environment (using docker for managing the dependencies an environment), but still be able to make rapid changes to your code and test it out while developing.

  • Build an image using the Dockerfile. Give it a tag of bindmountimg
docker build -t bindmountimg .


  • Run a container based on the image using docker run -it -p 8082:80 -v <absolute path to your working directory on the host machine>:/app -v /app/node_modules --rm --name bindmountcontainer bindmountimg
docker run -it -p 8082:80 -v "C:/Users/Clemens/OneDrive - HTL Krems/HTL/5CHIT/SYT/SYTD/Docker-Ex/Ex6":/app -v /app/node_modules --rm --name bindmountcontainer bindmountimg


  • Access the site in your browser

Under the URL: http://localhost:8082


  • Make changes to your code and change the HTML output of your site.
import express from "express";
 
const app = express();
 
app.get("/", (req, res) => {
    res.send(
        "<h2>Hi there! I'm using WhatsApp!</h2>" +
            "<h1>I have a bigger font!?</h1>"
    );
});
 
app.listen(process.env.PORT);

  • Refresh the page in the browser

Note: In Windows also type rs in the console to refresh the container.

rs


  • You should see your updated view.


# Answering Questions

  • How is the container port on which the application listens defined and managed?

The container listens on the port defined by the process.env.PORT:

PORT=8000
FROM node:14
 
WORKDIR /app
 
COPY package.json .
 
RUN npm install
 
COPY . .
 
# define build-variable (only in docker-build process available)
ARG DEFAULT_PORT=80
# sets 80 as default port
ENV PORT $DEFAULT_PORT
# tells docker, which port is used in container
EXPOSE $PORT
 
CMD [ "npm", "start" ]

  • What does the ARG command do in your Dockerfile, and how can you use that when building an image?

It defines a build-time-variable, that can only be used while the image-builds. The value can be overwritten by the command docker build --build-arg DEFAULT_PORT=3000 -t myapp .

Example: We want to create a docker-image for an application.

  • In development, it runs on port 3000
  • In production, it runs on port 80
# dev
docker build --build-arg PORT=3000 -t myapp-dev .
 
# prod
docker build --build-arg PORT=80 -t myapp-prod .

  • Why does the application listen on the port defined in the environment variable?

The value process.env.PORT is used to configure the server on app.listen(...)

  • If we want to change the port, we don’t have to modify the code (only edit .env file)
import express from "express";
 
const app = express();
 
app.get("/", (req, res) => {
    res.send(
        "<h2>Hi there! I'm using WhatsApp!</h2>" +
            "<h1>I have a bigger font!?</h1>"
    );
});
 
app.listen(process.env.PORT);

  • How can you set environment variables when running a container? (There are 2 ways. One might be connected to the .env file. But you should try it out to make sure.)
  1. .env file
PORT = 8000
...
  1. with docker run command
# -e variable=...
docker run -it -p 8083:80 -v "C:/Users/Clemens/OneDrive - HTL Krems/HTL/5CHIT/SYT/SYTD/Docker-Ex/Ex6":/app -v /app/node_modules -e PORT=9000 --rm --name bindmountcontainer bindmountimg

  • What is a .dockerignore file?

Specifies files, that should be excluded from a docker image when it’s built (can be compared with a .gitignore)

  • Image sizes smaller
  • Avoids copying unnecessary files
node_modules
Dockerfile
.git

  • What is nodemon and how did we use it in that example?

”Node” & “monitor”

nodemon is a utility, that automatically monitors changes in a Node.js app & automatically restarts the server when file-changes are detected.

  • Note: On Windows you have to type rs to refresh the files
  • eliminates the need for manual restarts

In our case, we used nodemon to run the server.mjs file through the start script in package.json, enabling automatic restarts whenever code is modified.

package.json:

{
    "name": "data-volume-example",
    "version": "1.0.0",
    "description": "",
    "main": "server.js",
    "license": "ISC",
    "scripts": {
        // runs "nodemon server.mjs"
        "start": "nodemon server.mjs"
    },
    "dependencies": {
        "body-parser": "^1.19.0",
        "express": "^4.17.1"
    },
    "devDependencies": {
        "nodemon": "2.0.4"
    }
}

  • Which part of the docker run command defines a bind mount?
docker run -it -p 8082:80 -v "C:/Users/Clemens/OneDrive - HTL Krems/HTL/5CHIT/SYT/SYTD/Docker-Ex/Ex6":/app -v /app/node_modules --rm --name bindmountcontainer bindmountimg

In this case, the docker run ... -v ["host_path"]:[container_path] ... defines the path on your host-computer, that you want to mount. Then the directory on the host file system is shared with the container.


  • Why did we have to specify an anonymous volume using -v /app/node_modules? What would happen if we left that out? (This last question is probably the most important question for you to understand how volumes and bind mounts interact with each other.)

Mounting the entire /app folder from the host could overwrite the node_modules in the container by the version of the host. By using an anonymous volume for our node_modules, docker keeps the dependencies isolated from other updates.

  • Allows updating the app code & makes sure, that the dependencies remain stable