K8S學習心得:Kubernetes的基本架構和概念

相信很多朋友在學習K8S的時候,能夠藉助yaml文檔把自己的應用部署到K8S集羣上,但是對於K8S內部的技術細節和實現原理並不瞭解,而這恰恰正是我們作爲開發者提升技術所欠缺的東西。那麼今天我們就來簡單總結一下K8S的基本架構和其中的各個組件的概念和原理。

在開始正式介紹K8S之前,我們首先要搞明白一個問題:K8S是用來幹什麼的?

一、 Kubernetes概況

首先,熟悉網購的朋友可能都知道,每年的雙十一期間,會有無數的訂單傳往淘寶,這對阿里的服務器系統的運算能力和承受能力是一個巨大的挑戰。毫無疑問,哪怕是一臺超級計算機,面對PB級別的訪問業務也會應接不暇。

雲計算技術的日益成熟彌補了企業計算能力的不足。所謂雲計算,就是把許多臺單獨的計算機的計算能力集中起來,承擔起單臺計算機無法承受的業務。實現雲計算,我們不得不提到“虛擬化”技術,也就是使不同系統不同配置的機器能夠提供相同環境的技術。

(歡迎移步我的另一片關於雲計算虛擬化的入門介紹博客)

容器技術則是輕量化的虛擬技術。它不需要虛擬出整個操作系統,只需要虛擬一個小規模的環境。2013年3月,dotCloud公司的創始人之一,28歲的Solomon Hykes正式決定,將Docker項目開源,這使得Docker容器引擎受到了廣大企業和開發者青睞。
在這裏插入圖片描述
平常使用過Docker等容器技術的朋友可能會有這樣的感受:當我們把應用部署在一個或幾個容器之中的時候,我們需要完成拉取鏡像、啓動容器、解決不同容器的通信問題、終止容器進程等等一系列操作,實在是非常不方便。也就是說,怎麼實現多臺計算機之間的業務調度和資源管理,是我們必須要解決的問題。

那麼,有沒有一款容器集羣管理工具,能夠幫我解決這些底層的東西,讓我專注於業務本身呢?

答案當然是有的,那就是我們今天的主角——Kubernetes,又被稱作K8S

在這裏插入圖片描述
Kubernetes源於希臘語,有“舵”或“飛行員”的意思。而K8S,則是由Kubernetes中間的八個字母縮寫爲數字8得來的。Google採用這個名字的深意就是:docker把自己定位成馱着集裝箱在大海上遨遊的鯨魚,那麼Kubernetes就是鯨魚的掌舵者,鯨魚必須按照其設定的路線巡遊。從上邊圖片中的土撥鼠也能看得出來,Kubernetes是使用GO語言開發的。

Kubernetes是一個完備的分佈式系統支撐平臺,具有完備的集羣管理能力,多擴多層次的安全防護和准入機制、多租戶應用支撐能力、透明的服務註冊和發現機制、內建智能負載均衡器、強大的故障發現和自我修復能力、服務滾動升級和在線擴容能力、可擴展的資源自動調度機制以及多粒度的資源配額管理能力。同時Kubernetes提供完善的管理工具,涵蓋了包括開發、部署測試、運維監控在內的各個環節。

從K8S的定義中我們可以知道,所謂Kubernetes的核心特點,就是能夠自主地管理容器保證雲平臺中的容器按照用戶的期望狀態運行(比如用戶想讓apache一直運行,用戶不需要關心怎麼去做,Kubernetes會自動去監控,然後去重啓,新建,總之,讓apache一直提供服務),同時,Kubernetes也提升了工具以及人性化方面,讓用戶能夠方便的部署自己的應用。

總而言之,Kubernetes的出現使得用戶能夠輕鬆地把自己的應用或業務部署在一個K8S集羣上,而避免了處理集羣中可能存在的調度、監控、運維、網絡等等諸多問題。那麼K8S究竟是怎麼做到這些的呢?接下來我們就來正式學習一下K8S集羣的基本架構。

二、 Kubernetes基本架構

在正式介紹K8S的架構之前,請各位讀者先思考一個問題:如果讓你來設計一個由50臺計算機組成的集羣,這個集羣可以提供一臺超級計算機的計算能力和負載能力,那麼你會怎麼設計?

