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
欄位中,self
和 oldSelf
變數可用,並參照自訂資源資料的先前和目前狀態,以供 CEL 表達式驗證。其他 Kubernetes API 欄位可能會宣告不同的變數。請參閱 API 欄位的 API 文件,以了解哪些變數可用於該欄位。
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 選項、程式庫或語言功能 | 已包含 | 可用性 |
---|---|---|
標準巨集 | has 、all 、exists 、exists_one 、map 、filter | 所有 Kubernetes 版本 |
標準函數 | 請參閱 官方標準定義列表 | 所有 Kubernetes 版本 |
同質聚合字面值 | 所有 Kubernetes 版本 | |
預設 UTC 時區 | 所有 Kubernetes 版本 | |
搶先驗證宣告 | 所有 Kubernetes 版本 | |
擴充字串程式庫,版本 1 | charAt 、indexOf 、lastIndexOf 、lowerAscii 、upperAscii 、replace 、split 、join 、substring 、trim | 所有 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 CrossTypeNumericComparisons | Kubernetes 版本 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 列表程式庫
列表程式庫包含 indexOf
和 lastIndexOf
,其工作方式與同名字串函數類似。這些函數會傳回列表中所提供元素的第一個或最後一個位置索引。
列表程式庫還包含 min
、max
和 sum
。Sum 支援所有數字類型以及持續時間類型。Min 和 max 支援所有可比較的類型。
isSorted
也作為便利函數提供,並支援所有可比較的類型。
範例
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
函數之外,正規表示式程式庫還提供 find
和 findAll
,從而實現更廣泛的正規表示式運算。
範例
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 物件具有 getScheme
、getHost
、getHostname
、getPort
、getEscapedPath
和 getQuery
存取器。
範例
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 資源檢查執行如下
- 指定要檢查的群組和資源:
Authorizer.group(string).resource(string) ResourceCheck
- 選擇性地呼叫以下建構器函數的任意組合,以進一步縮小授權檢查範圍。請注意,這些函數會傳回接收器類型,並且可以鏈結
ResourceCheck.subresource(string) ResourceCheck
ResourceCheck.namespace(string) ResourceCheck
ResourceCheck.name(string) ResourceCheck
- 呼叫
ResourceCheck.check(verb string) Decision
以執行授權檢查。 - 呼叫
allowed() bool
或reason() string
以檢查授權檢查的結果。
執行的非資源授權使用方式如下
- 僅指定路徑:
Authorizer.path(string) PathCheck
- 呼叫
PathCheck.check(httpVerb string) Decision
以執行授權檢查。 - 呼叫
allowed() bool
或reason() string
以檢查授權檢查的結果。
若要對服務帳戶執行授權檢查
Authorizer.serviceAccount(namespace string, name string) Authorizer
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 表達式 | 用途 |
---|---|
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 物件具有以下成員函數程式庫
成員函數 | 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 |
範例
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 類型 |
---|---|
具有屬性的 '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-string | int 或字串的聯集,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=date | timestamp (google.protobuf.Timestamp) |
'string' 具有 format=datetime | timestamp (google.protobuf.Timestamp) |
'string' 具有 format=duration | duration (google.protobuf.Duration) |
另請參閱:CEL 類型、OpenAPI 類型、Kubernetes 結構化結構描述。
對於 x-kubernetes-list-type
為 set
或 map
的陣列,相等性比較會忽略元素順序。例如,如果陣列表示 Kubernetes set
值,則 [1, 2] == [2, 1]
。
對具有 x-kubernetes-list-type
的陣列進行串連會使用列表類型的語意
set
X + Y
執行聯集,其中保留X
中所有元素的陣列位置,並附加Y
中不相交的元素,並保留其部分順序。map
X + Y
執行合併,其中保留X
中所有鍵的陣列位置,但當X
和Y
的鍵集相交時,值會被Y
中的值覆寫。具有不相交鍵的Y
中的元素會被附加,並保留其部分順序。
逸出
只有形式為 [a-zA-Z_.-/][a-zA-Z0-9_.-/]*
的 Kubernetes 資源屬性名稱可以從 CEL 存取。當在表達式中存取時,可存取的屬性名稱會根據以下規則逸出
逸出序列 | 等效屬性名稱 |
---|---|
__underscores__ | __ |
__dot__ | . |
__dash__ | - |
__slash__ | / |
__{keyword}__ | CEL 保留關鍵字 |
當您逸出任何 CEL 的保留關鍵字時,您需要比對確切的屬性名稱,請使用底線逸出(例如,單字 sprint
中的 int
不會逸出,也不需要逸出)。
逸出範例
屬性名稱 | 具有逸出屬性名稱的規則 |
---|---|
namespace | self.__namespace__ > 0 |
x-prop | self.x__dash__prop > 0 |
redact__d | self.redact__underscores__d > 0 |
string | self.startsWith('kube') |
資源限制
CEL 不是圖靈完備的,並提供各種生產安全控制來限制執行時間。CEL 的資源限制功能向開發人員提供有關表達式複雜度的回饋,並協助保護 API 伺服器免於在評估期間過度消耗資源。CEL 的資源限制功能用於防止 CEL 評估消耗過多的 API 伺服器資源。
資源限制功能的關鍵要素是成本單位,CEL 將其定義為追蹤 CPU 使用率的方式。成本單位獨立於系統負載和硬體。成本單位也是確定性的;對於任何給定的 CEL 表達式和輸入資料,CEL 解譯器對表達式的評估將始終產生相同的成本。
許多 CEL 的核心運算具有固定成本。最簡單的運算(例如 <
)的成本為 1。有些運算具有較高的固定成本,例如列表字面值宣告的固定基本成本為 40 個成本單位。
在本機程式碼中實作的函數呼叫會根據運算的時 間複雜度來估算成本。例如:使用正規表示式的運算(例如 match
和 find
)是使用 length(regexString)*length(inputString)
的近似成本來估算的。近似成本反映了 Go 的 RE2 實作的最壞情況時間複雜度。
執行時間成本預算
Kubernetes 評估的所有 CEL 表達式都受到執行時間成本預算的限制。執行時間成本預算是對實際 CPU 使用率的估計,透過在解譯 CEL 表達式時遞增成本單位計數器來計算。如果 CEL 解譯器執行的指令過多,則會超過執行時間成本預算,表達式的執行將會停止,並會產生錯誤。
某些 Kubernetes 資源定義了額外的執行時間成本預算,該預算限制了多個表達式的執行。如果表達式的總成本超出預算,則表達式的執行將會停止,並會產生錯誤。例如,自訂資源的驗證對於驗證自訂資源而評估的所有驗證規則具有每次驗證執行時間成本預算。
估計成本限制
對於某些 Kubernetes 資源,API 伺服器也可能會檢查 CEL 表達式的最壞情況估計執行時間是否會過於昂貴而無法執行。如果是這樣,API 伺服器會阻止將 CEL 表達式寫入 API 資源,方法是拒絕包含 CEL 表達式的建立或更新作業到 API 資源。此功能提供了更強的保證,確保寫入 API 資源的 CEL 表達式將在執行時間進行評估,而不會超過執行時間成本預算。