Tungsten Fabric SDN — SmartNIC vRouter
671
2025-04-01
Kubernetes 基本上是這兩年最熱門、最被人熟知的技術了,它為軟件工程師提供了強大的容器編排能力,模糊了開發和運維之間的邊界,讓我們開發、管理和維護一個大型的分布式系統和項目變得更加容易。
kubernetes-banner
這篇文章是整個 Kuberentes 架構設計與實現原理的開篇,文章會先簡單介紹 Kuberentes 的背景、依賴的技術,它的架構以及設計理念,最后會提及一些關鍵概念和實現原理。
介紹
作為一個目前在生產環境已經廣泛使用的開源項目 Kubernetes 被定義成一個用于自動化部署、擴容和管理容器應用的開源系統;它將一個分布式軟件的一組容器打包成一個個更容易管理和發現的邏輯單元。
Kubernetes 是希臘語『舵手』的意思,它最開始由 Google 的幾位軟件工程師創立,深受公司內部?Borg?和 Omega 項目的影響,很多設計都是從 Borg 中借鑒的,同時也對 Borg 的缺陷進行了改進,Kubernetes 目前是 Cloud Native Computing Foundation (CNCF) 的項目并且是很多公司管理分布式系統的解決方案。
docker-swarm-vs-kubernetes
在 Kubernetes 統治了容器編排這一領域之前,其實也有很多容器編排方案,例如?compose?和?Swarm,但是在運維大規模、復雜的集群時,這些方案基本已經都被 Kubernetes 替代了。
Kubernetes 將已經打包好的應用鏡像進行編排,所以如果沒有容器技術的發展和微服務架構中復雜的應用關系,其實也很難找到合適的應用場景去使用,所以在這里我們會簡單介紹 Kubernetes 的兩大『依賴』——容器技術和微服務架構。
容器技術
docker-logo
它讓開發者將自己的應用以及依賴打包到一個可移植的容器中,讓應用程序的運行可以實現環境無關。
我們能夠通過 Docker 實現進程、網絡以及掛載點和文件系統隔離的環境,并且能夠對宿主機的資源進行分配,這能夠讓我們在同一個機器上運行多個不同的 Docker 容器,任意一個 Docker 的進程都不需要關心宿主機的依賴,都各自在鏡像構建時完成依賴的安裝和編譯等工作,這也是為什么 Docker 是 Kubernetes 項目的一個重要依賴。
微服務架構
如果今天的軟件并不是特別復雜并且需要承載的峰值流量不是特別多,那么后端項目的部署其實也只需要在虛擬機上安裝一些簡單的依賴,將需要部署的項目編譯后運行就可以了。
microservices
但是隨著軟件變得越來越復雜,一個完整的后端服務不再是單體服務,而是由多個職責和功能不同的服務組成,服務之間復雜的拓撲關系以及單機已經無法滿足的性能需求使得軟件的部署和運維工作變得非常復雜,這也就使得部署和運維大型集群變成了非常迫切的需求。
小結
Kubernetes 的出現不僅主宰了容器編排的市場,更改變了過去的運維方式,不僅將開發與運維之間邊界變得更加模糊,而且讓 DevOps 這一角色變得更加清晰,每一個軟件工程師都可以通過 Kubernetes 來定義服務之間的拓撲關系、線上的節點個數、資源使用量并且能夠快速實現水平擴容、藍綠部署等在過去復雜的運維操作。
設計
這一小節我們將介紹 Kubernetes 的一些設計理念,這些關鍵字能夠幫助了解 Kubernetes 在設計時所做的一些選擇:
kubernetes-design
這里將按照順序分別介紹聲明式、顯式接口、無侵入性和可移植性這幾個設計的選擇能夠為我們帶來什么。
聲明式
聲明式(Declarative)的編程方式一直都會被工程師們拿來與命令式(Imperative)進行對比,這兩者是完全不同的編程方法。我們最常接觸的其實是命令式編程,它要求我們描述為了達到某一個效果或者目標所需要完成的指令,常見的編程語言 Go、Ruby、C++ 其實都為開發者了命令式的編程方法,
在 Kubernetes 中,我們可以直接使用 YAML 文件定義服務的拓撲結構和狀態:
apiVersion:?v1
kind:?Pod
metadata:
name:?rss-site
labels:
app:?web
spec:
containers:
-?name:?front-end
image:?nginx
ports:
-?containerPort:?80
-?name:?rss-reader
image:?nickchase/rss-php-nginx:v1
ports:
-?containerPort:?88
這種聲明式的方式能夠大量地減少使用者的工作量,極大地增加開發的效率,這是因為聲明式能夠簡化需要的代碼,減少開發人員的工作,如果我們使用命令式的方式進行開發,雖然在配置上比較靈活,但是帶來了更多的工作。
SELECT?*?FROM?posts?WHERE?user_id?=?1?AND?title?LIKE?'hello%';
SQL 其實就是一種常見的聲明式『編程語言』,它能夠讓開發者自己去指定想要的數據是什么,Kubernetes 中的 YAML 文件也有著相同的原理,我們可以告訴 Kubernetes 想要的最終狀態是什么,而它會幫助我們從現有的狀態進行遷移。
kubernetes-declarative-api
如果 Kubernetes 采用命令式編程的方式提供接口,那么工程師可能就需要通過代碼告訴 Kubernetes 要達到某個狀態需要通過哪些操作,相比于更關注狀態和結果聲明式的編程方式,命令式的編程方式更強調過程。
總而言之,Kubernetes 中聲明式的 API 其實指定的是集群期望的運行狀態,所以在出現任何不一致問題時,它本身都可以通過指定的 YAML 文件對線上集群進行狀態的遷移,就像一個水平觸發的系統,哪怕系統錯過了相應的事件,最終也會根據當前的狀態自動做出做合適的操作。
顯式接口
第二個 Kubernetes 的設計規范其實就是 —— 不存在內部的私有接口,所有的接口都是顯示定義的,組件之間通信使用的接口對于使用者來說都是顯式的,我們都可以直接調用。
kubernetes-external-api
當 Kubernetes 的接口不能滿足工程師的復雜需求時,我們需要利用已有的接口實現更復雜的特性,在這時 Kubernetes 的這一設計就不會成為自定義需求的障礙。
無侵入性
為了盡可能滿足用戶(工程師)的需求,減少工程師的工作量與任務并增強靈活性,Kubernetes 為工程師提供了無侵入式的接入方式,每一個應用或者服務一旦被打包成了鏡像就可以直接在 Kubernetes 中無縫使用,不需要修改應用程序中的任何代碼。
kuberentes-non-invasive
Docker 和 Kubernetes 就像包裹在應用程序上的兩層,它們兩個為應用程序提供了容器化以及編排的能力,在應用程序內部卻不需要任何的修改就能夠在 Docker 和 Kubernetes 集群中運行,這是 Kubernetes 在設計時選擇無侵入帶來最大的好處,同時無侵入的接入方式也是目前幾乎所有應用程序或者服務都必須考慮的一點。
可移植性
在微服務架構中,我們往往都會讓所有處理業務的服務變成無狀態的服務,以前在內存中存儲的數據、Session 等緩存,現在都會放到 Redis、ETCD 等數據庫中存儲,微服務架構要求我們對業務進行拆分并劃清服務之間的邊界,所以有狀態的服務往往會對架構的水平遷移帶來障礙。
然而有狀態的服務其實是無可避免的,我們將每一個基礎服務或者業務服務都變成了一個個只負責計算的進程,但是仍然需要有其他的進程負責存儲易失的緩存和持久的數據,Kubernetes 對這種有狀態的服務也提供了比較好的支持。
Kubernetes 引入了?PersistentVolume?和?PersistentVolumeClaim?的概念用來屏蔽底層存儲的差異性,目前的 Kubernetes 支持下列類型的?PersistentVolume:
kubernetes-persistent-volume
這些不同的?PersistentVolume?會被開發者聲明的?PersistentVolumeClaim?分配到不同的服務中,對于上層來講所有的服務都不需要接觸?PersistentVolume,只需要直接使用?PersistentVolumeClaim?得到的卷就可以了。
架構
Kubernetes 遵循非常傳統的客戶端服務端架構,客戶端通過 RESTful 接口或者直接使用 kubectl 與 Kubernetes 集群進行通信,這兩者在實際上并沒有太多的區別,后者也只是對 Kubernetes 提供的 RESTful API 進行封裝并提供出來。
kubernetes-architecture
每一個 Kubernetes 就集群都由一組 Master 節點和一系列的 Worker 節點組成,其中 Master 節點主要負責存儲集群的狀態并為 Kubernetes 對象分配和調度資源。
Master
作為管理集群狀態的 Master 節點,它主要負責接收客戶端的請求,安排容器的執行并且運行控制循環,將集群的狀態向目標狀態進行遷移,Master 節點內部由三個組件構成:
kubernetes-master-node
其中 API Server 負責處理來自用戶的請求,其主要作用就是對外提供 RESTful 的接口,包括用于查看集群狀態的讀請求以及改變集群狀態的寫請求,也是唯一一個與 etcd 集群通信的組件。
而 Controller 管理器運行了一系列的控制器進程,這些進程會按照用戶的期望狀態在后臺不斷地調節整個集群中的對象,當服務的狀態發生了改變,控制器就會發現這個改變并且開始向目標狀態遷移。
最后的 Scheduler 調度器其實為 Kubernetes 中運行的 Pod 選擇部署的 Worker 節點,它會根據用戶的需要選擇最能滿足請求的節點來運行 Pod,它會在每次需要調度 Pod 時執行。
Worker
其他的 Worker 節點實現就相對比較簡單了,它主要由 kubelet 和 kube-proxy 兩部分組成:
kubernetes-worker-node
kubelet 是一個節點上的主要服務,它周期性地從 API Server 接受新的或者修改的 Pod 規范并且保證節點上的 Pod 和其中容器的正常運行,還會保證節點會向目標狀態遷移,該節點仍然會向 Master 節點發送宿主機的健康狀況。
另一個運行在各個節點上的代理服務 kube-proxy 負責宿主機的子網管理,同時也能將服務暴露給外部,其原理就是在多個隔離的網絡中把請求轉發給正確的 Pod 或者容器。
實現原理
到現在,我們已經對 Kubernetes 有了一些簡單的認識和了解,也大概清楚了 Kubernetes 的架構,在這一小節中我們將介紹 Kubernetes 中的一些重要概念和實現原理。
對象
Kubernetes 對象是系統中的持久實體,它使用這些對象來表示集群中的狀態,這些對象能夠描述:
kubernetes-object
這些對象描述了哪些應用應該運行在集群中,它們請求的資源下限和上限以及重啟、升級和容錯的策略。每一個創建的對象其實都是我們對集群狀態的改變,這些對象描述的其實就是集群的期望狀態,Kubernetes 會根據我們指定的期望狀態不斷檢查對當前的集群狀態進行遷移。
type?Deployment?struct?{
metav1.TypeMeta?`json:",inline"`
metav1.ObjectMeta?`json:"metadata,omitempty"?protobuf:"bytes,1,opt,name=metadata"`
Spec?DeploymentSpec?`json:"spec,omitempty"?protobuf:"bytes,2,opt,name=spec"`
Status?DeploymentStatus?`json:"status,omitempty"?protobuf:"bytes,3,opt,name=status"`
}
每一個對象都包含兩個嵌套對象來描述規格(Spec)和狀態(Status),對象的規格其實就是我們期望的目標狀態,而狀態描述了對象的當前狀態,這部分一般由 Kubernetes 系統本身提供和管理,是我們觀察集群本身的一個接口。
Pod
Pod 是 Kubernetes 中最基本的概念,它也是 Kubernetes 對象模型中我們可以創建或者部署的最小并且最簡單的單元。
kubernetes-pod
它將應用的容器、存儲資源以及獨立的網絡 IP 地址等資源打包到了一起,表示一個最小的部署單元,但是每一個 Pod 中的運行的容器可能不止一個,這是因為 Pod 最開始設計時就能夠在多個進程之間進行協調,構建一個高內聚的服務單元,這些容器能夠共享存儲和網絡,非常方便地進行通信。
控制器
最后要介紹的就是 Kubernetes 中的控制器,它們其實是用于創建和管理 Pod 的實例,能夠在集群的曾名提供復制、發布以及健康檢查的功能,這些控制器其實都運行在 Kubernetes 集群的主節點上。
在 Kuberentes 的?kubernetes/pkg/controller/?目錄中包含了官方提供的一些常見控制器,我們可以通過下面這個函數看到所有需要運行的控制器:
func?NewControllerInitializers(loopMode?ControllerLoopMode)?map[string]InitFunc?{
controllers?:=?map[string]InitFunc{}
controllers["endpoint"]?=?startEndpointController
controllers["replicationcontroller"]?=?startReplicationController
controllers["podgc"]?=?startPodGCController
controllers["resourcequota"]?=?startResourceQuotaController
controllers["namespace"]?=?startNamespaceController
controllers["serviceaccount"]?=?startServiceAccountController
controllers["garbagecollector"]?=?startGarbageCollectorController
controllers["daemonset"]?=?startDaemonSetController
controllers["job"]?=?startJobController
controllers["deployment"]?=?startDeploymentController
controllers["replicaset"]?=?startReplicaSetController
controllers["horizontalpodautoscaling"]?=?startHPAController
controllers["disruption"]?=?startDisruptionController
controllers["statefulset"]?=?startStatefulSetController
controllers["cronjob"]?=?startCronJobController
//?...
return?controllers
}
這些控制器會隨著控制器管理器的啟動而運行,它們會監聽集群狀態的變更來調整集群中的 Kuberentes 對象的狀態,在后面的文章中我們會展開介紹一些常見控制器的實現原理。
總結
作為 Kubernetes 系列文章的開篇,我們已經了解了它出現的背景、依賴的關鍵技術,同時我們也介紹了 Kubernetes 的架構設計,主節點負責處理客戶端的請求、節點的調度,最后我們提到了幾個 Kuberentes 中非常重要的概念:對象、Pod 和控制器,在接下來的文章中我們會深入介紹 Kuberentes 的實現原理。
https://mp.weixin.qq.com/s?__biz=MzAxNjk4ODE4OQ==&mid=2247484993&idx=1&sn=4c9a6d966101dd3adffde15e01449c03&chksm=9bed2733ac9aae2556feb103b28e7a4c99862f4c5ddbbcb307b4abb2e1c77339d761e9167214&scene=21#wechat_redirect
架構設計 容器 Kubernetes
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。