首先,我們總得有幾臺機器是專門負責對整個集羣進行管理和調度的吧?如果這幾臺計算機能夠提供對外界的接口,那就太好不過了。在實際的K8S集羣中,我們往往會分出幾臺計算機專門處理這樣的管理事務,它們被稱作“Master”節點。

其次,我們必須要有大量的機器負責處理實際的業務。因此,我們就可以把剩下的機器都用於提供計算能力,它們應當具有正常的運算能力和通信能力。實際中這些節點被稱作“Node”節點,而在每一個Node節點中,都運行着若干Pod進程,處理實際的業務。
在這裏插入圖片描述

1. Master

K8S集羣的管理節點,負責管理集羣,提供集羣的資源數據訪問入口。它擁有etcd進行存儲,運行Api Server進程,Controller Manager服務進程及Scheduler服務進程,關聯工作節點Node。
  
在這裏插入圖片描述

  • kube-apiserver:是整個k8s集羣對外提供服務的唯一接口,它提供請求過濾、訪問控制等機制,是各組件的協調者,此API是聲明式的(簡單說就是用戶想要什麼規格的容器直接跟kube-apiserver說就行了,過程不用你管)。用戶的合法請求會被API放行,然後存入etcd中。

    是否合法指的是:etcd就好像公司領導,kuber-apiserver就是門口保安,領導規定,必須什麼樣的人你能放進來。k8s將etcd所能接受的數據規格範式加以封裝定義在了kube-apiserver中,符合規格才能放行。

  • kube-scheduler:資源調度器。kube-apiserver收到新建Pod的請求,識別其合法並存入etcd,然後kube-scheduler去watch kube-apiserver知道此需求,根據預定的調度策略評估出一個最合適Node節點來運行Pod,如果沒有最合適,那就隨機,最後會把調度的結果記錄在etcd中。

  • kube-controller:控制器。就好比人類的大腦一樣,負責維護集羣的狀態、故障檢測與恢復、自動擴展、節點狀態等等。kube-controller有一個control loop的機制,它會循環檢測集羣中Pod的狀態,假如Nginx啓的不是預期的80端口,那就由kube-controller來控制容器重啓、重建,直到達到預期效果爲止。

  • etcd:是一個鍵值對(key:value)格式的存儲系統,保存應用程序配置信息。

總而言之,如果把ApiServer比作“接待大廳”的話,那麼Controller就是負責掌控Api對象運行週期的“控制室”,Scheduler就是負責提供調度策略的“調度室”,而etcd則是存儲着所有資源對象信息的“存儲室”。

比方說,用戶發送新建Nginx容器的請求給kube-apiserver,kube-apiserver識別其合法後以鍵值對的方式存入etcd,kube-scheduler和kube-controller通過watch kube-apiserver知道此需求,然後kube-scheduler負責資源分配並決定容器運行在哪個Node上,至於運行時所需的鏡像及運行的健康狀態的維護都由kube-controller來負責,kube-controller會循環將當前容器的狀態與watch到的用戶預期的需求做對比,看是否匹配。

2. Node

Node是Kubernetes集羣架構中運行Pod的服務節點(亦叫agent或minion)。Node是Kubernetes集羣操作的單元,用來承載被分配Pod的運行,是Pod運行的宿主機。關聯Master管理節點,擁有名稱和IP、系統資源信息。運行docker eninge服務,守護進程kunelet及負載均衡器kube-proxy。

在這裏插入圖片描述
每個Node節點都運行着以下一組關鍵進程。

  • kubelet:負責對Pod對於的容器的創建、啓停等任務。
  • kube-proxy:實現Kubernetes Service的通信與負載均衡機制的重要組件。
  • Docker Engine:Docker引擎,負責本機容器的創建和管理工作。
  • container runtime:負責pod和容器的運行,即cri。

Node節點可以在運行期間動態增加到Kubernetes集羣中,默認情況下,kubelet會想master註冊自己,這也是Kubernetes推薦的Node管理方式,kubelet進程會定時向Master彙報自身情報,如操作系統、Docker版本、CPU和內存,以及有哪些Pod在運行等等,這樣Master可以獲知每個Node節點的資源使用情況,冰實現高效均衡的資源調度策略。

3. K8S部署流程

