查看原文獲得更好閱讀體驗(yàn)。
剛開始接觸K8s的同學(xué)可能都會覺得有一定的學(xué)習(xí)難度,撲面而來的各種概念到底是什么。比如,如何提供一個(gè)服務(wù)給別人,我是應(yīng)該用Pod還是用Deployment來運(yùn)行我的應(yīng)用等,在接下來的文章中,希望能夠解答你的這些疑惑。
Kubernetes可以看做云原生時(shí)代的操作系統(tǒng),統(tǒng)一管理下層的基礎(chǔ)設(shè)施,如計(jì)算資源、網(wǎng)絡(luò)資源、存儲資源等等。將集群中存在的各種復(fù)雜關(guān)系抽象成各種API資源,以統(tǒng)一的方式暴露出各種接口,也便于未來的擴(kuò)展以及開發(fā)團(tuán)隊(duì)根據(jù)自己的需要定制。因此,我們可以看到在K8s中Docker僅僅是容器運(yùn)行時(shí)的一個(gè)實(shí)現(xiàn)而已,只要遵守它的約定,實(shí)際上是可以替換為適合的其他容器技術(shù)的?;谶@樣的設(shè)計(jì)思路,理清各種API對象的作用和關(guān)系就變得很重要了,只有理解了才能正確地使用K8s,接下來我們就通過一張關(guān)系圖一點(diǎn)點(diǎn)的來說明。
通過Pod終結(jié)單容器的蠻荒時(shí)代
在接觸K8s之前,大多人首先要接觸到的就是Docker。我們得到一個(gè)容器的鏡像之后,要把應(yīng)用運(yùn)行起來最簡單的方式就是
docker run的命令。然而在實(shí)際的生產(chǎn)環(huán)境中,很少僅靠一個(gè)單容器就能夠滿足。比如,一個(gè)Web前端的應(yīng)用,可能還得依賴后端的一個(gè)容器服務(wù);后端的容器可能需要數(shù)據(jù)庫服務(wù);后端的服務(wù)需要多副本等等場景。在這些假想的場景中,比較真實(shí)的需求就是這些容器應(yīng)用需要共享同一個(gè)網(wǎng)絡(luò)棧,同一個(gè)存儲卷等,還有它們的生命周期如何管理調(diào)度。這個(gè)時(shí)候,僅僅依靠容器無法解決這個(gè)問題,我們第一個(gè)選手Pod就閃亮登場了。
Pod 內(nèi)共享配置
有了Pod之后,同一個(gè)Pod內(nèi)的容器可以共享很多信息,也可能需要讀取同一份配置。比如Pod內(nèi)有兩個(gè)容器需要訪問同一個(gè)數(shù)據(jù)庫,那么我們可以把相關(guān)的配置信息寫到ConfigMap里。那如果還有一些比較敏感的信息的話,就需要放到Secret對象中,它其實(shí)是一個(gè)保存在 Etcd 里的鍵值對數(shù)據(jù)。這樣,你把 Credential 信息以 Secret 的方式存在 Etcd 里,Kubernetes 就會在你指定的 Pod(比如,Web 應(yīng)用的 Pod)啟動(dòng)時(shí),自動(dòng)把 Secret 里的數(shù)據(jù)以 Volume 的方式掛載到容器里。
任務(wù)和定時(shí)任務(wù)
有了Pod之后,事情就變得更清晰了。在集群內(nèi),我們可能會有多種形式的要求。比如,我們可能希望一個(gè)應(yīng)用每天固定時(shí)間運(yùn)行或者只允許運(yùn)行一次,可能希望某個(gè)應(yīng)用以守護(hù)進(jìn)程的方式運(yùn)行。在K8s里,自然也有方案來解決這些問題。
首先來看定時(shí)任務(wù)的需求,假設(shè)我的系統(tǒng)內(nèi)有一個(gè)全網(wǎng)信息排行榜展示,要求每天需要在凌晨0點(diǎn)的時(shí)候更新一次。這個(gè)需求在K8s里就可以用CronJob來搞定。而如果僅僅需要執(zhí)行一次的任務(wù),那就直接使用Job對象就可以了。
默默工作的DaemonSet
再接下來,可能需要以守護(hù)進(jìn)程的方式運(yùn)行一個(gè)應(yīng)用。比如,我想要在后臺進(jìn)行日志的收集。這個(gè)時(shí)候DaemonSet就派上了用場,它會保證在所有的目標(biāo)節(jié)點(diǎn)上運(yùn)行一個(gè)Pod的副本。在這期間,如果有新的Node加入到K8s集群中的話,它也會自動(dòng)完成調(diào)度,在新的機(jī)器上運(yùn)行一個(gè)Pod副本。因此,前面說的監(jiān)控、日志等任務(wù)很適合用DaemonSet的方式執(zhí)
Deployment管理Pod
說完DaemonSet,下一個(gè)重點(diǎn)Deployment來了。前面說過容器之間的關(guān)聯(lián)關(guān)系、共享資源等問題需要處理,從而引入了Pod。對于Pod,也是同樣的問題需要解決,只不過高了一個(gè)抽象層次罷了。因?yàn)槊媾RPod的生命周期管理、調(diào)度、多副本等問題需要解決,聰明的設(shè)計(jì)者引入了Deployment。它可以根據(jù)我們的需求(比如通過標(biāo)簽)將Pod調(diào)度到目標(biāo)機(jī)器上,調(diào)度完成之后,它還會繼續(xù)幫我們繼續(xù)監(jiān)控容器是否在正確運(yùn)行,一旦出現(xiàn)問題,會立刻告訴我們Pod的運(yùn)行不正常以及尋找可能的解決方案,比如目標(biāo)節(jié)點(diǎn)不可用的時(shí)候它可以快速地調(diào)度到別的機(jī)器上去。另外,如果需要對應(yīng)用擴(kuò)容提升響應(yīng)能力的時(shí)候,通過Deployment可以快速地進(jìn)行擴(kuò)展。
在實(shí)際的工作中,Deployment并不是直接控制著Pod的,中間實(shí)際上還有一個(gè)ReplicaSet,但是在這里為了簡化理解過程,可以先忽略。
提供容器服務(wù)
前面的內(nèi)容主要是圍繞著Pod自身的運(yùn)行調(diào)度管理,下面面臨的問題是解決如何將服務(wù)提供給第三方的問題。
- 對內(nèi)提供服務(wù)
首先要解決的是將服務(wù)提供給同一個(gè)集群內(nèi)的其他服務(wù)使用??赡軇?cè)腴T的同學(xué)會問為什么我們不能直接使用Pod的IP呢?原因是這樣,前面也說過Pod是會被管理調(diào)度的,可能被調(diào)度到不同的機(jī)器上,同時(shí)生命周期也可能會發(fā)生變化。這導(dǎo)致一個(gè)應(yīng)用的IP可能會隨時(shí)發(fā)生變化,那么直接使用Pod的IP自然是不合理的。
針對這個(gè)問題K8s提供了Service對象來解決。
但是,并不是說Service就有一個(gè)固定的IP。而且,它和Pod IP還有很不一樣的地方。Pod的網(wǎng)絡(luò)是K8s在物理機(jī)上建立了一層Overlay Network實(shí)現(xiàn)的,而且在網(wǎng)卡上能夠看到這個(gè)網(wǎng)絡(luò)的地址。但是Service是一個(gè)完全虛擬的網(wǎng)絡(luò)層,并不會存在于任何網(wǎng)絡(luò)設(shè)備上。它通過修改集群內(nèi)部的路由規(guī)則,僅對集群內(nèi)部有效。Deploment創(chuàng)建好應(yīng)用之后,再為它生成一個(gè)Service對象。接下來就可以通過Service的域名訪問到服務(wù),形式是
<Service Name>.<NameSpace>,比如你有為Deployment的應(yīng)用創(chuàng)建了一個(gè)名為portal的Service在默認(rèn)的命名空間,那么集群內(nèi)想要通過Http訪問這個(gè)應(yīng)用,就可以使用http://portal.default。這個(gè)域名僅在集群內(nèi)有效,因?yàn)槭莾?nèi)部的一個(gè)DNS負(fù)責(zé)解析。
- 對外提供服務(wù)
說完如何給內(nèi)部提供服務(wù)以后,剩下的就是如何給外部提供服務(wù)了。在K8s里把這個(gè)叫做Ingress,正如其名,它是集群的入口。比如我們的集群Web應(yīng)用想要讓用戶能夠訪問,那必然要在Ingress入口上增加一條解析記錄。這一點(diǎn),熟悉像Nginx的朋友應(yīng)該比較容易理解,事實(shí)上Nginx Ingress也是K8s生態(tài)中的一個(gè)成員。
關(guān)于Ingress的使用,在之前我曾寫過一篇使用Traefik作為Ingress的文章,我們可以通過Traefik實(shí)現(xiàn)為需要暴露的服務(wù)提供負(fù)載均衡、自動(dòng)簽發(fā)Https證書、限流等很多功能,如果有興趣可以點(diǎn)擊查看。