准入 Webhook 最佳實踐

在 Kubernetes 中設計和部署准入 Webhook 的建議。

本頁面提供了在 Kubernetes 中設計**准入 Webhook** 時的最佳實踐和注意事項。此資訊適用於執行准入 Webhook 伺服器或修改或驗證你的 API 請求的第三方應用程式的叢集操作員。

在閱讀本頁面之前,請確保你熟悉以下概念:

良好 Webhook 設計的重要性

當任何建立、更新或刪除請求傳送到 Kubernetes API 時,就會發生准入控制。准入控制器會攔截符合你定義的特定條件的請求。然後,這些請求會發送到變更准入 Webhook 或驗證准入 Webhook。這些 Webhook 通常用於確保物件規範中的特定欄位存在或具有特定的允許值。

Webhook 是擴充套件 Kubernetes API 的強大機制。設計不當的 Webhook 常常會導致工作負載中斷,因為 Webhook 對叢集中物件的控制權過大。像其他 API 擴充套件機制一樣,Webhook 很難大規模測試其與所有工作負載、其他 Webhook、外掛和附加元件的相容性。

此外,在每個版本中,Kubernetes 都會透過新功能、將功能提升到 Beta 或穩定狀態以及棄用功能來新增或修改 API。即使穩定的 Kubernetes API 也可能會發生變化。例如,Pod API 在 v1.29 中發生了變化,添加了邊車容器功能。雖然 Kubernetes 物件因新的 Kubernetes API 而進入損壞狀態的情況很少見,但與早期版本的 API 正常工作的 Webhook 可能無法協調該 API 的最新更改。這可能導致你在將叢集升級到更新版本後出現意外行為。

本頁面描述了常見的 Webhook 故障場景,以及如何透過謹慎周到地設計和實現 Webhook 來避免這些場景。

識別你是否使用准入 Webhook

即使你不執行自己的准入 Webhook,你在叢集中執行的一些第三方應用程式也可能使用變更或驗證准入 Webhook。

要檢查你的叢集是否有任何變更准入 Webhook,請執行以下命令:

kubectl get mutatingwebhookconfigurations

輸出列出了叢集中的任何變更准入控制器。

要檢查你的叢集是否有任何驗證准入 Webhook,請執行以下命令:

kubectl get validatingwebhookconfigurations

輸出列出了叢集中的任何驗證准入控制器。

選擇准入控制機制

Kubernetes 包含多種准入控制和策略執行選項。瞭解何時使用特定選項可以幫助你提高延遲和效能,減少管理開銷,並避免版本升級期間出現問題。下表描述了允許你在准入期間變更或驗證資源的機制:

Kubernetes 中的變更和驗證准入控制
機制描述用例
變更准入 Webhook在准入前攔截 API 請求,並使用自定義邏輯根據需要進行修改。
  • 進行在資源准入前必須發生的關鍵修改。
  • 進行需要高階邏輯的複雜修改,例如呼叫外部 API。
變更准入策略在准入前攔截 API 請求,並使用通用表示式語言 (CEL) 表示式根據需要進行修改。
  • 進行在資源准入前必須發生的關鍵修改。
  • 進行簡單的修改,例如調整標籤或副本計數。
驗證准入 Webhook在准入前攔截 API 請求,並根據複雜策略宣告進行驗證。
  • 在資源准入前驗證關鍵配置。
  • 在准入前強制執行複雜策略邏輯。
驗證准入策略在准入前攔截 API 請求,並根據 CEL 表示式進行驗證。
  • 在資源准入前驗證關鍵配置。
  • 使用 CEL 表示式強制執行策略邏輯。

通常,當你想要一種可擴充套件的方式來宣告或配置邏輯時,請使用**Webhook**准入控制。當你想要宣告更簡單的邏輯而無需執行 Webhook 伺服器的開銷時,請使用基於內建 CEL 的准入控制。Kubernetes 專案建議你儘可能使用基於 CEL 的准入控制。

對 CustomResourceDefinitions 使用內建驗證和預設值

如果你使用CustomResourceDefinitions,請不要使用准入 Webhook 來驗證 CustomResource 規範中的值或設定欄位的預設值。Kubernetes 允許你在建立 CustomResourceDefinitions 時定義驗證規則和預設欄位值。

要了解更多資訊,請參閱以下資源:

效能和延遲

本節描述了提高效能和減少延遲的建議。總而言之,這些建議如下:

  • 整合 Webhook 並限制每個 Webhook 的 API 呼叫次數。
  • 使用審計日誌檢查重複執行相同操作的 Webhook。
  • 使用負載均衡來確保 Webhook 的可用性。
  • 為每個 Webhook 設定較小的超時值。
  • 在 Webhook 設計期間考慮叢集可用性需求。

