场景1
CMD ["java", "-jar", "app.jar"]
这时候java程序的PID=1也就是容器的主进程
执行docker stop <container> 也就等于 kill -15 1,这时候只要java程序能够处理SIGTERM信号即可
场景2
CMD ["/home/default/start.sh"]
start.sh
#!/bin/bash echo "[INFO] 开始运行" java -jar app.jar
很多时候我们一般会用一个shell脚本作为容器的主进程,这样启动逻辑就很灵活
执行docker stop <container> 也就等于 kill -15 1,shell脚本收到SIGTERM信号后并不会把信号传给它的子进程,也就是说java程序不会做任何动作,直到宽限期到期会强制关闭容器等于kill -9
docker 使用docker stop -t 参数指定宽限期默认是10秒,kubernetes里面使用 terminationGracePeriodSeconds: 30
方法1
那么shell如何传递SIGTERM信号给它的子进程?
#!/bin/bash echo "[INFO] 开始运行" java -jar app.jar & pid="$!" _kill() { echo "[INFO] Receive sigterm" kill $pid wait $pid exit 143 } trap _kill SIGTERM wait
步骤是把java程序后台启动以获得它的PID,最后一行加入wait命令防止shell退出,trap命令捕捉SIGTERM信号并执行一个命令
方法2
假设shell脚本里面只需求启动一个子程序,其实有更简洁的办法
#!/bin/bash echo "[INFO] 开始运行" exec java -jar app.jar
exec特性是不产生新的子进程而是当前shell进程,因此exec之后的命令将不会执行
方法3
docker run --init 也就是加入 --init参数 https://docs.docker.com/engine/reference/run/
或者在镜像里面加入tini https://github.com/krallin/tini
ENV TINI_VERSION v0.19.0 ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini RUN chmod +x /tini ENTRYPOINT ["/tini", "--"] CMD ["/home/default/start.sh"]
经过测试tini更多关心的还是僵尸进程的清理,对于子进程信号传递(特别是多级子进程)默认情况下支持的并不好,使用tini最好配合exec命令