圖文並茂!帶你深度解析 Kubernetes

導語 | 在雲原生技術發展的浪潮之中,Kubernetes 作爲容器編排領域的事實標準和雲原生領域的關鍵項目,其誕生與完善有着對應的技術歷史背景,瞭解這個過程,對於系統的理解 Kubernetes 的核心思想、架構設計、實現原理等會很有幫助。

在雲原生技術發展的浪潮之中,Kubernetes 伴隨着容器技術的發展,成爲了目前雲時代的操作系統。Kubernetes 作爲容器編排領域的事實標準和雲原生領域的關鍵項目,已經是雲原生時代工程師最需要理解與實踐的核心技術。

但技術的發展從來都不是一蹴而就,Kubernetes 的誕生與完善也有其對應的技術歷史背景,瞭解其誕生與發展的過程,對於更加系統的理解其核心思想、架構設計、實現原理等內容會大有幫助。因此,本文從 Kubernetes 的誕生背景與 Why Kubernetes 兩個方面,來完成對 Kubernetes 的概述。

一、Kubernetes 誕生背景

如果要了解 Kubernetes 的誕生,就繞不開整個雲計算的發展歷程。瞭解了雲計算的發展的過程,就會明白,Kubernetes 是雲計算發展到一定程度的必然產物。

(一)雲計算發展歷程

雲計算發展歷程的時間軸如下圖所示,從物理機過渡到傳統的 IaaS 階段,進而發展爲早期的 PaaS,直至發展到如今的基於 Kubernetes 架構的新興 PaaS 平臺。

用戶使用資源的形態也由早期的物理機過渡到虛擬機,再進化到目前更輕量的 Docker 容器。本質上雲計算實現的關鍵突破就在於資源使用方式的改變,其最初解決的核心的問題就是應用的託管即應用部署與管理問題。

(二)早期物理機時代

雲計算之前,開發者如需部署管理服務,需要根據需求,進行配置、管理與運維物理機。整體上維護困難,成本高昂,重複勞動,風險隨機。以至於當年流傳着運維的傳統藝能:上線拜祖,如下圖所示:

在那個時代,應用部署與管理面臨着以下諸多問題:

(三)IaaS 平臺

Infrastructure as a service (IaaS) 基礎設施即服務,用戶可以按需去申請基礎設施資源(包括:計算、存儲、網絡)。

IaaS 商業化道路上的一個標誌性事件:2006 年 AWS 推出了 EC2(亞馬遜彈性雲端運算),其基於 Xen 虛擬化技術,用戶可以在 web 界面上配置、獲取虛擬機資源,部署應用。通過規模化來降低邊際成本。

IaaS 的底層核心技術是虛擬化技術。虛擬化技術是一種資源關聯技術,是將計算機的各種實體資源,如服務器、網絡、存儲等,進行抽象、整合、管理與再分配的一種技術。最常用的一種方案是基於虛擬機(Hypervisor-based)的虛擬化實現。其通過一個軟件層的封裝,提供和物理硬件相同的輸入輸出表現,實現了操作系統和計算機硬件的解耦,將 OS 和計算機從一對一轉變爲多對多(實際上是一對多)的關係。該軟件層稱爲虛擬機管理器(VMM/Hypervisor),它分爲兩大類:裸金屬架構、宿主機架構。

裸金屬架構:VMM 直接安裝和運行在物理機上;VMM 自帶虛擬內核的管理和使用底層的硬件資源。業界的 Xen、VMWare ESX 都是裸金屬架構。

宿主機架構:物理機上首先會裝一個操作系統,VMM 安裝和運行在操作系統上;在 VMM 再去裝其他虛擬機操作系統,依賴與操作系統對硬件設備的支持與資源的管理。這種架構的好處是,VMM 會變得非常簡單,因爲可以基於操作系統去管理系統資源,VMM 只需要做額外的虛擬化工作。Oracle VirtualBox,VMWare Workstation、KVM 都是這種架構,宿主機架構是目前虛擬化技術的主流架構。

