返回文章列表

Kubernetes 狀態應用程式管理與最佳實踐

本文探討在 Kubernetes 中管理有狀態應用程式的挑戰和最佳實踐,涵蓋 StatefulSets 的使用、儲存管理、Operator 的角色以及進入控制和授權機制。文章深入解析了 ReplicaSet 的限制以及 StatefulSets 如何克服這些限制,並提供實用的 YAML

容器技術 雲端原生

在 Kubernetes 環境中,管理有狀態應用程式需要特別關注儲存和生命週期管理。選擇合適的儲存方案,例如外部資料函式庫或雲端託管服務,並評估應用程式碼的無狀態化可行性,是確保應用程式可擴充套件性和可靠性的關鍵。Kubernetes 提供了 PersistentVolume 和 PersistentVolumeClaim 來管理儲存,但資料的冗餘和備份仍需額外考量。設定正確的回收策略對於資料生命週期管理至關重要,避免敏感資料意外刪除。StatefulSets 提供了比 ReplicaSets 更精細的控制,可以確保 Pod 的順序命名、縮減和單獨定址,以及 PersistentVolume 的可靠掛載。

傳統的 ReplicaSet 在管理有狀態應用程式方面存在一些限制,例如 Pod 的隨機命名和縮減、無法透過名稱或 IP 直接呼叫,以及 Pod 可以在任何時候重新啟動和移動。StatefulSets 克服了這些限制,提供了順序命名、按相反順序縮減、可透過名稱單獨定址,以及強制使用 PersistentVolume 範本等特性。透過無頭 Service,StatefulSets 中的每個 Pod 都可以被單獨存取,方便管理和監控。組態 StatefulSet 時,需要定義 Service、StatefulSet 規格、容器組態、Sidecar 容器以及 PersistentVolumeClaim。Sidecar 容器可以輔助管理 MongoDB 副本集等任務。

管理狀態與有狀態應用程式

在容器化的世界中,管理應用程式的狀態是一項挑戰。Kubernetes 提供了多種機制來支援有狀態應用程式的佈署和管理。

考量工作負載的狀態管理

在設計應用程式時,首先要考慮的是哪些工作負載需要將狀態儲存在磁碟上。可以透過以下幾種方式處理:

  • 使用外部服務,如資料函式庫系統。
  • 使用雲端供應商提供的託管服務,該服務與目前使用的 API 相容,例如 MongoDB 或 MySQL 作為服務。

修改應用程式碼以實作無狀態化

評估修改應用程式碼以使其更無狀態所需的努力是必要的。這樣可以提高應用程式的可擴充套件性和可靠性。

Kubernetes 中的儲存管理

Kubernetes 可以跟蹤和掛載卷,當工作負載被排程時。然而,它還沒有處理儲存在這些卷中的資料的冗餘和備份。CSI 規範新增了一個 API,供供應商插入原生快照技術(如果儲存後端可以支援)。

資料生命週期的管理

驗證卷將儲存的資料的正確生命週期是非常重要的。預設情況下,動態組態的 PersistentVolumes 的回收策略是刪除,當 pod 被刪除時,卷將從後端儲存供應商中刪除。對於敏感資料或可用於法醫分析的資料,應設定為保留。

有狀態應用程式

與流行的看法相反,Kubernetes 從一開始就支援有狀態應用程式,從 MySQL、Kafka 和 Cassandra 到其他技術。然而,在早期,這些佈署通常很複雜,並且通常只適用於小型工作負載,需要大量的工作來實作擴充套件和永續性。

ReplicaSet 的限制

要完全理解有狀態應用程式的關鍵差異,您必須瞭解典型的 ReplicaSet 如何排程和管理 pod,以及這如何對傳統的有狀態應用程式造成危害:

  • ReplicaSet 中的 pod 被擴充套件並分配隨機名稱。
  • ReplicaSet 中的 pod 被任意縮減。
  • ReplicaSet 中的 pod 永遠不會透過其名稱或 IP 地址直接呼叫,而是透過與 Service 的關聯。
  • ReplicaSet 中的 pod 可以在任何時候重新啟動並移動到另一個節點。
  • ReplicaSet 中具有 PersistentVolume 對映的 pod 僅透過宣告連結,但任何具有新名稱的新 pod 都可以在重新排程時接管宣告。

