#作者:程宏斌
文章目錄
- 動態準入控制
- 什么是準入 Webhook?
- 嘗試準入Webhook
- 先決條件
- 編寫一個準入 Webhook 服務器
- 部署準入 Webhook 服務
- 即時配置準入 Webhook
- 對 API 服務器進行身份認證
- Webhook 請求與響應
- Webhook 配置
- 匹配請求-規則
- 匹配請求:objectSelector
- 匹配請求:namespaceSelector
- 匹配請求:matchPolicy
- 匹配請求:matchConditions
- 調用Webhook
- 服務引用
- 副作用
- 超時
- 再調用策略
- 失敗策略
動態準入控制
什么是準入 Webhook?
準入 Webhook 是一種用于接收準入請求并對其進行處理的 HTTP 回調機制。 可以定義兩種類型的準入 Webhook, 即驗證性質的準入 Webhook 和變更性質的準入 Webhook。 變更性質的準入 Webhook 會先被調用。它們可以修改發送到 API 服務器的對象以執行自定義的設置默認值操作。
在完成了所有對象修改并且 API 服務器也驗證了所傳入的對象之后, 驗證性質的 Webhook 會被調用,并通過拒絕請求的方式來強制實施自定義的策略。
說明:
如果準入 Webhook 需要保證它們所看到的是對象的最終狀態以實施某種策略。 則應使用驗證性質的準入 Webhook,因為對象被修改性質 Webhook 看到之后仍然可能被修改。
嘗試準入Webhook
準入 Webhook 本質上是集群控制平面的一部分。你應該非常謹慎地編寫和部署它們。 如果你打算編寫或者部署生產級準入 Webhook, 請閱讀用戶指南以獲取相關說明。 在下文中,我們將介紹如何快速試驗準入 Webhook。
先決條件
- 確保啟用 MutatingAdmissionWebhook 和 ValidatingAdmissionWebhook 控制器。 這里是一組推薦的準入控制器, 通常可以啟用。
- 確保啟用了 admissionregistration.k8s.io/v1 API。
編寫一個準入 Webhook 服務器
請參閱 Kubernetes e2e 測試中的 Admission Webhook 服務器的實現。 Webhook 處理由 API 服務器發送的 AdmissionReview 請求,并且將其決定作為 AdmissionReview 對象以相同版本發送回去。
有關發送到 Webhook 的數據的詳細信息,請參閱 Webhook 請求。
要獲取來自 Webhook 的預期數據,請參閱 Webhook 響應。
示例準入 Webhook 服務器置 ClientAuth 字段為空, 默認為 NoClientCert 。這意味著 Webhook 服務器不會驗證客戶端的身份,認為其是 API 服務器。 如果你需要雙向 TLS 或其他方式來驗證客戶端, 請參閱如何對 API 服務器進行身份認證。
部署準入 Webhook 服務
e2e 測試中的 Webhook 服務器通過 deployment API 部署在 Kubernetes 集群中。該測試還將創建一個 Service 作為 Webhook 服務器的前端。 參見相關代碼。
你也可以在集群外部署 Webhook。這樣做需要相應地更新你的 Webhook 配置。
即時配置準入 Webhook
你可以通過 ValidatingWebhookConfiguration 或者 MutatingWebhookConfiguration 動態配置哪些資源要被哪些準入 Webhook 處理。
對 API 服務器進行身份認證
如果你的 Webhook 需要身份驗證,則可以將 API 服務器配置為使用基本身份驗證、持有者令牌或證書來向 Webhook 提供身份證明。完成此配置需要三個步驟。
啟動 API 服務器時,通過 --admission-control-config-file 參數指定準入控制配置文件的位置。
在準入控制配置文件中,指定 MutatingAdmissionWebhook 控制器和 ValidatingAdmissionWebhook 控制器應該讀取憑據的位置。 憑證存儲在 kubeConfig 文件中(是??的,與 kubectl 使用的模式相同),因此字段名稱為 kubeConfigFile。
Webhook 請求與響應
請求
Webhook 發送 POST 請求時,請設置 Content-Type: application/json 并對 admission.k8s.io API 組中的 AdmissionReview 對象進行序列化,將所得到的 JSON 作為請求的主體。
Webhook 可以在配置中的 admissionReviewVersions 字段指定可接受的 AdmissionReview 對象版本:
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
webhooks:
- name: my-webhook.example.com
admissionReviewVersions: ["v1", "v1beta1"]
創建 Webhook 配置時,admissionReviewVersions 是必填字段。 Webhook 必須支持至少一個當前和以前的 API 服務器都可以解析的 AdmissionReview 版本。
API 服務器將發送的是 admissionReviewVersions 列表中所支持的第一個 AdmissionReview 版本。 如果 API 服務器不支持列表中的任何版本,則不允許創建配置。
如果 API 服務器遇到以前創建的 Webhook 配置,并且不支持該 API 服務器知道如何發送的任何 AdmissionReview 版本,則調用 Webhook 的嘗試將失敗,并依據失敗策略進行處理。
響應
Webhook 使用 HTTP 200 狀態碼、Content-Type: application/json 和一個包含 AdmissionReview 對象的 JSON 序列化格式來發送響應。該 AdmissionReview 對象與發送的版本相同,且其中包含的 response 字段已被有效填充。
response 至少必須包含以下字段:
- uid,從發送到 Webhook 的 request.uid 中復制而來
- allowed,設置為 true 或 false
Webhook 配置
要注冊準入 Webhook,請創建 MutatingWebhookConfiguration 或 ValidatingWebhookConfiguration API 對象。 MutatingWebhookConfiguration 或ValidatingWebhookConfiguration 對象的名稱必須是有效的 DNS 子域名。
每種配置可以包含一個或多個 Webhook。如果在單個配置中指定了多個 Webhook,則應為每個 Webhook 賦予一個唯一的名稱。 這是必需的,以使生成的審計日志和指標更易于與激活的配置相匹配。
每個 Webhook 定義以下內容。
匹配請求-規則
每個 Webhook 必須指定用于確定是否應將對 apiserver 的請求發送到 Webhook 的規則列表。 每個規則都指定一個或多個 operations、apiGroups、apiVersions 和 resources 以及資源的 scope:
- operations 列出一個或多個要匹配的操作。 可以是 CREATE、UPDATE、DELETE、CONNECT 或 * 以匹配所有內容。
- apiGroups 列出了一個或多個要匹配的 API 組。“” 是核心 API 組。“*” 匹配所有 API 組。
- apiVersions 列出了一個或多個要匹配的 API 版本。“*” 匹配所有 API 版本。
- resources 列出了一個或多個要匹配的資源。
- “*” 匹配所有資源,但不包括子資源。
- “/” 匹配所有資源,包括子資源。
- “pods/*” 匹配 pod 的所有子資源。
- “*/status” 匹配所有 status 子資源。
- scope 指定要匹配的范圍。有效值為 “Cluster”、“Namespaced” 和 “"。 子資源匹配其父資源的范圍。默認值為 "”。
- “Cluster” 表示只有集群作用域的資源才能匹配此規則(API 對象 Namespace 是集群作用域的)。
- “Namespaced” 意味著僅具有名字空間的資源才符合此規則。
- “*” 表示沒有作用域限制。
如果傳入請求與任何 Webhook rules 的指定 operations、groups、versions、 resources 和 scope 匹配,則該請求將發送到 Webhook。
以下是可用于指定應攔截哪些資源的規則的其他示例。
匹配請求:objectSelector
通過指定 objectSelector,Webhook 能夠根據可能發送的對象的標簽來限制哪些請求被攔截。 如果指定,則將對 objectSelector 和可能發送到 Webhook 的 object 和 oldObject 進行評估。如果兩個對象之一與選擇算符匹配,則認為該請求已匹配。
空對象(對于創建操作而言為 oldObject,對于刪除操作而言為 newObject), 或不能帶標簽的對象(例如 DeploymentRollback 或 PodProxyOptions 對象) 被認為不匹配。
僅當選擇使用 Webhook 時才使用對象選擇器,因為最終用戶可以通過設置標簽來 跳過準入 Webhook。
匹配請求:namespaceSelector
通過指定 namespaceSelector, Webhook 可以根據具有名字空間的資源所處的名字空間的標簽來選擇攔截哪些資源的操作。
namespaceSelector 根據名字空間的標簽是否匹配選擇算符,決定是否針對具名字空間的資源 (或 Namespace 對象)的請求運行 Webhook。 如果對象是除 Namespace 以外的集群范圍的資源,則 namespaceSelector 標簽無效。
匹配請求:matchPolicy
API 服務器可以通過多個 API 組或版本來提供對象。
例如,如果一個 Webhook 僅為某些 API 組/版本指定了規則(例如 apiGroups:[“apps”], apiVersions:[“v1”,“v1beta1”]),而修改資源的請求是通過另一個 API 組/版本(例如 extensions/v1beta1)發出的,該請求將不會被發送到 Webhook。
matchPolicy 允許 Webhook 定義如何使用其 rules 匹配傳入的請求。 允許的值為 Exact 或 Equivalent。
- Exact 表示僅當請求與指定規則完全匹配時才應攔截該請求。
- Equivalent 表示如果某個請求意在修改 rules 中列出的資源, 即使該請求是通過其他 API 組或版本發起,也應攔截該請求。
在上面給出的示例中,僅為 apps/v1 注冊的 Webhook 可以使用 matchPolicy: - matchPolicy: Exact 表示不會將 extensions/v1beta1 請求發送到 Webhook
- matchPolicy:Equivalent 表示將 extensions/v1beta1 請求發送到 Webhook (將對象轉換為 Webhook 指定的版本:apps/v1)
建議指定 Equivalent,確保升級后啟用 API 服務器中資源的新版本時, Webhook 繼續攔截他們期望的資源。
當 API 服務器停止提供某資源時,該資源不再被視為等同于該資源的其他仍在提供服務的版本。 例如,extensions/v1beta1 中的 Deployment 已被廢棄,計劃在 v1.16 中移除。
移除后,帶有 apiGroups:[“extensions”], apiVersions:[“v1beta1”], resources: [“deployments”] 規則的 Webhook 將不再攔截通過 apps/v1 API 來創建的 Deployment。 因此,Webhook 應該優先注冊穩定版本的資源。
匹配請求:matchConditions
特性狀態: Kubernetes v1.30 [stable] (enabled by default: true)
如果你需要細粒度地過濾請求,你可以為 Webhook 定義匹配條件。 如果你發現匹配規則、objectSelectors 和 namespaceSelectors 仍然不能提供你想要的何時進行 HTTP 調用的過濾條件,那么添加這些條件會很有用。 匹配條件是 CEL 表達式。 所有匹配條件都必須為 true 才能調用 Webhook。
匹配條件可以訪問以下 CEL 變量:
- object - 來自傳入請求的對象。對于 DELETE 請求,該值為 null。 該對象版本可能根據 matchPolicy 進行轉換。
- oldObject - 現有對象。對于 CREATE 請求,該值為 null。
- request - AdmissionReview 的請求部分,不包括 object 和 oldObject。
- authorizer - 一個 CEL 鑒權組件。可用于對請求的主體(經過身份認證的用戶)執行鑒權檢查。 更多詳細信息,請參閱 Kubernetes CEL 庫文檔中的 Authz。
- authorizer.requestResource - 對配置的請求資源(組、資源、(子資源)、名字空間、名稱)進行授權檢查的快捷方式。
調用Webhook
API 服務器確定請求應發送到 Webhook 后,它需要知道如何調用 Webhook。 此信息在 Webhook 配置的 clientConfig 節中指定。
Webhook 可以通過 URL 或服務引用來調用,并且可以選擇包含自定義 CA 包,以用于驗證 TLS 連接。
URL
url 以標準 URL 形式給出 Webhook 的位置(scheme://host:port/path)。
host 不應引用集群中運行的服務;通過指定 service 字段來使用服務引用。 主機可以通過某些 API 服務器中的外部 DNS 進行解析。 (例如,kube-apiserver 無法解析集群內 DNS,因為這將違反分層規則)。host 也可以是 IP 地址。
請注意,將 localhost 或 127.0.0.1 用作 host 是有風險的, 除非你非常小心地在所有運行 apiserver 的、可能需要對此 Webhook 進行調用的主機上運行。這樣的安裝方式可能不具有可移植性,即很難在新集群中啟用。
scheme 必須為 “https”;URL 必須以 “https://” 開頭。
使用用戶或基本身份驗證(例如:user:password@)是不允許的。 使用片段(#…)和查詢參數(?..)也是不允許的。
服務引用
clientConfig 內部的 Service 是對該 Webhook 服務的引用。 如果 Webhook 在集群中運行,則應使用 service 而不是 url。 服務的 namespace 和 name 是必需的。 port 是可選的,默認值為 443。path 是可選的,默認為 “/”。
副作用
Webhook 通常僅對發送給他們的 AdmissionReview 內容進行操作。 但是,某些 Webhook 在處理 admission 請求時會進行帶外更改。
進行帶外更改的(產生“副作用”的)Webhook 必須具有協調機制(如控制器), 該機制定期確定事物的實際狀態,并調整由準入 Webhook 修改的帶外數據以反映現實情況。 這是因為對準入 Webhook 的調用不能保證所準入的對象將原樣保留,或根本不保留。 以后,Webhook 可以修改對象的內容,在寫入存儲時可能會發生沖突, 或者服務器可以在持久保存對象之前關閉電源。
此外,處理 dryRun: true admission 請求時,具有副作用的 Webhook 必須避免產生副作用。 一個 Webhook 必須明確指出在使用 dryRun 運行時不會有副作用, 否則 dry-run 請求將不會發送到該 Webhook,而 API 請求將會失敗。
Webhook 使用 Webhook 配置中的 sideEffects 字段顯示它們是否有副作用:
- None:調用 Webhook 沒有副作用。
- NoneOnDryRun:調用 Webhook 可能會有副作用,但是如果將帶有 dryRun: true 屬性的請求發送到 Webhook,則 Webhook 將抑制副作用(該 Webhook 可識別 dryRun)。
超時
由于 Webhook 會增加 API 請求的延遲,因此應盡快完成自身的操作。 timeoutSeconds 用來配置在將調用視為失敗之前,允許 API 服務器等待 Webhook 響應的時間長度。
如果超時在 Webhook 響應之前被觸發,則基于失敗策略,將忽略 Webhook 調用或拒絕 API 調用。
超時值必須設置在 1 到 30 秒之間。
再調用策略
變更性質的準入插件(包括 Webhook)的任何一種排序方式都不會適用于所有情況。 (參見 https://issue.k8s.io/64333 示例)。 變更性質的 Webhook 可以向對象中添加新的子結構(例如向 pod 中添加 container), 已經運行的其他修改插件可能會對這些新結構有影響 (就像在所有容器上設置 imagePullPolicy 一樣)。
要允許變更性質的準入插件感應到其他插件所做的更改, 如果變更性質的 Webhook 修改了一個對象,則會重新運行內置的變更性質的準入插件, 并且變更性質的 Webhook 可以指定 reinvocationPolicy 來控制是否也重新調用它們。
可以將 reinvocationPolicy 設置為 Never 或 IfNeeded。 默認為 Never。
- Never: 在一次準入測試中,不得多次調用 Webhook。
- IfNeeded: 如果在最初的 Webhook 調用之后被其他對象的插件修改了被接納的對象, 則可以作為準入測試的一部分再次調用該 Webhook。
要注意的重要因素有: - 不能保證附加調用的次數恰好是一。
- 如果其他調用導致對該對象的進一步修改,則不能保證再次調用 Webhook。
- 使用此選項的 Webhook 可能會重新排序,以最大程度地減少額外調用的次數。
- 要在確保所有修改都完成后驗證對象,請改用驗證性質的 Webhook (推薦用于有副作用的 Webhook)。
失敗策略
failurePolicy 定義了如何處理準入 Webhook 中無法識別的錯誤和超時錯誤。允許的值為 Ignore 或 Fail。
- Ignore 表示調用 Webhook 的錯誤將被忽略并且允許 API 請求繼續。
- Fail 表示調用 Webhook 的錯誤導致準入失敗并且 API 請求被拒絕。