如何阻止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
相关产品推荐
相关产品推荐

