Kubernetes 控制器是自動化管理的關鍵,本文探討控制器模式的實踐,包含如何選擇適當的儲存位置,例如 Labels、Annotations 和 ConfigMaps,並提供 Shell 指令碼範例說明控制器如何監控 ConfigMap 並執行對應操作。更進一步,文章延伸探討 Operator 模式,說明如何利用自定義資源定義 (CRD) 擴充套件 Kubernetes API,並以 Prometheus Operator 為例,講解 CRD 的定義、使用方法以及與控制器的整合。最後,文章比較了 ConfigMap 和 CRD 的優缺點,並介紹 API 聚合層等更高階的擴充套件方式,以及 Kubebuilder 和 Operator Framework 等 Operator 開發工具,提供開發者更全面的 Kubernetes 擴充套件知識。

Kubernetes 控制器模式:管理資源與組態的實踐

Kubernetes 中的控制器(Controller)是實作自動化管理的重要元件,它們能夠持續監控資源定義的變化並根據需要執行相應的操作。選擇適當的儲存位置對於控制器資料至關重要,本文將探討使用 Labels、Annotations 和 ConfigMaps 的考量因素。

使用 Labels、Annotations 和 ConfigMaps 的考量

Labels

Labels 是資源後設資料的一部分,可以被任何控制器監控。它們在後端資料函式庫中被索引,可以高效地被查詢。適用於需要選擇器功能的場景,例如比對 Service 或 Deployment 的 Pods。但 Labels 有語法限制,只允許使用字母數字名稱和值。

Annotations

Annotations 是 Labels 的優秀替代方案。當值不符合 Labels 的語法限制時,應使用 Annotations。由於 Annotations 不被索引,因此適用於非識別資訊,不用於控制器查詢的鍵。優先使用 Annotations 可以避免對 Kubernetes 內部效能產生負面影響。

ConfigMaps

當控制器需要額外的資訊,而這些資訊不適合儲存在 Labels 或 Annotations 中時,可以使用 ConfigMaps。ConfigMaps 可以被控制器監視和讀取。然而,自定義資源定義(CRDs)更適合設計自定義的目標狀態規格,推薦使用 CRDs 而不是普通的 ConfigMaps。但註冊 CRDs 需要提升叢集級別的許可權,如果沒有這些許可權,ConfigMaps 仍是最佳替代方案。

簡單的控制器範例

以下是一些值得研究的簡單控制器範例:

  • jenkins-x/exposecontroller:監控 Service 定義,如果檢測到 metadata 中的 expose 註解,則自動為 Service 建立 Ingress 物件以實作外部存取。
  • stakater/Reloader:監控 ConfigMap 和 Secret 物件的變化,並對相關的工作負載執行滾動升級。
  • Flatcar Linux Update Operator:當檢測到 Node 資源物件上的特定註解時,重新啟動執行在 Flatcar Container Linux 上的 Kubernetes 節點。

實作範例:根據 Shell 指令碼的控制器

以下是一個具體範例:一個由單個 shell 指令碼組成的控制器,監控 Kubernetes API 對 ConfigMap 資源的變更。如果 ConfigMap 被註解 k8spatterns.io/podDeleteSelector,則當 ConfigMap 變更時,所有符合給定標籤選擇器的 Pods 都會被刪除。

ConfigMap 範例

apiVersion: v1
kind: ConfigMap
metadata:
  name: webapp-config
  annotations:
    k8spatterns.io/podDeleteSelector: "app=webapp"
data:
  message: "Welcome to Kubernetes Patterns !"

這個 ConfigMap 被控制器監控,當它變更時,所有帶有 app=webapp 標籤的 Pods 都會被重新啟動,以擷取變更後的組態。

控制器指令碼

namespace=${WATCH_NAMESPACE:-default}
base=http://localhost:8001
ns=namespaces/$namespace
curl -N -s $base/api/v1/${ns}/configmaps?watch=true | \
while read -r event
do 
  # ...
done

內容解密:

此指令碼首先設定要監控的名稱空間,若未指定則預設為 default。接著,它建構了存取 Kubernetes API 的 URL,並使用 curl 命令開啟一個長輪詢的 HTTP 請求,以監控 ConfigMap 的變更事件。每當事件到達時,指令碼會讀取事件並進行處理。

使用 Downward API 設定監控名稱空間

透過 Downward API,可以將當前名稱空間注入到控制器的環境變數中,如下所示:

env:
- name: WATCH_NAMESPACE
  valueFrom:
    fieldRef:
      fieldPath: metadata.namespace