我們簡單分析一下K8S集羣部署業務的流程,並簡單看一看各個組件的作用。

  1. 準備包含應用程序的Deployment的yaml文件,然後通過kubectl客戶端工具發送給ApiServer。

  2. ApiServer接收到客戶端的請求並將資源內容存儲到數據庫(etcd)中。

  3. Controller組件(包括scheduler、replication、endpoint)監控資源變化並作出反應。

  4. ReplicaSet檢查數據庫變化,創建期望數量的pod實例。

  5. Scheduler再次檢查數據庫變化,發現尚未被分配到具體執行節點(node)的Pod,然後根據一組相關規則將pod分配到可以運行它們的節點上,並更新數據庫,記錄pod分配情況。

  6. Kubelete監控數據庫變化,管理後續pod的生命週期,發現被分配到它所在的節點上運行的那些pod。如果找到新pod,則會在該節點上運行這個新pod。

三、 Kubernetes中的資源(API)對象

API對象是K8s集羣中的管理操作單元。K8s集羣系統每支持一項新功能,引入一項新技術,一定會新引入對應的API對象,支持對該功能的管理操作。例如副本集Replica Set對應的API對象是RS。

每個API對象都有3大類屬性:元數據metadata、規範spec和狀態status。

  • 元數據metadata是用來標識API對象的,每個對象都至少有3個元數據:namespace,name和uid;除此以外還有各種各樣的標籤labels用來標識和匹配不同的對象,例如用戶可以用標籤env來標識區分不同的服務部署環境,分別用env=dev、env=testing、env=production來標識開發、測試、生產的不同服務。
  • 規範spec描述了用戶期望K8s集羣中的分佈式系統達到的理想狀態(Desired State),例如用戶可以通過複製控制器Replication Controller設置期望的Pod副本數爲3。
  • 狀態status描述了系統實際當前達到的狀態(Status),例如系統當前實際的Pod副本數爲2;那麼複製控制器當前的程序邏輯就是自動啓動新的Pod,爭取達到副本數爲3。

所有Kubernetes中的資源,比如Pod,都通過一個叫URI的東西來區分,這個URI有一個UID,URI的重要組成部分是:對象的類型(比如pod),對象的名字,對象的命名空間,對於特殊的對象類型,在同一個命名空間內,所有的名字都是不同的,在對象只提供名稱,不提供命名空間的情況下,這種情況是假定是默認的命名空間。UID是時間和空間上的唯一。

那麼總體介紹完了API對象的定義和特性之後,我們就來簡單瞭解一下幾種重要的K8S集羣中常見的API對象。

在這裏插入圖片描述

1. Pod

K8s有很多技術概念,同時對應很多API對象,最重要的也是最基礎的是微服務Pod。從之前的架構圖像中我們也能看出,每一個Node上都運行着若干個Pod。Pod是在K8s集羣中運行部署應用或服務的最小單元。Pod的設計理念是支持多個容器在一個Pod中共享網絡地址和文件系統,可以通過進程間通信和文件共享這種簡單高效的方式組合完成服務。

Pod對多容器的支持是K8s最基礎的設計理念。比如你運行一個操作系統發行版的軟件倉庫,一個Nginx容器用來發布軟件,另一個容器專門用來從源倉庫做同步,這兩個容器的鏡像不太可能是一個團隊開發的,但是他們一塊兒工作才能提供一個微服務;這種情況下,不同的團隊各自開發構建自己的容器鏡像,在部署的時候組合成一個微服務對外提供服務。

Pod是K8s集羣中所有業務類型的基礎,可以看作運行在K8s集羣中的小機器人,不同類型的業務就需要不同類型的小機器人去執行。目前K8s中的業務主要可以分爲長期伺服型(long-running)、批處理型(batch)、節點後臺支撐型(node-daemon)和有狀態應用型(stateful application),分別對應的小機器人控制器爲Deployment、Job、DaemonSet和PetSet,本文後面會挑重點進行介紹。

2. 複製控制器(Replication Controller,RC)

RC是K8s集羣中最早的保證Pod高可用的API對象。通過監控運行中的Pod來保證集羣中運行指定數目的Pod副本。指定的數目可以是多個也可以是1個;少於指定數目,RC就會啓動運行新的Pod副本;多於指定數目,RC就會殺死多餘的Pod副本。即使在指定數目爲1的情況下,通過RC運行Pod也比直接運行Pod更明智,因爲RC也可以發揮它高可用的能力,保證永遠有1個Pod在運行。RC是K8s較早期的技術概念,只適用於長期伺服型的業務類型,比如控制小機器人提供高可用的Web服務。