設計低延遲的准入 Webhook

變更准入 Webhook 是按順序呼叫的。根據變更 Webhook 的設定,某些 Webhook 可能會被多次呼叫。每次變更 Webhook 呼叫都會增加准入過程的延遲。這與驗證 Webhook 不同,後者是並行呼叫的。

在設計變更 Webhook 時,請考慮你的延遲要求和容忍度。叢集中變更 Webhook 越多,延遲增加的可能性就越大。

考慮以下因素以減少延遲:

  • 整合對不同物件執行類似變更的 Webhook。
  • 減少變更 Webhook 伺服器邏輯中進行的 API 呼叫次數。
  • 限制每個變更 Webhook 的匹配條件,以減少特定 API 請求觸發的 Webhook 數量。
  • 將小型 Webhook 整合到一個伺服器和配置中,以幫助進行排序和組織。

防止競爭控制器造成的迴圈

考慮叢集中執行的任何其他元件,這些元件可能與你的 Webhook 所做的變更發生衝突。例如,如果你的 Webhook 添加了一個其他控制器刪除的標籤,你的 Webhook 將再次被呼叫。這會導致迴圈。

要檢測這些迴圈,請嘗試以下操作:

  1. 更新你的叢集審計策略以記錄審計事件。使用以下引數:

    • level: RequestResponse
    • verbs: ["patch"]
    • omitStages: RequestReceived

    設定審計規則,為你的 Webhook 變更的特定資源建立事件。

  2. 檢查你的審計事件,檢視 Webhook 是否被多次重新呼叫,並且相同的補丁被應用於相同的物件,或者物件是否被多次更新和回滾。

設定較小的超時值

准入 Webhook 應該儘快評估(通常在幾毫秒內),因為它們會增加 API 請求的延遲。為 Webhook 使用較小的超時。

有關詳細資訊,請參閱超時

使用負載均衡器確保 Webhook 可用性

准入 Webhook 應該利用某種形式的負載均衡來提供高可用性和效能優勢。如果 Webhook 在叢集內執行,你可以在型別為ClusterIP的服務後面執行多個 Webhook 後端。

使用高可用性部署模型

在設計 Webhook 時,請考慮叢集的可用性要求。例如,在節點停機或區域中斷期間,Kubernetes 會將 Pod 標記為NotReady,以允許負載均衡器將流量重新路由到可用區域和節點。對 Pod 的這些更新可能會觸發你的變更 Webhook。根據受影響的 Pod 數量,變更 Webhook 伺服器有超時或導致 Pod 處理延遲的風險。因此,流量將無法按你需要的那樣快速重新路由。

在編寫 Webhook 時,請考慮上述示例中的情況。排除因 Kubernetes 響應不可避免的事件而導致的操作。

請求過濾

本節提供了關於過濾哪些請求觸發特定 Webhook 的建議。總而言之,這些建議如下:

  • 限制 Webhook 範圍,以避免系統元件和只讀請求。
  • 將 Webhook 限制到特定名稱空間。
  • 使用匹配條件執行精細的請求過濾。
  • 匹配物件的所有版本。

限制每個 Webhook 的範圍

只有當 API 請求與相應的 Webhook 配置匹配時,才會呼叫准入 Webhook。限制每個 Webhook 的範圍,以減少對 Webhook 伺服器不必要的呼叫。考慮以下範圍限制:

  • 避免匹配kube-system名稱空間中的物件。如果你在kube-system名稱空間中執行自己的 Pod,請使用objectSelector來避免變更關鍵工作負載。
  • 不要變更節點租約,它們作為 Lease 物件存在於kube-node-lease系統名稱空間中。變更節點租約可能導致節點升級失敗。僅在你確信控制元件不會使叢集面臨風險的情況下,才將驗證控制應用於此名稱空間中的 Lease 物件。
  • 不要變更 TokenReview 或 SubjectAccessReview 物件。這些始終是隻讀請求。修改這些物件可能會損壞你的叢集。
  • 透過使用namespaceSelector將每個 Webhook 限制到特定名稱空間。

使用匹配條件過濾特定請求

准入控制器支援多個欄位,你可以使用這些欄位來匹配符合特定條件的請求。例如,你可以使用namespaceSelector來過濾針對特定名稱空間的請求。

對於更精細的請求過濾,請在 Webhook 配置中使用matchConditions欄位。此欄位允許你編寫多個 CEL 表示式,這些表示式必須評估為true才能使請求觸發你的准入 Webhook。使用matchConditions可以顯著減少對 Webhook 伺服器的呼叫次數。