內容解密:

此設定允許控制器動態地取得其佈署所在的名稱空間,並據此監控相應的 ConfigMap 資源。

控制器模式:Kubernetes中的自定義控制器實作

在Kubernetes中,控制器模式是一種重要的設計模式,用於實作自定義的控制邏輯,以管理和維護叢集中的資源。以下是一個根據shell指令碼的簡單控制器例項,用於演示控制器模式的基本概念。

控制器邏輯

控制器的主要邏輯是監聽特定資源的事件,並根據事件型別執行相應的操作。在本例中,控制器監聽ConfigMap資源的變更事件,並根據ConfigMap中的註解(annotation)刪除比對的Pod。

curl -N -s $base/api/v1/${ns}/configmaps?watch=true | \
while read -r event
do
  type=$(echo "$event" | jq -r '.type')
  config_map=$(echo "$event" | jq -r '.object.metadata.name')
  annotations=$(echo "$event" | jq -r '.object.metadata.annotations')
  if [ "$annotations" != "null" ]; then
    selector=$(echo $annotations | \
      jq -r "\
        to_entries |\
        .[] |\
        select(.key == \"k8spatterns.io/podDeleteSelector\") |\
        .value |\
        @uri \
      ")
  fi
  if [ $type = "MODIFIED" ] && [ -n "$selector" ]; then
    pods=$(curl -s $base/api/v1/${ns}/pods?labelSelector=$selector |\
      jq -r .items[].metadata.name)
    for pod in $pods; do
      curl -s -X DELETE $base/api/v1/${ns}/pods/$pod
    done
  fi
done

內容解密:

  1. 事件監聽:使用curl命令監聽ConfigMap資源的變更事件,並將事件讀入while迴圈中進行處理。
  2. 事件解析:使用jq命令解析事件中的型別、ConfigMap名稱和註解。
  3. 註解處理:檢查ConfigMap中是否存在特定的註解(k8spatterns.io/podDeleteSelector),並提取其值作為Pod選擇器。
  4. Pod刪除:如果事件型別為MODIFIED且存在有效的Pod選擇器,則查詢比對的Pod並逐一刪除。

JQ表示式解析

在上述指令碼中,使用了jq命令來解析JSON資料。以下是一個關鍵的jq表示式,用於提取ConfigMap中的特定註解並轉換為Pod選擇器。

selector=$(echo $annotations | \
  jq -r "\
    to_entries |\
    .[] |\
    select(.key == \"k8spatterns.io/podDeleteSelector\") |\
    .value |\
    @uri \
  ")