3. 副本集(Replica Set,RS)

RS是新一代RC,提供同樣的高可用能力,區別主要在於RS後來居上,能支持更多種類的匹配模式。副本集對象一般不單獨使用,而是作爲Deployment的理想狀態參數使用。

4. 部署(Deployment)

部署表示用戶對K8s集羣的一次更新操作。部署是一個比RS應用模式更廣的API對象,可以是創建一個新的服務,更新一個新的服務,也可以是滾動升級一個服務。滾動升級一個服務,實際是創建一個新的RS,然後逐漸將新RS中副本數增加到理想狀態,將舊RS中的副本數減小到0的複合操作;這樣一個複合操作用一個RS是不太好描述的,所以用一個更通用的Deployment來描述。以K8s的發展方向,未來對所有長期伺服型的的業務的管理,都會通過Deployment來管理。

5. 服務(Service)

RC、RS和Deployment只是保證了支撐服務的微服務Pod的數量,但是沒有解決如何訪問這些服務的問題。一個Pod只是一個運行服務的實例,隨時可能在一個節點上停止,在另一個節點以一個新的IP啓動一個新的Pod,因此不能以確定的IP和端口號提供服務。要穩定地提供服務需要服務發現和負載均衡能力。服務發現完成的工作,是針對客戶端訪問的服務,找到對應的的後端服務實例。在K8s集羣中,客戶端需要訪問的服務就是Service對象。每個Service會對應一個集羣內部有效的虛擬IP,集羣內部通過虛擬IP訪問一個服務。

6. 任務(Job)

Job是K8s用來控制批處理型任務的API對象。批處理業務與長期伺服業務的主要區別是批處理業務的運行有頭有尾,而長期伺服業務在用戶不停止的情況下永遠運行。Job管理的Pod根據用戶的設置把任務成功完成就自動退出了。成功完成的標誌根據不同的spec.completions策略而不同:單Pod型任務有一個Pod成功就標誌完成;定數成功型任務保證有N個任務全部成功;工作隊列型任務根據應用確認的全局成功而標誌成功。

還有很多的API對象,這裏就不一一介紹了。如果沒個實例,說得太多,相信大家也是懵逼的。

四、 Kubernetes網絡介紹

在前幾部分中,我們瞭解了K8S的基本架構和相關資源對象的概念。那麼在K8S集羣中,不同pod、不同node之間的網絡是什麼樣的呢?讓我們來簡單看一看。

k8s的網絡模型設計的基礎原則是:每個pod都擁有一個獨立的IP,並且假定所有pod都在一個可以直接連通的扁平的網絡空間中,因此,不管他們是否在同一個node中,都能夠通過ip進行互相訪問。用戶就可以不用考慮如何在pod之間建立聯繫,也不用考慮容器端口映射到主機端口等問題。

但是在實際中,IP是以pod爲單位進行分配的,一個pod中的所有容器共享一個網絡堆棧(即共享一個網絡命名空間,包括IP、網絡設備、配置等等都是共享的)。按照這個網絡原則抽象出來的一個pod一個ip的模型也被叫做ip-per-pod模型。

1. 不同容器之間的網絡

容器之間的網絡,其實也就是CNI。CNI(Container Network Interface)是由一組用於配置Linux容器的網絡接口的規範和庫組成,同時還包含了一些插件。CNI僅關心容器創建時的網絡分配,和當容器被刪除時釋放網絡資源。
在這裏插入圖片描述
熟悉docker的同學會知道,docker創建的容器之間會通過一張docker0的網卡相互通信。而k8s社區爲了方便用戶適應各自的網絡情況,暴露出一套api接口------cni,用戶只要實現了這套接口就可以自定義容器網絡配置策略。

2. 不同Pod之間的網絡

在這裏插入圖片描述

3. 不同節點之間的網絡

k8s定義了一種資源Service,Kubernetes Service 定義了這樣一種抽象:一個 Pod 的邏輯分組,一種可以訪問它們的策略 —— 通常稱爲微服務。 這一組 Pod 能夠被 Service 訪問到,通常是通過 Label Selector實現的。