有關詳細資訊,請參閱匹配請求:matchConditions

匹配 API 的所有版本

預設情況下,准入 Webhook 會在影響指定資源的任何 API 版本上執行。Webhook 配置中的matchPolicy欄位控制此行為。在matchPolicy欄位中指定Equivalent值或省略該欄位以允許 Webhook 在任何 API 版本上執行。

有關詳細資訊,請參閱匹配請求:matchPolicy

變更範圍和欄位注意事項

本節提供了關於變更範圍和物件欄位的任何特殊注意事項的建議。總而言之,這些建議如下:

  • 僅修補你需要修補的欄位。
  • 不要覆蓋陣列值。
  • 儘可能避免變更中的副作用。
  • 避免自我變更。
  • 失敗開放並驗證最終狀態。
  • 規劃未來版本中的欄位更新。
  • 防止 Webhook 自我觸發。
  • 不要更改不可變物件。

僅修補所需欄位

准入 Webhook 伺服器傳送 HTTP 響應以指示如何處理特定的 Kubernetes API 請求。此響應是一個 AdmissionReview 物件。變更 Webhook 可以透過在響應中使用patchType欄位和patch欄位來新增特定的欄位進行變更,然後再允許准入。確保你只修改需要更改的欄位。

例如,考慮一個變更 Webhook,它配置為確保web-server Deployment 至少有三個副本。當建立 Deployment 物件的請求與你的 Webhook 配置匹配時,Webhook 應該只更新spec.replicas欄位中的值。

不要覆蓋陣列值

Kubernetes 物件規範中的欄位可能包含陣列。某些陣列包含鍵值對(例如容器規範中的envVar欄位),而其他陣列是無鍵的(例如 Pod 規範中的readinessGates欄位)。在某些情況下,陣列欄位中值的順序可能很重要。例如,容器規範中args欄位中引數的順序可能影響容器。

修改陣列時請考慮以下事項:

  • 儘可能使用add JSONPatch 操作而不是replace,以避免意外替換所需值。
  • 將不使用鍵值對的陣列視為集合。
  • 確保你修改的欄位中的值不需要以特定順序排列。
  • 除非絕對必要,否則不要覆蓋現有的鍵值對。
  • 修改標籤欄位時要小心。意外修改可能會導致標籤選擇器中斷,從而導致意外行為。

避免副作用

確保你的 Webhook 僅對傳送給它們的 AdmissionReview 的內容進行操作,並且不會進行帶外更改。這些額外的更改,稱為**副作用**,如果處理不當,可能會在准入期間導致衝突。如果 Webhook 沒有副作用,則.webhooks[].sideEffects欄位應設定為None

如果在准入評估期間需要副作用,則在處理dryRun設定為true的 AdmissionReview 物件時必須抑制它們,並且.webhooks[].sideEffects欄位應設定為NoneOnDryRun

有關詳細資訊,請參閱副作用

避免自我變更

在叢集內執行的 Webhook 如果配置為攔截啟動其自身 Pod 所需的資源,則可能導致其自身的部署死鎖。

例如,變更准入 Webhook 配置為僅當 Pod 中設定了特定標籤(例如env: prod)時才准入**建立** Pod 請求。Webhook 伺服器在未設定env標籤的 Deployment 中執行。

當執行 Webhook 伺服器 Pod 的節點變得不健康時,Webhook Deployment 會嘗試將 Pod 重新排程到另一個節點。但是,由於未設定env標籤,現有 Webhook 伺服器會拒絕請求。結果,遷移無法發生。

使用namespaceSelector排除 Webhook 正在執行的名稱空間。

避免依賴迴圈

依賴迴圈可能發生在以下場景中:

  • 兩個 Webhook 相互檢查對方的 Pod。如果兩個 Webhook 同時變得不可用,則兩個 Webhook 都無法啟動。
  • 你的 Webhook 攔截叢集附加元件(例如網路外掛或儲存外掛),而你的 Webhook 依賴這些元件。如果 Webhook 和依賴的附加元件同時變得不可用,則兩個元件都無法正常工作。

為了避免這些依賴迴圈,請嘗試以下操作:

失敗開放並驗證最終狀態

變更准入 Webhook 支援failurePolicy配置欄位。此欄位指示如果 Webhook 失敗,API 伺服器是否應准入或拒絕請求。Webhook 失敗可能由於超時或伺服器邏輯中的錯誤而發生。

預設情況下,准入 Webhook 將failurePolicy欄位設定為 Fail。如果 Webhook 失敗,API 伺服器將拒絕請求。但是,預設拒絕請求可能導致在 Webhook 停機期間合規請求被拒絕。