StatefulSets 的優勢

StatefulSets 使執行需要更可靠的節點/pod 行為的應用程式系統變得更容易。如果我們回顧一下 ReplicaSet 中典型的 pod 特性列表,StatefulSets 提供了幾乎完全相反的特性。原始規範在 Kubernetes 版本 1.3 中稱為 PetSets,被引入來滿足複雜資料管理系統等有狀態型別應用程式的一些關鍵排程和管理需求:

  • StatefulSet 中的 pod 被擴充套件並分配順序名稱。
  • StatefulSet 中的 pod 按相反的順序縮減。
  • StatefulSet 中的 pod 可以透過名稱在無頭 Service 後面單獨定址。
  • StatefulSet 中需要卷掛載的 pod 必須使用定義的 PersistentVolume 範本。

StatefulSet 的定義

StatefulSet 的規範看起來與 Deployment 非常相似,除了 Service 宣告和 PersistentVolume 範本。無頭 Service 應該首先建立,它定義了 pod 將被單獨定址的 Service。無頭 Service 與常規 Service 相同,但不進行正常的負載平衡。

apiVersion: v1
kind: Service
metadata:
  name: mongo
  labels:
    name: mongo
spec:
  ports:
  - port: 27017
    targetPort: 27017
  clusterIP: None # 這建立了無頭 Service
  selector:
    role: mongo

StatefulSet 組態範例

StatefulSet 的定義也將與 Deployment 完全相同,但有一些變化:

apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
  name: mongo
spec:
  serviceName: "mongo"
  replicas: 3
  template:
    metadata:
      labels:
        role: mongo
        environment: test
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: mongo
        image: mongo:3.4
        command:
        - mongod
        - "--replSet"
        - rs0
        - "--bind_ip"
        - "0.0.0.0"
        - "--smallfiles"
        - "--noprealloc"
        ports:
        - containerPort: 27017
        volumeMounts:
        - name: mongo-persistent-storage
          mountPath: /data/db
      - name: mongo-sidecar
        image: cvallance/mongo-k8s-sidecar
        env:
        - name: MONGO_SIDECAR_POD_LABELS
          value: "role=mongo,environment=test"
  volumeClaimTemplates:
  - metadata:
      name: mongo-persistent-storage
      annotations:
        volume.beta.kubernetes.io/storage-class: "fast"
    spec:
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 2Gi

程式碼解密:

  1. Service 定義:這段 YAML 定義了一個名為 mongo 的無頭 Service,用於讓 StatefulSet 中的 pod 被單獨存取。clusterIP 設定為 None 表示這是一個無頭 Service。
  2. StatefulSet 定義:這段 YAML 定義了一個名為 mongo 的 StatefulSet,包含三個副本,使用 MongoDB 映象,並組態了一個名為 mongo-persistent-storage 的 PersistentVolumeClaim,用於持久化儲存 MongoDB 資料。
  3. 容器組態:MongoDB 容器被組態為使用 --replSet 引數啟用副本集,並掛載持久化儲存到 /data/db
  4. Sidecar 容器:一個名為 mongo-sidecar 的容器被用來監控和管理 MongoDB 副本集。

管理狀態與有狀態應用程式的最佳實踐

大型分散式應用程式需要狀態管理與複雜的組態操作,這些都可以透過 Kubernetes 的 StatefulSets 和 Operators 獲得改善。Operators 仍在不斷演進,但由於得到了廣大社群的支援,以下最佳實踐根據目前的功能:

使用 StatefulSets 的考量

  • 應謹慎決定是否使用 StatefulSets,因為有狀態的應用程式通常需要更深入的管理,而 Kubernetes 目前尚無法完全滿足這些需求(未來可能會透過 Operators 來解決這個問題)。
  • StatefulSet 的無頭服務(headless Service)不會自動建立,必須在佈署時手動建立,以正確地將 Pod 當作獨立節點進行定址。