對 Kubernetes 集羣中的應用,Kubernetes 提供了簡單的 Endpoints API,只要 Service 中的一組 Pod發生變更,應用程序就會被更新。 對非 Kubernetes 集羣中的應用,Kubernetes 提供了基於 VIP(虛擬IP) 的網橋的方式訪問 Service,再由 Service 重定向到相應的 Pod。
在這裏插入圖片描述
在 Kubernetes 集羣中,每個 Node 運行一個 kube-proxy(服務代理) 進程。kube-proxy 負責爲 Service 實現了一種 VIP(虛擬 IP)的形式,而不是 ExternalName 的形式。

服務代理模式主要分三種,分別爲userspace代理模式、iptables代理模式、ipvs代理模式。這裏僅介紹userspace代理模式。

  • userspace 代理模式: kube-proxy 會監視 Kubernetes master 對 Service 對象和 Endpoints 對象的添加和移除。 對每個 Service,它會在本地 Node 上打開一個端口(隨機選擇)。 任何連接到“代理端口”的請求,都會被代理到 Service 的 Pods 中的某個上面。 使用哪個 Pod,是基於 Service 的 SessionAffinity 來確定的。 最後,它安裝 iptables 規則,捕獲到達該 Service 的 clusterIP(是虛擬 IP)和 Port 的請求,並重定向到代理端口,代理端口再代理請求到 backend Pod。
    在這裏插入圖片描述

3. 集羣與外界之間的網絡

Ingress 是從Kubernetes集羣外部訪問集羣內部服務的入口。

通常情況下,service和pod僅可在集羣內部網絡中通過IP地址訪問。所有到達邊界路由器的流量或被丟棄或被轉發到其他地方。Ingress是授權入站連接到達集羣服務的規則集合。

五、 K8S實例中的Pod

關於如何在K8S中部署項目,我在之前的博客中已經舉過把Mysql鏡像部署在K8S集羣上並且遠程訪問的例子。這裏就不再贅述如何把項目部署在K8S上了,我們的重點在於探究當我們使用kubectl創建一個Pod時,它在集羣中經歷了什麼樣的過程。
在這裏插入圖片描述

  1. 用戶提交創建Pod的請求,可以通過API Server的REST API ,也可用Kubectl命令行工具,支持Json和Yaml兩種格式;

  2. API Server 處理用戶請求,存儲Pod數據到Etcd;

  3. Schedule通過和 API Server的watch機制,查看到新的pod,嘗試爲Pod綁定Node;

  4. 過濾主機:調度器用一組規則過濾掉不符合要求的主機,比如Pod指定了所需要的資源,那麼就要過濾掉資源不夠的主機;

  5. 主機打分:對第一步篩選出的符合要求的主機進行打分,在主機打分階段,調度器會考慮一些整體優化策略,比如把一個Replication Controller的副本分佈到不同的主機上,使用最低負載的主機等;

  6. 選擇主機:選擇打分最高的主機,進行binding操作,結果存儲到Etcd中;

  7. kubelet根據調度結果執行Pod創建操作: 綁定成功後,會啓動container, docker run, scheduler會調用API Server的API在etcd中創建一個bound pod對象,描述在一個工作節點上綁定運行的所有pod信息。運行在每個工作節點上的kubelet也會定期與etcd同步bound pod信息,一旦發現應該在該工作節點上運行的bound pod對象沒有更新,則調用Docker API創建並啓動pod內的容器。

六、 總結

Kubernetes作爲容器集羣管理工具,於2015年7月22日迭代到 v 1.0並正式對外公佈,這意味着這個開源容器編排系統可以正式在生產環境使用。Kubernetes項目凝結了Google過去十年間在生產環境的經驗和教訓,從Borg的多任務Alloc資源塊到Kubernetes的多副本Pod,在Docker等高級引擎帶動容器技術興起和大衆化的同時,爲容器集羣管理提供獨了到見解和新思路。

作爲開發人員,我們除了要能夠熟練掌握Kubernetes中的各種命令行、具備部署擴容排錯等運維能力,還應該時刻注意對K8S各構件和底層原理的學習。只有打好K8S項目的原理基礎,我們才能提升個人在就業或者工作方面的競爭力,而不至於對發生的問題束手無策。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章