Kubernetes 提供的容器、Pod 和 Service 等原語,構成了雲原生應用佈署和管理的根本。不同於傳統的物件導向程式設計,Kubernetes 引入的這些分散式原語,更側重於應用在分散式環境下的生命週期管理。容器作為應用封裝和佈署的基本單位,具備輕量、可移植和易於擴充套件的特性。Pod 則將一個或多個容器組合在一起,分享網路和儲存資源,形成 Kubernetes 中的最小排程單元。而 Service 則為 Pod 提供了一個穩定的網路入口,使得應用程式可以透過固定的 IP 地址和埠存取 Pod,而無需關心 Pod 的實際位置和生命週期。這些原語共同構成了 Kubernetes 應用管理的基礎,使得開發者可以更輕鬆地構建、佈署和管理雲原生應用。

雲原生應用的基礎:容器與 Kubernetes 原語

在現代軟體開發中,Kubernetes 已成為雲原生應用的核心平台。它提供了一套分散式原語,用於構建和管理跨多節點和程式的分散式系統。這些原語與傳統的物件導向程式設計(OOP)概念有著本質的區別。

從物件導向到分散式原語

在 OOP 世界中,我們使用類別、物件、封裝、繼承和多型等概念來構建應用程式。Java 語言和 JVM 提供了本地的、程式內的建構塊。然而,Kubernetes 引入了一套新的分散式原語,用於構建分散式系統。下表對比了本地和分散式原語的不同實作:

概念本地原語分散式原語
行為封裝類別容器映像
行為例項物件容器
重用單位.jar容器映像
組合類別 A 包含類別 BSidecar 模式
繼承類別 A 繼承類別 B容器的 FROM 父映像
佈署單位.jar/.war/.earPod
建構/執行時隔離模組、套件、類別Namespace、Pod、容器
初始化前置條件建構子Init 容器
初始化後觸發器Init-methodpostStart
銷毀前觸發器Destroy-methodpreStop
清理程式finalize()、shutdown hook-

容器:Kubernetes 的基本建構塊

容器映像就像類別,而容器則像是物件。我們可以透過擴充套件容器映像來重用和修改行為,就像擴充套件類別一樣。同樣地,我們可以透過將容器放入 Pod 中來實作容器組合。

容器的特性

  1. 單一功能單位:容器映像是解決單一問題的功能單位。
  2. 獨立所有權:容器映像由一個團隊擁有,並有自己的釋出週期。
  3. 自包含:容器映像定義並攜帶其執行時依賴。
  4. 不可變性:容器映像一旦構建完成,就不會改變;它會被組態。
  5. 資源定義:容器映像定義其資源需求和外部依賴。
  6. 明確的 API:容器映像具有明確的 API 以暴露其功能。

容器與物件導向的類別比

  • 容器映像就像類別,而容器就像物件。
  • Init 容器類別似於物件建構子。
  • DaemonSets 類別似於在背景執行的守護執行緒(如 Java 的垃圾回收器)。

為何容器如此重要?

在 Kubernetes 中,容器扮演著基礎性的角色。建立模組化、可重用、單一用途的容器映像對於任何專案的長期成功以及整個容器的生態系統都至關重要。除了提供封裝和隔離的技術特性外,容器還代表了分散式應用中的一個基本功能單位。

容器與Pod的基本特性

容器是一種可拋棄且安全的實體,可以在任何時刻進行擴充套件或縮減。除了這些特性之外,一個適當的容器映像(container image)還具備模組化、可引數化以及可重複使用的特點,能夠在不同的環境中執行。擁有小型、模組化且可重複使用的容器映像,有助於長期建立更專門化和穩定的容器映像,就像程式語言世界中的優秀可重複使用函式庫一樣。

Pod:容器管理的基礎單位

檢視容器的特性後,我們可以發現它們非常適合實作微服務(microservices)原則。一個容器映像提供了一個單一的功能單元,隸屬於一個團隊,擁有獨立的發布週期,並且提供佈署和執行時的隔離。大多數時候,一個微服務對應到一個容器映像。

然而,大多數雲原生平台提供了另一種基本元素來管理一組容器的生命週期——在Kubernetes中,這被稱為Pod。Pod是一個原子單位,用於排程、佈署和執行時隔離一組容器。Pod中的所有容器總是被排程到相同的宿主機上,一起佈署和擴充套件,並且可以分享檔案系統、網路和行程名稱空間。這種共同的生命週期使得Pod中的容器可以透過檔案系統或網路進行互動,使用localhost或宿主機行程間通訊機制(如果需要,例如出於效能考慮)。Pod也代表了一個應用程式的安全邊界。雖然在同一個Pod中可以有具有不同安全引數的容器,但通常所有容器都會具有相同的存取層級、網路分割和身份。

