Docker 容器優雅終止方案_包裝設計

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網動廣告出品的網頁設計,採用精簡與質感的CSS語法,提升企業的專業形象與簡約舒適的瀏覽體驗,讓瀏覽者第一眼就愛上她。

原文鏈接:Docker 容器優雅終止方案

作為一名系統重啟工程師(SRE),你可能經常需要重啟容器,畢竟 Kubernetes 的優勢就是快速彈性伸縮和故障恢復,遇到問題先重啟容器再說,幾秒鐘即可恢復,實在不行再重啟系統,這就是系統重啟工程師的殺手鐧。然而現實並沒有理論上那麼美好,某些容器需要花費 10s 左右才能停止,這是為啥?有以下幾種可能性:

  1. 容器中的進程沒有收到 SIGTERM 信號。
  2. 容器中的進程收到了信號,但忽略了。
  3. 容器中應用的關閉時間確實就是這麼長。

對於第 3 種可能性我們無能為力,本文主要解決 1 和 2。

如果要構建一個新的 Docker 鏡像,肯定希望鏡像越小越好,這樣它的下載和啟動速度都很快,一般我們都會選擇一個瘦了身的操作系統(例如 AlpineBusybox 等)作為基礎鏡像。

問題就在這裏,這些基礎鏡像的 init 系統也被抹掉了,這就是問題的根源!

init 系統有以下幾個特點:

  • 它是系統的第一個進程,負責產生其他所有用戶進程。
  • init 以守護進程方式存在,是所有其他進程的祖先。
  • 它主要負責:
    • 啟動守護進程
    • 回收孤兒進程
    • 將操作系統信號轉發給子進程

1. Docker 容器停止過程

對於容器來說,init 系統不是必須的,當你通過命令 docker stop mycontainer 來停止容器時,docker CLI 會將 TERM 信號發送給 mycontainer 的 PID 為 1 的進程。

  • 如果 PID 1 是 init 進程 – 那麼 PID 1 會將 TERM 信號轉發給子進程,然後子進程開始關閉,最後容器終止。
  • 如果沒有 init 進程 – 那麼容器中的應用進程(Dockerfile 中的 ENTRYPOINTCMD 指定的應用)就是 PID 1,應用進程直接負責響應 TERM 信號。這時又分為兩種情況:
    • 應用不處理 SIGTERM – 如果應用沒有監聽 SIGTERM 信號,或者應用中沒有實現處理 SIGTERM 信號的邏輯,應用就不會停止,容器也不會終止。
    • 容器停止時間很長 – 運行命令 docker stop mycontainer 之後,Docker 會等待 10s,如果 10s 后容器還沒有終止,Docker 就會繞過容器應用直接向內核發送 SIGKILL,內核會強行殺死應用,從而終止容器。

2. 容器進程收不到 SIGTERM 信號?

如果容器中的進程沒有收到 SIGTERM 信號,很有可能是因為應用進程不是 PID 1,PID 1 是 shell,而應用進程只是 shell 的子進程。而 shell 不具備 init 系統的功能,也就不會將操作系統的信號轉發到子進程上,這也是容器中的應用沒有收到 SIGTERM 信號的常見原因。

問題的根源就來自 Dockerfile,例如:

FROM alpine:3.7
COPY popcorn.sh .
RUN chmod +x popcorn.sh
ENTRYPOINT ./popcorn.sh

ENTRYPOINT 指令使用的是 shell 模式,這樣 Docker 就會把應用放到 shell 中運行,因此 shell 是 PID 1。

解決方案有以下幾種:

方案 1:使用 exec 模式的 ENTRYPOINT 指令

與其使用 shell 模式,不如使用 exec 模式,例如:

FROM alpine:3.7
COPY popcorn.sh .
RUN chmod +x popcorn.sh
ENTRYPOINT ["./popcorn.sh"]

這樣 PID 1 就是 ./popcorn.sh,它將負責響應所有發送到容器的信號,至於 ./popcorn.sh 是否真的能捕捉到系統信號,那是另一回事。

