修改資源配置。
mutate
規(guī)則可用于修改匹配資源瞻润,并以 RFC 6902 JSON 補丁或策略性合并補丁的形式編寫。
通過使用 JSONPatch - RFC 6902格式的補丁绍撞,您可以對正在創(chuàng)建的資源進行精確更改。戰(zhàn)略合并補丁對于控制帶有列表的元素的合并行為很有用傻铣。無論采用哪種方法,當需要以給定方式修改對象時非洲,都會使用 mutate
規(guī)則鸭限。
資源變更發(fā)生在驗證之前两踏,因此驗證規(guī)則不應(yīng)與變更部分執(zhí)行的更改相矛盾。要更改除受 AdmissionReview 請求約束的現(xiàn)有資源之外的現(xiàn)有資源梦染,請使用 mutateExisting 策略。
如果鏡像的 tag
是latest帕识,則此策略將 imagePullPolicy
設(shè)置為 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 結(jié)尾的鏡像
- (image): "*:latest"
# 設(shè)置 imagePullPolicy 為 "IfNotPresent"
imagePullPolicy: "IfNotPresent"
RFC 6902 JSON補丁
JSON Patch 實現(xiàn)為稱為 patchesJson6902 的變更方法,提供了一種精確的資源變更方法并支持以下操作(在 op 字段中):
add
replace
remove
使用 Kyverno肮疗, add
和 replace
具有相同的行為(即,兩個操作都將添加或替換目標元素)伪货。
當需要特定變更而 patchesStrategicMerge
無法滿足需求時们衙,patchesJson6902
方法會很有用超歌。例如,當需要對數(shù)組中的特定對象進行變更時巍举,可以將索引指定為 patchesJson6902 變更規(guī)則的一部分。
patchesJson6902 和其它變更方式的差異之一是,patchesJson6902 不支持使用條件錨(conditional anchors)蜓谋。可以使用 preconditions代替桃焕。patchesJson6902 變更會直接作用到 Pod 上,并不會通過 auto-gen feature將規(guī)則轉(zhuǎn)到更高級別的控制器上观堂,例如 Deployments 和 StatefulSets。因此师痕,在為 Pod 編寫此類變異規(guī)則時溃睹,可能需要創(chuàng)建多個規(guī)則來覆蓋所有相關(guān)的 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 是空數(shù)據(jù)笔横,則以下策略會在 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
此策略規(guī)則將元素添加到列表中吹缔。 在這種情況下,它添加了一個新的 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 變更并不會轉(zhuǎn)到高級別的 Pod 控制器上。
當需要將對象附加到對象數(shù)組時画机,例如在 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 進行轉(zhuǎn)義。下面的示例是添加一個 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
方法的其他一些功能包括:
添加不存在的路徑
添加不存在的數(shù)組
給數(shù)組尾部添加一個元素 (使用負索引 -1)
策略性合并補丁
kubectl 命令使用帶有特殊指令的策略性合并補丁來控制元素合并行為侯繁。Kyverno 支持這種類型的補丁來變更資源。patchStrategicMerge 會覆蓋一個局部資源定義贮竟。
該策略向 Pod 添加一個新容器,設(shè)置 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[] 數(shù)組是,name 關(guān)鍵字必須指定為一個條件錨(即 (name): "*")控硼,為了在其他字段上發(fā)生合并。
使用錨點的條件邏輯
與 validate
規(guī)則一樣卡乾,mutate
規(guī)則也支持條件錨。有關(guān)條件的更多信息幔妨,請參閱錨點部分鹦赎。
anchor 字段误堡,由括號和可選的前導(dǎo)字符標記,允許對變更進行條件處理锁施。
mutate 規(guī)則支持兩種類型的錨點:
錨點 | Tag | 行為 |
---|---|---|
Conditional | () | 使用 tag 和 value 作為 “if” 條件 |
Add if not present | +() | 如果 tag 不存在杖们,則添加 tag 值 |
Global | <() | 全局錨點為true 時添加 pattern |
錨點值支持通配符:
- 匹配零個或多個字母數(shù)字字符
? - 匹配單個字母數(shù)字字符
只有 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 值是一個對象或數(shù)組,整個對象或數(shù)組都必須匹配吃媒。換句話說瓤介,整個對象或數(shù)組成為“if”子句的一部分赘那。不支持條件錨 tag 嵌套。
Add if not present anchor
該錨點的作用是募舟,如果一個字段尚未定義,則添加該字段值拱礁。這是通過使用 add 錨(“add if not present”錨的縮寫)和 tag 的符號 +(...) 來完成的琢锋。
add
錨會應(yīng)用為變更的一部分呢灶。通常,每個非錨 tag 值都應(yīng)用為變更的一部分鸯乃。如果 tag
上設(shè)置了 add 錨,tag 和 value 僅在資源中不存在時才應(yīng)用飒责。
例如仆潮,此策略匹配并變更具有 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
與驗證規(guī)則類似拾并,變更規(guī)則可以使用全局錨。當使用全局錨時屏歹,錨內(nèi)部的條件為 true 時,意味著無論它與全局錨點如何相關(guān)之碗,都將應(yīng)用 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
錨點處理流程
變更條件的錨點處理行為如下:
首先偏窝,處理所有條件錨收恢。當?shù)谝粋€條件錨返回 false 時祭往,處理停止。只有所有條件錨都返回true時链沼,才會處理變更。注意括勺,對于具有復(fù)雜 tag(對象或數(shù)組)值的條件錨缆八,整個值(子)對象被視為條件的一部分疾捍,如上所述。
接下來乱豆,處理所有沒有錨的標簽值和所有添加錨標簽以應(yīng)用于變更。
變更現(xiàn)有資源
與通過 AdmissionReview 實現(xiàn)的標準變更策略不同宛裕,Kyverno 1.7.0+ 支持用 patchesStrategicMerge
和 patchesJson6902
變更現(xiàn)有資源瑟啃。變更集群中現(xiàn)有的資源的策略會在后臺運行揩尸。和傳統(tǒng)的變更策略一樣,這些變更現(xiàn)有資源的策略也是由 AdmissionReview 觸發(fā)的岩榆,但適用于現(xiàn)有的坟瓢、甚至不同的資源。它們也可以配置為更新策略本身犹撒。
要定義這樣的策略,需要在 match
中指定觸發(fā)資源识颊。在每個變更規(guī)則的 mutate.targets 中指定目標資源(要在后臺變更的資源)。注意谊囚,單個規(guī)則中的所有目標資源必須使用相同的 schema怕享。例如镰踏,如果一個變更現(xiàn)有資源策略的規(guī)則是變更 Pod 和 Deployment,這個策略就會失敗奠伪,因為它們的 OpenAPI V3 schema不同(除了 metadata
)。
注意:要給 Kyverno ServiceAccount 授予合適的權(quán)限绊率。你需要創(chuàng)建一個有合適權(quán)限的 ClusterRole谨敛,并通過 ClusterRoleBinding 將其綁定到 Kyverno ServiceAccount滤否。
這個策略,當命名空間 staging 中名為 dictionary-1 的 ConfigMap 類型的觸發(fā)資源發(fā)生變更時藐俺,它會給同樣在 staging 命名空間中的名為 secret-1 的目標資源設(shè)置 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
默認情況下欲芹,安裝時不會應(yīng)用上述策略×飧福可以通過 mutateExistingOnPolicyUpdate 屬性配置此行為颈娜。如果你設(shè)置 mutateExistingOnPolicyUpdate 為 true浙宜,Kyverno 會在收到 AdmissionReview CREATE 或 UPDATE 事件時,變更策略中指定的粟瞬、現(xiàn)有的 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
...
引用目標資源的變量
要引用目標資源中的數(shù)據(jù)亩钟,您可以定義變量 target乓梨,后跟所需屬性的路徑清酥。例如,使用 target.metadata.labels.env 引用目標資源中的標簽 env焰轻。
這個策略復(fù)制 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 }}"
使用 {{ @ }} 特殊變量以引用目標資源的內(nèi)聯(lián)值辱志。
這個策略將 foo 命名空間中名為 cmone 的觸發(fā)器 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}}-{{@}}"
一旦一個變更已有資源的策略成功應(yīng)用,會有一個事件和注解被添加到目標資源中已球。
$ 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
要對策略應(yīng)用程序失敗進行故障排除,您可以檢查 UpdateRequest 自定義資源以獲取詳細信息智亮。Kyverno 會自動清理成功的 UpdateRequest。
例如阔蛉,如果沒有給 Kyverno 授予相應(yīng)的權(quán)限弃舒,你會在 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
變更規(guī)則排序(級聯(lián))
在某些情況下状原,可能需要將多級變異規(guī)則應(yīng)用于傳入的資源。規(guī)則A中的 match
聲明會對資源應(yīng)用一個變更遭笋,變更的結(jié)果會觸發(fā)規(guī)則B中的 match 聲明,從而應(yīng)用第二個瓦呼。這種情況下喂窟,Kyverno 可以適應(yīng)更復(fù)雜的變更規(guī)則央串,但是規(guī)則排序?qū)τ诒WC一致的結(jié)果很重要。
例如质和,假設(shè)您希望為每個傳入的 Pod 分配一個標簽,描述它包含的應(yīng)用程序類型饲宿。對那些 image
中包含 cassandra 或 mongo 字符串的厦酬,希望使用標簽 type=database≌淘模可以通過下面的策略實現(xiàn):
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*"
此外,假設(shè)需要為某些應(yīng)用程序類型定義備份策略减噪。對那些 type=database 的應(yīng)用程序短绸,需要指定另外一個標簽 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 能夠執(zhí)行級聯(lián)變更,從而使傳入的 Pod 在匹配到第一個規(guī)則并變更后豪治,進而匹配到第二個規(guī)則發(fā)生第二次變更。這些情況下负拟,規(guī)則必須按照其依賴關(guān)系的順序從上到下排序烦衣,并存儲在同一策略中掩浙。生成的策略定義如下所示:
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 鏡像創(chuàng)建 Pod 來測試級聯(lián)變更策略。
$ 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>
可以看到厨姚,根據(jù)變更規(guī)則,type=database 和 backup-needed=yes 都被添加到 Pod 中谬墙。
驗證如果 Pod 中已存在標簽 backup-needed=no今布,之后觸發(fā)規(guī)則一拭抬,而不會觸發(fā)規(guī)則二。
$ kubectl run cassandra --image=cassandra:latest --labels backup-needed=no
使用 get 或 describe 可以驗證 backup-needed 標簽并未被變更規(guī)則修改造虎。
$ 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 字段的值也可以解析為一個簡單的字符串數(shù)組,例如在上下文變量中定義的字符串撒穷。list 字段的值不應(yīng)包含在大括號中,即使它是 JMESPath
表達式端礼。
每次遍歷是禽笑,一個 element 變量都會被添加到處理上下文中蛤奥。可以通過 element.<name>
來引用元素中的數(shù)據(jù)凡桥,name
是屬性名。例如缅刽,當 request.object
是一個 Pod 而使用 request.object.spec.containers
列表時啊掏,可以在 foreach 中使用 element.image
來引用容器鏡像衰猛。
每個 foreach 聲明中可以包含(可選)以下聲明:
Context: 添加僅在每個循環(huán)迭代中可用的額外外部數(shù)據(jù)。
Preconditions: 控制何時跳過循環(huán)迭代啡省。
對應(yīng) foreach 聲明中的 patchesJson6902 類型娜睛,會有一個叫做 elementIndex 的額外變量冕杠,該變量可以在循環(huán)中引用索引編號。
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
以預(yù)先添加受信任的 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
開始晴裹。由于容器名稱中可能包含破折號(-)(必須轉(zhuǎn)義)被济,因此 {{element.name}}
變量用雙引號指定涧团。