在企業(yè)實(shí)際在使用容器這類資源的時(shí)候,除了技術(shù)本身,要考慮的其他問題也會(huì)很多。企業(yè)管理的容器有千千萬萬,出于效率考慮,對(duì)于有特殊需求的容器如何進(jìn)行批量創(chuàng)建和管理呢,這就需要在統(tǒng)一管理平臺(tái)按照相應(yīng)的模板進(jìn)行創(chuàng)建與維護(hù)。
前言
容器作為一種新的資源類型,已經(jīng)應(yīng)用于各大公司,G行也不例外。容器的輕量化特性使得它能夠在故障發(fā)生時(shí)快速進(jìn)行重建,將對(duì)業(yè)務(wù)的影響降到最低,我們通常稱它為自愈。在Kube.NETes中,自動(dòng)重建的邏輯是什么?為什么我們經(jīng)常說到Pod自愈而不是容器自愈?如何判定Pod狀態(tài),Pod都有什么狀態(tài)?重啟策略都有什么,是什么推動(dòng)重啟?對(duì)于不同的重啟資源需求,G行如何實(shí)現(xiàn)?下文將一一講述。
圖1 Kubernetes架構(gòu)
一、容器自愈
談到容器自愈,為什么說是通過Pod自愈實(shí)現(xiàn)的呢?Pod是一組(一個(gè)或多個(gè))容器,這些容器共享Pod的存儲(chǔ)、網(wǎng)絡(luò)、以及運(yùn)行容器的聲明。且Pod是K8s(即Kubernetes)的最小部署管理計(jì)算單元。K8s是CNCF(Cloud Native Computing Foundation)的畢業(yè)項(xiàng)目之一,是目前市場(chǎng)使用最廣泛的自動(dòng)化部署、擴(kuò)展和管理成千上萬容器化應(yīng)用程序的開源系統(tǒng),也是G行采用的容器管理工具。Pod運(yùn)行在被稱作是Node的節(jié)點(diǎn)中,節(jié)點(diǎn)既可以是物理機(jī),也可以是虛擬機(jī)。當(dāng)K8s判定Pod出現(xiàn)問題且需要被殺死的時(shí)候,會(huì)在運(yùn)轉(zhuǎn)正常且資源充足的Node節(jié)點(diǎn)重建完成,從而完成自愈。
圖2 CNCF的畢業(yè)項(xiàng)目和孵化中的項(xiàng)目
二、Pod狀態(tài)判定
應(yīng)用程序可能因多種原因而變得不可靠,例如:暫時(shí)失聯(lián)、配置錯(cuò)誤、應(yīng)用程序錯(cuò)誤、硬件故障、資源緊張等。那么如何檢測(cè)運(yùn)行著應(yīng)用程序的pod健康狀態(tài)是好還是壞呢,K8s是通過探針來檢查的,探針有三種:ReadinessProbe、StartupProbe及LivenessProbe。
ReadinessProbe(就緒)探針:用于判斷容器服務(wù)是否可用(即是否為ready狀態(tài)),只有Ready狀態(tài)的Pod才可以接收服務(wù)請(qǐng)求。
StartupProbe(啟動(dòng))探針:適用于應(yīng)用程序啟動(dòng)緩慢、網(wǎng)絡(luò)訪問延遲等場(chǎng)景,相應(yīng)造成容器啟動(dòng)緩慢的情況。
LivenessProbe(存活)探針:用于判斷容器是否存活①(即是否為running狀態(tài)),如果探針檢測(cè)到容器不健康,則Kubelet將殺死這個(gè)容器,并根據(jù)容器的重啟策略(重啟策略后面會(huì)說)做相應(yīng)的處理。
G行通過同時(shí)使用ReadinessProbe(就緒)探針和LivenessProbe(存活)探針來判定Pod的狀態(tài)。
K8s提供了5個(gè)控制這些探針的選項(xiàng):
圖片
而這三種探針均可以通過以下三種方式②實(shí)現(xiàn):
01HTTPGetAction
通過容器的IP地址、端口號(hào)及路徑調(diào)用http get方法,如果相應(yīng)的狀態(tài)碼大于等于200且小于400,則認(rèn)定容器是健康狀態(tài)。
02TCPSocketAction
通過容器的IP地址和端口號(hào)執(zhí)行tcp檢查,如果能夠建立tcp連接,則表明容器健康。
03ExecAction
在容器內(nèi)部運(yùn)行一個(gè)命令,如果返回碼為0,則表示容器健康,否則表示不健康。
G行使用HTTPGetAction和TCPSocketAction兩種方式查看連接情況。
運(yùn)維人員可以使用Kubectl命令行客戶端或Yaml部署模板來配置探針和IP地址、端口號(hào)、路徑連接方式。下面是以LivenessProbe探針、HTTPGetAction連接方式為例,編寫的Yaml文件:
圖片
03Pod重啟策略
知道如何檢查Pod的健康狀態(tài)了,如果不健康,該如何進(jìn)行重啟呢?這里就需要說到Pod的重啟策略(restartPolicy)了。
重啟策略應(yīng)用于Pod內(nèi)的所有容器,由Pod所處的Node上的Kubelet進(jìn)行判斷和重啟操作。當(dāng)某個(gè)容器異常退出或者健康檢查失敗時(shí),Kubelet會(huì)根據(jù)重啟策略的設(shè)置進(jìn)行相應(yīng)的操作。
Pod的重啟策略有Always、OnFAIlure、Never三種,Default為Always。
Always:當(dāng)容器失效時(shí),由Kubelet自動(dòng)重啟該容器
OnFailure:當(dāng)容器終止運(yùn)行且退出碼不為0時(shí),由Kubelet自動(dòng)重啟該容器。
Never:不論容器運(yùn)行狀態(tài)如何,Kubelet都不會(huì)重啟該容器。
圖3 示例Pod在G行Prometheus的可視化平臺(tái)Grafana上的重啟情況,紅線為閾值
Pod如何重啟
重啟策略明確了,那么是靠誰來完成重啟的呢?是靠控制器來實(shí)現(xiàn)的。
在講述控制器之前,先考慮這樣一個(gè)場(chǎng)景:Pod沒有副本,如果這個(gè)Pod出現(xiàn)問題,上面跑的容器應(yīng)用也就無法運(yùn)轉(zhuǎn)了,服務(wù)也就中斷了。所以在K8s中,在創(chuàng)Pod之初,就是以多副本的形式創(chuàng)建的。這種負(fù)責(zé)Pod副本的創(chuàng)建、Pod重啟、調(diào)度及全生命周期自動(dòng)控制的組件叫做控制器(也叫工作負(fù)載,即workload)。
控制器根據(jù)不同的功能分為Replicaset與Deployment、 DaemonSet、StatefulSet、Job和Cronjob幾種。這幾種控制器G行都有使用,并且在G行自建的容器統(tǒng)一管理平臺(tái)進(jìn)行了部署。這個(gè)平臺(tái)融合管理了云上所有的應(yīng)用相關(guān)容器資源,提高了運(yùn)維效率。
圖4 G行容器統(tǒng)一管理平臺(tái)登錄首頁
Replicaset與Deployment
圖片
圖5 Deployment控制器下兩種Pod滾動(dòng)升級(jí)方式,分別是Recreate和Rolling update,各適用于大版本和小版本
ReplicaSet(簡(jiǎn)稱rs)是Replication Controller的升級(jí)版,是副本集的意思,用于保證K8s集群中有指定數(shù)量的Pods副本在運(yùn)行③。一般不單獨(dú)使用,而是作為Deployment理想狀態(tài)的參數(shù)使用。且擁有集合式的標(biāo)簽選擇器,可以選擇多個(gè)標(biāo)簽。
Deployment被稱作是副本控制集(即rs)的控制器,通過為應(yīng)用程序創(chuàng)建一個(gè)或多個(gè)rs以管理應(yīng)用程序的多個(gè)版本。通過每個(gè)副本控制集(rs)的模板和副本保持的能力來管理無狀態(tài)應(yīng)用的工作負(fù)載(應(yīng)用程序集群),比如web集群。
DaemonSet
圖6 展示了DaemonSet這種類型的控制器的Pods部署
DaemonSet是特殊類型的Deployment,在集群中的全部或者部分節(jié)點(diǎn)上,每個(gè)節(jié)點(diǎn)上有且僅有一份Pod的副本資源在運(yùn)行。比如系統(tǒng)的監(jiān)控、日志的收集、分布式存儲(chǔ)、網(wǎng)絡(luò)的代理等,都需要每個(gè)成員節(jié)點(diǎn)上有且只有一個(gè)Pod。例如:每個(gè)Node上只需要運(yùn)行一個(gè)日志采集程序Logstach,或者只運(yùn)行一個(gè)性能監(jiān)控程序Prometheus Node Exporter。
而且后續(xù)加入集群的新的節(jié)點(diǎn)也會(huì)自動(dòng)創(chuàng)建一個(gè)相同的Pod對(duì)象。管理員也可以使用Nodeselector(節(jié)點(diǎn)選擇器)配合節(jié)點(diǎn)Label指定僅在部分具有特定特征的節(jié)點(diǎn)上運(yùn)行指定的Pod對(duì)象。
StatefulSet
圖7 有狀態(tài)集在一個(gè)Pod故障被殺死,并重建新Pod的情況
用來管理有狀態(tài)應(yīng)用的工作負(fù)載(應(yīng)用程序集群),管理Pods集合的部署和伸縮,并為這些Pods提供持久存儲(chǔ)和持久標(biāo)識(shí)符。與Depolyment類似,Statefulset管理基于相同容器模板的一組Pods。但與Depolyment不同的是,Statefulset為每個(gè)Pod維護(hù)了一個(gè)有粘性的ID。這些Pods是基于相同的模板,但是不能相互替換,即無論怎么調(diào)度,每個(gè)Pod都有一個(gè)永久不變的ID。
Job和Cronjob
圖8 Job和Cronjob在完成工作后回收算力的場(chǎng)景展示
Job是用來定義并啟動(dòng)一個(gè)批處理任務(wù),是單次性作業(yè)控制器。這任務(wù)通常并行或者串行啟動(dòng)多個(gè)計(jì)算進(jìn)程去處理一批工作項(xiàng),即work item,工作項(xiàng)處理完成后,整個(gè)批處理任務(wù)結(jié)束。比如Hadoop的離線數(shù)據(jù)處理、視頻解碼、或者HPC業(yè)務(wù)等,都需要很多個(gè)節(jié)點(diǎn)提供一個(gè)集中式的大算力。與傳統(tǒng)算力相比,Job控制器能在保留結(jié)果的同時(shí),快速回收算力,因?yàn)橐粋€(gè)Job完成后,就會(huì)立即殺死使用的Pod。
Cronjob控制器用于周期性調(diào)度Job控制器。傳統(tǒng)環(huán)境使用的一般用到的是7*24小時(shí)不間斷的備份服務(wù)器,現(xiàn)在可以使用Cronjob控制器周期性的起一個(gè)Job作業(yè),做完后,保留結(jié)果,殺死Pod,回收算力。Cronjob基本照搬了linux操作系統(tǒng)的周期性任務(wù)Crontab,用minutes、hours、dayofmonth、month、dayofweek來進(jìn)行定義。
需要注意,一些控制器對(duì)Pod的重啟策略要求,G行也是按如下執(zhí)行的:
Replicaset或者DaemonSet:必須設(shè)置為always,需要保證該容器持續(xù)運(yùn)行。
Job:onFailure或Never,確保容器執(zhí)行完成后不再重啟。
特定場(chǎng)景的Pod重啟
Pod對(duì)Node有特定要求的場(chǎng)景:
Node節(jié)點(diǎn)的配置是多種類型的,比如有的安裝了SSD磁盤,有的沒有。有的是AMD64的,有的是AMD32的。如果想著把新建的Pod運(yùn)行在AMD64的Node上,該怎么做呢。下面是Pod的Yaml配置:
圖片
Pod之間相互依存或者互斥的場(chǎng)景:
現(xiàn)在想新建這么兩個(gè)Pod,有互相依存關(guān)系,需要放到同一個(gè)Region內(nèi),該如何實(shí)現(xiàn)呢。像這種互為親和或者互斥關(guān)系的Pod是通過在Yaml文件中增加TopologyKey屬性,來聲明目標(biāo)拓?fù)鋬?nèi)的Pod是否在一起的。下面是一個(gè)親和的例子,目標(biāo)Pod的標(biāo)簽值是App:”Nginx”。親和Pod的標(biāo)簽值也是一樣。增加的TopologyKey值為topology.kubernetes.io/region,意味著這兩個(gè)Pod給配置到了同一個(gè)Region下。
圖片
這些相互依賴或者相互之間頻繁調(diào)用的Pod,需要盡可能的部署在同一個(gè)Zone、機(jī)房、機(jī)架、Node節(jié)點(diǎn)。反之,就需要讓這些Pod盡可能的互相遠(yuǎn)離。簡(jiǎn)而言之,就是Pod之間在同一個(gè)拓?fù)溆蛑泄泊婊蚧コ狻M負(fù)溆蛑赣上嗤乩砜臻g中的幾個(gè)Node節(jié)點(diǎn)組成。
一些常規(guī)的拓?fù)溆蛴校?/p>
kubernetes.io/hostname;topology.kubernetes.io/region;topology.kubernetes.io/zone。
四、總結(jié)
在企業(yè)實(shí)際在使用容器這類資源的時(shí)候,除了技術(shù)本身,要考慮的其他問題也會(huì)很多。企業(yè)管理的容器有千千萬萬,出于效率考慮,對(duì)于有特殊需求的容器如何進(jìn)行批量創(chuàng)建和管理呢,這就需要在統(tǒng)一管理平臺(tái)按照相應(yīng)的模板進(jìn)行創(chuàng)建與維護(hù)。在Pod進(jìn)行重建后,如何保證客戶端應(yīng)用屏蔽這些Pod IP地址的變化及數(shù)量的變化呢,這就需要通過定義Pod上層的Service進(jìn)行保障。不同的應(yīng)用系統(tǒng)有自身的架構(gòu)特點(diǎn),在進(jìn)行控制器配置的時(shí)候如何進(jìn)行考量呢,需要選擇哪種控制器呢,這就需要進(jìn)行交付前的資源配置梳理。等等問題。總之,在考慮到基本的技術(shù)特性的同時(shí),再應(yīng)用系統(tǒng)自身特點(diǎn)相結(jié)合,才能夠?qū)⒓夹g(shù)的最大價(jià)值加以發(fā)揮。
五、附錄
① 容器的狀態(tài)一共有五個(gè):created(已創(chuàng)建)、running(運(yùn)行中)、paused(暫停)、exited(停止)、dead(死亡)、restarted(重啟中)、removing(遷移中)。②每種探測(cè)方式,還需要額外設(shè)置initialDelaySeconds和timeoutSeconds這兩個(gè)參數(shù)。initialDelaySeconds表示容器啟動(dòng)后進(jìn)行首次健康檢查的等待時(shí)間,單位是秒。timeoutSeconds表示健康檢查請(qǐng)求發(fā)送后等待響應(yīng)的超時(shí)時(shí)間,單位是秒。如果超時(shí),則Kubelet認(rèn)為容器無法提供服務(wù),會(huì)重啟該容器。③例如:Pod所在節(jié)點(diǎn)發(fā)生宕機(jī),K8s就會(huì)第一時(shí)間觀察到這個(gè)故障,并自動(dòng)創(chuàng)建一個(gè)新Pod對(duì)象,將其調(diào)度到其他合適的節(jié)點(diǎn)上,K8s會(huì)實(shí)時(shí)監(jiān)控集群中目標(biāo)Pod的副本數(shù)量,并盡力與Deployment中聲明的Replicas數(shù)量保持一致。