今天跟大家一起分享一個(gè)不經(jīng)常想起來(lái)础倍,但十分有用的暗黑小招式 -- Container Lifecycle Hooks。沒(méi)有錯(cuò)胎挎,Docker 在 kubernetes 運(yùn)行中也有類(lèi)似于許多編程語(yǔ)言框架中的組件生命周期鉤子沟启。鉤子使 Container 能夠了解其管理生命周期中的事件,并在執(zhí)行相應(yīng)的生命周期鉤子時(shí)運(yùn)行在處理程序中實(shí)現(xiàn)的代碼犹菇。 這樣一來(lái)德迹,我們每個(gè)應(yīng)用程序都可以在啟動(dòng)之前進(jìn)行一些準(zhǔn)備工作或者結(jié)束之前進(jìn)行一些清理工作。
Kubernetes 給我們暴露兩個(gè)?Lifecycle Hooks 入口 (好摳啊揭芍,你看看 openresty 提供了多少 Hooks)
1. PostStart :在創(chuàng)建容器后立即執(zhí)行胳搞。但是,無(wú)法保證掛鉤將在容器ENTRYPOINT之前執(zhí)行称杨。沒(méi)有參數(shù)傳遞給處理程序肌毅。由于無(wú)法保證和容器內(nèi)其它進(jìn)程啟動(dòng)的順序相關(guān)聯(lián),所以不是應(yīng)用程序進(jìn)行啟動(dòng)前配置的最佳解決方案姑原。
2. PreStop:在銷(xiāo)毀容器之前即執(zhí)行悬而。它是阻塞的,意味著它是同步的锭汛,所以它必須在刪除容器的調(diào)用之前完成笨奠。沒(méi)有參數(shù)傳遞給處理程序。很適合作為應(yīng)用程序優(yōu)雅退出的機(jī)制的唤殴,可以定義一系列的行為來(lái)釋放容器占有的資源般婆、進(jìn)行通知和告警來(lái)實(shí)現(xiàn)優(yōu)雅退出。
Container可以通過(guò)下面的兩種類(lèi)型的鉤子處理程序:
Exec - 執(zhí)行特定命令朵逝,例如pre-stop.sh腺兴,在Container的cgroups和名稱(chēng)空間內(nèi)。命令執(zhí)行過(guò)程消耗的 CPU 和內(nèi)存受到容器的 limits 參數(shù)限制廉侧。
HTTP - 對(duì)Container上的特定端點(diǎn)執(zhí)行HTTP請(qǐng)求页响。
重要提示:
通常,Hook 命令只會(huì)執(zhí)行一次段誊。如果你要想多次執(zhí)行效果闰蚕,那就自己寫(xiě) shell 或者 可以執(zhí)行的程序,讓你的邏輯可以重復(fù)執(zhí)行连舍。
上面大致說(shuō)了一些?Container Lifecycle Hooks 的基本概念没陡,希望大家有所了解。如果還沒(méi)有看明白,還是自行百度下盼玄。那么我就舉一些“栗子”看看這東西怎么用贴彼,然后是不是非常簡(jiǎn)單的就能上手呢?(實(shí)際 so easy埃儿,一看就會(huì)用F髡獭)
PostStart?
真的不想寫(xiě)PostStart,基本用的超級(jí)少童番。 不過(guò)還是舉一個(gè)例精钮,因?yàn)榭梢宰鲆恍┲苓叚h(huán)境同步或者設(shè)置環(huán)境變量的小工作。 或者你期望啟動(dòng)的時(shí)候工作完全就是異步執(zhí)行剃斧,PostStart 不會(huì)讓你失望轨香。
通過(guò)上面的內(nèi)容我們可以看到,在容器啟動(dòng)的時(shí)候幼东,我將外部一個(gè)目錄里面的自定義的 index.html 去覆蓋 nginx 容器中默認(rèn)的 index.html臂容。 但是由于 PostStart 和?ENTRYPOINT 之間沒(méi)有保證誰(shuí)先會(huì)執(zhí)行,所以希望在 nginx 啟動(dòng)之前更換 index.html 的小小期望根蟹,怕是沒(méi)有辦法實(shí)現(xiàn)策橘。?
但是有一種場(chǎng)景,就是容器和和運(yùn)行內(nèi)容是分離的情況娜亿,就一定需要在?ENTRYPOINT 將外部的執(zhí)行代碼同步到容器內(nèi)執(zhí)行。這個(gè)是什么鬼場(chǎng)景蚌堵,沒(méi)有毛病买决。我們常見(jiàn)的是代碼和環(huán)境一起封裝到 docker image 里面,然后版本更迭是重新向 kubernetes 推送 delpoyment吼畏。而這個(gè)場(chǎng)景督赤,代碼和容器是分離的,容器啟動(dòng)前就從 http 或則其他文件服務(wù)器同步內(nèi)容文件下來(lái)運(yùn)容器內(nèi)的服務(wù)泻蚊。 更新這些文件躲舌,就可以完成版本更新。而不需要重新分發(fā)容器性雄,而只需要簡(jiǎn)單的重啟下容器就行没卸。 要知道在一個(gè) 300 以上 node 的 kubernetes 的集群里分發(fā)一個(gè)新的 docker image,尤其是生產(chǎn)集群秒旋,是一項(xiàng)非常有挑戰(zhàn)的工作约计。
那PostStart 既然不能幫我們完成上面的場(chǎng)景,怎么辦迁筛? 不慌煤蚌,老李來(lái)幫你。 我們?cè)诤竺婢蜁?huì)看到?Init Container。?
PreStop
我們還是繼續(xù)通過(guò)部署一個(gè) nginx 的 pod 尉桩。通過(guò)在 delpoyment.yaml 中添加 PreStop 字段筒占,告訴 Kubernetes 在未來(lái) Pod 消亡的時(shí)候,需要執(zhí)行文件中定義的 command蜘犁。 用過(guò)執(zhí)行這個(gè)命令翰苫,讓 nginx 執(zhí)行優(yōu)雅退出。
PreStop 可能是我覺(jué)得在整個(gè)生命周期中最有用沽瘦,實(shí)用場(chǎng)景最多革骨。 比如:
1. 關(guān)閉前等待某一個(gè)狀態(tài)完成;
2. 關(guān)閉前同步一些狀態(tài)(數(shù)據(jù))到其他的地方析恋;
3. 關(guān)閉前通知某一個(gè)系統(tǒng)或者更新一個(gè)狀態(tài)良哲;
..............
這里好多場(chǎng)景,我就不一一列舉助隧,請(qǐng)各位看官自行腦洞筑凫。 總得一說(shuō),PreStop 就是為了讓你在 Pod 在結(jié)束之前執(zhí)行一些列你想要完成的動(dòng)作并村,當(dāng)然再?gòu)?qiáng)調(diào)一下巍实,這些動(dòng)作都是同步執(zhí)行的。
重要提示:
這里需要考慮的一個(gè)問(wèn)題在容器異常終止或者崩潰的情況下哩牍,將沒(méi)有機(jī)會(huì)進(jìn)行PreStop操作棚潦。所以這里要考慮下異常處理得問(wèn)題。膝昆。丸边。 千萬(wàn)別忘記了。
Init Container
老李偷偷傳你一招秘籍荚孵,好好拿著妹窖。 保不齊那天就能救命。那我們一起來(lái)看看它的概念收叶。
Init Container 就是做初始化工作的容器骄呼。它可是同步的,同步的判没,同步的蜓萄,重要的事情我說(shuō)了3遍〕畏澹可以有一個(gè)或多個(gè)執(zhí)行命令或者動(dòng)作绕德,如果有多個(gè),這些 Init Container 按照定義的順序依次執(zhí)行摊阀,只有所有的InitContainer 執(zhí)行完后耻蛇,主容器才啟動(dòng)踪蹬。由于一個(gè)Pod里的存儲(chǔ)卷是共享的,所以 Init Container 里產(chǎn)生的數(shù)據(jù)可以被主容器使用到臣咖。
Init Container可以在多種K8S資源里被使用到如Deployment跃捣、DaemonSet, PetSet/StatefulSet、Job等夺蛇,但歸根結(jié)底都是在Pod啟動(dòng)時(shí)疚漆,在主容器啟動(dòng)前執(zhí)行,做初始化工作刁赦。
看到上面的這些東西內(nèi)心是不是一個(gè)大石頭落地了娶聘,總算有一個(gè)東西跟 PostStop 配對(duì)了。 不多說(shuō)甚脉,我們看看 deployment.yaml 怎么寫(xiě)丸升。
寫(xiě)到這里,還有一些重要的提示牺氨,一定要牢記:
1. 如果 Pod 的 Init 容器失敗狡耻,Kubernetes 會(huì)不斷地重啟該 Pod,直到 Init 容器成功為止猴凹。然而夷狰,如果 Pod 對(duì)應(yīng)的 restartPolicy 為 Never,它不會(huì)重新啟動(dòng)郊霎。
2. Init 容器支持應(yīng)用容器的全部字段和特性沼头,但不支持 Readiness Probe,因?yàn)樗鼈儽仨氃?Pod 就緒之前運(yùn)行完成书劝。
3. 如果為一個(gè) Pod 指定了多個(gè) Init 容器进倍,那些容器會(huì)按順序一次運(yùn)行一個(gè)。 每個(gè) Init 容器必須運(yùn)行成功庄撮,下一個(gè)才能夠運(yùn)行。
4. 因?yàn)?Init 容器可能會(huì)被重啟毙籽、重試或者重新執(zhí)行洞斯,所以 Init 容器的代碼應(yīng)該是冪等的。 特別地坑赡,被寫(xiě)到 EmptyDirs 中文件的代碼烙如,應(yīng)該對(duì)輸出文件可能已經(jīng)存在做好準(zhǔn)備。
5. 在 Pod 上使用 activeDeadlineSeconds毅否,在容器上使用 livenessProbe亚铁,這樣能夠避免 Init 容器一直失敗。 這就為 Init 容器活躍設(shè)置了一個(gè)期限螟加。
6. 在 Pod 中的每個(gè) app 和 Init 容器的名稱(chēng)必須唯一徘溢;與任何其它容器共享同一個(gè)名稱(chēng)吞琐,會(huì)在驗(yàn)證時(shí)拋出錯(cuò)誤。
7. 對(duì) Init 容器 spec 的修改然爆,被限制在容器 image 字段中站粟。 更改 Init 容器的 image 字段,等價(jià)于重啟該 Pod曾雕。