正如圖1-2所示,在開發和建置階段,微服務對應到一個由一個團隊開發和發布的容器映像。但在執行階段,微服務由Pod來表示,Pod是佈署、放置和擴充套件的單位。執行容器的唯一方式——無論是為了擴充套件還是遷移——都是透過Pod抽象。有時一個Pod包含多個容器。例如,一個容器化的微服務在執行時使用了一個輔助容器,如第16章“Sidecar”所述。

Pod的特性

Pod為設計根據微服務的應用程式提供了一套新的模式和原則。我們已經看到了設計良好的容器的一些特性;現在讓我們來看看Pod的一些特性:

  • Pod是排程的原子單位。這意味著排程器嘗試找到一個滿足屬於Pod的所有容器的需求的宿主機(我們在第15章“Init Container”中涵蓋了一些關於初始容器的具體內容)。如果您建立了一個包含多個容器的Pod,排程器需要找到一個具有足夠資源來滿足所有容器需求的宿主機。這種排程過程在第6章“Automated Placement”中有詳細描述。

  • Pod確保了容器的共置。由於共置,同一Pod中的容器有額外的方式來相互互動。最常見的通訊方式包括使用共用的本機檔案系統來交換資料、使用localhost網路介面,或使用某些宿主機行程間通訊(IPC)機制來實作高效能互動。

  • Pod具有一個IP地址、名稱和埠範圍,這些都是由屬於它的所有容器共用的。這意味著同一Pod中的容器必須小心組態,以避免埠衝突,就像在宿主機上分享網路空間的平行Unix行程需要小心一樣。

  • Pod是Kubernetes中應用程式存活的原子單位,但您不能直接存取Pod——這就是Service登場的場景。

Service:Pod的存取入口

Pod是短暫的。它們隨時可能因為各種原因而出現或消失(例如,擴充套件或縮減、容器健康檢查失敗、節點遷移)。只有在Pod被排程並啟動在某個節點上後,才會知道其IP地址。如果現有的節點不再健康,Pod可能會被重新排程到不同的節點上。這意味著Pod的網路位址可能會在其應用程式的生命週期中發生變化,因此需要另一種基本元素來進行服務發現和負載平衡。

這就是Kubernetes Service發揮作用的地方。Service是另一種簡單但強大的Kubernetes抽象,它將Service名稱永久地繫結到一個IP地址和埠號。因此,Service代表了一個命名入口,用於存取應用程式。在最常見的場景中,Service作為一組Pod的入口,但情況並不總是如此。Service是一種通用的基本元素,它也可能指向Kubernetes叢集外部提供的功能。因此,Service基本元素可用於服務發現和負載平衡,並且可以在不影回應用程式的情況下更改實作和擴充套件。

內容解密:

本段主要描述了 Kubernetes 中的 Pod 和 Service 的基本概念及其重要性。首先,Pod 是 Kubernetes 中的基本執行單元,它封裝了一個或多個容器,並提供了它們分享的環境,包括網路和儲存資源。接著,詳細說明瞭 Pod 的特性,如它是排程的基本單位,能夠確保容器之間的共置,提供分享的 IP 地址和埠範圍等。然後,闡述了 Service 的作用,即為 Pod 提供一個穩定的網路介面,使得應用程式可以透過 Service 存取 Pod,而不受 Pod IP 地址變化的影響。最後,強調了 Service 在服務發現和負載平衡方面的重要性,它使得應用程式可以靈活地擴充套件和更改實作,而不會影響到對外的介面。

圖表說明

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam rectangleBackgroundColor #F5F5F5
skinparam rectangleBorderColor #333333
skinparam arrowColor #333333

title 圖表說明

rectangle "managed by" as node1
rectangle "accessed through" as node2
rectangle "provides stable interface" as node3

node1 --> node2
node2 --> node3

@enduml

此圖示展示了 Container 被 Pod 管理,而 Pod 透過 Service 被存取,Service 為應用程式提供了穩定的介面。

程式碼範例

apiVersion: v1
kind: Pod
metadata:
  name: example-pod
spec:
  containers:
  - name: example-container
    image: example/image
    ports:
    - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: example-service
spec:
  selector:
    app: example
  ports:
  - name: http
    port: 80
    targetPort: 80
  type: ClusterIP

內容解密:

