• An Exhaustive Guide to Writing Dockerfiles for Node.js Web Apps
  • Praveen Durairaj
  • The Nuggets translation Project
  • Permanent link to this article: github.com/xitu/gold-m…
  • Translator: lsvih
  • Proofread by Raoul1996, song-han

A guide to Dockerfile for Node.js applications

TL; DR

This article covers examples of building node.js Web applications at multiple levels, from creating simple Dockerfiles to production environments. The following is a summary of the contents of this guide:

  • Use a suitable base image (Carbon for development, Alpine for production).
  • Used at development timenodemonHot loading.
  • Optimize Docker’s cache layer — use commands in the correct order and run only when needednpm install.
  • useservePackages deploy static files (e.g. React, Vue, Angular generated bundles).
  • usealpinePerform a multi-stage build in production environment to reduce the size of the final image file.
  • # suggestion — 1) use COPY instead of ADD 2)initFlag, handle CtrL-C and other kernel signals.

If you need code for the above steps, refer to GitHub Repo.

content

  1. Simple Dockerfile sample with.dockerignore file
  2. Use Nodemon to implement hot updates
  3. To optimize the
  4. Deploying static files
  5. Direct builds in production environments
  6. Multilevel builds in production environments

Let’s start with a hypothetical application called Node-app, a simple directory structure. In the top-level directory, including Dockerfile and package.json, the node app code will be stored in the SRC directory. For brevity, let’s assume that server.js defines a Node Express service running on port 8080.

├─ ├─ SRC ├─ ├.jsCopy the code

1. Simple Dockerfile sample

FROM node:carbon

# create app directory
WORKDIR /app

Install app dependencies
# Use wildcards to ensure that package.json and package-lock.json are copied where needed. (NPM version 5 and later) COPY package*.json./

RUN npm install
# If you need to build code in production, use:
# RUN npm install --only=production

# Package app source code
COPY src /app

EXPOSE 8080
CMD [ "node"."server.js" ]
Copy the code

We will use node: Carbon, the latest LTS version, as the base image.

When building the image, docker gets all the files in the context directory. To speed up docker builds, you can add.dockerignore to the context directory to exclude unwanted files and directories.

In general, your.dockerignore file should look like this:

.git
node_modules
npm-debug
Copy the code

Build and run this image:

$ cd node-docker
$ docker build -t node-docker-dev .
$ docker run --rm -it -p 8080:8080 node-docker-dev
Copy the code

