This is the third day of my participation in Gwen Challenge. For details, see Gwen Challenge.

So far, you’ve used pre-built Docker images, such as MySQL, Vue, Nginx, and WordPress. This section describes developing NodeJs applications in containers.

In this section, you will create a node.js “Hello” application, build the application as a Docker image, and start it from the container. Normally, this image can be deployed to a production server, and Docker Compose will be used to override some Settings to create a development and debugging environment. This allows coding development on the host PC so that the files are executed in a continuously running container. This has several benefits:

  • Docker will manage all dependencies — no need to install and maintain RunTimes
  • This process is no different from native development — you can use any editor and tool you like
  • Containers are isolated — applications affect the host PC, such as causing files to be deleted
  • The application can be distributed to other developers or testers at any time — the application can run the same way with zero configuration on any other device.

This section to create the code file in a project at https://github.com/QuintionTang/docker-nodejs

Container-based application development

Docker simplifies Web development: any Web application can run in a single container.

But… If you want to deploy a similar container to a real-time production server, the application is typically stateless. Any number of instances can be started, and any instance can respond to a request. In practice, applications should not store base state data in local files or memory.

For example, when a user logs in, the application stores the login credentials in memory. Using a single container during development, it all works as expected without problems.

If you deploy your application to a production server and run it in more than two containers, those containers receive requests through load balancing. Users access the system and container1 processes the login. The next request will probably be served by Container2, and the containers will not share login status.

Of course, the above problem can be solved by providing a central storage service for isolated containers to maintain the application’s persistent storage data, such as databases.

Stateless Web applications are a good way to do it. This allows you to quickly scale up and down in a production environment as users grow, automatically adding more machines/containers. If stateless is appropriate, it may not be feasible to transform stateful applications when addressing practical requirements.

None of this matters during development, because applications are usually run in a single container. If it is not practical, containers need not be used in production.

What is a Node. Js

This must be most of the nuggets of small partners know that there is no introduction here, citing a simple explanation.

Node.js is a popular, high-performance JavaScript runtime built using the V8 JavaScript engine of the Chrome browser. It is commonly used for server-side Web development, but has also been adopted by the front-end or client to build tools, desktop applications, embedded systems, and so on.

After installing Node.js, you can execute the JavaScript file with the following commands:

node index.js
Copy the code

What is a single-entry script file? In theory it can be named anything, and projects usually use index.js as an entry point.

The previous content has been using Docker images provided by Docker Hub. This section shows you how to build your own Docker image that can install and execute applications in both development and production environments.

Node.js may not be of interest to you, but Docker works in any language (PHP, Python, Ruby, Go, Rust, etc.).

Hello Application Overview

This project creates a “Hello” application using the Express.js framework of Node.js.

The application is run at http://localhost:3005/, and the format is Hello Devpoint! .

Calling the same URL from the client Ajax request returns a JSON-encoded object:

{ "message": "Hello Devpoint!" }
Copy the code

Ajax calls are recognized when the HTTP header of the incoming request is set. This is added by most Ajax libraries: X-requested-with, XMLHttpRequest.

Can be added to the URL path string, for example, http://localhost:3005/devpoint will return to the Hello Devpint! , the response content is:

{ "message": "Hello Devpoint!" }
Copy the code

Project initialization

Initialize the project by executing the following code in the project directory:

npm init
Copy the code

After you enter the basic information, package.json is generated in the project root directory.

To install Express, execute the following command:

npm install express --save
Copy the code

To respond to code changes during development, install Nodemon and execute the following command:

npm install nodemon --save-dev
Copy the code

Nodemon the tool used by nodemon to listen for file changes in node.js project and automatically restart the service. Next, add listening rules for the project, such as directories to be ignored:

{
    "script": "./index.js",
    "ext": "js json",
    "ignore": [
        "node_modules/"
    ],
    "legacyWatch": true,
    "delay": 200,
    "verbose": true
}
Copy the code

Modify the project package.json by adding the startup command under the scripts property:

Node. /index.js", "debug": "nodemon --trace-warnings --inspect=0.0.0.0:9229./index.js",Copy the code

The following command can be executed on the terminal:

  • npm start: Used in the production environment
  • npm run debug: Used for development and debugging

Apply the script index.js

The script defines a simple response request under the root route

"use strict"; Const port = process. The env. NODE_PORT | | 3005, / / define HTTP default port or from NODE_PORT environment variables for express = the require (" express "), app = express(); Get ("/:title?") , (req, res) => { const message = `Hello ${req.params.title || "Devpoint"}! `; if (req.xhr) { res.set("Access-Control-Allow-Origin", "*").json({ message }); } else { res.send(message); }}); // Start the HTTP service app.listen(port, () => console.log(' server running on port ${port} '));Copy the code

Next, execute the script:

npm run debug
Copy the code

Open the browser and enter http://localhost:3005/. You can see the response as follows

Now you can try to modify the contents of the index.js script. When updates are made, the terminal will restart the service and refresh the browser to see the updates.

So far, a simple NodeJS application has been completed. Next, I’ll show you how to run debugging in a Docker environment.

Docker configuration