下圖中,對比了物理機架構與宿主機虛擬化架構的區別。

虛擬化架構有如下的優勢:

當物理機轉變爲虛擬機之後,如何對多臺虛擬機的資源進行管理與調度,成爲了一個新的問題。

OpenStack 給出瞭解決方案,它是一個開源的分佈式的平臺,能夠統一管理多個服務器,按用戶需求進行分配與調度虛擬機。其本質上是一組分配、管理虛擬機的自動化工具腳本。

目前,OpenStack 已經發展成了 IaaS 的主流解決方案,即:OpenStack as IaaS。目前主流 IaaS 雲服務廠商底層都是利用 OpenStack 技術。

IaaS 平臺一定程度上提升了物理機的資源利用率,由物理機時代的低於 10%,提升到了 15%。但虛擬機對資源利用率的提升仍存在一定的侷限性,其相對笨重,啓動慢,自身消耗大(其完整運行了一套操作系統),自身加載就要消耗幾百兆的內存資源。此外,虛擬機可以預裝一些軟件,一定程度簡化了應用程序的依賴安裝。但應用程序的部署與打包,仍然需要開發人員各自解決,仍未高效的完成應用部署與分發。

(四)PaaS 平臺

Platform-as-a-Service (PaaS) 平臺即服務。PaaS 提供了包括服務器、存儲空間和網絡等基礎結構,但它並未包括中間件、開發工具、數據庫管理系統等。PaaS 旨在支持應用程序的完整生命週期:生成、部署、管理和更新,提供應用的託管能力。

在 IaaS 階段,服務廠商只提供虛擬機,虛擬機之上的軟件棧都由用戶管理,包括操作系統、持久化層、中間層、用戶程序。在 IaaS 層面用戶只是減少了關心底層硬件,而 PaaS 層面希望能夠進一步解放用戶,讓用戶真正只需關注應用本身。

目前一個成熟的 PaaS 平臺應具備的主要功能,如下圖所示:

早期 PaaS 平臺,更多關注運行時環境與依賴服務,而目前的 PaaS 平臺新增大量的支持服務,包括:認證授權、系統日誌、應用監控等,以上都是應用開發的常見需求。原則上:共用內容就應該抽象出統一通用的組件,由框架和平臺來實現。讓用戶只關心邏輯或應用本身,避免重複造輪子。

PaaS 在成熟之前也經歷了幾個階段,而 PaaS 早期的代表就不得不提 Cloud Foundry。Cloud Foundry 由 VMWare 開發,是第一款開源 PaaS 平臺(2011 年)。支持多種框架、語言、運行時環境、雲平臺及應用服務,使開發人員能夠快速進行應用的部署,無需擔心任何基礎架構的問題。

它主要功能包括以下:

Cloud Foundry 的出現,其描繪了 PaaS 平臺的初步形態,推動了 PaaS 的發展,具有劃時代的意義。

但其最終並未成爲 PaaS 主流,是因爲其存在一個核心不足:它只對應用和配置進行了打包,而沒有打包整體依賴(所謂的整體依賴包括:中間環境、操作系統文件)。所以它的包在跨平臺運行時,會出現運行失敗的現象。這個問題非常致命。

而且,早期 Cloud Foundry 主要是針對單一 Web 應用的管理,對分佈式應用所需的各項能力均未涉及,例如:服務發現、彈性擴縮等。

(五)Docker

Docker 公司的前身是 dotCloud,它是 2010 年成立,提供 Paas 服務的平臺。但當時 Cloud Foundry 做的相對完善和開放,2012 年底 dotClound 瀕臨倒閉,創始人決定把內部的打包平臺開源出去。因此,2013 年 3 月 dotCloud 公司在 github 平臺上開源了其內部的容器項目 Docker。Github 開源之後,受到了業界的熱烈追捧,從而 Docker 大火。公司後來也改名爲 Docker。

Docker 的成功,主要是通過鏡像完美解決了開發、測試、生產環境不一致的問題。它的口號是:Build、Shipand Run any App、Anywhere,即一處構建,到處運行。

