Kubernetes 中的通用表達式語言

通用表達式語言 (CEL) 用於 Kubernetes API 中,以宣告驗證規則、策略規則和其他約束或條件。

CEL 表達式直接在 API 伺服器 中評估,使 CEL 成為許多可擴展性使用案例中,進程外機制(例如 Webhook)的便捷替代方案。只要控制平面的 API 伺服器元件保持可用,您的 CEL 表達式就會繼續執行。

語言總覽

CEL 語言 具有簡單明瞭的語法,類似於 C、C++、Java、JavaScript 和 Go 中的表達式。

CEL 旨在嵌入到應用程式中。每個 CEL「程式」都是一個單一表達式,評估為單一值。CEL 表達式通常是簡短的「單行程式碼」,可以很好地內嵌到 Kubernetes API 資源的字串欄位中。

CEL 程式的輸入是「變數」。每個包含 CEL 的 Kubernetes API 欄位都會在 API 文件中宣告哪些變數可用於該欄位。例如,在 CustomResourceDefinitions 的 x-kubernetes-validations[i].rules 欄位中,selfoldSelf 變數可用,並參照自訂資源資料的先前和目前狀態,以供 CEL 表達式驗證。其他 Kubernetes API 欄位可能會宣告不同的變數。請參閱 API 欄位的 API 文件,以了解哪些變數可用於該欄位。

CEL 表達式範例

CEL 表達式及其用途範例
規則用途
self.minReplicas <= self.replicas && self.replicas <= self.maxReplicas驗證定義副本數的三個欄位是否適當排序
'Available' in self.stateCounts驗證在映射中是否存在具有 'Available' 鍵的條目
(self.list1.size() == 0) != (self.list2.size() == 0)驗證兩個清單中只有一個是非空的,但不能同時為非空
self.envars.filter(e, e.name = 'MY_ENV').all(e, e.value.matches('^[a-zA-Z]*$'))驗證鍵欄位 'name' 為 'MY_ENV' 的 listMap 條目的 'value' 欄位
has(self.expired) && self.created + self.ttl < self.expired驗證 'expired' 日期是否在 'create' 日期加上 'ttl' 持續時間之後
self.health.startsWith('ok')驗證 'health' 字串欄位是否具有字首 'ok'
self.widgets.exists(w, w.key == 'x' && w.foo < 10)驗證具有鍵 'x' 的 listMap 項目中的 'foo' 屬性是否小於 10
type(self) == string ? self == '99%' : self == 42驗證整數或字串欄位的整數和字串案例
self.metadata.name == 'singleton'驗證物件的名稱是否與特定值相符(使其成為單例)
self.set1.all(e, !(e in self.set2))驗證兩個 listSet 是否不相交
self.names.size() == self.details.size() && self.names.all(n, n in self.details)驗證 'details' 映射是否以 'names' listSet 中的項目作為鍵
self.details.all(key, key.matches('^[a-zA-Z]*$'))驗證 'details' 映射的鍵
self.details.all(key, self.details[key].matches('^[a-zA-Z]*$'))驗證 'details' 映射的值

CEL 選項、語言功能和程式庫

CEL 配置了以下選項、程式庫和語言功能,這些功能在指定的 Kubernetes 版本中引入

CEL 選項、程式庫或語言功能已包含可用性
標準巨集hasallexistsexists_onemapfilter所有 Kubernetes 版本
標準函數請參閱 官方標準定義列表所有 Kubernetes 版本
同質聚合字面值所有 Kubernetes 版本
預設 UTC 時區所有 Kubernetes 版本
搶先驗證宣告所有 Kubernetes 版本
擴充字串程式庫,版本 1charAtindexOflastIndexOflowerAsciiupperAsciireplacesplitjoinsubstringtrim所有 Kubernetes 版本
Kubernetes 列表程式庫請參閱 Kubernetes 列表程式庫所有 Kubernetes 版本
Kubernetes 正規表示式程式庫請參閱 Kubernetes 正規表示式程式庫所有 Kubernetes 版本
Kubernetes URL 程式庫請參閱 Kubernetes URL 程式庫所有 Kubernetes 版本
Kubernetes 授權器程式庫請參閱 Kubernetes 授權器程式庫所有 Kubernetes 版本
Kubernetes 數量程式庫請參閱 Kubernetes 數量程式庫Kubernetes 版本 1.29+
CEL 可選類型請參閱 CEL 可選類型Kubernetes 版本 1.29+
CEL CrossTypeNumericComparisons請參閱 CEL CrossTypeNumericComparisonsKubernetes 版本 1.29+

