修改資源配置。
mutate
規則可用于修改匹配資源,并以 RFC 6902 JSON 補丁或策略性合并補丁的形式編寫。
通過使用 JSONPatch - RFC 6902格式的補丁,您可以對正在創建的資源進行精確更改。戰略合并補丁對于控制帶有列表的元素的合并行為很有用。無論采用哪種方法,當需要以給定方式修改對象時,都會使用 mutate
規則。
資源變更發生在驗證之前,因此驗證規則不應與變更部分執行的更改相矛盾。要更改除受 AdmissionReview 請求約束的現有資源之外的現有資源,請使用 mutateExisting 策略。
如果鏡像的 tag
是latest,則此策略將 imagePullPolicy
設置為 IfNotPresent:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: set-image-pull-policy
spec:
rules:
- name: set-image-pull-policy
match:
any:
- resources:
kinds:
- Pod
mutate:
patchStrategicMerge:
spec:
containers:
# 匹配以 :latest 結尾的鏡像
- (image): "*:latest"
# 設置 imagePullPolicy 為 "IfNotPresent"
imagePullPolicy: "IfNotPresent"
RFC 6902 JSON補丁
JSON Patch 實現為稱為 patchesJson6902 的變更方法,提供了一種精確的資源變更方法并支持以下操作(在 op 字段中):
add
replace
remove
使用 Kyverno, add
和 replace
具有相同的行為(即,兩個操作都將添加或替換目標元素)。
當需要特定變更而 patchesStrategicMerge
無法滿足需求時,patchesJson6902
方法會很有用。例如,當需要對數組中的特定對象進行變更時,可以將索引指定為 patchesJson6902 變更規則的一部分。
patchesJson6902 和其它變更方式的差異之一是,patchesJson6902 不支持使用條件錨(conditional anchors)。可以使用 preconditions代替。patchesJson6902 變更會直接作用到 Pod 上,并不會通過 auto-gen feature將規則轉到更高級別的控制器上,例如 Deployments 和 StatefulSets。因此,在為 Pod 編寫此類變異規則時,可能需要創建多個規則來覆蓋所有相關的 Pod 控制器。
此補丁策略添加或替換任何命名空間中名稱為 config-game 的 ConfigMap 中的條目。
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: policy-patch-cm
spec:
rules:
- name: pCM1
match:
any:
- resources:
names:
- config-game
kinds:
- ConfigMap
mutate:
patchesJson6902: |-
- path: "/data/ship.properties"
op: add
value: |
type=starship
owner=utany.corp
- path: "/data/newKey1"
op: add
value: newValue1
如果您的 ConfigMap 是空數據,則以下策略會在 config-game 中添加一個條目。
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: policy-add-cm
spec:
rules:
- name: pCM1
match:
any:
- resources:
names:
- config-game
kinds:
- ConfigMap
mutate:
patchesJson6902: |-
- path: "/data"
op: add
value: {"ship.properties": "{\"type\": \"starship\", \"owner\": \"utany.corp\"}"}
這是從 Secret 中刪除標簽的補丁示例:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: policy-remove-label
spec:
rules:
- name: "Remove unwanted label"
match:
any:
- resources:
kinds:
- Secret
mutate:
patchesJson6902: |-
- path: "/metadata/labels/purpose"
op: remove
此策略規則將元素添加到列表中。 在這種情況下,它添加了一個新的 busybox 容器和一個命令。請注意,因為 path
語句是一個精確的 schema 元素,所以這僅適用于 direct Pod,而不適用于更高級別的對象,例如 Deployment。
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: insert-container
spec:
rules:
- name: insert-container
match:
any:
- resources:
kinds:
- Pod
mutate:
patchesJson6902: |-
- op: add
path: "/spec/containers/1"
value: {"name":"busybox","image":"busybox:latest"}
- op: add
path: "/spec/containers/1/command"
value:
- ls
注意:就像之前提到的,作用于 Pod 上的 patchesJson6902 變更并不會轉到高級別的 Pod 控制器上。
當需要將對象附加到對象數組時,例如在 pod.spec.tolerations 中,在路徑末尾使用破折號 (-)。
mutate:
patchesJson6902: |-
- op: add
path: "/spec/tolerations/-"
value: {"key":"networkzone","operator":"Equal","value":"dmz","effect":"NoSchedule"}
JSON Patch 使用 JSON Pointer 來引用鍵,帶有波浪號 (~) 和正斜杠 (/) 字符的鍵需要分別用 ~0 和 ~1 進行轉義。下面的示例是添加一個 key 為 config.linkerd.io/skip-outbound-ports,value 為 "8200" 的 annotation。
- op: add
path: /spec/template/metadata/annotations/config.linkerd.io~1skip-outbound-ports
value: "8200"
patchesJson6902
方法的其他一些功能包括:
添加不存在的路徑
添加不存在的數組
給數組尾部添加一個元素 (使用負索引 -1)
策略性合并補丁
kubectl 命令使用帶有特殊指令的策略性合并補丁來控制元素合并行為。Kyverno 支持這種類型的補丁來變更資源。patchStrategicMerge 會覆蓋一個局部資源定義。
該策略向 Pod 添加一個新容器,設置 imagePullPolicy,添加一個命令,并添加一個 label,其中 label 的 key 是 “name”,value 來自 AdmissionReview 中的 Pod name。同樣的,這次的覆蓋行為也只作用于 Pod,而不會作用于高級別的 Deployment。
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: strategic-merge-patch
spec:
rules:
- name: set-image-pull-policy-add-command
match:
any:
- resources:
kinds:
- Pod
mutate:
patchStrategicMerge:
metadata:
labels:
name: "{{request.object.metadata.name}}"
spec:
containers:
- name: "nginx"
image: "nginx:latest"
imagePullPolicy: "Never"
command:
- ls
注意,當使用 patchStrategicMerge 修改 pod.spec.containers[] 數組是,name 關鍵字必須指定為一個條件錨(即 (name): "*"),為了在其他字段上發生合并。
使用錨點的條件邏輯
與 validate
規則一樣,mutate
規則也支持條件錨。有關條件的更多信息,請參閱錨點部分。
anchor 字段,由括號和可選的前導字符標記,允許對變更進行條件處理。
mutate 規則支持兩種類型的錨點:
錨點 | Tag | 行為 |
---|---|---|
Conditional | () | 使用 tag 和 value 作為 “if” 條件 |
Add if not present | +() | 如果 tag 不存在,則添加 tag 值 |
Global | <() | 全局錨點為true 時添加 pattern |
錨點值支持通配符:
- 匹配零個或多個字母數字字符
? - 匹配單個字母數字字符
只有 patchStrategicMerge 變更方法中支持條件錨。
Conditional anchor
如果錨標記存在并且值與指定值匹配,則條件錨評估為 true。如果 tag 不存在或值不匹配,則處理停止。一旦處理停止,列表中的任何子元素或任何剩余的兄弟元素都不會被處理。
下面的示例會將所有 name 有值并以“secure”開頭的端口的 port 字段添加或替換為 6443,
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: policy-set-port
spec:
rules:
- name: "Set port"
match:
any:
- resources:
kinds :
- Endpoints
mutate:
patchStrategicMerge:
subsets:
- ports:
- (name): "secure*"
port: 6443
如果錨點的 tag 值是一個對象或數組,整個對象或數組都必須匹配。換句話說,整個對象或數組成為“if”子句的一部分。不支持條件錨 tag 嵌套。
Add if not present anchor
該錨點的作用是,如果一個字段尚未定義,則添加該字段值。這是通過使用 add 錨(“add if not present”錨的縮寫)和 tag 的符號 +(...) 來完成的。
add
錨會應用為變更的一部分。通常,每個非錨 tag 值都應用為變更的一部分。如果 tag
上設置了 add 錨,tag 和 value 僅在資源中不存在時才應用。
例如,此策略匹配并變更具有 emptyDir 卷的 pod,以添加 safe-to-evict 注解(如果未指定)。
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: add-safe-to-evict
annotations:
pod-policies.kyverno.io/autogen-controllers: none
spec:
rules:
- name: "annotate-empty-dir"
match:
any:
- resources:
kinds:
- Pod
mutate:
patchStrategicMerge:
metadata:
annotations:
+(cluster-autoscaler.kubernetes.io/safe-to-evict): true
spec:
volumes:
- <(emptyDir): {}
Global Anchor
與驗證規則類似,變更規則可以使用全局錨。當使用全局錨時,錨內部的條件為 true 時,意味著無論它與全局錨點如何相關,都將應用 pattern 的其余部分。
例如,下面的策略會添加一個名為 my-secret 的 imagePullSecret 到任何容器鏡像以 corp.reg.com 開頭的 Pod中。
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: add-imagepullsecrets
spec:
rules:
- name: add-imagepullsecret
match:
any:
- resources:
kinds:
- Pod
mutate:
patchStrategicMerge:
spec:
containers:
- <(image): "corp.reg.com/*"
imagePullSecrets:
- name: my-secret
下面的 Pod 符合此條件,因此添加了名為 my-secret 的 imagePullSecret。
apiVersion: v1
kind: Pod
metadata:
name: static-web
labels:
role: myrole
spec:
containers:
- name: web
image: corp.reg.com/nginx
ports:
- name: web
containerPort: 80
protocol: TCP
錨點處理流程
變更條件的錨點處理行為如下:
首先,處理所有條件錨。當第一個條件錨返回 false 時,處理停止。只有所有條件錨都返回true時,才會處理變更。注意,對于具有復雜 tag(對象或數組)值的條件錨,整個值(子)對象被視為條件的一部分,如上所述。
接下來,處理所有沒有錨的標簽值和所有添加錨標簽以應用于變更。
變更現有資源
與通過 AdmissionReview 實現的標準變更策略不同,Kyverno 1.7.0+ 支持用 patchesStrategicMerge
和 patchesJson6902
變更現有資源。變更集群中現有的資源的策略會在后臺運行。和傳統的變更策略一樣,這些變更現有資源的策略也是由 AdmissionReview 觸發的,但適用于現有的、甚至不同的資源。它們也可以配置為更新策略本身。
要定義這樣的策略,需要在 match
中指定觸發資源。在每個變更規則的 mutate.targets 中指定目標資源(要在后臺變更的資源)。注意,單個規則中的所有目標資源必須使用相同的 schema。例如,如果一個變更現有資源策略的規則是變更 Pod 和 Deployment,這個策略就會失敗,因為它們的 OpenAPI V3 schema不同(除了 metadata
)。
注意:要給 Kyverno ServiceAccount 授予合適的權限。你需要創建一個有合適權限的 ClusterRole,并通過 ClusterRoleBinding 將其綁定到 Kyverno ServiceAccount。
這個策略,當命名空間 staging 中名為 dictionary-1 的 ConfigMap 類型的觸發資源發生變更時,它會給同樣在 staging 命名空間中的名為 secret-1 的目標資源設置 label foo=bar。
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: "mutate-existing-secret"
spec:
rules:
- name: "mutate-secret-on-configmap-event"
match:
any:
- resources:
kinds:
- ConfigMap
names:
- dictionary-1
namespaces:
- staging
mutate:
targets:
- apiVersion: v1
kind: Secret
name: secret-1
namespace: "{{ request.object.metadata.namespace }}"
patchStrategicMerge:
metadata:
labels:
foo: bar
默認情況下,安裝時不會應用上述策略。可以通過 mutateExistingOnPolicyUpdate 屬性配置此行為。如果你設置 mutateExistingOnPolicyUpdate 為 true,Kyverno 會在收到 AdmissionReview CREATE 或 UPDATE 事件時,變更策略中指定的、現有的 secret。
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: "mutate-existing-secret"
spec:
mutateExistingOnPolicyUpdate: true
rules:
- name: "mutate-secret-on-configmap-event"
match:
any:
- resources:
kinds:
- ConfigMap
names:
- dictionary-1
namespaces:
- staging
...
引用目標資源的變量
要引用目標資源中的數據,您可以定義變量 target,后跟所需屬性的路徑。例如,使用 target.metadata.labels.env 引用目標資源中的標簽 env。
這個策略復制 ConfigMaps 中 target.data.key 的值到它的標簽 env。
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: sync-cms
spec:
mutateExistingOnPolicyUpdate: false
rules:
- name: concat-cm
match:
any:
- resources:
kinds:
- ConfigMap
names:
- cmone
namespaces:
- foo
mutate:
targets:
- apiVersion: v1
kind: ConfigMap
name: cmtwo
namespace: bar
- apiVersion: v1
kind: ConfigMap
name: cmthree
namespace: bar
patchesJson6902: |-
- op: add
path: "/metadata/labels/env"
value: "{{ target.data.key }}"
使用 {{ @ }} 特殊變量以引用目標資源的內聯值。
這個策略將 foo 命名空間中名為 cmone 的觸發器 ConfigMap 中 keyone 的值添加到目標 ConfigMaps 中,并作為 data 中 keynew 的前綴。
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: sync-cms
spec:
mutateExistingOnPolicyUpdate: false
rules:
- name: concat-cm
match:
any:
- resources:
kinds:
- ConfigMap
names:
- cmone
namespaces:
- foo
mutate:
targets:
- apiVersion: v1
kind: ConfigMap
name: cmtwo
namespace: bar
- apiVersion: v1
kind: ConfigMap
name: cmthree
namespace: bar
patchStrategicMerge:
data:
keynew: "{{request.object.data.keyone}}-{{@}}"
一旦一個變更已有資源的策略成功應用,會有一個事件和注解被添加到目標資源中。
$ kubectl describe deploy foobar
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal PolicyApplied 29s (x2 over 31s) kyverno-mutate policy add-sec/add-sec-rule applied
$ kubectl get deploy foobar -o yaml
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
...
policies.kyverno.io/last-applied-patches: |
add-sec-rule.add-sec.kyverno.io: added /spec/template/spec/containers/0/securityContext
要對策略應用程序失敗進行故障排除,您可以檢查 UpdateRequest 自定義資源以獲取詳細信息。Kyverno 會自動清理成功的 UpdateRequest。
例如,如果沒有給 Kyverno 授予相應的權限,你會在 updaterequest.status 中看到如下錯誤信息:
$ kubectl get ur -n kyverno
NAME POLICY RULETYPE RESOURCEKIND RESOURCENAME RESOURCENAMESPACE STATUS AGE
ur-swsdg add-sec mutate Deployment foobar default Failed 84s
$ kubectl describe ur ur-swsdg -n kyverno
Name: ur-swsdg
Namespace: kyverno
...
Status:
Message: deployments.apps "foobar" is forbidden: User "system:serviceaccount:kyverno:kyverno-service-account" cannot update resource "deployments" in API group "apps" in the namespace "default"
State: Failed
變更規則排序(級聯)
在某些情況下,可能需要將多級變異規則應用于傳入的資源。規則A中的 match
聲明會對資源應用一個變更,變更的結果會觸發規則B中的 match 聲明,從而應用第二個。這種情況下,Kyverno 可以適應更復雜的變更規則,但是規則排序對于保證一致的結果很重要。
例如,假設您希望為每個傳入的 Pod 分配一個標簽,描述它包含的應用程序類型。對那些 image
中包含 cassandra 或 mongo 字符串的,希望使用標簽 type=database。可以通過下面的策略實現:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: database-type-labeling
spec:
rules:
- name: assign-type-database
match:
any:
- resources:
kinds:
- Pod
mutate:
patchStrategicMerge:
metadata:
labels:
type: database
spec:
(containers):
- (image): "*cassandra* | *mongo*"
此外,假設需要為某些應用程序類型定義備份策略。對那些 type=database 的應用程序,需要指定另外一個標簽 backup-needed 值為 yes 或 no。僅當尚未指定標簽時才會添加標簽,因為操作員可以選擇是否需要保護。 該策略的定義如下。
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: database-backup-labeling
spec:
rules:
- name: assign-backup-database
match:
any:
- resources:
kinds:
- Pod
selector:
matchLabels:
type: database
mutate:
patchStrategicMerge:
metadata:
labels:
+(backup-needed): "yes"
這種情況下,Kyverno 能夠執行級聯變更,從而使傳入的 Pod 在匹配到第一個規則并變更后,進而匹配到第二個規則發生第二次變更。這些情況下,規則必須按照其依賴關系的順序從上到下排序,并存儲在同一策略中。生成的策略定義如下所示:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: database-protection
spec:
rules:
- name: assign-type-database
match:
any:
- resources:
kinds:
- Pod
mutate:
patchStrategicMerge:
metadata:
labels:
type: database
spec:
(containers):
- (image): "*cassandra* | *mongo*"
- name: assign-backup-database
match:
any:
- resources:
kinds:
- Pod
selector:
matchLabels:
type: database
mutate:
patchStrategicMerge:
metadata:
labels:
+(backup-needed): "yes"
通過使用 Cassandra 鏡像創建 Pod 來測試級聯變更策略。
$ kubectl run cassandra --image=cassandra:latest
pod/cassandra created
使用 get 或 describe 查看變更后的 Pod 的 metadata:
$ kubectl describe po cassandra
Name: cassandra
Namespace: default
<snip>
Labels: backup-needed=yes
run=cassandra
type=database
<snip>
可以看到,根據變更規則,type=database 和 backup-needed=yes 都被添加到 Pod 中。
驗證如果 Pod 中已存在標簽 backup-needed=no,之后觸發規則一,而不會觸發規則二。
$ kubectl run cassandra --image=cassandra:latest --labels backup-needed=no
使用 get 或 describe 可以驗證 backup-needed 標簽并未被變更規則修改。
$ kubectl describe po cassandra
Name: cassandra
Namespace: default
<snip>
Labels: backup-needed=no
type=database
<snip>
foreach
一個 foreach 聲明可以包含多個條目來處理不同的子元素,例如 一個處理容器列表,另一個處理 Pod 中的 initContainers 列表。
foreach 必須包含一個 list 屬性,定義了要處理的元素列表,以及一個 patchStrategicMerge 或 patchesJson6902 聲明。例如,使用 list 聲明遍歷 Pod 中的 containers 列表:
list: request.object.spec.containers
patchStrategicMerge:
spec:
containers:
...
當處理一個 foreach
時,Kyverno 引擎將評估 list 作為 JMESPath 表達式以檢索零個或多個子元素以供進一步處理。list 字段的值也可以解析為一個簡單的字符串數組,例如在上下文變量中定義的字符串。list 字段的值不應包含在大括號中,即使它是 JMESPath
表達式。
每次遍歷是,一個 element 變量都會被添加到處理上下文中。可以通過 element.<name>
來引用元素中的數據,name
是屬性名。例如,當 request.object
是一個 Pod 而使用 request.object.spec.containers
列表時,可以在 foreach 中使用 element.image
來引用容器鏡像。
每個 foreach 聲明中可以包含(可選)以下聲明:
Context: 添加僅在每個循環迭代中可用的額外外部數據。
Preconditions: 控制何時跳過循環迭代。
對應 foreach 聲明中的 patchesJson6902 類型,會有一個叫做 elementIndex 的額外變量,該變量可以在循環中引用索引編號。
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: foreach-json-patch
spec:
rules:
- name: add-security-context
match:
any:
- resources:
kinds:
- Pod
preconditions:
any:
- key: "{{ request.operation }}"
operator: Equals
value: CREATE
mutate:
foreach:
- list: "request.object.spec.containers"
patchesJson6902: |-
- path: /spec/containers/{{elementIndex}}/securityContext
op: add
value: {"runAsNonRoot" : true}
如下是一個完整的 patchStrategicMerge 方法示例,該方法改變 image
以預先添加受信任的 registry 地址。
apiVersion : kyverno.io/v1
kind: ClusterPolicy
metadata:
name: prepend-registry
spec:
background: false
rules:
- name: prepend-registry-containers
match:
any:
- resources:
kinds:
- Pod
preconditions:
all:
- key: "{{request.operation}}"
operator: In
value:
- CREATE
- UPDATE
mutate:
foreach:
- list: "request.object.spec.containers"
patchStrategicMerge:
spec:
containers:
- name: "{{ element.name }}"
image: registry.io/{{ images.containers."{{element.name}}".name}}:{{images.containers."{{element.name}}".tag}}
注意,patchStrategicMerge
作用于 request.object
。因此,patch
要以 spec
開始。由于容器名稱中可能包含破折號(-)(必須轉義),因此 {{element.name}}
變量用雙引號指定。