上周,抽空聽了兩場雲栖大會(一場關于k8s 雲原生的發展趨勢,一場是關于k8s安全的話題), 這幾年, 容器領域的技術,發展太快了,工具層出不窮 , 也越來越容易使用了, 逐漸替代了一些硬體的功能; 傳統的運維正面臨前所未有的挑戰, 如果不了解這個領域的技術,未來可想而知
451 研究說未來 76%的企業将上雲, 你覺得呢 ?
Auther: Makr.wei
當年Twitter飛速增長期,伺服器的擴容常常跟不上使用者的爆炸式增長,是以經常會出現當機,這時候會出現一隻小鳥拉着鲸魚的圖檔, 就是著名的失敗之鲸, 08/09年的Twitter,那絕對是Twitter的一段暗黑曆史,可愛的失敗鲸是以得以頻頻出鏡。它不僅僅是當機的辨別,也象征了Twitter的程式猿在修複程式之時,這些粉絲真誠的相信Twitter會越來越好。
Twitter 急需一個資源管理系統來幫助他們擺脫可怕的“失敗之鲸”,09年,參考Google Borg的開源工具Mesos,進入了Twitter的視野,被用于解決當機問題。同時,幾位大牛開始遊說Google全球資料中心網絡的負責人開放Borg,對于“是否要把營運Goolgle的秘密武器作為開源技術拱手讓人?”這個問題讓Google管理層十分糾結,一直猶豫不決, 但最終,谷歌準許發起開源項目Kubernetes(開源版的Borg),把這一重要工具貢獻給世界,從此,Kubernetes和Docker成為黃金搭檔,迅速傳播開來。正是因為k8s社群的高度活躍,以及功能快速疊代, 在2019年5月,Twitter宣布放棄已經管理300000台主機,使用了十年之久的Mesos, 轉投Kubernetes社群。
基于Cloud-native的Kubernetes是當今最流行的應用架構,相關技術通過子產品化和複雜度轉移,來減輕研發的痛苦。
從頭開始建立微服務,或者把大系統拆解成微服務,可參考很多重要的理論文章和實踐經驗。這些實踐都基于 Eric Evans 的 Domain-Drive Design, 和有界上下文(Bounded Context)和聚合的理論文章。 Bounded Context 為我們把大模型分解為各種小元件提供依據, 通過聚合有界内容組成子產品,并定義事務邊界。無論是否是分布式微服務系統,都圍繞組織架構,體系結構和運作時狀态提供解決方案,除了領域模型之外,還有一系列複雜的問題需要我們考慮。
Docker和Kubernetes,為我們提供了很多新的元素和抽象工具解決分布式系統的問題,為分布式系統帶來了大量的便利,不過,遵循Garbage in ,Garbage out 定律,我們放進容器的是垃圾,那麼,我們也将得到一個超級垃圾體。
代碼層面: 每一個參數,方法,執行個體化對象都在将來的長期運維中起到非常重要的影響,無論使用什麼容器工具,開發團隊傳遞物都非常重要。程式員要盡可能的寫出邏輯清晰的代碼,完成足夠的自動化測試,持續重構提升代碼品質,這是一個具備工匠精神的程式員的本分。
領域設計: 領域驅動的設計是讓軟體實作盡可能貼近真實世界的一種思想。這個技術是OOP理論的延展,準确的業務模型,清晰的事務邊界,易用的接口和豐富的API,是容器自動化的基礎。
微服務架構, 為設計持續變化的分布式系統,提供了有價值的規範和實踐指導,這些原則可以幫助實作正常的系統需求,比如動态擴充、彈性伸縮和快速跌代
容器可以幫助我們快速的搭建一套标準的分布式系統, 子產品建構和容器複用是實作Cloud-native 應用的一個先決條件,當容器數量不斷增長,維護系統正常運作,管理各種資源就需要一個強大的編排工具。
Cloud native用于描述自動化,內建化,微系統化,子產品化和工具化的大規模服務平台。
Distributed Primitives
JVM在本地作業系統程序中編譯運作,而Kubernetes提供分布式系統的基本單元和運作工具,可分布運作在多個Node的程序中,增加了一種高維視角。
我們仍然需要使用OOP建構系統的元件,但是我們可用Kubernetes的基本元素完成一些系統間的行為
基本元素和分布式基本元素有些共性,但不能直接替換,他們工作在不同系統的層面,比如,容器化,我們仍然需要使用Java編寫程式,但我們可以用 K8s 中的Cron Job 來替代Java中的ExecutorService的實作
Container
容器是Cloud-native的一個基本建構單元,相對于OOP,容器的Image可以類比Class,而Container則類似于Object,我們可以繼承Class,同樣的的Image也可以繼承其它的Image; Object可以組裝功能,同理可以将互相協作的Container放入Pod來進行容器組合。k8s可以像Jvm一樣實作多主機互動,并負責資源的運作和管理。Container初始化像Java的構造函數, DaemonSets 類似于java背景線程(Daemon thread)。Pod有時類似于IOC架構,當多個容器共享生命周期,可以直接互相通路。
Container是單個業務的功能單元
Image屬于團隊,擁有自己的釋出周期
Image有自己的運作時依賴和資源需求
Image是自包含,自定義的,承載運作時依賴
Image是不可變的,一旦建立,不可更改,且已完成配置
Image已完成運作時依賴和資源需求
Image擁有明确的定義的Api來暴露功能
Container是一次性的,随時可以安全的擴容和縮減
除了上面這些特點之外,Image業務子產品建立是基于參數化的,以便于在不同環境複用。并且Image也必須實作參數化以适應各種應用場景。 原子化,子產品化, 可複用化的Image,類似于程式設計語言中的類庫
Pods
容器的Image提供單一功能單元,屬于單個團隊,具有獨立的釋出周期,并提供部署和運作時資源隔離。 大多數情況下,一個微服務對應于一個Image。
但是,Cloud-Native 提供了一個新元素來管理Kubernetes中一組容器的生命周期,它被稱為Pod。 Pod是一組容器排程,部署和運作的原子單元。 一個Pod裡的所有容器始終安排在同一Node上,無論是擴充還是Node遷移,都可以一起部署,還可以共享檔案系統,網絡和NameSpace。 這個聯合體生命周期内允許Pod中的容器通過檔案系統互相互動或者網絡互動。
在開發和建立期,微服務對應于一個開發團隊的鏡像,在運作時,微服務呈現為一個Pod,Pod是部署,編排,伸縮的機關,無論是擴充還是遷移,運作容器的唯一方法是通過Pod。
Pod的一些特性
Pod是排程任務的最小單元。 排程程式會嘗試尋找滿足Pod裡的容器資源的Node。如果建立包含多個Container的Pod,排程程式會尋找滿足所有container資源的Node。
托管容器, 同一Pod中的容器有獨特互動方式,最常見的通信方式包括使用共享本地檔案系統交換資料,網絡層互動,或使用主機程序間通信(IPC)機制進行高性能互動
Pod具有屬于它的所有容器共享的IP位址,名稱和端口範圍。必須仔細配置同一Pod中的容器以避免端口沖突,就像同一主機上并行的Unix程序一樣。
Pod是我們的應用程式在Kubernetes系統的原子單元,不能直接通路,那麼,我們該如何通路Pod的應用呢? 答案是Service
Service
Pods 可以随時啟動或者銷毀,比如在對Pod擴容或者縮減,健康心跳失敗,或者遷移Node時。Pod的IP的位址隻有在Pod啟動後才會生效,假設目前運作的Node出問題,Pod也可能會自動遷移到其它Node。這就意味着Pod的生命周期中IP位址可能會發生變化,如果這時另外一個Pod裡的資源,通過IP位址通路被遷移的POD裡的資源,就會發生異常,而Service就可以作為隔離層隔離這種差異,實作自動的服務發現。
這就是Kubernetes Services發揮作用的地方。Service是一個簡單但功能強大的Kubernetes抽象層,它将Service名稱永久綁定到IP位址和端口上。 是以,Service代表通路應用程式的入口點。在最常見的場景中,Service充當一組Pod的入口點,但情況可能并非總是如此。 Service是一個通用元素,它也可能指向Kubernetes叢集外部的資源。是以,Service可用于服務發現和負載平衡,允許在不影響服務使用者的情況下改變Pod。
Label
通過上面的内容,我們可以知道微服務在建立時時一個容器,運作時是一個Pod,那麼,是麼是由多個微服務組成的應用系統呢 ? 在這裡,kubernetes提供了另外兩個元素,可以幫助我們定義應用程式的概念:标簽和名稱空間。
在微服務出現之前,應用程式是具有版本控制和釋出周期的單體部署單元。應用程式的采用.war,.ear或其打包格式。 但随後,應用程式被拆分為微服務,這些微服務是獨立開發,釋出,運作,設定或擴充的。 使用微服務,應用程式的概念會減少,并且我們不再需要在應用程式級别執行關鍵建構工作。但是,如果您仍需要一種方法來制定某些獨立服務屬于某個應用程式,則可以使用标簽。讓我們假設我們将一個單體應用程式拆分為三個微服務,将另一個應用程式拆分為兩個微服務。
現在,我們有五個Pod定義(可能還有更多的Pod執行個體),這些定義獨立于開發和運作時的觀點。但是,我們可能仍需要指出前三個Pod代表一個應用程式,而另外兩個Pod代表另一個應用程式。甚至Pod也可以是獨立的,提供一個業務功能,但他們可能互相依賴。 例如,一個Pod負責前端的容器,另外兩個Pod負責提供後端接口。如果這些Pod中的任何停機,那麼從業務角度來看,應用程式是不可用的。使用标簽選擇器給我們能夠查詢和識别一組Pod,并作為一個邏輯單元進行管理 ,下圖顯示了如何使用标簽對分布式應用程式的各個部分進行分組 。
ReplicaSet使用Label來保持特定Pod的某些執行個體運作。 這意味着每個Pod定義都需要具有用于排程的唯一标簽組合
排程程式也大量使用标簽。 排程程式使用标簽來共置或傳播Pod,以将Pod放置在滿足Pods要求的節點上
Label可以訓示一組Pod的邏輯分組,并為它們提供應用程式辨別。
除了前面的典型用例之外,标簽還可用于存儲中繼資料。很難準确的說标簽可以用于什麼,但最好有足夠的标簽描述Pod的所有重要特征。 例如,使用标簽來指明應用程式的邏輯分組,業務特征和關鍵性,指定的運作時依賴等等
排程程式可以使用這些标簽進行更細粒度的排程,或者可以通過指令行使用相同的标簽來大規模地管理比對的Pod。 但是,我們不應該過分誇大并提前添加太多标簽。我們應該根據實際需要建立标簽。删除标簽風險很大,因為沒有很明确的方法可以表明标簽的用途,此類操作可能導緻的不可預知的影響
對于金融系統,監管對機房内伺服器劃分,通路權限,安全級别有嚴格的指導。通過标簽定義,我們可以配置設定哪些Pod在指定節點(或者說指定的實體機,機櫃)上建立。
Annotations
Annotation與Label類似,也使用key/value鍵值對的形式進行定義。Label具有嚴格的命名規則,它定義的是Kubernetes對象的中繼資料(Metadata),并且用于Label Selector。Annotation則是使用者任意定義的“附加”資訊,以便于外部工具進行查找。
用Annotation來記錄的資訊包括:
build資訊、release資訊、Docker鏡像資訊等,例如時間戳、release id号、PR号、鏡像hash值、docker registry位址等;
日志庫、監控庫、分析庫等資源庫的位址資訊;
程式調試工具資訊,例如工具名稱、版本号等;
團隊的聯系資訊,例如電話号碼、負責人名稱、網址等
Namespaces
Namespaces是Kubernetes系統中的另一個非常重要的概念,通過将系統内部的對象“配置設定”到不同的Namespace中,形成邏輯上分組的不同項目、小組或使用者組,當不同的分組在整個叢集中使用共享資源的同時,還能被分别管理。
Kubernetes叢集在啟動後,會建立一個名為“default”的Namespace,通過Kubectl可以檢視到。
Namespaces用于k8s的資源管理
Namespaces為Container,Pod,Service或ReplicaSet等資源提供工作範圍。資源名稱在Namespaces中必須是唯一的,而且不能跨越它們。
預設情況下,Namespaces為資源提供界限,但不會隔離這些資源,阻止一個資源通路另一個資源。例如,隻要Pod IP位址已知,開發Namespaces中的Pod就可以從生産Namespaces通路另一個Pod。但是,如果需要,還有Kubernetes插件可提供網絡隔離,以實作跨Namespaces的真正多租戶模式。
每個k8s服務必須有Namespaces,有相應的DNS位址,Namespaces的命名格式如. .svc.cluster.local。所有k8s服務的URI都含有NameSpaces的名稱位址。這個類似java 裡class的package name, 作用也非常類似。
用ResourceQuotas限制Namespaces的資源。叢集管理者可以使用ResourceQuotas控制Namespaces中建立的各種對象個數。 例如,開發人員可以限定Namespaces隻允許5個ConfigMaps,5個Secrets,5個Services,5個ReplicaSet,5個PersistentVolumeClaims和10個Pod
用ResourceQuotas指定NameSpaces 資源配比。 例如,在容量為32 GB RAM和16個核心的群集中,可以為生産環境NameSpaces配置設定一半資源(16 GB RAM和8個核心),然後配置設定8 GB RAM和4個核心,4 GB RAM和2個核心用于開發,測試的NameSpaces。
PersistentVolume & PersistentVolumeClaims
PersistentVolume(PV)在叢集中是由管理者配置的網絡存儲。 PV是容量插件,如Volumes,其生命周期獨立于使用PV的任何單個pod。PV在k8s叢集中的作用類似我們經常在Linux伺服器上挂載的NFS,可以很友善的做mount 映射
PersistentVolumeClaim(PVC)是由使用者進行存儲的請求。 它類似于pod。 Pod可以請求特定級别的資源(CPU和記憶體)。 指定可用資源的大小和通路模式(例如,可以一次讀/寫或多次隻讀)。
雖然PersistentVolumeClaims允許使用者端使用抽象存儲資源,對于不同的問題,使用者端通常需要使用不同資源(例如性能)。叢集管理者要能夠提供各種PersistentVolumes不同的方式,不僅僅是大小和通路模式,但使用者端不需要了解這些資料卷實作的細節。
PV是群集中的資源。PVC是對這些資源的請求,并且還充當對資源可用性檢查。PV和PVC之間的互相作用遵循以下生命周期:Provisioning ——-> Binding ——–>Using——>Releasing——>Recycling
準備Provisioning---通過叢集外的存儲系統或者雲平台來提供存儲持久化支援。
靜态提供Static:叢集管理者建立多個PV。 它們攜帶可供叢集使用者使用的真實存儲的詳細資訊。
動态提供Dynamic:當管理者建立的靜态PV都不比對使用者的PersistentVolumeClaim時,叢集可能會嘗試為PVC動态配置卷。 此配置基于StorageClasses:PVC必須請求一個類,并且管理者必須已建立并配置該類才能進行動态配置。
綁定Binding---使用者建立pvc并指定需要的資源和通路模式。在找到可用pv之前,pvc會保持未綁定狀态。
使用Using---使用者可在pod中像volume一樣使用pvc。
釋放Releasing---使用者删除pvc來回收存儲資源,pv将變成“released”狀态。由于還保留着之前的資料,這些資料需要根據不同的政策來處理,否則這些存儲資源無法被其他pvc使用。
回收Recycling---pv可以設定三種回收政策:保留(Retain),回收(Recycle)和删除(Delete)。
保留政策:允許人工處理保留的資料。
删除政策:将删除pv和外部關聯的存儲資源,需要插件支援。
回收政策:将執行清除操作,之後可以被新的pvc使用,需要插件支援。
下面這張圖是k8s元素互相關系的直覺展現
K8S是一個完備的分布式系統支撐平台,具有完備的叢集管理能力,多層次的安全防護和準入機制、多租戶應用支撐能力、透明的服務注冊和發現機制、內建智能負載均衡器、強大的故障發現和自我修複能力、服務滾動更新和線上擴容能力、可擴充的資源自動排程機制以及多粒度的資源配額管理能力。同時Kubernetes提供完善的管理工具,涵蓋了包括開發、部署測試、運維監控在内的各個環節, 減輕了以往運維手工操作的痛苦,為DevOps思想提供了強力的支撐。