dev.to 文章的 repo https://dev.to/itmayziii/node--docker-setup--3nc5-temp-slug-2056229
使用 Docker 设置 Node 的分步指南
了解如何在 Docker 容器内设置 Node JS
了解如何在 Docker 容器内设置 Node JS
本文的目标
- 拥有一个可以运行的 NodeJS 应用程序
- 确保进程不会因错误而退出,从而使 Node 应用具有弹性
- 代码更改时自动重启服务器,使 Node 应用更易于使用
- 利用 Docker 可以:
- 快速建立与生产相同的开发环境。
- 能够轻松地在本地和服务器上切换 Node 版本
- Docker 的所有其他优势
先决条件
- Docker 已安装
- 至少具备 Node 和 NPM 的入门级知识
如果你是那种只想看到最终结果的人,那么 GitHub repo 也许更适合你
1. 准备一个简单的 Node 应用
我们将使用 Express,因为它设置起来非常简单,而且这个框架很受欢迎。
在一个干净的目录中,让我们首先初始化 NPM,继续运行此命令并按照提示进行操作(提示中输入的内容对于本指南来说并不重要)
npm init
安装 Express
npm install --save-prod express
设置基本的 Express 服务器。下面的文件只是简单地启动一个 Node 进程,监听 3000 端口,并向/ 路由返回Hello World!消息。
src/index.js
const express = require('express')
const app = express()
const port = 3000
app.get('/', (req, res) => res.send('Hello World!'))
app.listen(port, () => {console.log(`Example app listening on port ${port}!`))
2. 设置 Docker 来运行我们的 Node 应用
我们将使用 docker-compose.yml 文件来启动和停止 Docker 容器,而无需输入冗长的 Docker 命令。你可以将此文件视为多个 Docker 容器的配置文件。
docker-compose.yml
version: "3"
services:
app:
container_name: app # How the container will appear when listing containers from the CLI
image: node:10 # The <container-name>:<tag-version> of the container, in this case the tag version aligns with the version of node
user: node # The user to run as in the container
working_dir: "/app" # Where to container will assume it should run commands and where you will start out if you go inside the container
networks:
- app # Networking can get complex, but for all intents and purposes just know that containers on the same network can speak to each other
ports:
- "3000:3000" # <host-port>:<container-port> to listen to, so anything running on port 3000 of the container will map to port 3000 on our localhost
volumes:
- ./:/app # <host-directory>:<container-directory> this says map the current directory from your system to the /app directory in the docker container
command: "node src/index.js" # The command docker will execute when starting the container, this command is not allowed to exit, if it does your container will stop
networks:
app:
现在我们已经配置好了,让我们用这个命令启动docker容器。这意味着启动配置文件中定义的容器,并在后台运行它们(-d)。
docker-compose up -d
现在你应该能够在浏览器中访问localhost:3000并查看 你还应该能够通过运行来验证你的容器是否正在运行Hello World!
docker ps
用于管理此容器的有用 docker 命令
列出所有正在运行的容器
docker ps
列出所有容器,无论它们是否正在运行
docker ps -a
从同一目录中的 docker-compose.yml 文件启动容器
docker-compose up -d
从同一目录中的 docker-compose.yml 文件停止容器
docker-compose stop
从同一目录中的 docker-compose.yml 文件重新启动容器
docker-compose restart
查看 Docker 容器中的日志文件
docker-compose logs -f
3. 增强应用程序的弹性
如果您之前使用过 Node,那么您可能知道,如果应用程序中出现错误(例如未捕获的异常),它会关闭相应的 Node 进程。这对我们来说真是个坏消息,因为我们的代码中难免会有 bug,而且我们无法保证代码 100% 无错误。解决这个问题的方法通常是创建一个新的进程来监视我们的 Node 应用,并在应用退出时重新启动它。市面上有很多解决方案,例如 Linux 的 supervisord、NPM 的 forever 包和 PM2 等等……我们只需在本指南中挑选一个即可。
我将重点介绍PM2,因为我对它最熟悉,并且除了进程管理之外,它还具有一些其他功能,例如文件监视,这在我们的下一部分中会很有用。
安装 PM2
npm install --save-prod pm2
PM2 可以通过命令行使用,但我们将设置一个简单的配置文件,就像我们对 docker-compose.yml 文件所做的那样,以防止我们重复输入长命令
生态系统.config.js
const path = require('path')
module.exports = {
apps: [{
name: 'app',
script: 'src/index.js', // Your entry point
instances: 1,
autorestart: true, // THIS is the important part, this will tell PM2 to restart your app if it falls over
max_memory_restart: '1G'
}]
}
现在我们应该更改我们的 docker-compose.yml 文件以使用 PM2 来启动我们的应用程序,而不是直接从 index.js 启动它。
docker-compose.yml(仅更改了命令选项)
version: "3"
services:
app:
container_name: app # How the container will appear when listing containers from the CLI
image: node:10 # The <container-name>:<tag-version> of the container, in this case the tag version aligns with the version of node
user: node # The user to run as in the container
working_dir: "/app" # Where to container will assume it should run commands and where you will start out if you go inside the container
networks:
- app # Networking can get complex, but for all intents and purposes just know that containers on the same network can speak to each other
ports:
- "3000:3000" # <host-port>:<container-port> to listen to, so anything running on port 3000 of the container will map to port 3000 on our localhost
volumes:
- ./:/app # <host-directory>:<container-directory> this says map the current directory from your system to the /app directory in the docker container
command: "npx pm2 start ecosystem.config.js --no-daemon" # The command docker will execute when starting the container, this command is not allowed to exit, if it does your container will stop
networks:
app:
需要注意的是,修改 docker-compose.yml 文件不会影响正在运行的容器。为了使更改生效,您需要重启容器。
docker-compose restart
太好了,我们现在应该回到locahost:3000 的正常运行应用程序,但现在我们的应用程序在出现错误时不会崩溃。
4. 使我们的应用程序易于开发
您可能已经注意到,一旦 Node 进程启动,更改代码实际上不会有任何效果,直到您重新启动该 Node 进程,而对于我们来说,这意味着每次进行更改时都要重新启动 Docker 容器。这听起来太糟糕了。如果我们可以在更改代码时自动重新启动 Node 进程,那就太理想了。过去,我曾尝试过引入文件监视实用程序,并使用该文件监视实用程序在文件更改时重新启动 Docker,或者我会使用 Nodemon,但在使用 Docker 时这样做会有一些注意事项。最近,我一直使用 PM2 在文件更改时重新启动我的 Node 进程,由于我们已经从上一步将其引入,因此无需安装其他依赖项。
ecosystem.config.js(仅添加了监视选项)**
const path = require('path')
module.exports = {
apps: [{
name: 'app',
script: 'src/index.js',
instances: 1,
autorestart: true,
watch: process.env.NODE_ENV !== 'production' ? path.resolve(__dirname, 'src') : false,
max_memory_restart: '1G'
}]
}
如果我们没有将 NODE_ENV 环境变量设置为production,上面的配置文件现在将监视src目录。您可以通过更改 index.js 文件来测试它,使其除了Hello World!之外还向浏览器打印其他内容。同样,在此功能生效之前,您需要重新启动 Docker 容器,因为您更改了 PM2 运行容器的方式。
docker-compose restart
需要注意的是,重新启动 Node 进程可能需要一秒钟才能完成,如果您想观察它何时完成,您可以查看 Docker 日志以了解 PM2 何时完成重新启动 Node 进程。
docker-compose logs -f
总结
-
我们的目标之一是能够轻松更改 Node 版本,您可以通过更改docker-compose.yml文件中的图像选项来实现这一点。
-
本地安装依赖项需要使用本地 NPM 和 Node 版本,如果本地版本与 Docker 版本不同,有时可能会导致冲突。使用同一个 Docker 容器安装依赖项更安全。您可以使用以下命令,它将使用该容器安装依赖项,然后将其删除
docker run --rm -i -v <absolute-path-to-your-project-locally>:/app -w /app node:10 npm install
- 如上所述,如果本地 Node 版本与 Docker 运行的版本不同,可能会出现问题。为了保持一致性,最好在容器内运行命令。您可以使用以下命令进入容器内部:
docker exec -it app bash
上述命令将把你放入容器中,以便你可以继续从那里运行命令,即npm run start或npm run test
如果你不想进入容器,你可以运行如下命令
docker exec -t app bash -c "npm run start"