- 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 time
nodemon
Hot loading. - Optimize Docker’s cache layer — use commands in the correct order and run only when needed
npm install
. - use
serve
Packages deploy static files (e.g. React, Vue, Angular generated bundles). - use
alpine
Perform a multi-stage build in production environment to reduce the size of the final image file. - # suggestion — 1) use COPY instead of ADD 2)
init
Flag, handle CtrL-C and other kernel signals.
If you need code for the above steps, refer to GitHub Repo.
content
- Simple Dockerfile sample with.dockerignore file
- Use Nodemon to implement hot updates
- To optimize the
- Deploying static files
- Direct builds in production environments
- 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.