He who will not rest will not work. Lenin
This article will not explain the use of Docker command, installation, etc., because the previous article teaches you how to learn Docker entry into practice has been explained in detail, if not clear, you can click the link to go back and look again. This article focuses on how to implement Docker containerization and some practical optimization in Node.js project, as well as some common problems. Of course, if you have any other problems in use, you are welcome to leave a comment in the comment section.
About the author: Jun May, Nodejs Developer, a post-90s youth who loves technology and likes sharing. The public account “Nodejs Technology Stack”, Github open source project www.nodejs.red
What can you learn from this passage?
- How to container a Node.js service with Docker
- Dynamically set environment variables to build different versions of a Dockerfile file
- How does the Node.js private NPM package authenticate when building an image
- Egg.js framework Docker container should pay attention to the problem
- Docker image volume and build time optimization
Docker transforms a Node.js application
We’ll start this article by creating a simple Node.js application, then creating a Docker image for the application, and building and running it
Create the Node.js project
First we need to create an app.js to start an HTTP service, then we will use Docker to run the program
const http = require('http');
const PORT = 30010;
const server = http.createServer((req, res) = > {
res.end('Hello Docker');
})
server.listen(PORT, () => {
console.log('Running on http://localhost:', PORT, 'NODE_ENV', process.env.NODE_ENV);
});
Copy the code
Json file, which describes your application and its dependencies. If you have written Node.js, you should be familiar with it. NPM run dev and NPM run pro are added to scripts. Because I want to show you how to pass in parameters at build time to dynamically set environment variables.
{
"name": "hello-docker"."version": "1.0.2"."description": ""."author": "May"."main": "app.js"."scripts": {
"dev": "NODE_ENV=dev node app.js"."pro": "NODE_ENV=pro node app.js"}}Copy the code
Dockerfile file
This is the information contained in a Dockerfile file. These commands are also explained in Docker Getting Started and Practices
FROM node:10.0-alpine
RUN apk --update add tzdata \
&& cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo "Asia/Shanghai" > /etc/timezone \
&& apk del tzdata
RUN mkdir -p /usr/src/nodejs/
WORKDIR /usr/src/nodejs/
# add npm package
COPY package.json /usr/src/nodejs/package.json
RUN cd /usr/src/nodejs/
RUN npm i
# copy code
COPY . /usr/src/nodejs/
EXPOSE 30010
CMD npm run dev
Copy the code
Create a.dockerignore file under the Dockerfile sibling to avoid putting your local debug files, node_modules, and so on into the Docker container
.git
node_modules
npm-debug.log
Copy the code
At this point, you can build a Docker image by running the following command
$ docker image build -t mayjun/hello-docker
Copy the code
Docker run -d -p 30010:30010 mayjun/hello-docker can run a docker container, but there is a problem. There is only one environment that can be packaged by CMD NPM run dev above, but you can also create a file to implement it or some other method.
Dynamically set environment variables
In order to solve the above problem, MY idea is to pass in parameters to dynamically set the environment variables during the image build, and make changes to the Dockerfile file. See the following implementation:
EXPOSE 30010
ARG node_env # new
ENV NODE_ENV=$node_env # new
CMD npm run ${NODE_ENV} # modified
Copy the code
Here’s an explanation of the code above
- The ARG directive defines a variable that the user can pass to the builder at build time by using the Docker build command with the –build-arg = flag
ARG node_env
- Use ENV to reference this variable in a Dockerfile
ENV NODE_ENV=$node_env
- That’s the step to use
CMD npm run ${NODE_ENV}
All that is left is to pass in parameters dynamically when building the image
$docker image build --build-arg node_env=dev -t mayjun/hello-docker:1.0.2Build the test environment$docker image build --build-arg node_env= pro-t mayjun/hello-docker:1.0.2# Build the production environment
Copy the code
Run the container
$ docker run -d-p 30010:30010 mayjun/hello-docker:1.0.2 $docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 2BC6e62cd0 e8 mayjun/hello - docker: 1.0.2"/ bin/sh -c 'NPM run..."3 minutes ago Up 3 minutes 0.0.0.0:30010->30010/ TCP Elastic_boumanCopy the code
Viewing container Logs
Docker logs -f 2bc6e62cd0e8 > [email protected] dev /usr/src/nodejs > NODE_ENV=dev node app.js Running on http://localhost: 30010 NODE_ENV devCopy the code
Docker pull Mayjun /hello-docker:1.0.2 docker pull Mayjun /hello-docker:1.0.2
Docker and Node.js private NPM packages
If you are using private NPM packages in your project, there will be an error during the Dcoker image building process. If it is outside the container, you can use NPM login to login to the account that has the permission of the NPM private package to solve this problem. But you can’t do that with Docker.
Create the authentication token
To install the private package we need to “create an authentication token” so that we can access our private NPM package within the continuous integration environment and Docker container. How to create this can be found at docs.npmjs.com/creating-an…
Implementation method
We need to add the following two commands to create Dockerfile:
# 528DAS62-e03e-4dc2-ba67-********** This Token is the authentication Token created for you
RUN echo "//registry.npmjs.org/:_authToken=528das62-e03e-4dc2-ba67-**********" > /root/.npmrc
RUN cat /root/.npmrc
Copy the code
Egg framework Docker container
In the Egg, if it is egg-scripts start –daemon, remove –daemon and go to egg-scripts start, otherwise the Docker container will not start.
Json. The Dockerfile file is the same as the first Docker-generated Node.js application above
package.json
{
"scripts": {
"start": "egg-scripts start" / / remove - daemon}}Copy the code
Docker container can not run, did you encounter it? Github.com/eggjs/egg/i…
Docker image volume and build time optimization
If a mirror is not optimized, the volume is usually very large. The following are some optimizations made in practice.
The RUN/COPY stratification
Each instruction in a Dockerfile creates an image layer, and each image layer can be reused and cached without changes to the Dockerfile instruction or copied project file.
The following code can be found in the Mayjun/Hello-docker :latest repository. In the following example, the NPM module is reinstalled after the source code is changed regardless of whether package.json is changed
#...
WORKDIR /usr/src/nodejs/hello-docker
COPY . /usr/src/nodejs/hello-docker
RUN npm install
#...
Copy the code
The improved code is shown below. We have made package.json ahead of schedule so that the NPM package will not be reinstalled without changes to package.json, which will also reduce deployment time.
#...
WORKDIR /usr/src/nodejs/
# add npm package
COPY package.json /usr/src/app/package.json
RUN cd /usr/src/app/
RUN npm i
# copy code
COPY . /usr/src/app/
#...
Copy the code
Node.js Alpine mirror optimization
Mayjun /hello-docker:1.0.0 This image is also searchable in the Docker repository and is about 688MB before optimization
$Docker images REPOSITORY TAG IMAGE ID CREATED SIZE Mayjun/Hello – Docker 1.0.0 7217fb3e9daa 5 seconds ago 688MB
Optimization with Alpine
Alpine is a small Linux distribution, and it is easiest to choose the Alpine version of Node.js if you want to dramatically reduce the image size. In addition, the time zone of Alpine is not domestic by default, and requires Dockerfile to configure the time zone.
FROM node:10.0-alpine
RUN apk --update add tzdata \
&& cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo "Asia/Shanghai" > /etc/timezone \
&& apk del tzdata
RUN echo "Asia/Shanghai" > /etc/timezone
RUN mkdir -p /usr/src/nodejs/
WORKDIR /usr/src/nodejs/
# add npm package
COPY package.json /usr/src/app/package.json
RUN cd /usr/src/app/
RUN npm i
# copy code
COPY . /usr/src/app/
EXPOSE 30010
CMD npm start
Copy the code
Repackaged mayjun/ Hello-Docker :1.1.0 If you look at the effect again, you can see that the image file has been reduced from 688MB to 85.3MB, which is still a large volume optimization
$Docker Images REPOSITORY TAG IMAGE ID CREATED SIZE Mayjun/Hello docker 1.1.0 169e05b8197d 3 minutes ago 85.3MBCopy the code
Do not pack the devDependencies package in production
Json file devDependencies object, filtered by specifying the production parameter after NPM I
The improvements are as follows:
FROM node:10.0-alpine
# omit...
# add npm package
COPY package.json /usr/src/app/package.json
RUN cd /usr/src/app/
RUN npm i --production # Change is here
# omit...
Copy the code
Repackaged a version of Mayjun/Hello-docker :1.2.0. If you look again, you can see that the image file has been reduced from 85.3MB to 72.3MB
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mayjun/hello-docker 1.2.0 f018aa578711 3 seconds ago 72.3MB
Copy the code
Q&A
Question1
The following command generates the following error when deleting a mirror:
$ docker rmi 6b1c2775591e
Error response from daemon: conflict: unable to delete 6b1c2775591e (must be forced) - image is referenced in multiple repositories
Copy the code
If you are careful, you may find that the image ID 6b1C2775591e points to both hello-Docker and Mayjun/hello-Docker repositories, which is also the cause of the deletion failure
$Docker images REPOSITORY TAG IMAGE ID CREATED SIZE mysql 5.7 383867b75fd2 6 days ago 373MB Hello - Docker latest 6b1c2775591e 7 days ago 675MB mayjun/hello-docker latest 6b1c2775591e 7 days ago 675MBCopy the code
Delete mayjun/ Hello-docker from mayjun/ Hello-Docker by using a repository and tag
$ docker rmi mayjun/hello-docker
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mysql 5.7 383867b75fd2 6 days ago 373MB
hello-docker latest 6b1c2775591e 7 days ago 675MB
Copy the code
Question2
The following error message is displayed when you run the command to delete an image:
$ docker rmi 9be467fd1285
Error response from daemon: conflict: unable to delete 9be467fd1285 (cannot be forced) - image is being used by running container 1febfb05b850
Copy the code
If a container is running, stop the container, delete the container, and then delete the image
$ docker container kill 1febfb05b850 # stop container
$ docker rm 1febfb05b850 # delete container
$ docker rmi 9be467fd1285 # delete mirror
Copy the code
Question3
Set the WORKDIR to be the same as below
. WORKDIR /usr/src/nodejs/# add npm package
COPY package.json /usr/src/node/package.json The directory is inconsistent
RUN cd /usr/src/node/ The directory is inconsistent
RUN npm i
...
Copy the code
For example, if the working directory is different from the actual COPY directory, the following error will be reported:
Then change it to consistent as follows
. WORKDIR /usr/src/nodejs/# add npm package
COPY package.json /usr/src/nodejs/package.json # change to consistent
RUN cd /usr/src/nodejs/ # change to consistent
RUN npm i
...
Copy the code