當您的應用程序是經過docker的方式運行時,在終止應用程序前進行適當的清理,也就是所謂的優雅退出也很是重要。儘管這僅僅取決於確保信號到達您的應用程序並對其進行處理,但仍有不少方面可能致使錯誤。python
從原則上講,這很是簡單:當您(或您的集羣管理工具)運行docker stop時,Docker會向您的應用程序的入口點發送可配置的信號; SIGTERM是默認值。在對個人應用程序進行容器化時,我肯定了四個陷阱(但只進入了其中三個!),以便爲您節省一些調查時間。docker
1:您使用了錯誤的ENTRYPOINT 格式 shell
Dockerfile容許您使用一種誘人的便捷字符串(稱爲shell格式)來定義入口點:數組
ENTRYPOINT "/app/bin/your-app arg1 arg2"
或者更使人討厭的 exec 格式,它使您能夠將命令行做爲JSON數組提供:app
ENTRYPOINT ["/app/bin/your-app", "arg1", "arg2"]
長話短說:始終使用後一種exec格式。 Shell格式將您的入口點做爲 /bin/sh -c 的子命令來運行,它帶有不少問題,其中一個值得注意的問題是您在應用程序中永遠看不到信號。框架
2:您的入口點是一個Shell腳本,您沒有exec工具
若是您以常規方式從Shell腳本運行應用程序,則Shell將以新進程生成應用程序,而且您的應用程序將不會收到來自Docker的信號。命令行
您須要作的就是告訴您的Shell用您的應用程序替換自身。爲此,shell具備 exec 命令(與前面講到的 exec 格式類似)。詳情見exec syscall。日誌
因此替換code
/app/bin/your-app
爲:
exec /app/bin/your-app
您確實使用了exec,可是您經過啓動子shell欺騙了本身
我一直是runit日誌記錄的忠實擁護者,您只需在沒有時間戳的狀況下就將日誌記錄到stdout,而且runit會在全部日誌條目前添加tai64n
時間戳。
例如:
exec /app/bin/your-app | tai64n
可是,這致使您的應用程序在子shell中執行,其結果一般是:沒有信號給您。
3:您監聽了錯誤的信號
儘管SIGTERM在進程管理器中盛行,但許多框架仍但願使用SIGINT ,例如Control-C中止應用程序。特別是在Python生態系統中,一般要作:
try: do_work() except KeyboardInterrupt: cleanup()
若是不採起任何進一步的措施,則若是您的應用程序收到SIGTERM,將永遠不會調用cleanup()。更糟的是,若是您的PID爲1,那麼超過最大等待時間,而後將SIGKILL發送到入口點以前,實際上什麼也不會發生。
所以,若是您曾經想知道爲何docker stop
會花費這麼長時間–這可能的緣由:您沒有監聽SIGTERM,而且因爲PID爲1,信號從您的進程中彈回。沒有清理,緩慢關閉。
最簡單的解決方法是在Dockerfile中添加一行:
STOPSIGNAL SIGINT
可是,您實際上應該嘗試同時支持這兩種信號,並首先避免成爲PID 1。
總結