內容解密:

  1. to_entries:將JSON物件轉換為鍵值對陣列。
  2. .[]:遍歷陣列中的每個元素。
  3. select(.key == \"k8spatterns.io/podDeleteSelector\"):篩選出鍵名為k8spatterns.io/podDeleteSelector的元素。
  4. .value:提取篩選出的元素的值。
  5. @uri:將值進行URI編碼,以便用作Pod選擇器。

佈署控制器

控制器的佈署涉及建立一個包含兩個容器的Pod:一個是Kubernetes API代理容器,另一個是執行控制器指令碼的主容器。

apiVersion: apps/v1
kind: Deployment
spec:
  template:
    spec:
      serviceAccountName: config-watcher-controller
      containers:
      - name: kubeapi-proxy
        image: k8spatterns/kubeapi-proxy
      - name: config-watcher
        # ...

內容解密:

  1. serviceAccountName:指定服務賬戶名稱,用於授權控制器存取Kubernetes API。
  2. containers:定義了兩個容器:kubeapi-proxyconfig-watcher
  3. kubeapi-proxy:使用k8spatterns/kubeapi-proxy映象,提供Kubernetes API代理功能。
  4. config-watcher:執行控制器指令碼的容器,使用包含curljq的Alpine基礎映象。

Operator 模式:將操作知識封裝於 Kubernetes 中

Operator 是一種控制器,它使用自定義資源定義(CRD)將特定應用程式的操作知識以演算法和自動化的形式封裝起來。Operator 模式允許我們擴充套件上一章所述的 Controller 模式,以實作更大的彈性和表現力。

問題背景

在前一章「Controller」中,我們學習瞭如何以簡單和解耦的方式擴充套件 Kubernetes 平台。然而,對於更複雜的使用場景,普通的自定義控制器功能不夠強大,因為它們僅限於監控和管理 Kubernetes 內建資源。此外,有時我們希望向 Kubernetes 平台新增新的概念,這需要額外的域物件。

例如,假設我們選擇 Prometheus 作為我們的監控解決方案,並希望以明確定義的方式將其作為監控工具新增到 Kubernetes 中。如果能有一個 Prometheus 資源來描述我們的監控設定和所有佈署細節,像定義其他 Kubernetes 資源一樣,不是很棒嗎?此外,我們能否擁有與需要監控的服務相關的資源(例如,使用標籤選擇器)?

這些情況正是自定義資源定義(CRD)資源非常有用的典型使用案例。它們透過向 Kubernetes 叢集新增自定義資源,並像使用原生資源一樣使用它們,從而擴充套件了 Kubernetes API。自定義資源與對這些資源進行操作的控制器共同構成了 Operator 模式。

自定義資源定義(CRD)與 Operator

正如 Jimmy Zelinskie 所說,Operator 的特性可以被精確地描述出來。Operator 利用 CRD 擴充套件 Kubernetes API,使得使用者可以像操作內建資源一樣操作自定義資源。結合控制器對這些自定義資源的操作,Operator 模式為 Kubernetes 平台帶來了更大的靈活性和可擴充套件性。

CRD 的關鍵作用

  1. 擴充套件 Kubernetes API:CRD 允許向 Kubernetes 叢集新增新的資源型別,使其能夠支援更多種類別的工作負載和應用場景。
  2. 封裝操作知識:透過將特定應用程式的操作邏輯封裝在 Operator 中,可以實作對應用程式生命週期的全面管理。
  3. 提高自動化水平:Operator 可以根據自定義資源的變化自動執行相應的操作,從而減少人工干預,提高系統的穩定性和可靠性。

Operator 的優勢

  1. 增強 Kubernetes 的可擴充套件性:Operator 模式允許開發人員根據具體需求擴充套件 Kubernetes 的功能,使其能夠更好地支援各種複雜的應用場景。
  2. 簡化應用程式管理:透過封裝操作知識,Operator 可以簡化應用程式的佈署、升級和維護工作,降低管理的複雜度。
  3. 提高系統的可靠性:Operator 的自動化能力可以及時檢測和修復問題,提高系統的整體可靠性和穩定性。

Kubernetes Operator 模式與自定義資源

Kubernetes Operator 是一種控制器,它結合了對 Kubernetes 及特定領域的深入理解,從而實作自動化管理原本需要人工介入的任務。這種模式的核心在於利用 Kubernetes 的擴充套件能力,透過自定義資源定義(CRD)來管理和操作特定領域的概念。

自定義資源定義(CRD)

CRD 允許我們擴充套件 Kubernetes 的功能,使其能夠管理自定義資源。這些資源透過 Kubernetes API 進行管理,並最終儲存在後端儲存 etcd 中。CoreOS Prometheus Operator 就是透過這種方式實作了 Prometheus 與 Kubernetes 的無縫整合。

CRD 定義範例

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: prometheuses.monitoring.coreos.com
spec:
  group: monitoring.coreos.com
  names:
    kind: Prometheus
    plural: prometheuses
  scope: Namespaced
  versions:
  - name: v1
    storage: true
    served: true
    schema:
      openAPIV3Schema: ....

內容解密:

  • apiVersionkind 指定了這是一個 CRD 資源。
  • metadata.name 定義了 CRD 的名稱。
  • spec.group 指定了該 CRD 所屬的 API 群組。
  • spec.names.kindspec.names.plural 定義了資源的種類別和複數形式名稱。
  • spec.scope 指定了資源的作用範圍(名稱空間或叢集級別)。
  • spec.versions 列出了該 CRD 支援的版本,並指定了儲存版本和服務版本。
  • schema.openAPIV3Schema 用於定義資源的 OpenAPI V3 架構,用於驗證自定義資源。

自定義資源的 Subresources

Kubernetes 允許為 CRD 指定兩個子資源:scalestatus

Subresource 定義範例

kind: CustomResourceDefinition
# ...
spec:
  subresources:
    status: {}
    scale:
      specReplicasPath: .spec.replicas
      statusReplicasPath: .status.replicas
      labelSelectorPath: .status.labelSelector

內容解密:

  • status 子資源允許單獨更新資源的狀態。
  • scale 子資源用於管理資源的副本數量。
  • specReplicasPathstatusReplicasPathlabelSelectorPath 分別指定了宣告副本數量、實際副本數量和標籤選擇器的 JSON 路徑。

自定義資源例項

一旦定義了 CRD,就可以建立相應的自定義資源。

Prometheus 自定義資源範例

apiVersion: monitoring.coreos.com/v1
kind: Prometheus
metadata:
  name: prometheus
spec:
  serviceMonitorSelector:
    matchLabels:
      team: frontend
  resources:
    requests:
      memory: 400Mi

內容解密:

  • metadata 部分遵循 Kubernetes 資源的標準格式。
  • spec 部分包含 CRD 特有的內容,並根據 CRD 中的驗證規則進行驗證。

Controller 和 Operator 分類別

根據 Operator 的功能,可以將其大致分為兩類別:

  1. 安裝型 CRD:用於在 Kubernetes 上安裝和管理應用程式,如 Prometheus Operator。
  2. 應用型 CRD:代表特定領域的概念,使應用程式能夠與 Kubernetes 深度整合,如 ServiceMonitor CRD。

這些分類別並非絕對,Operator 可以作用於多種型別的 CRD,以實作不同的功能。透過這種方式,Kubernetes Operator 模式為管理和自動化複雜應用程式提供了一個強大的框架。

控制器與運算元的類別探討

在探討控制器(Controller)與運算元(Operator)的區別時,我們發現運算元本質上是一種特殊的控制器,它利用自定義資源定義(CRD)來擴充套件 Kubernetes 的功能。

使用 ConfigMap 作為 CRD 的替代方案

在某些情況下,開發者可能會選擇使用 ConfigMap 來替代 CRD。這種做法在某些叢集設定中特別有用,例如在公共叢集(如 OpenShift Online)中,由於許可權限制無法註冊 CRD。使用 ConfigMap 可以封裝領域邏輯,但它也有一些缺點:

  • 缺乏對 CRD 的工具支援,如 kubectl get
  • 無法在 API 伺服器級別進行驗證
  • 對 API 版本控制的支援不足
  • 對狀態列位的建模能力有限

使用 ConfigMap 的優缺點分析

使用 ConfigMap 的主要優點是無需 cluster-admin 許可權,這使得它在某些受限環境中成為一個可行的替代方案。然而,其缺點也不容忽視,特別是在需要精細許可權控制和嚴格的 API 管理時。

自定義資源定義(CRD)的優勢

相較於 ConfigMap,CRD 提供了更豐富的功能和更強的安全性。它允許開發者定義自定義資源的結構和行為,並且可以透過 RBAC(根據角色的存取控制)進行精細的許可權管理。這使得 CRD 成為構建複雜應用程式的理想選擇。

控制器與運算元的實作差異

從實作的角度來看,控制器可以根據其管理的資源型別進行區分。如果控制器僅使用原生的 Kubernetes 物件,那麼其實作相對簡單,因為所需的型別資訊已經包含在 Kubernetes 客戶端函式庫中。然而,如果控制器需要管理自定義資源,則需要額外的處理,例如使用無模式方法或根據 OpenAPI 架構定義自定義型別。

控制器與運算元的光譜

此圖示展示了控制器與運算元的類別,從簡單的資源定義選項到更為複雜的自定義資源。運算元位於這個光譜的高階端,因為它使用了自定義資源。

高階 Kubernetes 擴充套件:API 聚合

對於某些應用場景,Kubernetes 提供的 CRD 可能仍然不足以滿足需求。在這種情況下,可以透過 API 聚合層來擴充套件 Kubernetes API。這涉及到建立一個自定義的 APIService 資源,並將其註冊到 Kubernetes API 伺服器。

API 聚合示例

apiVersion: apiregistration.k8s.io/v1beta1
kind: APIService
metadata:
  name: v1alpha1.sample-api.k8spatterns.io
spec:
  group: sample-api.k8spatterns.io
  service:
    name: custom-api-server
  version: v1alpha1

內容解密:

  • apiVersionkind 指定了資源的型別和版本。
  • metadata.name 定義了 APIService 的名稱。
  • spec.groupspec.version 定義了自定義 API 的組和版本。
  • spec.service.name 指定了提供自定義 API 服務的服務名稱。

運算元開發與佈署工具

有多個工具和框架可以幫助開發和佈署運算元,包括:

  • Kubebuilder:由 Kubernetes SIG API Machinery 開發,提供了一套框架和函式庫,用於建立 Kubernetes API。
  • Operator Framework:一個 CNCF 專案,提供了 Operator SDK、Operator Lifecycle Manager 和 Operator Hub 等元件。
  • Metacontroller:由 Google Cloud Platform 提供,簡化了運算元的開發過程。

這些工具為開發者提供了豐富的功能和靈活性,使得構建和管理複雜的 Kubernetes 應用變得更加容易。