Docker 的核心技術有三個:NameSpaces 做視圖隔離、Cgroups 做資源限制,UnionFS 聯合文件系統,統一 mount。通俗理解:NameSpaces、Cgroups 通給進程設置屬性,實現進程的隔離與限制,UnionFS 給進程構造文件系統。這三項技術均有 linux 內核提供,Docker 本身並沒有創造新的技術。

但是 Docker 創造性的通過鏡像整體打包了應用的依賴環境,包括:操作系統文件、中間依賴層、APP。

Docker 通過鏡像分層複用的方式進行了優化。共用只讀層,節省存儲空間,提高鏡像推送、拉取效率,鏡像的操作是增量式。

利用 UnionFS 實現合併,多個只讀層加一個可寫層 mount 成一個目錄。並且上面的層會覆蓋下面的層,當對底層的只讀層修改時會採用寫時複製策略(copy-on-write)。寫時複製的含義:當另一個層第一次需要寫入該文件時(在構建鏡像或運行容器時),該文件會被複制到該讀寫層並被修改。該機制大大減少了容器的啓動時間(啓動時新建的可寫層只有很少的文件寫入),但容器運行後每次第一次修改的文件都需要先將整個文件複製到 container layer 中。

如下圖所示,Docker 相比於虛擬機操作系統級的資源隔離,實現了進程級資源隔離,極大提升了資源利用率。具備以下特點:

(六)容器編排

當 Docker 解決了應用打包的問題後,PaaS 上應用大規模部署與管理的問題愈發突出。此時,業內明白:容器本身沒有 “價值”,有價值的是容器編排。

容器編排 (Orchestration):對 Docker 及容器進行更高級更靈活的管理,按照用戶的意願和整個系統的規則,完全自動化的處理好容器之間的各種關係(對象之間的關係遠重要於對象本身)。

容器技術做爲底層基礎技術,只能用來創建和啓動容器的小工具,最終只能充當平臺項目的 “幕後英雄”。用戶最終部署的還是他們的網站、服務、數據庫,甚至是雲計算業務。這就需要一個真正的 PaaS 平臺,讓用戶把自己的容器應用部署在此之上。

在以上的歷史背景之下,2014 年左右,Docker、Mesos、Google 相繼發佈自己的 PaaS 平臺,容器編排之爭正式開始。

Docker 發佈了 Swarm 平臺,Swarm 擅長跟 Docker 生態無縫集成,docker 用戶可以低成本過渡。其最大亮點是使用 Docker 項目原有的容器管理 API 來完成集羣管理。例如:單機 Docker 項目: docker run “我的容器”。集羣 Docker 項目:docker run-H“我的 Swarm 集羣 API 地址” “我的容器”。

Mesos 平臺,擅長大規模集羣的調度與管理。它是 Apache 基金會下的一個開源集羣管理器,最初是由 Berkeley 分校開發的。它爲應用程序提供了跨集羣的資源管理和調度 API。之後轉向支持 PaaS 業務,推出了 Marathon 項目。它是一個高度成熟的 PaaS 項目,旨在讓用戶便捷管理一個數萬級別的物理機集羣,可使用容器在這個集羣裏自由部署應用。

Google 推出的是 Kubernetes 平臺,整個系統的前身是 Borg 系統,Kubernetes 平臺是 Google 在容器化基礎設施領域十多年來實踐經驗的沉澱與昇華。

經過近 3 年的角逐,容器編排之爭的勝利者是 Kubernetes。

Kubernetes,讀者一定會有一個疑問:爲什麼最後是 Kubernetes?

每個人對這個問題,都有一些自己的理解,本文從技術方面對該問題進行了闡述。

二、Why Kubernetes

Kubernetes 源於希臘語,意爲 “舵手”。k8s 縮寫是因爲 k 和 s 之間有八個字符的原因。它是 google 在 2015 開源的容器調度編排的平臺。它是建立在 Google 大規模運行生產工作負載(Borg 系統)十幾年經驗的基礎上, 結合了社區中最優秀的想法和實踐,已經成爲了目前容器編排的事實標準。

