場景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命令