日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

中的stop_谈谈stop容器

發(fā)布時間:2023/12/31 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 中的stop_谈谈stop容器 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

docker stop

對于docker來說,一般來說通過docker stop命令來實現(xiàn)停止容器,而不是docker kill。

具體命令如下:

docker stop [OPTIONS] CONTAINER [CONTAINER...]

容器內(nèi)的主進程(PID為1的進程)將收到SIGTERM,并在寬限期之后收到SIGKILL。在容器中的應用程序,可以選擇忽略和不處理SIGTERM信號,不過一旦達到超時時間,程序就會被系統(tǒng)強行kill掉,因為SIGKILL信號是直接發(fā)往系統(tǒng)內(nèi)核的,應用程序沒有機會去處理它。

至于這個寬限期默認是10s,當然可以通過參數(shù)來制定具體時間。

docker stop --helpUsage: docker stop [OPTIONS] CONTAINER [CONTAINER...]Stop one or more running containersOptions:--help Print usage-t, --time int Seconds to wait for stop before killing it (default 10)

而對于k8s來說,pod的寬限期默認是30s。通過terminationGracePeriodSeconds參數(shù)設置。

為什么需要優(yōu)雅stop docker ?

你的程序需要一些退出工作,比如保存checkpoint,回收一些資源對象等。如果你的服務是一個http server,那么你需要完成已經(jīng)處理的請求。如果是長鏈接,你還需要主動關(guān)閉keepalive。

如果你是在k8s中運行容器,那么k8s整個機制是一種基于watch的并行機制,我們不能保證操作的串行執(zhí)行。比如在刪除一個Pod的時候,需要更改iptables規(guī)則,LB的upstream 摘除等。

你的應用程序為什么接收不到SIGTERM停機信號?

  • 你的業(yè)務進程不是1號進程

Dockerfile中支持兩種格式定義入口點:shell格式和exec 格式。

exec格式 如下:

ENTRYPOINT ["/app/bin/your-app", "arg1", "arg2"]

該格式能保證你的主進程接受到停機信號。

示例:

程序代碼如下:

package mainimport ("fmt""os""os/signal""syscall""time" )func main() {c := make(chan os.Signal)// 監(jiān)聽信號signal.Notify(c, syscall.SIGTERM)go func() {for s := range c {switch s {case syscall.SIGTERM:fmt.Println("退出:", s)ExitFunc()default:fmt.Println("其他信號:", s)}}}()fmt.Println("啟動了程序")sum := 0for {sum++fmt.Println("休眠了:", sum, "秒")time.Sleep(1 * time.Second)} }func ExitFunc() {fmt.Println("開始退出...")fmt.Println("執(zhí)行清理...")fmt.Println("結(jié)束退出...")os.Exit(0) }

Dockerfiler如下,我們采用多階段構(gòu)建:

FROM golang:latest as builderWORKDIR /go/src COPY main.go .RUN CGO_ENABLED=0 go build -o stop ./main.goFrom alpine:latestWORKDIR /root/ COPY --from=builder /go/src/stop . RUN chmod +x /root/stopENTRYPOINT ["/root/stop"]

構(gòu)建鏡像:

docker build -t stop . Sending build context to Docker daemon 3.584kB Step 1/9 : FROM golang:latest as builder latest: Pulling from library/golang 376057ac6fa1: Pull complete 5a63a0a859d8: Pull complete 496548a8c952: Pull complete 2adae3950d4d: Pull complete 039b991354af: Pull complete 0cca3cbecb14: Pull complete 59c34b3f33f3: Pull complete Digest: sha256:1e36f8e9ac49d5ee6d72e969382a698614551a59f4533d5d61590e3deeb543a7 Status: Downloaded newer image for golang:latest---> 7e5e8028e8ec Step 2/9 : WORKDIR /go/src---> Running in efb1e4b1c200 Removing intermediate container efb1e4b1c200---> 312e98c07647 Step 3/9 : COPY main.go .---> 2dc4088e6548 Step 4/9 : RUN CGO_ENABLED=0 go build -o stop ./main.go---> Running in 6d18a1ef07ff Removing intermediate container 6d18a1ef07ff---> a207b2ecdd67 Step 5/9 : From alpine:latest latest: Pulling from library/alpine Digest: sha256:9a839e63dad54c3a6d1834e29692c8492d93f90c59c978c1ed79109ea4fb9a54 Status: Downloaded newer image for alpine:latest---> f70734b6a266 Step 6/9 : WORKDIR /root/---> Running in a308fc079da2 Removing intermediate container a308fc079da2---> a14716065730 Step 7/9 : COPY --from=builder /go/src/stop .---> 3573b92b9ab3 Step 8/9 : RUN chmod +x /root/stop---> Running in f620b3287636 Removing intermediate container f620b3287636---> 3cbc57300792 Step 9/9 : ENTRYPOINT ["/root/stop"]---> Running in 86f23ea9306f Removing intermediate container 86f23ea9306f---> 283788e6ad37 Successfully built 283788e6ad37 Successfully tagged stop:latest

在一個終端中運行該鏡像:

docker run stop

在另外一個終端stop該容器:

docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 91eeef705489 stop "/root/stop" 12 seconds ago Up 11 seconds clever_leavittdocker stop 91eeef705489 91eeef705489

最終有如下輸出:

啟動了程序 休眠了: 1 秒 休眠了: 2 秒 休眠了: 3 秒 休眠了: 4 秒 休眠了: 5 秒 休眠了: 6 秒 休眠了: 7 秒 休眠了: 8 秒 休眠了: 9 秒 休眠了: 10 秒 休眠了: 11 秒 休眠了: 12 秒 休眠了: 13 秒 休眠了: 14 秒 休眠了: 15 秒 休眠了: 16 秒 休眠了: 17 秒 休眠了: 18 秒 休眠了: 19 秒 休眠了: 20 秒 休眠了: 21 秒 休眠了: 22 秒 退出: terminated 開始退出... 執(zhí)行清理... 結(jié)束退出...

通過標準輸出,我們的程序接受到了SIGTERM信號,并執(zhí)行了一些退出工作。

shell格式 如下:

ENTRYPOINT "/app/bin/your-app arg1 arg2"

Shell格式將您的入口點作為 /bin/sh -c 的子命令來運行。

示例:

代碼不變,Dockerfile更改為:

FROM golang:latest as builderWORKDIR /go/src COPY main.go .RUN CGO_ENABLED=0 go build -o stop ./main.goFrom alpine:latestWORKDIR /root/ COPY --from=builder /go/src/stop . RUN chmod +x /root/stopENTRYPOINT "/root/stop"

構(gòu)建新的鏡像:

$ docker build -t stop-shell -f Dockerfile-shell .Sending build context to Docker daemon 4.608kB Step 1/9 : FROM golang:latest as builder---> 7e5e8028e8ec Step 2/9 : WORKDIR /go/src---> Using cache---> 312e98c07647 Step 3/9 : COPY main.go .---> Using cache---> 2dc4088e6548 Step 4/9 : RUN CGO_ENABLED=0 go build -o stop ./main.go---> Using cache---> a207b2ecdd67 Step 5/9 : From alpine:latest---> f70734b6a266 Step 6/9 : WORKDIR /root/---> Using cache---> a14716065730 Step 7/9 : COPY --from=builder /go/src/stop .---> Using cache---> 3573b92b9ab3 Step 8/9 : RUN chmod +x /root/stop---> Using cache---> 3cbc57300792 Step 9/9 : ENTRYPOINT "/root/stop"---> Running in 199ca0277b08 Removing intermediate container 199ca0277b08---> e0fe6a86ee1e Successfully built e0fe6a86ee1e Successfully tagged stop-shell:latest

重復上面的步驟,最終觀察到的結(jié)果如下:

動了程序 休眠了: 1 秒 休眠了: 2 秒 休眠了: 3 秒 休眠了: 4 秒 休眠了: 5 秒 休眠了: 6 秒 休眠了: 7 秒 休眠了: 8 秒 休眠了: 9 秒 休眠了: 10 秒 休眠了: 11 秒 休眠了: 12 秒 休眠了: 13 秒 休眠了: 14 秒 休眠了: 15 秒 休眠了: 16 秒 休眠了: 17 秒 休眠了: 18 秒 休眠了: 19 秒 休眠了: 20 秒 休眠了: 21 秒 休眠了: 22 秒 休眠了: 23 秒 休眠了: 24 秒 退出: terminated 開始退出... 執(zhí)行清理... 結(jié)束退出...

shell格式,我們的主程序也接受到了停機信號,并做了退出工作。

為了驗證,我們docker exec 到運行的docker-shell容器中,執(zhí)行ps:

docker exec -it 0299308034e7 sh ~ # ps PID USER TIME COMMAND1 root 0:00 /root/stop12 root 0:00 sh17 root 0:00 ps

我們的應用進程是1號進程,所以我們依舊可以接收到SIGTERM信號。

當我們的應用程序直接是啟動的入口,那么在接受停機信號方面,兩種格式并沒有什么區(qū)別。

如果我們的啟動腳本是一個類似于run.sh 的shell腳本,又會怎么樣那?

當我們以一個shell腳本啟動我們的應用程序,那么我們的應用程序不再是1號進程,此時,shell進程并不會通知我們的應用進程退出,我們需要在shell腳本中做一些特殊的處理,才能實現(xiàn)同樣的效果。

需要做的就是告訴你的Shell用你的應用程序替換自身。為此,shell具有 exec 命令(與前面講到的 exec 格式相似)。詳情見exec syscall。

在run.sh 中替換

/app/bin/your-app

為:

exec /app/bin/your-app

示例:

我們的run.sh 腳本如下:

#!/bin/shexec /root/stop

然后我們的Dockerfile 變更為:

FROM golang:latest as builderWORKDIR /go/src COPY main.go .RUN CGO_ENABLED=0 go build -o stop ./main.goFrom alpine:latestWORKDIR /root/ COPY --from=builder /go/src/stop . COPY run.sh . RUN chmod +x /root/stopENTRYPOINT ["/root/run.sh"]

構(gòu)建新的鏡像之后,運行該鏡像:

docker run stop-shell-runsh啟動了程序 休眠了: 1 秒 休眠了: 2 秒 休眠了: 3 秒

然后進入到容器中執(zhí)行ps:

docker exec -it 97adce7dd7e4 sh ~ # ps PID USER TIME COMMAND1 root 0:00 /root/stop14 root 0:00 sh19 root 0:00 ps

可以看到雖然我們的啟動腳本是run.sh,但是經(jīng)過exec 之后,應用程序成為了1號進程。

停止運行容器查看停機狀況:

docker stop 97adce7dd7e4

然后可以看到容器有如下輸出:

休眠了: 104 秒 休眠了: 105 秒 休眠了: 106 秒 休眠了: 107 秒 休眠了: 108 秒 休眠了: 109 秒 休眠了: 110 秒 休眠了: 111 秒 休眠了: 112 秒 休眠了: 113 秒 休眠了: 114 秒 休眠了: 115 秒 休眠了: 116 秒 休眠了: 117 秒 退出: terminated 開始退出... 執(zhí)行清理... 結(jié)束退出...
  • 監(jiān)聽了錯誤的信號

并不是所有的代碼框架都支持SIGTERM,比如Python的生態(tài)中,經(jīng)常是SIGINT。

例如:

try:do_work() except KeyboardInterrupt:cleanup()

所以默認是發(fā)送SIGTERM信號,我們依舊可以設置成其他的信號。

最簡單的解決方法是在Dockerfile中添加一行:

STOPSIGNAL SIGINT雖然我們將應用程序作為1號進程,可以接收到信號,但是也帶來其他的問題,比如僵尸進程。該問題在docker使用過程中很普遍存在。大家可以參考我另外一篇文章--避免在Docker鏡像下將NodeJS作為PID 1運行。

最佳實踐

使用 init 系統(tǒng)。這里我們推薦使用 tini。

Tini是你可能想到的最簡單的 init。 Tini所做的全部工作就是span出子進程,并等待它退出,同時收獲僵尸進程并執(zhí)行信號轉(zhuǎn)發(fā)。

使用 tini 有以下好處:

  • 它可以保護您免受意外創(chuàng)建僵尸進程的軟件的侵害,因為僵尸進程可能(隨著時間的推移!)使整個系統(tǒng)缺乏PID(并使其無法使用)。
  • 它可確保默認信號處理程序適用于您在Docker鏡像中運行的軟件。例如,對于Tini,即使您沒有顯式安裝信號處理程序,SIGTERM也會正確終止您的進程。
  • 它完全透明地執(zhí)行!沒有Tini的Docker鏡像將與Tini一起使用,而無需進行任何更改。

示例:

新的Dockerfile如下:

FROM golang:latest as builderWORKDIR /go/src COPY main.go .RUN CGO_ENABLED=0 go build -o stop ./main.goFrom alpine:latestRUN apk add --no-cache tini WORKDIR /root/ COPY --from=builder /go/src/stop . RUN chmod +x /root/stopENTRYPOINT ["/sbin/tini", "--", "/root/stop"]

構(gòu)建鏡像:

docker build -t stop-tini -f Dockerfile-tini .

運行tini鏡像:

$ docker run stop-tini啟動了程序 休眠了: 1 秒 休眠了: 2 秒 休眠了: 3 秒 休眠了: 4 秒 休眠了: 5 秒 休眠了: 6 秒 休眠了: 7 秒...

此時在另外一個終端執(zhí)行 docker exec 進入到容器中,并執(zhí)行 ps:

docker exec -it a727bd6617f4 sh ~ # ps PID USER TIME COMMAND1 root 0:00 /sbin/tini -- /root/stop7 root 0:00 /root/stop14 root 0:00 sh20 root 0:00 ps

此時可以看到,tini是1號進程,我們的應用程序是1號進程的子進程(7號)。

停止該容器:

docker stop a727bd6617f4

最終我們的運行容器有以下輸出:

休眠了: 82 秒 休眠了: 83 秒 休眠了: 84 秒 休眠了: 85 秒 休眠了: 86 秒 退出: terminated 開始退出... 執(zhí)行清理... 結(jié)束退出...

可以看到我們業(yè)務進程雖然不是1號進程,但是也接受到了停機信號。

當然這一切都歸功于tini,tini將信號轉(zhuǎn)發(fā)到了我們的應用程序。

總結(jié)

以上是生活随笔為你收集整理的中的stop_谈谈stop容器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。