StatefulSet 操作注意事項

  • 當應用程式需要順序命名和可靠的擴充套件時,不一定意味著需要分配 PersistentVolumes。
  • 如果叢集中的某個節點無回應,任何屬於 StatefulSet 的 Pod 都不會自動刪除,而是會在寬限期後進入「Terminating」或「Unknown」狀態。要清除這些 Pod,可以移除叢集中的節點物件、重新啟動 kubelet 並直接刪除 Pod,或使用 Operator 強制刪除 Pod。強制刪除應該是最後的選擇,並且需要小心確認被刪除 Pod 的節點不會重新上線,因為叢集中將會有兩個具有相同名稱的 Pod。

處理無回應 Pod 的方法

  • 可以使用 kubectl delete pod nginx-0 --grace-period=0 --force 強制刪除 Pod。
  • 即使強制刪除了 Pod,它仍可能保持「Unknown」狀態,此時可以透過對 API 伺服器進行補丁來刪除該條目,並讓 StatefulSet 控制器建立新的 Pod 例項:kubectl patch pod nginx-0 -p '{"metadata":{"finalizers":null}}'

關閉連線和資料同步

  • 如果執行的複雜資料系統涉及長官者選舉或資料複製確認過程,請使用 preStop 鉤子來正確關閉連線、強制進行長官者選舉,或在刪除 Pod 之前透過優雅關閉程式驗證資料同步。

使用 Operators 管理複雜應用程式

  • 當需要有狀態資料的應用程式是複雜的資料管理系統時,應檢查是否存在可協助管理應用程式更複雜生命週期元件的 Operator。如果應用程式是內部開發的,可能值得研究是否將其封裝為 Operator,以增加應用的可管理性。

與 Operators 的角色

Operators 已成為擴充套件 Kubernetes API 以及為 Kubernetes 中的複雜系統流程引入最佳實踐和營運監督的標準方法。OperatorHub 是發現 Kubernetes 生態系統中已發布的 Operators 的良好資源,他們維護著一份更新的精選 Operators 清單。隨著 Kubernetes 的不斷發展,Operators 將在簡化有狀態應用程式的管理方面發揮越來越重要的作用。

為何使用 Operators?

  • 封裝領域特定知識:Operators 將執行特定應用程式所需的領域特定知識封裝到擴充套件 Kubernetes 的特定控制器中。
  • 簡化複雜操作:它們簡化了佈署、擴充套件、升級、備份和執行一般維護操作等複雜任務。
  • 支援複雜資料服務:由於其自定義控制器邏輯,Operators 特別適合用於複雜的資料服務和有狀態系統。

建構 Operators 的最佳實踐

若您對瞭解 Operators 的工作原理感興趣,第 21 章將為您提供開發 Operator 的入門和最佳實踐。此外,《Kubernetes Operators》(O’Reilly)一書由 Jason Dobies 和 Joshua Wood 撰寫,提供了更深入的 Operator 建置。

重點回顧

  • 謹慎使用 StatefulSets,因為它們需要更深入的管理。
  • 正確組態無頭服務以定址個別 Pod。
  • 考慮使用 Operators 來管理複雜的生命週期操作。
  • 小心處理無回應的 Pod,以避免名稱衝突。
  • 使用 preStop 鉤子確保資料同步和連線關閉。

透過遵循這些最佳實踐,您可以更有效地在 Kubernetes 中管理和執行有狀態應用程式,充分利用 StatefulSets 和 Operators 的優勢。

進入控制與授權機制

控制對 Kubernetes API 的存取是確保叢集安全性的關鍵,同時也能作為實施政策和治理的手段,適用於所有使用者、工作負載和叢集元件。在本章中,我們將探討如何使用進入控制器(Admission Controllers)和授權模組來啟用特定功能,以及如何根據特定需求進行自定義。

Kubernetes API 請求流程

在深入進入控制和授權之前,讓我們先回顧一下 API 請求透過 API 伺服器的流程。圖 17-1 展示了進入控制和授權在該流程中的位置和作用。該圖描述了從左到右透過 Kubernetes API 伺服器的端對端請求流程,直到物件被接受並儲存。請特別注意進入控制和授權的順序。

