You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何阻止Docker容器提前退出以生成NYC覆盖率报告?

解决Docker Stop时NYC覆盖率未生成的问题

问题分析

你在Cluster模式的Node.js程序中使用NYC统计覆盖率,通过dumb-init传递SIGINT信号时能正常生成覆盖率,但执行docker stop时容器提前退出,导致NYC来不及写入数据。核心原因是:

  • docker stop默认先发送SIGTERM信号,等待10秒后强制发送SIGKILL
  • 当前进程/脚本未正确处理SIGTERM信号,或等待时间不足,导致NYC无法完成收尾

解决方案

1. 延长Docker停止等待时间

直接通过docker stop的--time参数延长等待时间,给NYC足够的时间写入数据:

docker stop --time=30 <容器ID/名称>

如果使用Docker Compose,在服务配置中添加优雅停止时长:

services:
  your-service:
    # ...其他配置
    stop_grace_period: 30s

2. 让Node.js Cluster进程正确响应SIGTERM

在Cluster主进程中监听SIGTERM,触发所有子进程优雅退出,待全部子进程结束后再终止主进程,确保NYC完成覆盖率写入:

const cluster = require('cluster');
const os = require('os');

if (cluster.isPrimary) {
  const workers = [];
  // 启动子进程
  for (let i = 0; i < os.cpus().length; i++) {
    workers.push(cluster.fork());
  }

  // 处理SIGTERM信号
  process.on('SIGTERM', () => {
    let exitedCount = 0;
    console.log('主进程收到终止信号,开始关闭子进程');

    workers.forEach(worker => {
      worker.send('shutdown');
      worker.on('exit', () => {
        exitedCount++;
        if (exitedCount === workers.length) {
          console.log('所有子进程已退出,主进程终止');
          process.exit(0);
        }
      });
    });

    // 超时兜底,避免无限等待
    setTimeout(() => {
      console.log('等待子进程超时,强制终止');
      process.exit(1);
    }, 25000);
  });
} else {
  // 子进程业务服务(示例为HTTP服务)
  const http = require('http');
  const server = http.createServer((req, res) => {
    res.end('Hello World');
  }).listen(9000);

  // 监听主进程的关闭指令
  process.on('message', (msg) => {
    if (msg === 'shutdown') {
      console.log('子进程收到关闭信号,开始优雅退出');
      server.close(() => {
        console.log('子进程服务已关闭');
        process.exit(0);
      });
    }
  });
}

3. 修改启动脚本,转发SIGTERM到Node.js进程

默认情况下,shell不会将SIGTERM信号转发给后台运行的子进程,需要在migrate-and-run.sh中添加信号捕获逻辑:

set -a
. ".env$ENVIRONMENT_NAME"
set +a

echo $BUILD_NAME
if [ "$BUILD_NAME" == "local" ]
then
    npx sequelize-cli db:drop
    npx sequelize-cli db:create
fi

npx sequelize-cli db:migrate

# seed data for local builds
if [ "$BUILD_NAME" == "local" ]
then
    for file in seeders/*
    do
        npx sequelize-cli db:seed --seed $file
    done
fi

# 启动应用并记录PID
yarn start &
APP_PID=$!

# 捕获SIGTERM信号并转发给应用进程
trap "kill -TERM $APP_PID" SIGTERM

# 等待应用进程退出
wait $APP_PID

4. 优化dumb-init配置

确保dumb-init正确转发所有信号给子进程,修改Dockerfile中的ENTRYPOINT:

ENTRYPOINT ["dumb-init", "--"]
CMD ["sh", "scripts/migrate-and-run.sh"]

--参数会让dumb-init将所有接收到的信号转发给子进程,避免信号丢失。


内容的提问来源于stack exchange,提问作者Neha

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 03:28:15