docker中npm start启动node应用程序的问题

问题描述 投票:0回答:3

我读了一些 Docker 和 Node.js 最佳实践文章,例如https://github.com/nodejs/docker-node/blob/main/docs/BestPractices.md使用 Docker 容器化 Node.js Web 应用程序的 10 个最佳实践适用于 Node 和 NPM 的 Dockerfile 良好实践 。所有这些文章至少是在 2021 年写或更新的,我不列出 2021 年之前写的文章,但也有不少。

他们都反对

CMD ["npm", "run", "start"]
。主要原因是 npm 会“吞掉”退出信号,例如 SIGTERM 和 SIGINT,因此我的节点应用程序中的正常关闭代码将无法运行。 我猜旧的 npm 就是这种情况(虽然我没有测试它),但我已经测试了 node14+npm6 和 node16+npm8,我可以验证 npm6/8 确实

NOT

吞下这些事件和我的优雅运行关闭代码。不确定这是否是因为 npm 修复了它。 所以唯一的问题仍然是还有 1 个进程 npm 要运行,即 NPM 以

PID 1

运行。有些文章说问题是“PID 1 不会响应 SIGINT”,但我已经证实事实并非如此。 许多文章(例如这个

nodejs doc

)仅建议CMD [ "node", "server.js" ],但也在

https://github.com/nodejs/docker-node/blob/main/docs/BestPractices.md#handling-kernel-signals
说“以 PID 1 运行的 Node.js 进程不会响应 SIGINT (CTRL-C) 和类似信号。”,即 Nodejs 自己的文档本身矛盾(但我确实看到 NodeJS 作为 PID 1 响应 SIGINT) 所以我对

CMD ["npm", "run", "start"]

CMD [ "node", "server.js" ]
 的问题感到困惑
对于我的应用程序,还有 1 个考虑因素,我的 npm 脚本具有预挂钩以使应用程序正确运行,我有

prestart

npm 脚本使

npm start
工作。所以目前我只使用
CMD ["npm", "run", "start"]
但我对如何在 docker 中启动我的节点应用程序的“最佳实践”感到困惑。
---更新---

我发现了 npm

lifecycle 的已解决问题:将 SIGTERM 传播给子进程

所以他们

确实

修复了它,但该问题的最新评论是在2017,其中说“是的,这不起作用,至少对于bash;npm在shell中运行其生命周期过程,而bash不会将 SIGTERM 转发给其子级。” 我意识到我只在我的 mac 和 CentOS 服务器以及基于 alpine 的 docker 上进行了测试。也可能是因为我在CMD中使用exec形式,而不是shell形式,所以我得到了退出信号。

Node.js 和 Kubernetes 的正常关闭

说他们的 alpine 镜像没有使用 npm start 获得 SIGTERM,而我在 alpine3.15 上测试,我可以得到。

    

node.js docker npm signals alpine-linux
3个回答
0
投票

CMD [ "bash", "-c", "npm run db:migrate && node ./dist/server/index.js" ]

其中 
npm run db:migrate && node ./dist/server/index.js

是我的

npm start
 的内容
    


0
投票

CMD [ "npm", "run", "start" ]

查看docker容器上的进程图

$ ps ajxf PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND 0 1 1 1 pts/0 1 Ssl+ 0 0:01 npm run start 1 19 1 1 pts/0 1 S+ 0 0:00 sh -c -- node server.js 19 20 1 1 pts/0 1 Sl+ 0 0:00 \_ node server.js

npm

进程会产生一个

shell
进程,然后再生成
node
进程。这意味着
npm
不会将
node
进程作为直接子进程生成。
导致

npm

进程无法将信号传递给

node
进程。

这与

npm 在本地的行为不同,它直接生成

node
进程。

带节点

CMD [ "node", "server.js" ]

流程图

$ ps ajxf PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND 0 1 1 1 pts/0 1 Ssl+ 0 0:00 node server.js

Node.js 并未设计为以 PID 1 运行,这会导致在 Docker 内部运行时出现意外行为。例如,以 PID 1 运行的 Node.js 进程将不会响应 SIGINT (CTRL-C) 和类似信号

解决方案:

CMD [ "bash", "-c", "node server.js" ]

或者使用
tini

/

s6
初始化系统
    


0
投票
debian

的节点 Docker,例如18(或 18-bookworm)、18-slim(或 18-bookworm-slim)、18-bullseye、18-bullseye-slim、18-buster、18-buster-slim 不响应 SIGINT 信号。 我已经测试了来自节点 14+ 的所有基于 alpine 的 docker,它们都响应 SIGINT 信号。所以对我来说,这是选择基于 alpine 的镜像而不是基于 Debian 的 slim 镜像的另一个原因。

© www.soinside.com 2019 - 2024. All rights reserved.