You can access this app at [http://localhost:8080](http://localhost:8080.). Use Ctrl+C to exit the program.

Now, assuming you want to run the above code every time you change it (such as when deployed locally), you need to mount the code source file into the container when you start or stop the Node service.

$ docker run --rm -it -p 8080:8080 -v $(pwd):/app \
             node-docker-dev bash
root@id:/app# node src/server.js
Copy the code

2. Use Nodemon to implement hot update

Nodemon is a popular package that monitors files in your directory at runtime and will automatically restart your Node application when any files change.

FROM node:carbon

# create app directory
WORKDIR /app

# Install Nodemon for hot updates
RUN npm install -g nodemon

Install app dependencies
# Use wildcards to ensure that package.json and package-lock.json are copied where needed. (NPM version 5 and later) COPY package*.json./

RUN npm install

# Package app source code
COPY src /app

EXPOSE 8080
CMD [ "nodemon"."server.js" ]
Copy the code

We will build an image and run Nodemon to rebuild the code if the files in the app directory change.

$ cd node-docker
$ docker build -t node-hot-reload-docker .
$ docker run --rm -it -p 8080:8080 -v $(pwd):/app \
             node-hot-reload-docker bash
root@id:/app# nodemon src/server.js
Copy the code

All changes in the app directory will trigger rebuild, and all changes will be displayed in real time on [http://localhost:8080](http://localhost:8080.). Please note that we have mounted the file to the container so that Nodemon can work properly.

3. The optimization

In your Dockerfiles, it’s best to use COPY instead of ADD unless you need to automatically extract tar files (see Docker best practices).

Bypassing the package.json start command and instead “burning” the app directly to the image file. Therefore, do not use Dockerfile CMD:

$ CMD ["npm"."start"]
Copy the code

Instead, use:

$ CMD ["node"."server.js"]
Copy the code

Instead. This reduces the number of processes running in the container and allows Node.js processes to receive exit signals such as SIGTERM and SIGINT that NPM processes ignore (see Node.js Docker best practices).

You can also wrap your Node.js processes with tini lightweight set initializations using the –init flag, and they can also respond to kernel signals like SIGTERM (Ctrl-c). For example, you can use:

$ docker run --rm -it --init -p 8080:8080 -v $(pwd):/app \
             node-docker-dev bash
Copy the code

4. Deploy static files

The Dockerfile above assumes that you are running API services built from Node.js. So here’s what to do if you want to deploy your react. js, ue. Js, and angular. js applications with Node.js.

FROM node:carbon

# create app directory
WORKDIR /app

Install app dependencies
RUN npm -g install serve
# use wildcards to copy package.json with package-lock.json
COPY package*.json ./

RUN npm install

# Package app source code
COPY src /app
React, Vue, Angular package into static files
RUN npm run build

EXPOSE 8080
Deploy the dist directory on port 8080
CMD ["serve"."-s"."dist"."-p"."8080"]
Copy the code

As you can see, when you build React, Vue, or Angular UI apps, use NPM run Build to compress JS and CSS files to generate the final bundle. Here we use the NPM (serve) (https://www.npmjs.com/package/serve) package to deploy static files.

Alternatively, there are several alternatives: 1) build the packaged files locally and then use nginx Docker to deploy these static files. 2) Build using CI/CD workflows.

5. Direct builds in production

FROM node:carbon

# create app directory
WORKDIR /app

Install app dependencies
# RUN npm -g install serve

# use wildcards to copy package.json with package-lock.json
COPY package*.json ./

RUN npm install

# Package app source code
COPY src /app

Use react/vue/angular to generate static files
# RUN npm run build

EXPOSE 8080
To deploy static files, use:
#CMD ["serve", "-s", "dist", "-p", "8080"]
CMD [ "node"."server.js" ]
Copy the code

Build and run the all-in-one image:

$ cd node-docker
$ docker build -t node-docker-prod .
$ docker run --rm -it -p 8080:8080 node-docker-prod
Copy the code

Since the base is Debian, the image will be around 700MB after the build (depending on your source code). Let’s explore how to reduce the size of this file.

6. Multi-level builds in production environments

With multilevel builds, multiple FROM statements are used in the Dockerfile, but only the files built in the final phase are used. This way, the resulting image will contain only the dependencies needed in the production server, ideally with very small files.

# ---- Base Node ----
FROM node:carbon AS base
# create app directory
WORKDIR /app

# ---- Dependencies ----
FROM base AS dependencies  
# use wildcards to copy package.json with package-lock.json
COPY package*.json ./
Install the dependencies contained in 'devDependencies'
RUN npm install

# ---- Copy Files/Build ----
FROM dependencies AS build  
WORKDIR /app
COPY src /app
Use react/vue/angular to generate static files
# RUN npm run build

# --- Release with Alpine ----FROM the node: 8.9 - alpine AS the release# create app directory
WORKDIR /app
# optional command:
# RUN npm -g install serve
COPY --from=dependencies /app/package.json ./
Install app dependencies
RUN npm install --only=production
COPY --from=build /app ./
#CMD ["serve", "-s", "dist", "-p", "8080"]
CMD ["node"."server.js"]
Copy the code

Using the method above, Alpine built an image file of about 70MB in size, 10 times smaller than before. Building with the Alpine version effectively reduces the size of the image.

If you have any suggestions for the previous approach, or would like to see other use cases, please let the author know.

Join the Reddit/HackerNews discussion 🙂


Also, have you tried deploying a Node.js Web application on Hasura? This is actually the fastest way to deploy a Node.js application to an HTTPS domain (just use Git push). IO /hub/nodejs-… Templates for a quick start! All project templates in Hasura come with DockerFiles and Kubernetes standard files that you can define as you like.

Thank Tanmai Gopal


The Nuggets Translation Project is a community that translates quality Internet technical articles from English sharing articles on nuggets. The content covers Android, iOS, front-end, back-end, blockchain, products, design, artificial intelligence and other fields. If you want to see more high-quality translation, please continue to pay attention to the Translation plan of Digging Gold, the official Weibo, Zhihu column.