本段程式碼定義了一個名為 example-pod 的 Pod 和一個名為 example-service 的 Service。其中,Pod 中包含一個名為 example-container 的容器,該容器使用了 example/image 映象並開放了 80 埠。Service 使用了 app: example 的標籤選擇器來關聯相關的 Pod,並將外部的 80 埠對映到 Pod 的 80 埠,提供了一個 ClusterIP 型別的 Service。這使得應用程式可以透過 example-service 這個穩定的介面存取到對應的 Pod。

Kubernetes 中的應用程式識別與資源管理

在微服務架構中,應用程式由多個獨立的服務組成,這些服務需要被有效地管理和識別。Kubernetes 提供了多種原語(primitives)來幫助定義和管理應用程式,包括 Labels、Annotations 和 Namespaces。

Labels:應用程式識別與管理

Labels 是 Kubernetes 中用於標識和組織資源的一種機制。它們以鍵值對的形式存在,可以附加到 Pod、Service 等資源上。透過使用 Labels,可以對資源進行邏輯分組,從而實作對分散式應用程式的管理。

Labels 的用途

  • ReplicaSets:使用 Labels 來確保特定 Pod 的例項數量。
  • 排程器(Scheduler):利用 Labels 將 Pod 排程到滿足特定要求的節點上。
  • 應用程式識別:為一組 Pod 提供邏輯上的分組,賦予它們應用程式的身份。

使用 Labels 的最佳實踐

  • 使用足夠的 Labels 來描述 Pod 的重要方面,如應用程式的分組、業務特性、關鍵性等。
  • 謹慎新增 Labels,避免過多不必要的 Labels。
  • 移除 Labels 時需謹慎,因為這可能會產生不可預見的後果。

Annotations:附加後設資料

Annotations 與 Labels 類別似,也是以鍵值對的形式存在,但它們主要用於儲存不可搜尋的後設資料,供機器使用。

Annotations 的用途

  • 儲存構建 ID、發行 ID、映像資訊、時間戳等後設資料。
  • 為各種工具和函式庫附加額外的後設資料。

Namespaces:資源隔離與管理

Namespaces 是 Kubernetes 中的另一個重要概念,用於將叢集劃分為邏輯上的資源池。它們提供了一個範圍,用於 Kubernetes 資源,並允許對叢集的子集應用授權和其他策略。

Namespaces 的特性

  • 資源範圍:Namespaces 為容器、Pod、Service 等資源提供了一個範圍。
  • 資源名稱唯一性:在同一個 Namespace 內,資源名稱必須是唯一的。
  • 預設不提供隔離:Namespaces 預設情況下不提供資源之間的隔離。

使用 Namespaces 的場景

  • 代表不同的軟體環境,如開發、測試、整合測試或生產環境。
  • 實作多租戶,提供團隊工作區、專案和特定應用程式的隔離。
此圖示說明瞭 Kubernetes 中 Labels、Annotations 和 Namespaces 之間的關係,以及它們各自的主要用途。

可預測需求:容器資源管理基礎

在分享雲端環境中成功佈署、管理和共存的基礎,在於識別和宣告應用程式的資源需求和執行時依賴。本章節將探討「可預測需求」模式,說明如何宣告應用程式的需求,無論是硬性執行時依賴還是資源需求。

問題背景

Kubernetes 可以管理不同程式語言編寫的應用程式,只要該應用程式可以在容器中執行。然而,不同語言有不同的資源需求。通常,編譯語言執行速度更快,所需的記憶體也較少,而即時編譯或解釋型語言則需要更多的資源。除了資源需求外,應用程式執行時還依賴於平台管理的功能,如資料儲存或應用程式組態。

解決方案

瞭解容器執行時的需求對於兩個主要原因非常重要。首先,當所有執行時依賴都已定義且資源需求都已預估時,Kubernetes 可以就如何在叢集中最有效地利用硬體做出明智的決定。在一個具有大量不同優先順序程式的分享資源環境中,確保成功共存的唯一方法是提前瞭解每個程式的需求。然而,明智的放置只是其中一方面。

容器資源組態

容器資源組態對於容量規劃也至關重要。根據特定的服務需求和服務總數,我們可以為不同的環境進行容量規劃,並提出最具成本效益的主機組態,以滿足整個叢集的需求。服務資源組態和容量規劃是長期成功叢集管理的關鍵。

在探討資源組態之前,讓我們先看看如何宣告執行時依賴。

執行時依賴

最常見的執行時依賴之一是檔案儲存,用於儲存應用程式狀態。容器的檔案系統是短暫的,當容器關閉時會丟失。Kubernetes 提供了一個名為 volume 的 Pod 級儲存工具,可以在容器重新啟動後繼續存在。