舉個例子,假設使用上面的 Dockerfile 來構建鏡像,popcorn.sh 腳本每過一秒打印一次日期:

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

上新台中搬家公司提供您一套專業有效率且人性化的辦公室搬遷、公司行號搬家及工廠遷廠的搬家服務

#!/bin/sh

while true
do
    date
    sleep 1
done

構建鏡像並創建容器:

 → docker build -t truek8s/popcorn .
 → docker run -it --name corny --rm truek8s/popcorn

打開另外一個終端執行停止容器的命令,並計時:

 → time docker stop corny

因為 popcorn.sh 並沒有實現捕獲和處理 SIGTERM 信號的邏輯,所以需要 10s 左右才能停止容器。要想解決這個問題,就要往腳本中添加信號處理代碼,讓它捕獲到 SIGTERM 信號時就終止進程:

#!/bin/sh

# catch the TERM signal and then exit
trap "exit" TERM

while true
do
    date
    sleep 1
done

注意:下面這條指令與 shell 模式的 ENTRYPOINT 指令是等效的:

ENTRYPOINT ["/bin/sh", "./popcorn.sh"]

方案 2:直接使用 exec 命令

如果你就想使用 shell 模式的 ENTRYPOINT 指令,也不是不可以,只需將啟動命令追加到 exec 後面即可,例如:

FROM alpine:3.7
COPY popcorn.sh .
RUN chmod +x popcorn.sh
ENTRYPOINT exec ./popcorn.sh

這樣 exec 就會將 shell 進程替換為 ./popcorn.sh 進程,PID 1 仍然是 ./popcorn.sh

方案 3:使用 init 系統

如果容器中的應用默認無法處理 SIGTERM 信號,又不能修改代碼,這時候方案 1 和 2 都行不通了,只能在容器中添加一個 init 系統。init 系統有很多種,這裏推薦使用 tini,它是專用於容器的輕量級 init 系統,使用方法也很簡單:

  1. 安裝 tini
  2. tini 設為容器的默認應用
  3. popcorn.sh 作為 tini 的參數

具體的 Dockerfile 如下:

FROM alpine:3.7
COPY popcorn.sh .
RUN chmod +x popcorn.sh
RUN apk add --no-cache tini
ENTRYPOINT ["/sbin/tini", "--", "./popcorn.sh"]

現在 tini 就是 PID 1,它會將收到的系統信號轉發給子進程 popcorn.sh

如果你想直接通過 docker 命令來運行容器,可以直接通過參數 --init 來使用 tini,不需要在鏡像中安裝 tini。如果是 Kubernetes 就不行了,還得老老實實安裝 tini。

3. 使用 tini 后應用還需要處理 SIGTERM 嗎?

最後一個問題:如果移除 popcorn.sh 中對 SIGTERM 信號的處理邏輯,容器會在我們執行停止命令后立即終止嗎?

答案是肯定的。在 Linux 系統中,PID 1 和其他進程不太一樣,準確地說應該是 init 進程和其他進程不一樣,它不會執行與接收到的信號相關的默認動作,必須在代碼中明確實現捕獲處理 SIGTERM 信號的邏輯,方案 1 和 2 乾的就是這個事。

普通進程就簡單多了,只要它收到系統信號,就會執行與該信號相關的默認動作,不需要在代碼中显示實現邏輯,因此可以優雅終止。

Kubernetes 1.18.2 1.17.5 1.16.9 1.15.12離線安裝包發布地址http://store.lameleg.com ,歡迎體驗。 使用了最新的sealos v3.3.6版本。 作了主機名解析配置優化,lvscare 掛載/lib/module解決開機啟動ipvs加載問題, 修復lvscare社區netlink與3.10內核不兼容問題,sealos生成百年證書等特性。更多特性 https://github.com/fanux/sealos 。歡迎掃描下方的二維碼加入釘釘群 ,釘釘群已經集成sealos的機器人實時可以看到sealos的動態。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

※產品缺大量曝光嗎?你需要的是一流包裝設計!

窩窩觸角包含自媒體、自有平台及其他國家營銷業務等,多角化經營並具有國際觀的永續理念。