圖 17-1. Kubernetes API 請求流程

此圖示展示了 Kubernetes API 請求的完整流程,包括進入控制和授權的階段。

圖表翻譯: 此圖示詳細展示了 Kubernetes API 請求從接收到處理的整個流程,涵蓋了進入控制和授權的關鍵步驟。

進入控制(Admission Control)

進入控制器位於 Kubernetes API 伺服器請求流程的路徑上,並在身份驗證和授權階段之後接收請求。它們用於在將請求物件儲存之前對其進行驗證或變更(或兩者兼有)。變更型進入控制器可以修改它們允許的請求物件,而驗證型進入控制器則不能。

為什麼進入控制很重要?

由於進入控制器位於所有 API 伺服器請求的路徑上,因此可以以多種不同的方式使用它們。進入控制器的使用通常可以分為以下三類別:

  • 政策和治理:強制執行業務需求的政策,例如:

    • 在 dev 名稱空間中只能使用內部雲端負載平衡器。
    • Pod 中的所有容器必須具有資源限制。
    • 為所有資源新增預定義的標準標籤或註解,使其可被現有工具發現。
    • 所有 Ingress 資源只能使用 HTTPS。
  • 安全性:強制執行一致的安全性態勢,例如:

    • 使用 Pod Security Admission 控制器根據 Pod 規格中定義的安全敏感欄位來決定是否允許 Pod。
    • 禁止特權容器或使用主機檔案系統中的特定路徑。
  • 資源管理:提供叢集使用者的最佳實踐,例如:

    • 確保所有 Ingress 的完整網域名稱(FQDN)都屬於特定的字尾。
    • 確保 Ingress FQDN 不重疊。

進入控制器型別

進入控制器分為兩類別:標準型和動態型。標準型進入控制器被編譯到 API 伺服器中,並作為每個 Kubernetes 版本的外掛提供,需要在 API 伺服器啟動時進行組態。動態控制器則可以在執行時組態,並在核心 Kubernetes 程式碼之外開發。目前,唯一的動態進入控制型別是進入 Webhook,它透過 HTTP 回撥接收進入請求。

使用範例

--enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeClaimResize,DefaultStorageClass,ResourceQuota,DefaultTolerationSeconds,MutatingAdmissionWebhook,ValidatingAdmissionWebhook

內容解密:

上述命令列舉了啟用特定進入控制器的範例。其中包括:

  • NamespaceLifecycle:確保名稱空間存在且有效。
  • LimitRanger:強制執行資源限制。
  • ServiceAccount:為 Pod 自動新增 ServiceAccount。
  • MutatingAdmissionWebhookValidatingAdmissionWebhook:用於組態 Webhook 端點,以處理變更和驗證請求。

這些進入控制器透過強制執行特定的規則和策略,確保了叢集的安全性和穩定性。

最佳實踐與自定義

要根據特定需求自定義進入控制,可以啟用或停用特定的進入控制器,並組態 Webhook 端點以處理複雜的驗證和變更邏輯。此外,透過結合使用標準型和動態型進入控制器,可以實作更靈活和強大的叢集治理能力。

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
  name: example-webhook
webhooks:
- name: example-webhook.example.com
  clientConfig:
    service:
      name: example-webhook-service
      namespace: default
    caBundle: <BASE64_encoded_PEM>
  rules:
  - apiGroups:
    - ""
    apiVersions:
    - v1
    operations:
    - CREATE
    resources:
    - pods
    scope: Namespaced

內容解密:

此 YAML 組態檔案定義了一個驗證 Webhook 組態,用於攔截對 Pod 的建立操作。其中包括:

  • clientConfig:指定了處理 Webhook 請求的服務名稱和名稱空間,以及用於驗證服務身份的 CA 包。
  • rules:定義了該 Webhook 組態所應用的 API 群組、版本、操作和資源範圍。

透過這種方式,可以實作對特定資源操作的細粒度控制,以滿足特定的安全和治理需求。