示例说明
docker stop 默认给容器 10 秒宽限期,随后 SIGKILL 强杀。如果应用不处理 SIGTERM,正在处理的请求会被拦腰截断,写入到一半的数据可能损坏。正确姿势是收到信号后先让健康检查变假让负载均衡摘流量,再等待在途请求完成,最后有意识地以零码退出,配合编排器的滚动更新实现无缝发布。
技术要点
- SIGTERM 与 SIGINT 都要注册处理器
- 就绪探针翻转阻止新流量进入
- close 超时兜底防止无限等待
代码解读
shutdown 里先把 shuttingDown 置真,live 端点随即返回未就绪,编排器会停止转发新请求;app.close 会停止接受新连接并等待现有请求完成,timeout 参数防止个别慢请求拖过宽限期;最后显式 exit(0) 让容器以成功状态终止。
使用建议
宽限期要与编排器配置匹配,Kubernetes 默认 30 秒而 Docker 默认 10 秒,应用内预算要小于平台预算。后台队列消费者停机前应等待当前消息处理完并确认。绝不要在信号处理器里做长阻塞操作。
代码示例
import fastify from 'fastify';
const app = fastify({ logger: true });
let shuttingDown = false;
app.get('/live', async () => ({ ready: !shuttingDown }));
app.post('/jobs', async () => ({ queued: true }));
const server = await app.listen({ port: 3000, host: '0.0.0.0' });
async function shutdown(signal) {
shuttingDown = true;
console.log('received', signal, 'draining connections');
const deadline = Date.now() + 20_000;
await app.close({ timeout: 15_000 });
if (Date.now() > deadline) {
console.warn('grace period exceeded, forcing exit');
}
process.exit(0);
}
process.on('SIGTERM', () => shutdown('SIGTERM'));
process.on('SIGINT', () => shutdown('SIGINT'));
// Dockerfile 用途提示:
// STOPSIGNAL SIGTERM 让 docker stop 发送可捕获信号而非默认逻辑
console.log('docker-ready on port 3000');