最簡單的 volume 型別是 emptyDir,它與 Pod 的生命週期相同。當 Pod 被刪除時,其內容也會丟失。要使 volume 在 Pod 重新啟動後繼續存在,需要將其備份到另一種儲存機制。如果您的應用程式需要讀取或寫入長期儲存中的檔案,則必須在容器定義中使用 volumes 顯式宣告該依賴,如範例 2-1 所示。

範例 2-1:對 PersistentVolume 的依賴
apiVersion: v1
kind: Pod
metadata:
  name: random-generator
spec:
  containers:
    # 容器定義內容

內容解密:

  • apiVersionkind 定義了 Kubernetes 資源的版本和型別,在此例中為 Pod。
  • metadata 包含了 Pod 的後設資料,如名稱。
  • spec 定義了 Pod 的規格,包括其內部的容器和其他設定。
  • 使用 volumes 可以宣告對持久化儲存的需求,讓 Kubernetes 知道該 Pod 需要持久化儲存。

Kubernetes 資源管理關鍵概念

  • 資源需求宣告:明確宣告應用程式所需的資源,如 CPU 和記憶體。
  • 執行時依賴:宣告應用程式對特定資源的依賴,如持久化儲存。
  • 容量規劃:根據服務需求和總數進行容量規劃,以確保叢集資源的最佳利用。

透過遵循「可預測需求」模式,開發者可以確保其應用程式在 Kubernetes 環境中高效、可靠地執行。

Kubernetes 中的資源依賴與組態管理

在 Kubernetes 中,Pod 的執行往往依賴於多種外部資源,如 PersistentVolumeClaim(PVC)、ConfigMap 和 Secret。這些依賴關係對於 Pod 的排程和執行至關重要。

PersistentVolumeClaim(PVC)依賴

PVC 是 Kubernetes 中用於持久化儲存的資源。當一個 Pod 需要使用特定的儲存卷時,它必須依賴於對應的 PVC 是否存在且已經繫結。如果 PVC 不存在或未繫結,Pod 將無法被排程。

apiVersion: v1
kind: Pod
metadata:
  name: random-generator
spec:
  containers:
  - image: k8spatterns/random-generator:1.0
    name: random-generator
    volumeMounts:
    - mountPath: "/logs"
      name: log-volume
  volumes:
  - name: log-volume
    persistentVolumeClaim:
      claimName: random-generator-log

內容解密:

  • volumeMounts 指定容器內掛載的卷,本例中將 log-volume 掛載到 /logs 目錄。
  • volumes 定義了 Pod 使用的卷型別,本例中使用的是 PVC,名稱為 random-generator-log
  • 這裡的 Pod 依賴於名為 random-generator-log 的 PVC,如果 PVC 不存在,Pod 將無法正常排程。

ConfigMap 和 Secret 依賴

ConfigMap 和 Secret 是 Kubernetes 中用於管理組態資訊的資源。Pod 可以透過環境變數或檔案系統來使用這些組態資訊。如果所需的 ConfigMap 或 Secret 不存在,容器可能無法啟動。

apiVersion: v1
kind: Pod
metadata:
  name: random-generator
spec:
  containers:
  - image: k8spatterns/random-generator:1.0
    name: random-generator
    env:
    - name: PATTERN
      valueFrom:
        configMapKeyRef:
          name: random-generator-config
          key: pattern

內容解密:

  • env 定義了容器的環境變數,本例中 PATTERN 環境變數從名為 random-generator-config 的 ConfigMap 中取得。
  • 這裡的 Pod 依賴於名為 random-generator-config 的 ConfigMap,如果 ConfigMap 不存在,容器可能無法正常啟動。

資源組態

在 Kubernetes 中,資源組態(Resource Profiles)對於容器的執行同樣重要。資源包括 CPU、記憶體(Memory)等,它們分為可壓縮資源(如 CPU)和不可壓縮資源(如記憶體)。

apiVersion: v1
kind: Pod
metadata:
  name: random-generator
spec:
  containers:
  - image: k8spatterns/random-generator:1.0
    name: random-generator
    resources:
      requests:
        cpu: 100m
        memory: 200Mi
      limits:
        memory: 200Mi

內容解密:

  • resources.requests 指定了容器初始請求的資源量,本例中請求了 100m 的 CPU 和 200Mi 的記憶體。
  • resources.limits 指定了容器可以使用的最大資源量,本例中記憶體限制為 200Mi。
  • 如果容器超出了記憶體限制,它可能會被驅逐;CPU 資源則可以被壓縮,避免了因超限而被驅逐的情況。