CEL 函數、功能和語言設定支援 Kubernetes 控制平面的回滾。例如,「CEL 可選值」在 Kubernetes 1.29 中引入,因此只有該版本或更新版本的 API 伺服器才會接受對使用「CEL 可選值」的 CEL 表達式的寫入請求。但是,當叢集回滾到 Kubernetes 1.28 時,已儲存在 API 資源中的使用「CEL 可選值」的 CEL 表達式將繼續正確評估。

Kubernetes CEL 程式庫

除了 CEL 社群程式庫之外,Kubernetes 還包含 CEL 程式庫,這些程式庫在 Kubernetes 中使用 CEL 的任何地方都可用。

Kubernetes 列表程式庫

列表程式庫包含 indexOflastIndexOf,其工作方式與同名字串函數類似。這些函數會傳回列表中所提供元素的第一個或最後一個位置索引。

列表程式庫還包含 minmaxsum。Sum 支援所有數字類型以及持續時間類型。Min 和 max 支援所有可比較的類型。

isSorted 也作為便利函數提供,並支援所有可比較的類型。

範例

使用列表程式庫函數的 CEL 表達式範例
CEL 表達式用途
names.isSorted()驗證名稱列表是否按字母順序排列
items.map(x, x.weight).sum() == 1.0驗證物件列表的「權重」總和是否為 1.0
lowPriorities.map(x, x.priority).max() < highPriorities.map(x, x.priority).min()驗證兩組優先順序是否不重疊
names.indexOf('should-be-first') == 1要求列表中的第一個名稱為特定值

請參閱 Kubernetes 列表程式庫 godoc 以取得更多資訊。

Kubernetes 正規表示式程式庫

除了 CEL 標準程式庫提供的 matches 函數之外,正規表示式程式庫還提供 findfindAll,從而實現更廣泛的正規表示式運算。

範例

使用正規表示式程式庫函數的 CEL 表達式範例
CEL 表達式用途
"abc 123".find('[0-9]+')在字串中尋找第一個數字
"1, 2, 3, 4".findAll('[0-9]+').map(x, int(x)).sum() < 100驗證字串中的數字總和小於 100

請參閱 Kubernetes 正規表示式程式庫 godoc 以取得更多資訊。

Kubernetes URL 程式庫

為了更容易和更安全地處理 URL,已新增以下函數

  • isURL(string) 檢查字串是否為根據 Go 的 net/url 套件的有效 URL。字串必須是絕對 URL。
  • url(string) URL 將字串轉換為 URL,如果字串不是有效的 URL,則會產生錯誤。

一旦透過 url 函數解析,產生的 URL 物件具有 getSchemegetHostgetHostnamegetPortgetEscapedPathgetQuery 存取器。

範例

使用 URL 程式庫函數的 CEL 表達式範例
CEL 表達式用途
url('https://example.com:80/').getHost()取得 URL 的 'example.com:80' 主機部分
url('https://example.com/path with spaces/').getEscapedPath()傳回 '/path%20with%20spaces/'

請參閱 Kubernetes URL 程式庫 godoc 以取得更多資訊。

Kubernetes 授權器程式庫

對於 API 中類型為 Authorizer 的變數可用的 CEL 表達式,授權器可用於對請求的主體(已驗證使用者)執行授權檢查。