其實看到 Docker 和 Kubernetes 的 Logo,就可以很快明白 Kubernetes 的作用。Docker 的 Logo 是一條鯨魚船,運載着許多封裝好的集裝箱 (container),代表着一次打包到處運行的意圖。而 Kubernetes 的 Logo 就是這條船的方向舵!

對於 Why Kubernetes?很多人都有自己的理解,接下來筆者從技術的角度,闡述一下自己的觀點。Kubernetes 技術上的成功,個人認爲核心在於三個關鍵點:

(一)Kubernetes 前身

Kubernetes 的基礎特性,並不是幾個工程師突然 “拍腦袋” 想出來的東西,而是 Google 公司在容器化基礎設施領域多年來實踐經驗的沉澱與昇華。這個實踐與昇華的過程,就是 Kubernetes 的前身是 Borg 系統。

Borg 系統一直以來都被譽爲 Google 內部最強大的 “祕密武器”,是 Google 整個基礎設施的核心依賴。很多應用框架已經運行在 Borg 上多年,其中包括了內部的 MapReduce、GFS、BigTable、Megastore 等,上層應用程序更是有這些耳熟能詳的產品:Gmail、Google Docs、Google Search 等。

其架構圖如下所示:

架構分析:

根據 2015 年 4 月 google 發佈的 Large-scale clustermanagement at Google with Borg,與其 2020 年 7 月發佈的 Borg: the nextgeneration,兩篇論文中的數據表明:Borg 系統通過對在線任務與離線任務進行混合部署,可以節約 20%-30% 的資源,極大提高了資源利用率。下表是 2011 年與 2019 年的 Borg 集羣,與 2015 年 AWS、Facebool、Twitter 數據中心資源利用率的對比圖。

對於成熟高效的 Borg 系統,繼承者 Kubernetes 從中獲得了寶貴的經驗:

(二)Kubernetes 架構

Kubernets 整體架構,如下所示:

整個系統由控制面 (Master) 與數據面 (Worker Node) 組成。Master 核心組件:

Kubernetes 架構具備高可用:一方面 Master 節點高可用;另一方面所部署的業務也是高可用的。系統高可用的核心在於冗餘部署,當某一個節點或程序出現異常時,其他節點或程序能分擔或替換工作。Master 節點高可用,主要由以下幾個方面的設計實現:

Work Node 節點由以下組件組成:

Kubernetes 中 API Server 的核心功能是提供 Kubernetes 各類資源對象(如 Pod、RC、Service 等)的增、刪、改、查及 Watch 等 HTTP REST 接口,成爲集羣內各個功能模塊之間數據交互和通信的中心樞紐,是整個系統的數據總線和數據中心。除此之外,它還是集羣管理的 API 入口,提供了完備的集羣安全機制。API Server 是由多實例同時工作,各個組件通過負載均衡連到具體的 API Server 實例上。

如下所示,各組件與 API Server 通信時,採用 List-Watch 機制,通過 API server 獲取 etcd 配置與狀態信息,進而觸發行爲。以下圖爲例是 kubectl 創建一個 deployment 時,各個組件與 API Server 的流程交互。

Api Server 的作用:

(三)Kubernetes 核心設計

Kubernetes 取的巨大的成功,與它良好的核心設計緊密相關。筆者認爲 Kubernetes 有三大核心設計:

Kubernetes 在對象抽象方面,核心創新在於 Pod 對象的設計。容器設計本身是一種 “單進程” 模型。該表述不是指容器裏只能啓動一個進程,而是指容器無法管理多個進程。只有容器內 PID=1 的進程生命週期才受到容器管理(該進程退出後,容器也會退出),其他進程都是 PID=1 的進程的子進程。根據容器設計模式,傳統架構中多個緊密配合的業務進程(例如業務進程與日誌收集進程,業務進程與業務網絡代理進程)應該部署成多個容器。但這些容器之間存在親密的關係,需要一起調度和直接共享某些資源(網絡和存儲)。

