在 Kubernetes 中,你可以通過存儲(chǔ)多個(gè)對象配置文件在一個(gè)目錄中,并使用 kubectl apply 命令來批量創(chuàng)建和更新資源對象。這個(gè)過程會(huì)遞歸地處理目錄中的每個(gè)文件,逐一創(chuàng)建或更新對應(yīng)的資源。重要的是,這個(gè)方法會(huì)保留對現(xiàn)有資源所做的任何修改,而不會(huì)把這些變更回寫到配置文件中去。此外,kubectl diff 命令可以在你應(yīng)用變更之前提供一個(gè)預(yù)覽,讓你看到將要發(fā)生的改變內(nèi)容。
一、準(zhǔn)備
1、安裝 kubectl;
2、必須擁有一個(gè) Kubernetes 的集群,同時(shí)必須配置 kubectl 命令行工具與集群通信。 建議在至少有兩個(gè)不作為控制平面主機(jī)的節(jié)點(diǎn)的集群上運(yùn)行本教程。 如果還沒有集群,可以通過 Minikube 構(gòu)建一個(gè)自己的集群,或者可以使用下面的 Kubernetes 練習(xí)環(huán)境之一:
- Killercoda
- 玩轉(zhuǎn) Kubernetes
要獲知版本信息,請輸入 kubectl version.
二、權(quán)衡取舍
kubectl 工具能夠支持三種對象管理方式:
- 指令式命令
- 指令式對象配置
- 聲明式對象配置
三、概覽
聲明式對象管理需要用戶對 Kubernetes 對象定義和配置有比較深刻的理解。 如果還沒有這方面的知識儲(chǔ)備,請先閱讀下面的文檔:
- 使用指令式命令管理 Kubernetes 對象;
- 使用配置文件對 Kubernetes 對象進(jìn)行指令式管理。
以下是本教程中使用的術(shù)語的定義:
- 對象配置文件/配置文件:一個(gè)定義 Kubernetes 對象的配置的文件。 本主題展示如何將配置文件傳遞給 kubectl apply。 配置文件通常存儲(chǔ)于類似 Git 這種源碼控制系統(tǒng)中;
- 現(xiàn)時(shí)對象配置/現(xiàn)時(shí)配置:由 Kubernetes 集群所觀測到的對象的現(xiàn)時(shí)配置值。 這些配置保存在 Kubernetes 集群存儲(chǔ)(通常是 etcd)中;
- 聲明式配置寫者/聲明式寫者:負(fù)責(zé)更新現(xiàn)時(shí)對象的人或者軟件組件。 本主題中的聲明式寫者負(fù)責(zé)改變對象配置文件并執(zhí)行 kubectl apply 命令以寫入變更。
四、如何創(chuàng)建對象
使用 kubectl apply 來創(chuàng)建指定目錄中配置文件所定義的所有對象,除非對應(yīng)對象已經(jīng)存在:
kubectl apply -f <目錄>
此操作會(huì)在每個(gè)對象上設(shè)置 kubectl.kubernetes.io/last-applied-configuration: ‘{…}’ 注解。注解值中包含了用來創(chuàng)建對象的配置文件的內(nèi)容。
注意:添加 -R 標(biāo)志可以遞歸地處理目錄。
下面是一個(gè)對象配置文件示例:
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: selector: matchLabels: app: nginx minReadySeconds: 5 template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.14.2 ports: - containerPort: 80
執(zhí)行 kubectl diff 可以打印出將被創(chuàng)建的對象:
kubectl diff -f https://k8s.io/examples/application/simple_deployment.yaml
diff 使用服務(wù)器端試運(yùn)行(Server-side Dry-run) 功能特性;而該功能特性需要在 kube-apiserver 上啟用。由于 diff 操作會(huì)使用試運(yùn)行模式執(zhí)行服務(wù)器端 apply 請求,因此需要為用戶配置 PATCH、CREATE 和 UPDATE 操作權(quán)限。
使用 kubectl apply 來創(chuàng)建對象:
kubectl apply -f https://k8s.io/examples/application/simple_deployment.yaml
使用 kubectl get 打印其現(xiàn)時(shí)配置:
kubectl get -f https://k8s.io/examples/application/simple_deployment.yaml -o yaml
輸出顯示注解 kubectl.kubernetes.io/last-applied-configuration 被寫入到現(xiàn)時(shí)配置中,并且其內(nèi)容與配置文件相同:
kind: Deployment metadata: annotations: # ... # 此為 simple_deployment.yaml 的 JSON 表示 # 在對象創(chuàng)建時(shí)由 kubectl apply 命令寫入 kubectl.kubernetes.io/last-applied-configuration: | {"apiVersion":"apps/v1","kind":"Deployment", "metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"}, "spec":{"minReadySeconds":5,"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}}, "spec":{"containers":[{"image":"nginx:1.14.2","name":"nginx", "ports":[{"containerPort":80}]}]}}}} # ... spec: # ... minReadySeconds: 5 selector: matchLabels: # ... app: nginx template: metadata: # ... labels: app: nginx spec: containers: - image: nginx:1.14.2 # ... name: nginx ports: - containerPort: 80 # ... # ... # ... # ...
五、更新對象
也可以使用 kubectl apply 來更新某個(gè)目錄中定義的所有對象,即使那些對象已經(jīng)存在。 這一操作會(huì)隱含以下行為:
在現(xiàn)時(shí)配置中設(shè)置配置文件中出現(xiàn)的字段;
在現(xiàn)時(shí)配置中清除配置文件中已刪除的字段。
kubectl diff -f <目錄> kubectl apply -f <目錄>
注意:使用 -R 標(biāo)志遞歸處理目錄。
下面是一個(gè)配置文件示例:
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: selector: matchLabels: app: nginx minReadySeconds: 5 template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.14.2 ports: - containerPort: 80
使用 kubectl apply 來創(chuàng)建對象:
kubectl apply -f https://k8s.io/examples/application/simple_deployment.yaml
使用 kubectl get 打印現(xiàn)時(shí)配置:
kubectl get -f https://k8s.io/examples/application/simple_deployment.yaml -o yaml
輸出顯示,注解 kubectl.kubernetes.io/last-applied-configuration 被寫入到現(xiàn)時(shí)配置中,并且其取值與配置文件內(nèi)容相同。
kind: Deployment metadata: annotations: # ... # 此為 simple_deployment.yaml 的 JSON 表示 # 在對象創(chuàng)建時(shí)由 kubectl apply 命令寫入 kubectl.kubernetes.io/last-applied-configuration: | {"apiVersion":"apps/v1","kind":"Deployment", "metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"}, "spec":{"minReadySeconds":5,"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}}, "spec":{"containers":[{"image":"nginx:1.14.2","name":"nginx", "ports":[{"containerPort":80}]}]}}}} # ... spec: # ... minReadySeconds: 5 selector: matchLabels: # ... app: nginx template: metadata: # ... labels: app: nginx spec: containers: - image: nginx:1.14.2 # ... name: nginx ports: - containerPort: 80 # ... # ... # ... # ...
通過 kubectl scale 命令直接更新現(xiàn)時(shí)配置中的 replicas 字段。 這一命令沒有使用 kubectl apply:
kubectl scale deployment/nginx-deployment --replicas=2
使用 kubectl get 來打印現(xiàn)時(shí)配置:
kubectl get deployment nginx-deployment -o yaml
輸出顯示,replicas 字段已經(jīng)被設(shè)置為 2,而 last-applied-configuration 注解中并不包含 replicas 字段。
apiVersion: apps/v1 kind: Deployment metadata: annotations: # ... # 注意注解中并不包含 replicas # 這是因?yàn)楦虏⒉皇峭ㄟ^ kubectl apply 來執(zhí)行的 kubectl.kubernetes.io/last-applied-configuration: | {"apiVersion":"apps/v1","kind":"Deployment", "metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"}, "spec":{"minReadySeconds":5,"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}}, "spec":{"containers":[{"image":"nginx:1.14.2","name":"nginx", "ports":[{"containerPort":80}]}]}}}} # ... spec: replicas: 2 # 由 scale 命令填寫 # ... minReadySeconds: 5 selector: matchLabels: # ... app: nginx template: metadata: # ... labels: app: nginx spec: containers: - image: nginx:1.14.2 # ... name: nginx ports: - containerPort: 80 # ...
現(xiàn)在更新 simple_deployment.yaml 配置文件,將鏡像文件從 nginx:1.14.2 更改為 nginx:1.16.1,同時(shí)刪除minReadySeconds 字段:
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.16.1 # 更新該鏡像 ports: - containerPort: 80
應(yīng)用對配置文件所作更改:
kubectl diff -f https://k8s.io/examples/application/update_deployment.yaml kubectl apply -f https://k8s.io/examples/application/update_deployment.yaml
使用 kubectl get 打印現(xiàn)時(shí)配置:
kubectl get -f https://k8s.io/examples/application/update_deployment.yaml -o yaml
輸出顯示現(xiàn)時(shí)配置中發(fā)生了以下更改:
- 字段 replicas 保留了 kubectl scale 命令所設(shè)置的值:2; 之所以該字段被保留是因?yàn)榕渲梦募胁]有設(shè)置 replicas;
- 字段 image 的內(nèi)容已經(jīng)從 nginx:1.14.2 更改為 nginx:1.16.1;
- 注解 last-applied-configuration 內(nèi)容被更改為新的鏡像名稱;
- 字段 minReadySeconds 被移除;
- 注解 last-applied-configuration 中不再包含 minReadySeconds 字段。
apiVersion: apps/v1 kind: Deployment metadata: annotations: # ... # 注解中包含更新后的鏡像 nginx 1.16.1 # 但是其中并不包含更改后的 replicas 值 2 kubectl.kubernetes.io/last-applied-configuration: | {"apiVersion":"apps/v1","kind":"Deployment", "metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"}, "spec":{"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}}, "spec":{"containers":[{"image":"nginx:1.16.1","name":"nginx", "ports":[{"containerPort":80}]}]}}}} # ... spec: replicas: 2 # 由 `kubectl scale` 設(shè)置,被 `kubectl apply` 命令忽略 # minReadySeconds 被 `kubectl apply` 清除 # ... selector: matchLabels: # ... app: nginx template: metadata: # ... labels: app: nginx spec: containers: - image: nginx:1.16.1 # 由 `kubectl apply` 設(shè)置 # ... name: nginx ports: - containerPort: 80 # ... # ... # ... # ...
注意:將 kubectl apply 與指令式對象配置命令 kubectl create 或 kubectl replace 混合使用是不受支持的。這是因?yàn)?create 和 replace 命令都不會(huì)保留 kubectl apply 用來計(jì)算更新內(nèi)容所使用的 kubectl.kubernetes.io/last-applied-configuration 注解值。
六、刪除對象
有兩種方法來刪除 kubectl apply 管理的對象。
1、kubectl delete -f <文件名>
使用指令式命令來手動(dòng)刪除對象是建議的方法,因?yàn)檫@種方法更為明確地給出了要?jiǎng)h除的內(nèi)容是什么, 且不容易造成用戶不小心刪除了其他對象的情況。
kubectl delete -f <文件名>
2、kubectl apply -f <目錄> –prune
作為 kubectl delete 操作的替代方式,可以在本地文件系統(tǒng)的目錄中的清單文件被刪除之后, 使用 kubectl apply 來辯識要?jiǎng)h除的對象。
在 Kubernetes 1.29 中,kubectl apply 可使用兩種剪裁模式:
- 基于 Allowlist 的剪裁:這種模式自 kubectl v1.5 版本開始就存在, 但由于其設(shè)計(jì)存在易用性、正確性和性能問題,因此仍處于 Alpha 階段。 基于 ApplySet 的模式設(shè)計(jì)用于取代這種模式;
- 基于 ApplySet 的剪裁:apply set 是一個(gè)服務(wù)器端對象(默認(rèn)是一個(gè) Secret), kubectl 可以使用它來在 apply 操作中準(zhǔn)確高效地跟蹤集合成員。 這種模式在 kubectl v1.27 中以 Alpha 引入,作為基于 Allowlist 剪裁的替代方案。
Allow list:
特性狀態(tài): Kubernetes v1.5 [alpha]
在 Allowlist 模式下使用 kubectl apply 命令時(shí)要小心使用 –prune 標(biāo)志。 哪些對象被剪裁取決于 –prune-allowlist、–selector 和 –namespace 標(biāo)志的值, 并且依賴于作用域中對象的動(dòng)態(tài)發(fā)現(xiàn)。特別是在調(diào)用之間更改標(biāo)志值時(shí),這可能會(huì)導(dǎo)致對象被意外刪除或保留。
要使用基于 Allowlist 的剪裁,可以添加以下標(biāo)志到 kubectl apply 調(diào)用:
- prune:刪除之前應(yīng)用的、不在當(dāng)前調(diào)用所傳遞的集合中的對象;
- prune-allowlist:一個(gè)需要考慮進(jìn)行剪裁的組-版本-類別(group-version-kind, GVK)列表。 這個(gè)標(biāo)志是可選的,但強(qiáng)烈建議使用,因?yàn)樗哪J(rèn)值是同時(shí)作用于命名空間和集群的部分類型列表, 這可能會(huì)產(chǎn)生令人意外的結(jié)果;
- selector/-l:使用標(biāo)簽選擇算符以約束要剪裁的對象的集合。此標(biāo)志是可選的,但強(qiáng)烈建議使用;
- all:用于替代 –selector/-l 以顯式選擇之前應(yīng)用的類型為 Allowlist 的所有對象。
基于 Allowlist 的剪裁會(huì)查詢 API 服務(wù)器以獲取與給定標(biāo)簽(如果有)匹配的所有允許列出的 GVK 對象, 并嘗試將返回的活動(dòng)對象配置與對象清單文件進(jìn)行匹配。如果一個(gè)對象與查詢匹配,并且它在目錄中沒有對應(yīng)的清單, 但它有一個(gè) kubectl.kubernetes.io/last-applied-configuration 注解,則它將被刪除。
kubectl apply -f <目錄> --prune -l <標(biāo)簽> --prune-allowlist=<gvk 列表>
帶剪裁(prune)行為的 apply 操作應(yīng)在包含對象清單的根目錄運(yùn)行。 如果對象之前被執(zhí)行了 apply 操作,具有給定的標(biāo)簽(如果有)且未出現(xiàn)在子目錄中, 在其子目錄中運(yùn)行可能導(dǎo)致對象被不小心刪除。
Apply set:
特性狀態(tài): Kubernetes v1.27 [alpha]
kubectl apply –prune –applyset 目前處于 Alpha 階段,在后續(xù)的版本中可能引入向后不兼容的變更。要使用基于 ApplySet 的剪裁,請?jiān)O(shè)置 KUBECTL_APPLYSET=true 環(huán)境變量, 并添加以下標(biāo)志到你的 kubectl apply 調(diào)用中:
- prune:刪除之前應(yīng)用的、不在當(dāng)前調(diào)用所傳遞的集合中的對象;
- applyset:是 kubectl 可以使用的對象的名稱,用于在 apply 操作中準(zhǔn)確高效地跟蹤集合成員。
KUBECTL_APPLYSET=true kubectl apply -f <目錄> --prune --applyset=<名稱>
默認(rèn)情況下,所使用的 ApplySet 父對象的類別是 Secret。 不過也可以按格式 –applyset=configmaps/<name> 使用 ConfigMap。 使用 Secret 或 ConfigMap 時(shí),如果對應(yīng)對象尚不存在,kubectl 將創(chuàng)建這些對象。
還可以使用自定義資源作為 ApplySet 父對象。 要啟用此功能,請為定義目標(biāo)資源的 CRD 打上標(biāo)簽:applyset.kubernetes.io/is-parent-type: true。 然后,創(chuàng)建你想要用作 ApplySet 父級的對象(kubectl 不會(huì)自動(dòng)為自定義資源執(zhí)行此操作)。 最后,按以下方式在 applyset 標(biāo)志中引用該對象: –applyset=<resource>.<group>/<name> (例如 widgets.custom.example.com/widget-name)。
使用基于 ApplySet 的剪裁時(shí),kubectl 會(huì)在將集合中的對象發(fā)送到服務(wù)器之前將標(biāo)簽 applyset.kubernetes.io/part-of=<parentID> 添加到集合中的每個(gè)對象上。 出于性能原因,它還會(huì)將該集合包含的資源類型和命名空間列表收集到當(dāng)前父對象上的注解中。 最后,在 apply 操作結(jié)束時(shí),它會(huì)在 API 服務(wù)器上查找由 applyset.kubernetes.io/part-of=<parentID> 標(biāo)簽定義的、屬于此集合所對應(yīng)命名空間(或適用的集群作用域)中對應(yīng)類型的對象。
注意事項(xiàng)和限制:
- 每個(gè)對象最多可以是一個(gè)集合的成員;
- 當(dāng)使用任何名命名空間的父級(包括默認(rèn)的 Secret)時(shí), –namespace 標(biāo)志是必需的。這意味著跨越多個(gè)命名空間的 ApplySet 必須使用集群作用域的自定義資源作為父對象;
- 要安全地在多個(gè)目錄中使用基于 ApplySet 的剪裁,請為每個(gè)目錄使用唯一的 ApplySet 名稱。
七、查看對象
可以使用 kubectl get 并指定 -o yaml 選項(xiàng)來查看現(xiàn)時(shí)對象的配置:
kubectl get -f <文件名 | URL> -o yaml
八、apply操作
patch 是一種更新操作,其作用域?yàn)閷ο蟮囊恍┨囟ㄗ侄味皇钦麄€(gè)對象。 這使得可以更新對象的特定字段集合而不必先要讀回對象。
kubectl apply 更新對象的現(xiàn)時(shí)配置,它是通過向 API 服務(wù)器發(fā)送一個(gè) patch 請求來執(zhí)行更新動(dòng)作的。所提交的補(bǔ)丁中定義了對現(xiàn)時(shí)對象配置中特定字段的更新。 kubectl apply 命令會(huì)使用當(dāng)前的配置文件、現(xiàn)時(shí)配置以及現(xiàn)時(shí)配置中保存的 last-applied-configuration 注解內(nèi)容來計(jì)算補(bǔ)丁更新內(nèi)容。
1、合并補(bǔ)丁計(jì)算
kubectl apply 命令將配置文件的內(nèi)容寫入到 kubectl.kubernetes.io/last-applied-configuration 注解中。 這些內(nèi)容用來識別配置文件中已經(jīng)移除的、因而也需要從現(xiàn)時(shí)配置中刪除的字段。 用來計(jì)算要?jiǎng)h除或設(shè)置哪些字段的步驟如下:
- 計(jì)算要?jiǎng)h除的字段,即在 last-applied-configuration 中存在但在配置文件中不再存在的字段;
- 計(jì)算要添加或設(shè)置的字段,即在配置文件中存在但其取值與現(xiàn)時(shí)配置不同的字段。
下面是一個(gè)例子。假定此文件是某 Deployment 對象的配置文件:
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.16.1 # 更新該鏡像 ports: - containerPort: 80
同時(shí)假定同一 Deployment 對象的現(xiàn)時(shí)配置如下:
apiVersion: apps/v1 kind: Deployment metadata: annotations: # ... # 注意注解中并不包含 replicas # 這是因?yàn)楦虏⒉皇峭ㄟ^ kubectl apply 來執(zhí)行的 kubectl.kubernetes.io/last-applied-configuration: | {"apiVersion":"apps/v1","kind":"Deployment", "metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"}, "spec":{"minReadySeconds":5,"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}}, "spec":{"containers":[{"image":"nginx:1.14.2","name":"nginx", "ports":[{"containerPort":80}]}]}}}} # ... spec: replicas: 2 # 按規(guī)模填寫 # ... minReadySeconds: 5 selector: matchLabels: # ... app: nginx template: metadata: # ... labels: app: nginx spec: containers: - image: nginx:1.14.2 # ... name: nginx ports: - containerPort: 80 # ...
下面是 kubectl apply 將執(zhí)行的合并計(jì)算:
- 通過讀取 last-applied-configuration 并將其與配置文件中的值相比較, 計(jì)算要?jiǎng)h除的字段。 對于本地對象配置文件中顯式設(shè)置為空的字段,清除其在現(xiàn)時(shí)配置中的設(shè)置, 無論這些字段是否出現(xiàn)在 last-applied-configuration 中。 在此例中,minReadySeconds 出現(xiàn)在 last-applied-configuration 注解中, 但并不存在于配置文件中。 動(dòng)作: 從現(xiàn)時(shí)配置中刪除 minReadySeconds 字段;
- 通過讀取配置文件中的值并將其與現(xiàn)時(shí)配置相比較,計(jì)算要設(shè)置的字段。 在這個(gè)例子中,配置文件中的 image 值與現(xiàn)時(shí)配置中的 image 不匹配。 動(dòng)作:設(shè)置現(xiàn)時(shí)配置中的 image 值;
- 設(shè)置 last-applied-configuration 注解的內(nèi)容,使之與配置文件匹配;
- 將第 1、2、3 步驟得出的結(jié)果合并,構(gòu)成向 API 服務(wù)器發(fā)送的補(bǔ)丁請求內(nèi)容。
下面是此合并操作之后形成的現(xiàn)時(shí)配置:
apiVersion: apps/v1 kind: Deployment metadata: annotations: # ... # 注解中包含更新后的鏡像 nginx 1.16.1, # 但是其中并不包含更改后的 replicas 值 2 kubectl.kubernetes.io/last-applied-configuration: | {"apiVersion":"apps/v1","kind":"Deployment", "metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"}, "spec":{"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}}, "spec":{"containers":[{"image":"nginx:1.16.1","name":"nginx", "ports":[{"containerPort":80}]}]}}}} # ... spec: selector: matchLabels: # ... app: nginx replicas: 2 # 由 `kubectl scale` 設(shè)置,被 `kubectl apply` 命令忽略 # minReadySeconds 此字段被 `kubectl apply` 清除 # ... template: metadata: # ... labels: app: nginx spec: containers: - image: nginx:1.16.1 # 由 `kubectl apply` 設(shè)置 # ... name: nginx ports: - containerPort: 80 # ... # ... # ... # ...
2、不同類型字段的合并方式
配置文件中的特定字段與現(xiàn)時(shí)配置合并時(shí),合并方式取決于字段類型。 字段類型有幾種:
- 基本類型:字段類型為 string、integer 或 boolean 之一。 例如:image 和 replicas 字段都是基本類型字段;
- 動(dòng)作: 替換;
- map:也稱作 object。類型為 map 或包含子域的復(fù)雜結(jié)構(gòu)。例如,labels、 annotations、spec 和 metadata 都是 map;
- 動(dòng)作: 合并元素或子字段;
- list:包含元素列表的字段,其中每個(gè)元素可以是基本類型或 map。 例如,containers、ports 和 args 都是 list。
- 動(dòng)作: 不一定。
當(dāng) kubectl apply 更新某個(gè) map 或 list 字段時(shí),它通常不會(huì)替換整個(gè)字段, 而是會(huì)更新其中的各個(gè)子元素。例如,當(dāng)合并 Deployment 的 spec 時(shí),kubectl 并不會(huì)將其整個(gè)替換掉。相反,實(shí)際操作會(huì)是對 replicas 這類 spec 的子字段來執(zhí)行比較和更新。
3、合并對基本類型字段的更新
基本類型字段會(huì)被替換或清除。
字段在對象配置文件中 | 字段在現(xiàn)時(shí)對象配置中 | 字段在 last-applied-configuration?中 | 動(dòng)作 |
是 | 是 | – | 將配置文件中值設(shè)置到現(xiàn)時(shí)配置上。 |
是 | 否 | – | 將配置文件中值設(shè)置到現(xiàn)時(shí)配置上。 |
否 | – | 是 | 從現(xiàn)時(shí)配置中移除。 |
否 | – | 否 | 什么也不做。保持現(xiàn)時(shí)值。 |
4、合并對 map 字段的變更
用來表示映射的字段在合并時(shí)會(huì)逐個(gè)子字段或元素地比較:
鍵存在于對象配置文件中 | 鍵存在于現(xiàn)時(shí)對象配置中 | 鍵存在于 last-applied-configuration?中 | 動(dòng)作 |
是 | 是 | – | 比較子域取值。 |
是 | 否 | – | 將現(xiàn)時(shí)配置設(shè)置為本地配置值。 |
否 | – | 是 | 從現(xiàn)時(shí)配置中刪除鍵。 |
否 | – | 否 | 什么也不做,保留現(xiàn)時(shí)值。 |
5、合并 list 類型字段的變更
對 list 類型字段的變更合并會(huì)使用以下三種策略之一:
- 如果 list 所有元素都是基本類型則替換整個(gè) list;
- 如果 list 中元素是復(fù)合結(jié)構(gòu)則逐個(gè)元素執(zhí)行合并操作;
- 合并基本類型元素構(gòu)成的 list。
策略的選擇是基于各個(gè)字段做出的。
6、如果 list 中元素都是基本類型則替換整個(gè) list
將整個(gè) list 視為一個(gè)基本類型字段?;蛘哒麄€(gè)替換或者整個(gè)刪除。 此操作會(huì)保持 list 中元素順序不變
示例: 使用 kubectl apply 來更新 Pod 中 Container 的 args 字段。 此操作會(huì)將現(xiàn)時(shí)配置中的 args 值設(shè)為配置文件中的值。 所有之前添加到現(xiàn)時(shí)配置中的 args 元素都會(huì)丟失。 配置文件中的 args 元素的順序在被添加到現(xiàn)時(shí)配置中時(shí)保持不變。
# last-applied-configuration 值 args: ["a", "b"] # 配置文件值 args: ["a", "c"] # 現(xiàn)時(shí)配置 args: ["a", "b", "d"] # 合并結(jié)果 args: ["a", "c"]
解釋: 合并操作將配置文件中的值當(dāng)做新的 list 值。
7、如果 list 中元素為復(fù)合類型則逐個(gè)執(zhí)行合并
此操作將 list 視為 map,并將每個(gè)元素中的特定字段當(dāng)做其主鍵。 逐個(gè)元素地執(zhí)行添加、刪除或更新操作。結(jié)果順序無法得到保證。
此合并策略會(huì)使用每個(gè)字段上的一個(gè)名為 patchMergeKey 的特殊標(biāo)簽。 Kubernetes 源代碼中為每個(gè)字段定義了 patchMergeKey: types.go。 當(dāng)合并由 map 組成的 list 時(shí),給定元素中被設(shè)置為 patchMergeKey 的字段會(huì)被當(dāng)做該元素的 map 鍵值來使用。
例如: 使用 kubectl apply 來更新 Pod 規(guī)約中的 containers 字段。 此操作會(huì)將 containers 列表視作一個(gè)映射來執(zhí)行合并,每個(gè)元素的主鍵為 name。
# last-applied-configuration 值 containers: - name: nginx image: nginx:1.16 - name: nginx-helper-a # 鍵 nginx-helper-a 會(huì)被刪除 image: helper:1.3 - name: nginx-helper-b # 鍵 nginx-helper-b 會(huì)被保留 image: helper:1.3 # 配置文件值 containers: - name: nginx image: nginx:1.16 - name: nginx-helper-b image: helper:1.3 - name: nginx-helper-c # 鍵 nginx-helper-c 會(huì)被添加 image: helper:1.3 # 現(xiàn)時(shí)配置 containers: - name: nginx image: nginx:1.16 - name: nginx-helper-a image: helper:1.3 - name: nginx-helper-b image: helper:1.3 args: ["run"] # 字段會(huì)被保留 - name: nginx-helper-d # 鍵 nginx-helper-d 會(huì)被保留 image: helper:1.3 # 合并結(jié)果 containers: - name: nginx image: nginx:1.16 # 元素 nginx-helper-a 被刪除 - name: nginx-helper-b image: helper:1.3 args: ["run"] # 字段被保留 - name: nginx-helper-c # 新增元素 image: helper:1.3 - name: nginx-helper-d # 此元素被忽略(保留) image: helper:1.3
解釋:
- 名為 “nginx-helper-a” 的容器被刪除,因?yàn)榕渲梦募胁淮嬖谕娜萜鳎?/li>
- 名為 “nginx-helper-b” 的容器的現(xiàn)時(shí)配置中的 args 被保留。 kubectl apply 能夠辯識出現(xiàn)時(shí)配置中的容器 “nginx-helper-b” 與配置文件 中的容器 “nginx-helper-b” 相同,即使它們的字段值有些不同(配置文件中未給定 args 值)。這是因?yàn)?patchMergeKey 字段(name)的值在兩個(gè)版本中都一樣;
- 名為 “nginx-helper-c” 的容器是新增的,因?yàn)樵谂渲梦募械倪@個(gè)容器尚不存在于現(xiàn)時(shí)配置中;
- 名為 “nginx-helper-d” 的容器被保留下來,因?yàn)樵?last-applied-configuration 中沒有與之同名的元素。
8、合并基本類型元素 list
在 Kubernetes 1.5 中,尚不支持對由基本類型元素構(gòu)成的 list 進(jìn)行合并。
選擇上述哪種策略是由源碼中給定字段的 patchStrategy 標(biāo)記來控制的: types.go。 如果 list 類型字段未設(shè)置 patchStrategy,則整個(gè) list 會(huì)被替換掉。
九、默認(rèn)字段值
API 服務(wù)器會(huì)在對象創(chuàng)建時(shí)其中某些字段未設(shè)置的情況下在現(xiàn)時(shí)配置中為其設(shè)置默認(rèn)值。
下面是一個(gè) Deployment 的配置文件。文件未設(shè)置 strategy:
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: selector: matchLabels: app: nginx minReadySeconds: 5 template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.14.2 ports: - containerPort: 80
使用 kubectl apply 創(chuàng)建對象:
kubectl apply -f https://k8s.io/examples/application/simple_deployment.yaml
使用 kubectl get 打印現(xiàn)時(shí)配置:
kubectl get -f https://k8s.io/examples/application/simple_deployment.yaml -o yaml
輸出顯示 API 在現(xiàn)時(shí)配置中為某些字段設(shè)置了默認(rèn)值。 這些字段在配置文件中并未設(shè)置。
apiVersion: apps/v1 kind: Deployment # ... spec: selector: matchLabels: app: nginx minReadySeconds: 5 replicas: 1 # API 服務(wù)器所設(shè)默認(rèn)值 strategy: rollingUpdate: # API 服務(wù)器基于 strategy.type 所設(shè)默認(rèn)值 maxSurge: 1 maxUnavailable: 1 type: RollingUpdate # API 服務(wù)器所設(shè)默認(rèn)值 template: metadata: creationTimestamp: null labels: app: nginx spec: containers: - image: nginx:1.14.2 imagePullPolicy: IfNotPresent # API 服務(wù)器所設(shè)默認(rèn)值 name: nginx ports: - containerPort: 80 protocol: TCP # API 服務(wù)器所設(shè)默認(rèn)值 resources: {} # API 服務(wù)器所設(shè)默認(rèn)值 terminationMessagePath: /dev/termination-log # API 服務(wù)器所設(shè)默認(rèn)值 dnsPolicy: ClusterFirst # API 服務(wù)器所設(shè)默認(rèn)值 restartPolicy: Always # API 服務(wù)器所設(shè)默認(rèn)值 securityContext: {} # API 服務(wù)器所設(shè)默認(rèn)值 terminationGracePeriodSeconds: 30 # API 服務(wù)器所設(shè)默認(rèn)值 # ...
在補(bǔ)丁請求中,已經(jīng)設(shè)置了默認(rèn)值的字段不會(huì)被重新設(shè)回其默認(rèn)值, 除非在補(bǔ)丁請求中顯式地要求清除。對于默認(rèn)值取決于其他字段的某些字段而言, 這可能會(huì)引發(fā)一些意想不到的行為。當(dāng)所依賴的其他字段后來發(fā)生改變時(shí), 基于它們所設(shè)置的默認(rèn)值只能在顯式執(zhí)行清除操作時(shí)才會(huì)被更新。
為此,建議在配置文件中為服務(wù)器設(shè)置默認(rèn)值的字段顯式提供定義, 即使所給的定義與服務(wù)器端默認(rèn)值設(shè)定相同。 這樣可以使得辯識無法被服務(wù)器重新基于默認(rèn)值來設(shè)置的沖突字段變得容易。
示例:
# last-applied-configuration spec: template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.14.2 ports: - containerPort: 80 # 配置文件 spec: strategy: type: Recreate # 更新的值 template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.14.2 ports: - containerPort: 80 # 現(xiàn)時(shí)配置 spec: strategy: type: RollingUpdate # 默認(rèn)設(shè)置的值 rollingUpdate: # 基于 type 設(shè)置的默認(rèn)值 maxSurge : 1 maxUnavailable: 1 template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.14.2 ports: - containerPort: 80 # 合并后的結(jié)果 - 出錯(cuò)! spec: strategy: type: Recreate # 更新的值:與 rollingUpdate 不兼容 rollingUpdate: # 默認(rèn)設(shè)置的值:與 "type: Recreate" 沖突 maxSurge : 1 maxUnavailable: 1 template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.14.2 ports: - containerPort: 80
解釋:
- 用戶創(chuàng)建 Deployment,未設(shè)置 strategy.type;
- 服務(wù)器為 strategy.type 設(shè)置默認(rèn)值 RollingUpdate,并為 strategy.rollingUpdate 設(shè)置默認(rèn)值;
- 用戶改變 strategy.type 為 Recreate。字段 strategy.rollingUpdate 仍會(huì)取其默認(rèn)設(shè)置值,盡管服務(wù)器期望該字段被清除。 如果 strategy.rollingUpdate 值最初于配置文件中定義, 則它們需要被清除這一點(diǎn)就更明確一些;
- apply 操作失敗,因?yàn)?strategy.rollingUpdate 未被清除。 strategy.rollingupdate 在 strategy.type 為 Recreate 不可被設(shè)定;
- 建議:以下字段應(yīng)該在對象配置文件中顯式定義:
- 如 Deployment、StatefulSet、Job、DaemonSet、ReplicaSet 和 ReplicationController 這類負(fù)載的選擇算符和 PodTemplate 標(biāo)簽;
- Deployment 的上線策略。
十、清除服務(wù)器端字段
沒有出現(xiàn)在配置文件中的字段可以通過將其值設(shè)置為 null 并應(yīng)用配置文件來清除。 對于由服務(wù)器按默認(rèn)值設(shè)置的字段,清除操作會(huì)觸發(fā)重新為字段設(shè)置新的默認(rèn)值。
十一、屬主切換
要將字段的屬主在配置文件和直接指令式寫者之間切換或者更改某個(gè)對象字段時(shí),應(yīng)該采用下面的方法:
- 使用 kubectl apply;
- 直接寫入到現(xiàn)時(shí)配置,但不更改配置文件本身,例如使用 kubectl scale。
1、將屬主從直接指令式寫者更改為配置文件
將字段添加到配置文件。針對該字段,不再直接執(zhí)行對現(xiàn)時(shí)配置的修改。 修改均通過 kubectl apply 來執(zhí)行。
2、將屬主從配置文件改為直接指令式寫者
在 Kubernetes 1.5 中,將字段的屬主從配置文件切換到某指令式寫者需要手動(dòng)執(zhí)行以下步驟:
- 從配置文件中刪除該字段;
- 將字段從現(xiàn)時(shí)對象的 kubectl.kubernetes.io/last-applied-configuration 注解中刪除。
十二、更改管理方法
Kubernetes 對象在同一時(shí)刻應(yīng)該只用一種方法來管理。 從一種方法切換到另一種方法是可能的,但這一切換是一個(gè)手動(dòng)過程。
1、從指令式命令管理切換到聲明式對象配置
從指令式命令管理切換到聲明式對象配置管理的切換包含以下幾個(gè)手動(dòng)步驟:
將現(xiàn)時(shí)對象導(dǎo)出到本地配置文件:
kubectl get <kind>/<name> -o yaml > <kind>_<name>.yaml
手動(dòng)移除配置文件中的 status 字段。
設(shè)置對象上的 kubectl.kubernetes.io/last-applied-configuration 注解:
kubectl replace --save-config -f <kind>_<name>.yaml
更改過程,使用 kubectl apply 專門管理對象。
2、從指令式對象配置切換到聲明式對象配置
在對象上設(shè)置 kubectl.kubernetes.io/last-applied-configuration 注解:
kubectl replace --save-config -f <kind>_<name>.yaml
自此排他性地使用 kubectl apply 來管理對象。
十三、定義控制器選擇算符
不建議更改控制器上的選擇算符。
建議的方法是定義一個(gè)不可變更的 PodTemplate 標(biāo)簽, 僅用于控制器選擇算符且不包含其他語義性的含義。
示例:
selector: matchLabels: controller-selector: "apps/v1/deployment/nginx" template: metadata: labels: controller-selector: "apps/v1/deployment/nginx"