API 資源檢查執行如下

  1. 指定要檢查的群組和資源:Authorizer.group(string).resource(string) ResourceCheck
  2. 選擇性地呼叫以下建構器函數的任意組合,以進一步縮小授權檢查範圍。請注意,這些函數會傳回接收器類型,並且可以鏈結
    • ResourceCheck.subresource(string) ResourceCheck
    • ResourceCheck.namespace(string) ResourceCheck
    • ResourceCheck.name(string) ResourceCheck
  3. 呼叫 ResourceCheck.check(verb string) Decision 以執行授權檢查。
  4. 呼叫 allowed() boolreason() string 以檢查授權檢查的結果。

執行的非資源授權使用方式如下

  1. 僅指定路徑:Authorizer.path(string) PathCheck
  2. 呼叫 PathCheck.check(httpVerb string) Decision 以執行授權檢查。
  3. 呼叫 allowed() boolreason() string 以檢查授權檢查的結果。

若要對服務帳戶執行授權檢查

  • Authorizer.serviceAccount(namespace string, name string) Authorizer
使用 URL 程式庫函數的 CEL 表達式範例
CEL 表達式用途
authorizer.group('').resource('pods').namespace('default').check('create').allowed()如果主體(使用者或服務帳戶)被允許在 'default' 命名空間中建立 Pod,則傳回 true。
authorizer.path('/healthz').check('get').allowed()檢查主體(使用者或服務帳戶)是否被授權對 /healthz API 路徑發出 HTTP GET 請求。
authorizer.serviceAccount('default', 'myserviceaccount').resource('deployments').check('delete').allowed()檢查服務帳戶是否被授權刪除部署。
功能狀態: Kubernetes v1.31 [alpha]

啟用 Alpha 版 AuthorizeWithSelectors 功能後,欄位和標籤選取器可以新增至授權檢查。

使用選取器授權函數的 CEL 表達式範例
CEL 表達式用途
authorizer.group('').resource('pods').fieldSelector('spec.nodeName=mynode').check('list').allowed()如果主體(使用者或服務帳戶)被允許列出具有欄位選取器 spec.nodeName=mynode 的 Pod,則傳回 true。
authorizer.group('').resource('pods').labelSelector('example.com/mylabel=myvalue').check('list').allowed()如果主體(使用者或服務帳戶)被允許列出具有標籤選取器 example.com/mylabel=myvalue 的 Pod,則傳回 true。

請參閱 Kubernetes Authz 程式庫Kubernetes AuthzSelectors 程式庫 godoc 以取得更多資訊。

Kubernetes 數量程式庫

Kubernetes 1.28 新增了對操作數量字串(例如 1.5G、512k、20Mi)的支援

  • isQuantity(string) 檢查字串是否為根據 Kubernetes 的 resource.Quantity 的有效數量。
  • quantity(string) Quantity 將字串轉換為數量,如果字串不是有效的數量,則會產生錯誤。

一旦透過 quantity 函數解析,產生的 Quantity 物件具有以下成員函數程式庫

Quantity 的可用成員函數
成員函數CEL 傳回值描述
isInteger()bool若且唯若在不發生錯誤的情況下可以安全地呼叫 asInteger,則傳回 true
asInteger()int如果可能,則傳回目前值作為 int64 的表示形式,否則如果轉換會導致溢位或精度損失,則會產生錯誤。
asApproximateFloat()float傳回數量的 float64 表示形式,這可能會損失精度。如果數量的值超出 float64 的範圍,則會傳回 +Inf/-Inf。
sign()int如果數量為正數,則傳回 1,如果為負數,則傳回 -1。如果為零,則傳回 0
add(<Quantity>)Quantity傳回兩個數量的總和
add(<int>)Quantity傳回數量和整數的總和
sub(<Quantity>)Quantity傳回兩個數量之間的差
sub(<int>)Quantity傳回數量和整數之間的差
isLessThan(<Quantity>)bool若且唯若接收器小於運算元,則傳回 true
isGreaterThan(<Quantity>)bool若且唯若接收器大於運算元,則傳回 true
compareTo(<Quantity>)int將接收器與運算元進行比較,如果它們相等,則傳回 0,如果接收器較大,則傳回 1,如果接收器小於運算元,則傳回 -1

