docker容器生命周期脚本通过entrypoint包装shell脚本实现启动前初始化与退出前清理,需确保脚本作为pid 1运行并用exec "$@"执行cmd、trap捕获sigterm,避免后台化或bash依赖;推荐结合docker-compose的depends_on与healthcheck提升健壮性。

Docker 容器生命周期脚本通常指在容器启动前、运行中或退出时执行的自定义逻辑,核心是通过 ENTRYPOINT 或 CMD 配合 shell 脚本实现,而不是 Docker 本身提供“钩子”机制。
用 ENTRYPOINT 包装启动脚本
这是最常用也最可靠的方式:把初始化逻辑(如配置生成、健康检查、依赖等待)写进一个 shell 脚本,再设为 ENTRYPOINT。Docker 启动容器时会先执行它,最后再调用 CMD(作为参数传给脚本)。
- 脚本开头用
#!/bin/sh,确保兼容性(Alpine 等镜像默认无 bash) - 脚本末尾用
exec "$@"执行原始 CMD,保证进程 PID 为 1,支持信号转发(如 SIGTERM 正常终止) - 示例结构:
#!/bin/sh # init.sh echo "→ 正在等待数据库就绪..." until nc -z db 5432; do sleep 2 done echo "→ 数据库已连接" <h1>生成配置文件</h1><p>envsubst /app/config.yml</p><h1>执行原始命令(CMD)</h1><p>exec "$@"</p>
构建时 COPY 脚本并设置 ENTRYPOINT:
COPY init.sh /init.sh RUN chmod +x /init.sh ENTRYPOINT ["/init.sh"] CMD ["your-app-binary", "--config", "/app/config.yml"]
处理容器停止前的清理动作
Docker 没有 pre-stop 钩子,但可通过捕获 SIGTERM 实现优雅退出。关键点是:脚本必须作为 PID 1 运行,且不忽略信号。
- 避免用
sh script.sh &启动后台进程——这会让 shell 成为 PID 1,而真正应用变成子进程,收不到信号 - 推荐用
trap捕获信号,在 exit 前执行清理 - 示例片段:
#!/bin/sh
cleanup() {
echo "→ 正在关闭连接、释放资源..."
# 如:关闭数据库连接、写入状态文件、通知上游服务
rm -f /tmp/lockfile
}
trap cleanup TERM INT
<h1>启动主程序(保持前台运行)</h1><p>exec "$@"</p>避免常见陷阱
很多问题源于对 PID 1 和信号机制理解偏差:
-
不要用 && 连接多条命令:如
./init.sh && exec myapp会导致 exec 后 shell 进程退出,PID 1 变成 myapp —— 看似正常,但若 init.sh 有 trap,将失效 -
不要后台化主进程:如
myapp & wait会让 wait 成为 PID 1,而 myapp 收不到 SIGTERM -
Alpine 镜像慎用 bash:默认无 bash,用
#!/bin/sh更稳妥;如需 bash 特性,显式安装并声明RUN apk add --no-cache bash
替代方案:用 docker-compose 的 depends_on + healthcheck
如果只是解决服务依赖顺序(如等 DB 启好),单纯靠脚本轮询不够健壮。建议结合:
-
depends_on中配置condition: service_healthy - 为目标服务(如 PostgreSQL)定义
healthcheck,确认端口通且能执行简单 SQL - 这样启动时自动等待,无需在应用侧重复判断