Docker compose, the image file is generally ready-made, you can view the “Docker for WEB developers (six) : Using Nginx to deploy static websites”, or the column “Docker for WEB developers”.

This article will introduce another way to make Docker images by yourself. It is generally recommended to make two images for the project, one for development and debugging, and one for production environment.

Dockerfiles

Dockerfile defines the build environment needed to install and execute the application, an image that can be easily run at any time.

It usually starts with the Docker Hub base image. The Nodejs image required for this article’s application:

Each tag refers to a separate image (created with its own Dockerfile) such as Node.js.

  • Many images are large, typically 100MB or more, because they contain complete imagesLinux OSOperating system.
  • slimThe mirror is generally a condensed versionLinux OS, including runNode.jsThe minimum set of packages required. If you want toNode.jsThese are useful when the container is deployed in a limited space environment.
  • alpineMirroring is based on Alpine Linux and is typically around 5MB. This version is useful if you need the smallest possible image and have limited reliance on operating system libraries.

Lts-alpine is good enough for this article’s application: it provides a small image with the latest version of Node.js.

Create a Dockerfile in the root directory of your application with the following code:

ENV WORKDIR=/data/ Node /app ENV NODE_ENV=production ENV NODE_PORT=3005 RUN mkdir -p $WORKDIR && chown -r node:node $WORKDIR # set working directory WORKDIR $WORKDIR # set active USER USER Json $WORKDIR/ # RUN NPM install && NPM cache clean EXPOSE $NODE_PORT CMD ["node", "./index.js"]Copy the code

The base image typically starts FROM Node :lts-alpine, and each line defines a step to install and run the Node.js application. See the Dockerfile guide for syntax for Dockerfile. Here are some common commands:

The command describe
# Beginning of comment
FROM Specifies the base image to start with, typically from a Docker Hub
ARG Build parameters, and ENV to. But the scope is different. ARG environment variables are only valid in Dockerfile, that is, only in the docker build process, the built image does not exist in this environment variable
ENV Defines environment variables that are used in subsequent code in the Dockerfile file and can also be used when the container is run
WORKDIR Set the working directory, which is the path inside the container
USER This command is used to specify the user and user group to execute the subsequent command in the mirror
VOLUME Mount data volumes
EXPOSE Declaration port
COPY Adds files (folders) to containers
RUN Execute when creating the image, that is, using the docker build command
CMD Executed when the container is run, that is, when the Docker run command is used

User security

When creating the image, the Dockerfile command is run as root (super) user. This operation is generally safe because the container can be restarted automatically in the event of a serious exception event.

Of course it’s safer to run the application as a more restricted user, so this example creates a user Node to launch the application. In this way, even if the application is maliciously controlled, it has no permission to operate files outside the folder where the application resides, limiting the security risk to the folder where the application resides.

Start the command

The best way to start the application is by calling its executable directly:

CMD [ "node", "./index.js" ]
Copy the code

This ensures that system messages such as STDERR are returned to Docker so that it can respond accordingly, such as restarting the container in the event of an application crash.

.dockerignore

The COPY command copies all application files from the host directory to the Docker image. In general, you do not need to COPY all files. In this case, you can use.dockerignore to define files or folders that do not need to be copied.

Dockerfile

.git
.gitignore
.config

.npm
.vscode
node_modules
package-lock.json
README.md
Copy the code

Build the mirror

Build a mirror of the inscription nodeHello from the Dockerfile and run the following command in the root directory:

docker image build -t nodehello .
Copy the code

The. Dot at the end of the command is essential; it represents the application path.

Docker image ls nodehello docker image ls nodehello

Start the container from the image

You can now start the NodeHello image with the following command

docker run -it --rm --name nodehello -p 3005:3005 nodehello
Copy the code

Open http://localhost:3005/ in your browser and see Hello Devpoint! The content.

To start the development environment, run the following command in the root directory of the project:

docker run -it --rm  --name nodehello  -p 3005:3005  -p 9229:9229  -e NODE_ENV=development  -v $PWD:/data/node/app --entrypoint '/bin/sh'  nodehello  -c 'npm install && npm run debug'
Copy the code

The command above starts the container in development mode and mounts the project directory on the host to the path /data/node/app in the container.

Now modify the index.js file, change Devpoint to Juejin, and go back to the browser to refresh and see the content changed to Hello Juejin! .

Debug Node.js using Chrome

Google Chrome and Chrome-based browsers such as Edge and Opera have a built-in Node.js debugger that ensures the application runs in a developer mode container and then launches the browser by typing: Chrome ://inspect.

If you do not see Remote targets, ensure that Discover Network Targets is selected and click Configure… And add a connection to <_229 class=” Calibre “> (or a network address if you are running the container on another device).

Click the inspect link below Target to start DevTools, switch to Console, and log server running on port 3005

Switch to the Source panel, according to clew press Ctrl | Cmd + P, input index, js, select the first path is: / data/node/app/index, js

Add two breakpoints, as shown:

Go back to the refresh page that opened http://localhost:3005/ and you can see that the program executed to the breakpoint.

Now, how to debug, you should be familiar with it, but I’m not going to expand it.

After the