Docker部署Node.js全栈应用Exit Code 137问题求助
问题描述
我正在开发基于Node.js、React-Native、Expo和Docker的全栈应用,执行docker-compose down或docker-compose stop命令时,前端与后端容器的退出状态为Exited(137),但数据库容器正常退出。我希望三个容器均能以状态0正常结束,已尝试使用init:true、tini以及信号处理方案,本地环境运行正常但Docker环境下问题依旧。
相关配置文件及代码
docker-compose.yml
version: '3.8' services: back: build: ./backend # 告诉Docker Compose在哪里找到Dockerfile(当前目录) ports: - "3000:3000" # 将容器的3000端口映射到本地的3000端口 environment: - NODE_ENV=development # 可添加其他需要的环境变量 volumes: - ./backend:/app - /app/node_modules depends_on: database: condition: service_healthy database: image: mysql:8.0.36 volumes: - ./backend/data/create_table.sql:/docker-entrypoint-initdb.d/create_table.sql ports: - 3306:3306 - 33060:33060 environment: - MYSQL_ROOT_PASSWORD=root - MYSQL_DATABASE=prueba - MYSQL_USER=user - MYSQL_PASSWORD=user healthcheck: test: ["CMD-SHELL", "mysql --user=user --password=user --database=prueba -e 'SELECT 1'"] interval: 10s timeout: 5s retries: 5 start_period: 10s front: build: ./frontend volumes: - ./frontend:/app - /app/node_modules stdin_open: true # 等价于docker run的-i参数 tty: true # 等价于docker run的-t参数 ports: - "8081:8081" - "19000:19000" - "19001:19001" - "19002:19002" environment: - NODE_ENV=development - EXPO_DEVTOOLS_LISTEN_ADDRESS=0.0.0.0 - CHOKIDAR_USEPOLLING=true depends_on: - back
后端Dockerfile
# 选择要使用的Node.js版本 FROM node:18-alpine3.19 # 设置容器内的工作目录 WORKDIR /app # 将项目配置文件(package.json和可选的package-lock.json)复制到工作目录 COPY package*.json ./ # 安装项目依赖 RUN npm install # 将项目剩余文件复制到工作目录 COPY . . # 暴露应用运行的端口 EXPOSE 3000 # 启动应用 CMD ["npm", "start"]
前端Dockerfile
# 使用Node.js基础镜像 FROM node:18-alpine3.19 # 配置容器内的工作目录 WORKDIR /app # 复制package.json和package-lock.json(如果存在) COPY package*.json ./ # 安装项目依赖 RUN npm install # 将项目文件复制到容器内 COPY . . # Expo CLI默认使用19000端口 EXPOSE 8081 EXPOSE 19000 EXPOSE 19001 EXPOSE 19002 # 启动Expo应用的命令 CMD ["npx", "expo", "start", "--tunnel"]
后端代码
const initDatabase = require('./data/sincro'); const express = require('express'); const app = express(); const PORT = process.env.PORT || 3000; app.get('/', (req, res) => { res.send("Hola Mundoooo!"); }); // 处理程序终止信号 process.on('SIGTERM', () => { console.log('SIGTERM signal received. Exiting.'); server.close(); process.exit(0); }); process.on('SIGINT', () => { console.log('SIGINT signal received. Exiting.'); server.close(); process.exit(0); }); // 初始化数据库后启动服务器 initDatabase().then(() => { server = app.listen(PORT, () => { console.log(`Server running on port ${PORT}`); }); });
解决方案
1. 后端容器修复
退出码137通常表示进程被强制杀死(内存不足或收到SIGKILL信号)。核心问题是npm start会启动子进程,导致信号无法直接传递给Node.js主进程。
具体操作:
- 修改后端Dockerfile的启动命令,直接用
node启动入口文件,绕过npm的信号拦截:CMD ["node", "app.js"] # 替换为你的实际入口文件名 - 优化信号处理逻辑,等待服务器关闭完成后再退出,避免强制终止:
process.on('SIGTERM', () => { console.log('收到SIGTERM信号,正在关闭服务器'); server.close((err) => { if (err) { console.error('服务器关闭失败:', err); process.exit(1); } console.log('服务器已关闭,退出进程'); process.exit(0); }); }); process.on('SIGINT', () => { console.log('收到SIGINT信号,正在关闭服务器'); server.close((err) => { if (err) { console.error('服务器关闭失败:', err); process.exit(1); } console.log('服务器已关闭,退出进程'); process.exit(0); }); });
2. 前端Expo容器修复
Expo CLI同样会创建子进程,加上tty: true和stdin_open: true的配置,可能干扰信号传递。
具体操作:
- 在docker-compose.yml的front服务中添加
init: true,让tini作为PID 1管理信号转发:front: build: ./frontend volumes: - ./frontend:/app - /app/node_modules stdin_open: true tty: true init: true # 新增该行 ports: - "8081:8081" - "19000:19000" - "19001:19001" - "19002:19002" environment: - NODE_ENV=development - EXPO_DEVTOOLS_LISTEN_ADDRESS=0.0.0.0 - CHOKIDAR_USEPOLLING=true depends_on: - back - 修改启动命令为
exec模式,确保信号直接传递给Expo进程:CMD ["sh", "-c", "exec npx expo start --tunnel"]
3. 通用优化
- 给所有Node.js服务(back和front)都添加
init: true,确保PID 1进程正确转发信号 - 如果不需要交互式终端,可去掉
stdin_open: true和tty: true配置,减少信号干扰
内容的提问来源于stack exchange,提问作者Martinikoh
相关产品推荐
相关产品推荐