透過將failurePolicy欄位設定為 Ignore,讓你的變更 Webhook“失敗開放”。使用驗證控制器檢查請求的狀態,以確保它們符合你的策略。

這種方法具有以下優點:

  • 變更 Webhook 停機不會影響合規資源的部署。
  • 策略執行在驗證准入控制期間發生。
  • 變更 Webhook 不會干擾叢集中的其他控制器。

規劃未來的欄位更新

通常,在設計 Webhook 時,請假設 Kubernetes API 可能會在更高版本中發生變化。不要編寫一個想當然地認為 API 穩定性的伺服器。例如,Kubernetes 中邊車容器的釋出在 Pod API 中添加了一個restartPolicy欄位。

防止你的 Webhook 自我觸發

響應廣泛 API 請求的變更 Webhook 可能會無意中自我觸發。例如,考慮一個響應叢集中所有請求的 Webhook。如果你將 Webhook 配置為為每次變更建立 Event 物件,它將響應其自己的 Event 物件建立請求。

為了避免這種情況,請考慮在你建立的任何資源中設定一個唯一的標籤。將此標籤從你的 Webhook 匹配條件中排除。

不要更改不可變物件

API 伺服器中的某些 Kubernetes 物件無法更改。例如,當你部署靜態 Pod時,節點上的 kubelet 會在 API 伺服器中建立映象 Pod來跟蹤靜態 Pod。但是,對映象 Pod 的更改不會傳播到靜態 Pod。

在准入期間不要嘗試變更這些物件。所有映象 Pod 都有kubernetes.io/config.mirror註解。為了在減少忽略註解的安全風險的同時排除映象 Pod,允許靜態 Pod 僅在特定名稱空間中執行。

變更 Webhook 排序和冪等性

本節提供了關於 Webhook 順序和設計冪等 Webhook 的建議。總而言之,這些建議如下:

  • 不要依賴特定的執行順序。
  • 在准入前驗證變更。
  • 檢查變更是否被其他控制器覆蓋。
  • 確保變更 Webhook 集是冪等的,而不僅僅是單個 Webhook。

不要依賴變更 Webhook 的呼叫順序

變更准入 Webhook 不會以一致的順序執行。各種因素可能會改變特定 Webhook 的呼叫時間。不要依賴你的 Webhook 在准入過程中的特定點執行。其他 Webhook 仍可能變更你修改的物件。

以下建議可能有助於最大限度地降低意外更改的風險:

確保叢集中的變更 Webhook 具有冪等性

每個變更准入 Webhook 都應具有**冪等性**。Webhook 應該能夠在它已經修改過的物件上執行,而不會在原始更改之外進行額外的更改。

此外,叢集中的所有變更 Webhook,作為一個集合,都應具有冪等性。在准入控制的變更階段結束後,每個單獨的變更 Webhook 都應該能夠在物件上執行,而不會對物件進行額外的更改。

根據你的環境,大規模確保冪等性可能具有挑戰性。以下建議可能有所幫助:

  • 使用驗證准入控制器來驗證關鍵工作負載的最終狀態。
  • 在預演叢集中測試你的部署,檢視是否有任何物件被相同的 Webhook 修改多次。
  • 確保每個變更 Webhook 的範圍是特定的和有限的。

以下示例顯示了冪等變更邏輯:

  1. 對於**建立** Pod 請求,將 Pod 的.spec.securityContext.runAsNonRoot欄位設定為 true。

  2. 對於**建立** Pod 請求,如果容器的.spec.containers[].resources.limits欄位未設定,則設定預設資源限制。

  3. 對於**建立** Pod 請求,如果不存在名為foo-sidecar的容器,則注入一個名為foo-sidecar的邊車容器。

在這些情況下,Webhook 可以安全地重新呼叫,或者准入一個已經設定了欄位的物件。