Kubernetes 抽象出一個 Pod 對象,是一組(一個或多個)容器, 這些容器共享存儲、網絡等, 這些容器是相對緊密的耦合在一起的。Pod 是 Kubernetes 內創建和管理的最小可調度單元,調度過程是按 Pod 整體所需資源一起進行調度的。Pod 本身只是邏輯上的概念,在容器管理這層並不認識 Pod 對象。

Pod 的實現需要使用一箇中間容器(Infra 容器),在這個 Pod 中,Infra 容器永遠是第一個被創建的容器,用戶定義的其他容器通過 Join Network Namespace 的方式與 Infra 容器關聯在一起。抽象一箇中間容器的原因在於各個業務容器是對等的,其啓動沒有嚴格的先後順序,需藉助中間容器實現共享網絡和存儲的目的。

其中,Node、Pod 與容器三者關係,如下圖所示。Node 表示一臺機器,可調度多個 Pod,而一個 Pod 內又能包含多個容器。

至此,再來通過 Kubernetes 中各個對象的關聯關係來更爲深刻的理解 Pod 的意義。下圖可以看出,Pod 其實是整個編排過程中操作的核心,很多對象直接或間接的同 Pod 相關聯。

Kubernetes 編排抽象的另一個核心對象是 Service 對象,它統一的解決了集羣內服務發現與負載均衡。Service 是對一組提供相同功能的 Pod 的抽象,爲其提供了一個統一的入口。Service 通過標籤選擇服務後端,匹配標籤的 Pod IP 和端口列表組成 endpoints,由 kube-proxy 負責將請求負載到相關的 endpoints。

下圖是 kube-proxy 通過 iptables 模式來實現 Service 的過程,Service 對象有一個虛擬 clusterIP,集羣內請求訪問 clusterIP 時,會由 iptables 規則負載均衡到後端 endpoints。

Declarative(聲明式設計)指的是一種軟件設計理念和編程方式,描述了目標狀態,由工具自行判斷當前狀態並執行相關操作至目標狀態。聲明式強調 What,目標是什麼。而 Imperative(命令式) 需要用戶描述一系列詳細指令來達到期望的目標狀態。命令式強調 How,具體如何做。

下圖描繪了一個場景:目標副本數爲 3。對於聲明式而言,用戶設定目標爲 3,系統獲取當前副本數爲 2,系統判定當前值與目標值的差爲 1,便自行加 1,最終實現副本數爲 3 的目標狀態。而對於命令式,需用戶判斷當前副本數爲 2,用戶給出指令副本 + 1,系統接收用戶指令,執行副本數 + 1 操作,最終系統副本數爲 3。

kubernetes 的一大核心設計就是採用了聲明式 API,利用該設計思想有效的實現了系統的自動化運行。Kubernetes 聲明式 API 指定了集羣期望的運行狀態,集羣控制器會通過 List&Watch 機制來獲取當前狀態,並根據當前狀態自動執行相應的操作至目標狀態。

Kubernetes 中,用戶通過提交定義好的 API 對象來聲明期望狀態,系統允許有多個 API 寫端,以 PATCH 方式對 API 對象進行修改。Kubectl 工具支持三種對象管理方式:命令式命令行、命令式對象配置 (yaml)、聲明式對象配置 (yaml)。舉例如下:命令式命令行:

kubectl run nginx –image nginx

命令式對象配置:

kubectl create –f nginx.yaml
kubectl replace –f nginx.yaml

以上先 kubectl create 再 kubectl replace 的操作,與命令式命令行不存在本質區別。只是把具體命令寫入 yaml 配置文件中而已。聲明式對象配置:

kubectl apply –f nginx.yaml

