优雅启动与关闭
容器优雅停机确保数据一致性,下面介绍信号处理与配置方法。
信号处理
停机流程
dockerfile
docker stop my-app
↓
发送 SIGTERM (默认等待 10s)
↓
容器处理中请求
↓
清理资源(关闭连接、刷盘)
↓
进程退出
↓
(超时 10s 后)
↓
发送 SIGKILL (强制终止)
信号处理示例
JavaScript
# Node.js 应用
FROM node:18
COPY server.js .
# PID 1 问题:shell 不转发信号
# CMD ["node", "server.js"] # 错误:shell 格式
# 正确:exec 格式
CMD ["node", "server.js"]
Bash
// server.js
process.on('SIGTERM', () => {
console.log('SIGTERM received, shutting down gracefully');
server.close(() => {
console.log('Closed all connections');
process.exit(0);
});
});
process.on('SIGKILL', () => {
console.log('SIGKILL received, forcing exit');
process.exit(1);
});
stop_grace_period
dockerfile
# 配置超时时间(默认 10s)
docker run -d --name my-app --stop-timeout 30 my-app
# Compose 配置
services:
app:
image: my-app
stop_grace_period: 30s
超时后发送 SIGKILL 强制终止,可能丢失数据。
PID 1 问题
Shell 格式 vs Exec 格式
dockerfile
# Shell 格式(不推荐)
CMD node server.js
# 启动 /bin/sh -c "node server.js"
# SIGTERM 发给 shell,不转发给 node
# Exec 格式(推荐)
CMD ["node", "server.js"]
# 直接启动 node server.js
# SIGTERM 直接发给 node
tini 解决方案
Bash
# 安装 tini
RUN apt-get install -y tini
# 使用 tini 作为 init 系统
ENTRYPOINT ["tini", "--"]
CMD ["node", "server.js"]
tini 是轻量级 init 系统,正确转发信号并回收僵尸进程。
优雅启动
预热检查
Java
#!/bin/bash
# entrypoint.sh
# 预热缓存
echo "Warming up cache..."
curl -s http://localhost:3000/warmup
# 检查依赖
until nc -z db 5432; do
echo "Waiting for DB..."
sleep 1
done
echo "Starting application..."
exec "$@"
优雅关闭示例
Java
Python
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
System.out.println("Shutting down gracefully");
dataSource.close();
server.stop();
}));
Python
text
import signal
import sys
def graceful_shutdown(signum, frame):
print("Shutting down gracefully")
server.close()
sys.exit(0)
signal.signal(signal.SIGTERM, graceful_shutdown)
要点总结
- docker stop 先发送 SIGTERM,等待 10s 后发送 SIGKILL
- Exec 格式 CMD 确保信号直接传给进程,Shell 格式会被 shell 拦截
- stop_grace_period 配置超时,避免过早强制终止
- tini 作为 init 系统正确转发信号并回收僵尸进程
- 应用需处理 SIGTERM 信号,完成请求处理和资源清理
📝 发现内容有误?点击此处直接编辑