以下示例顯示了非冪等變更邏輯:

  1. 對於**建立** Pod 請求,注入一個名為foo-sidecar並帶有當前時間戳字尾的邊車容器(例如foo-sidecar-19700101-000000)。

    重新呼叫 Webhook 可能導致同一個邊車被多次注入到 Pod 中,每次都使用不同的容器名稱。類似地,如果邊車已經存在於使用者提供的 Pod 中,Webhook 可能會注入重複的容器。

  2. 對於**建立**/**更新** Pod 請求,如果 Pod 具有標籤env,則拒絕,否則向 Pod 新增env: prod標籤。

    重新呼叫 Webhook 將導致 Webhook 在其自己的輸出上失敗。

  3. 對於**建立** Pod 請求,附加一個名為foo-sidecar的邊車容器,而不檢查是否存在foo-sidecar容器。

    重新呼叫 Webhook 將導致 Pod 中出現重複的容器,從而使請求無效並被 API 伺服器拒絕。

變更測試與驗證

本節提供了關於測試變更 Webhook 和驗證變更物件的建議。總而言之,這些建議如下:

  • 在預演環境中測試 Webhook。
  • 避免違反驗證的變更。
  • 測試小版本升級以檢查迴歸和衝突。
  • 在准入前驗證變更物件。

在預演環境中測試 Webhook

健壯的測試應成為你的新或更新 Webhook 釋出週期的核心部分。如果可能,請在與你的生產叢集非常相似的預演環境中測試你叢集 Webhook 的任何更改。至少,考慮使用minikubekind等工具為 Webhook 更改建立一個小型測試叢集。

確保變更不會違反驗證

你的變更 Webhook 不應破壞在准入前適用於物件的任何驗證。例如,考慮一個將 Pod 的預設 CPU 請求設定為特定值的變更 Webhook。如果該 Pod 的 CPU 限制設定為低於變更請求的值,則該 Pod 將無法准入。

根據在叢集中執行的驗證測試每個變更 Webhook。

測試小版本升級以確保行為一致

在將生產叢集升級到新的小版本之前,請在預演環境中測試你的 Webhook 和工作負載。比較結果以確保你的 Webhook 在升級後繼續按預期執行。

此外,請使用以下資源瞭解 API 更改:

在准入前驗證變更

變更 Webhook 在任何驗證 Webhook 執行之前完成執行。應用於物件的變更沒有穩定的順序。因此,你的變更可能會被稍後執行的變更 Webhook 覆蓋。

向叢集新增驗證准入控制器,例如 ValidatingAdmissionWebhook 或 ValidatingAdmissionPolicy,以確保你的變更仍然存在。例如,考慮一個變更 Webhook,它將restartPolicy: Always欄位插入到特定的 init 容器中,使它們作為邊車容器執行。你可以執行一個驗證 Webhook 來確保這些 init 容器在所有變更完成後保留了restartPolicy: Always配置。

有關詳細資訊,請參閱以下資源:

變更 Webhook 部署

本節提供了部署變更准入 Webhook 的建議。總而言之,這些建議如下:

  • 逐步推出 Webhook 配置,並按名稱空間監控問題。
  • 限制對編輯 Webhook 配置資源的訪問。
  • 限制對執行 Webhook 伺服器的名稱空間的訪問(如果伺服器在叢集內)。

安裝並啟用變更 Webhook

當你準備好將變更 Webhook 部署到叢集時,請使用以下操作順序:

  1. 安裝 Webhook 伺服器並啟動它。
  2. 將 MutatingWebhookConfiguration 清單中的failurePolicy欄位設定為 Ignore。這可以讓你避免由於配置錯誤的 Webhook 造成的干擾。
  3. 將 MutatingWebhookConfiguration 清單中的namespaceSelector欄位設定為測試名稱空間。
  4. 將 MutatingWebhookConfiguration 部署到你的叢集。

在測試名稱空間中監控 Webhook 以檢查任何問題,然後將 Webhook 推出到其他名稱空間。如果 Webhook 攔截了它不應該攔截的 API 請求,請暫停推出並調整 Webhook 配置的範圍。

限制對變更 Webhook 的編輯訪問

變更 Webhook 是強大的 Kubernetes 控制器。使用 RBAC 或其他授權機制來限制對你的 Webhook 配置和伺服器的訪問。對於 RBAC,確保以下訪問僅對受信任的實體可用:

  • 動詞:**create**、**update**、**patch**、**delete**、**deletecollection**
  • API 組:admissionregistration.k8s.io/v1
  • API 種類:MutatingWebhookConfigurations

如果你的變更 Webhook 伺服器在叢集中執行,請限制建立或修改該名稱空間中任何資源的訪問。

良好實現的示例

以下專案是“良好”自定義 Webhook 伺服器實現的示例。你可以將它們作為設計自己的 Webhook 的起點。不要按原樣使用這些示例;請將它們作為起點,並設計你的 Webhook 以在你的特定環境中良好執行。

下一步

本頁上的專案引用了提供 Kubernetes 所需功能的第三方產品或專案。Kubernetes 專案作者不對這些第三方產品或專案負責。有關詳細資訊,請參閱CNCF 網站指南

在提議新增額外第三方連結的更改之前,你應該閱讀內容指南

最後修改於 2025 年 2 月 19 日太平洋標準時間下午 4:15:新增依賴迴圈建議。(fc27b949e2)