Kubernetes 推薦使用:聲明式對象配置 (YAML)。kubectl replace 執行過程是通過新的 YAML 文件中的 API 對象來替換原有的 API 對象,而 Kubectl apply 執行了一個對原有 API 對象的 PATCH 操作。除此之外,YAML 配置文件用於 Kubernetes 對象的定義,還會有以下收益:

Kubernetes 的設計初衷就是支持可插拔的架構,解決 PaaS 平臺使用不方便、不易擴展等問題。爲了便於系統的擴展,Kubernetes 中開放了以下接口可對系統資源(計算、網絡、存儲)插件進行擴展,可分別對接不同的後端來實現自己的業務邏輯。

CRI(Container Runtime Interface):容器運行時接口,提供計算資源。CRI 接口設計的一個重要原則是隻關注接口本身,而不關心具體實現,kubelet 就只需要跟這個接口打交道。而作爲具體的容器項目,比如 Docker、rkt、containerd、kata container 它們就只需要自己提供一個該接口的實現,然後對 kubelet 暴露出 gRPC 服務即可。簡單來說,CRI 主要作用就是實現了 kubelet 和容器運行時之間的解耦。

CNI(Container Network Interface):容器網絡接口,提供網絡資源。跨主機容器間的網絡互通已經成爲基本要求,K8S 網絡模型要求所有容器都可以直接使用 IP 地址與其他容器通信,而無需使用 NAT;所有宿主機都可以直接使用 IP 地址與所有容器通信,而無需使用 NAT。反之亦然。容器自己的 IP 地址,和別人(宿主機或者容器)看到的地址是完全一樣的。K8S 提供了一個插件規範,稱爲容器網絡接口 (CNI),只要滿足 K8S 的基本需求,第三方廠商可以隨意使用自己的網絡棧,通過使用 overlay 網絡來支持多子網或者一些個性化使用場景,所以出現很多優秀的 CNI 插件被添加到 Kubernetes 集羣中。

CSI(Container Storage Interface):容器存儲接口,提供存儲資源。K8S 將存儲體系抽象出了外部存儲組件接口,第三方存儲廠商可以發佈和部署公開的存儲插件,而無需接觸 Kubernetes 核心代碼,同時爲 Kubernetes 用戶提供了更多的存儲選項。例如:AWS、NFS、Ceph。

Kubernetes 除了對系統資源可插件擴展外,也可以自定義 CRD(Custom Resource Definition)來擴展 API 對象,同時也支持編寫 Operator 對 CRD 進行控制。例如:對於一些有狀態應用(etcd),可以定義新的 CRD 對象,並編寫特定的 Operator(本質上是新的 controller)去實現控制邏輯。

Kubernetes 的調度器 Scheduler 也是可以擴展的,可以部署自定義的調度器,在整個集羣中還可以同時運行多個調度器實例,通過 pod.Spec.schedulerName 來選擇具體指定調度器(默認使用內置的調度器)。

三、小結

根據以上兩個章節的闡述,對於文章開頭的經典問題:如何纔能有效的部署與管理應用?到 Kubernets 大放異彩的今天,已經給出了答案:

感謝 Kubernetes,將開發、運維人員從繁重的應用部署與管理工作中解放出來。到目前爲止,Kubernetes 已經成爲了容器編排的事實標準,是新一代的基於容器技術的 PaaS 平臺的重要底層框架。

Kubernetes 的成熟,拉開了轟轟烈烈的雲原生技術發展的大幕!

參考資料:

1.Kubernetes 文檔

2.Kubernetes 權威指南 第五版

  1. 深入剖析 Kubernetes - 張磊

4.Docker 與 k8s 的前世今生

5.https://draveness.me/understanding-kubernetes/

** 作者簡介**

尹飛

騰訊後臺開發工程師

騰訊後臺開發工程師,專注於後臺遊戲開發,擅長分佈式開發,有豐富的 C++、Lua、Go 語言使用經驗。

騰訊雲開發者 騰訊雲官方社區公衆號,匯聚技術開發者羣體,分享技術乾貨,打造技術影響力交流社區。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/9nfQ0xJAAxTdiddzBtH51Q