範例

使用 URL 程式庫函數的 CEL 表達式範例
CEL 表達式用途
quantity("500000G").isInteger()測試轉換為整數是否會擲回錯誤
quantity("50k").asInteger()精確轉換為整數
quantity("9999999999999999999999999999999999999G").asApproximateFloat()有損轉換為浮點數
quantity("50k").add(quantity("20k"))新增兩個數量
quantity("50k").sub(20000)從數量中減去整數
quantity("50k").add(20).sub(quantity("100k")).sub(-50000)鏈結新增和減去整數和數量
quantity("200M").compareTo(quantity("0.2G"))比較兩個數量
quantity("150Mi").isGreaterThan(quantity("100Mi"))測試數量是否大於接收器
quantity("50M").isLessThan(quantity("100M"))測試數量是否小於接收器

類型檢查

CEL 是一種漸進類型語言

某些 Kubernetes API 欄位包含完全類型檢查的 CEL 表達式。例如,CustomResourceDefinitions 驗證規則是完全類型檢查的。

某些 Kubernetes API 欄位包含部分類型檢查的 CEL 表達式。部分類型檢查的表達式是指某些變數是靜態類型,而其他變數是動態類型的表達式。例如,在 ValidatingAdmissionPolicies 的 CEL 表達式中,request 變數是類型化的,但 object 變數是動態類型的。因此,包含 request.namex 的表達式將會類型檢查失敗,因為未定義 namex 欄位。但是,即使在 object 指向的資源種類未定義 namex 欄位的情況下,object.namex 也會通過類型檢查,因為 object 是動態類型的。

CEL 中的 has() 巨集可用於 CEL 表達式中,以檢查動態類型變數的欄位是否可存取,然後再嘗試存取欄位的值。例如

has(object.namex) ? object.namex == 'special' : request.name == 'special'

類型系統整合

顯示 OpenAPIv3 類型與 CEL 類型之間關係的表格
OpenAPIv3 類型CEL 類型
具有屬性的 'object'object / "訊息類型" (type(<object>) 評估為 selfType<uniqueNumber>.path.to.object.from.self)
具有 AdditionalProperties 的 'object'map
具有 x-kubernetes-embedded-type 的 'object'object / "訊息類型",'apiVersion'、'kind'、'metadata.name' 和 'metadata.generateName' 隱含地包含在結構描述中
具有 x-kubernetes-preserve-unknown-fields 的 'object'object / "訊息類型",未知欄位在 CEL 表達式中不可存取
x-kubernetes-int-or-stringint 或字串的聯集,self.intOrString < 100 || self.intOrString == '50%' 對於 50"50%" 都評估為 true
'array'list
具有 x-kubernetes-list-type=map 的 'array'具有基於映射的相等性與唯一鍵保證的列表
具有 x-kubernetes-list-type=set 的 'array'具有基於集合的相等性與唯一條目保證的列表
'boolean'boolean
'number' (所有格式)double
'integer' (所有格式)int (64)
無等效項uint (64)
'null'null_type
'string'string
'string' 具有 format=byte (base64 編碼)bytes
'string' 具有 format=datetimestamp (google.protobuf.Timestamp)
'string' 具有 format=datetimetimestamp (google.protobuf.Timestamp)
'string' 具有 format=durationduration (google.protobuf.Duration)

另請參閱:CEL 類型OpenAPI 類型Kubernetes 結構化結構描述

對於 x-kubernetes-list-typesetmap 的陣列,相等性比較會忽略元素順序。例如,如果陣列表示 Kubernetes set 值,則 [1, 2] == [2, 1]

