五分鐘 K8s 實戰(zhàn)-滾動更新與優(yōu)雅停機
當我們在生產(chǎn)環(huán)境發(fā)布應用時,必須要考慮到當前系統(tǒng)還有用戶正在使用的情況,所以盡量需要做到不停機發(fā)版。
所以在發(fā)布過程中理論上之前的 v1 版本依然存在,必須得等待 v2 版本啟動成功后再刪除歷史的 v1 版本。
如果 v2 版本啟動失敗 v1 版本不會做任何操作,依然能對外提供服務(wù)。
滾動更新
圖片
這是我們預期中的發(fā)布流程,要在 kubernetes 使用該功能也非常簡單,只需要在 spec 下配置相關(guān)策略即可:
spec:
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
這個配置的含義是:
- 使用滾動更新,當然還有 Recreate 用于刪除舊版本的 Pod,我們基本不會用這個策略。
- maxSurge:滾動更新過程中可以最多超過預期 Pod 數(shù)量的百分比,當然也可以填整數(shù)。
- maxUnavailable:滾動更新過程中最大不可用 Pod 數(shù)量超過預期的百分比。
這樣一旦我們更新了 Pod 的鏡像時,kubernetes 就會先創(chuàng)建一個新版本的 Pod 等待他啟動成功后再逐步更新剩下的 Pod。
圖片
優(yōu)雅停機
滾動升級過程中不可避免的又會碰到一個優(yōu)雅停機的問題,畢竟是需要停掉老的 Pod。
這時我們需要注意兩種情況:
- 停機過程中,已經(jīng)進入 Pod 的請求需要執(zhí)行完畢才能退出。
- 停機之后不能再有請求路由到已經(jīng)停機的 Pod
第一個問題如果我們使用的是 Go,可以使用一個鉤子來監(jiān)聽 kubernetes 發(fā)出的退出信號:
quit := make(chan os.Signal)
signal.Notify(quit, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGPIPE)
go func() {
<-quit
log.Printf("quit signal received, exit \n")
os.Exit(0)
}()
在這里執(zhí)行對應的資源釋放。
如果使用的是 spring boot 也有對應的配置:
server:
shutdown: "graceful"
spring:
lifecycle:
timeout-per-shutdown-phase: "20s"
當應用收到退出信號后,spring boot 將不會再接收新的請求,并等待現(xiàn)有的請求處理完畢。
但 kubernetes 也不會無限等待應用將 Pod 將任務(wù)執(zhí)行完畢,我們可以在 Pod 中配置
terminationGracePeriodSeconds: 30
來定義需要等待多長時間,這里是超過 30s 之后就會強行 kill Pod。
具體值大家可以根據(jù)實際情況配置
spec:
containers:
- name: example-container
image: example-image
lifecycle:
preStop:
exec:
command: ["sh", "-c", "sleep 10"]
同時我們也可以配置 preStop 做一個 sleep 來確保 kubernetes 將準備刪除的 Pod 在 Iptable 中已經(jīng)更新了之后再刪除 Pod。
這樣可以避免第二種情況:已經(jīng)刪除的 Pod 依然還有請求路由過來。具體可以參考 spring boot 文檔:
回滾
回滾其實也可以看作是升級的一種,只是升級到了歷史版本,在 kubernetes 中回滾應用非常簡單。
# 回滾到上一個版本
k rollout undo deployment/abc
# 回滾到指定版本
k rollout undo daemonset/abc --to-revisinotallow=3
同時 kubernetes 也能保證是滾動回滾的。
優(yōu)雅重啟
在之前的 如何優(yōu)雅重啟 kubernetes 的 Pod 那篇文章中寫過,如果想要優(yōu)雅重啟 Pod 也可以使用 rollout 命令,它也也可以保證是滾動重啟。
k rollout restart deployment/nginx
使用 kubernetes 的滾動更新確實要比我們以往的傳統(tǒng)運維簡單許多,就幾個命令的事情之前得寫一些復雜的運維腳本才能實現(xiàn)。