對具有 x-kubernetes-list-type 的陣列進行串連會使用列表類型的語意

set
X + Y 執行聯集,其中保留 X 中所有元素的陣列位置,並附加 Y 中不相交的元素,並保留其部分順序。
map
X + Y 執行合併,其中保留 X 中所有鍵的陣列位置,但當 XY 的鍵集相交時,值會被 Y 中的值覆寫。具有不相交鍵的 Y 中的元素會被附加,並保留其部分順序。

逸出

只有形式為 [a-zA-Z_.-/][a-zA-Z0-9_.-/]* 的 Kubernetes 資源屬性名稱可以從 CEL 存取。當在表達式中存取時,可存取的屬性名稱會根據以下規則逸出

CEL 識別碼逸出規則表
逸出序列等效屬性名稱
__underscores____
__dot__.
__dash__-
__slash__/
__{keyword}__CEL 保留關鍵字

當您逸出任何 CEL 的保留關鍵字時,您需要比對確切的屬性名稱,請使用底線逸出(例如,單字 sprint 中的 int 不會逸出,也不需要逸出)。

逸出範例

逸出的 CEL 識別碼範例
屬性名稱具有逸出屬性名稱的規則
namespaceself.__namespace__ > 0
x-propself.x__dash__prop > 0
redact__dself.redact__underscores__d > 0
stringself.startsWith('kube')

資源限制

CEL 不是圖靈完備的,並提供各種生產安全控制來限制執行時間。CEL 的資源限制功能向開發人員提供有關表達式複雜度的回饋,並協助保護 API 伺服器免於在評估期間過度消耗資源。CEL 的資源限制功能用於防止 CEL 評估消耗過多的 API 伺服器資源。

資源限制功能的關鍵要素是成本單位,CEL 將其定義為追蹤 CPU 使用率的方式。成本單位獨立於系統負載和硬體。成本單位也是確定性的;對於任何給定的 CEL 表達式和輸入資料,CEL 解譯器對表達式的評估將始終產生相同的成本。

許多 CEL 的核心運算具有固定成本。最簡單的運算(例如 <)的成本為 1。有些運算具有較高的固定成本,例如列表字面值宣告的固定基本成本為 40 個成本單位。

在本機程式碼中實作的函數呼叫會根據運算的時 間複雜度來估算成本。例如:使用正規表示式的運算(例如 matchfind)是使用 length(regexString)*length(inputString) 的近似成本來估算的。近似成本反映了 Go 的 RE2 實作的最壞情況時間複雜度。

執行時間成本預算

Kubernetes 評估的所有 CEL 表達式都受到執行時間成本預算的限制。執行時間成本預算是對實際 CPU 使用率的估計,透過在解譯 CEL 表達式時遞增成本單位計數器來計算。如果 CEL 解譯器執行的指令過多,則會超過執行時間成本預算,表達式的執行將會停止,並會產生錯誤。

某些 Kubernetes 資源定義了額外的執行時間成本預算,該預算限制了多個表達式的執行。如果表達式的總成本超出預算,則表達式的執行將會停止,並會產生錯誤。例如,自訂資源的驗證對於驗證自訂資源而評估的所有驗證規則具有每次驗證執行時間成本預算。

估計成本限制

對於某些 Kubernetes 資源,API 伺服器也可能會檢查 CEL 表達式的最壞情況估計執行時間是否會過於昂貴而無法執行。如果是這樣,API 伺服器會阻止將 CEL 表達式寫入 API 資源,方法是拒絕包含 CEL 表達式的建立或更新作業到 API 資源。此功能提供了更強的保證,確保寫入 API 資源的 CEL 表達式將在執行時間進行評估,而不會超過執行時間成本預算。

上次修改時間:2024 年 10 月 12 日 11:12 AM PST:Clean up using-api/cel.md (2f7dd7c114)