由於檢視k8s資源YAML時常看到沉長的YAML與手寫的格式,相差甚遠不利於閱讀,經過探索官方檔案,才理解什麼是Client-Side & Server-Side Apply。
先看一下我用client-go在生成Deployment的YAML格式,核心程式碼如下:
k8sDeployment, _ := clientSet.AppsV1().Deployments(namespace).Get(context.TODO(), deploymentName, metav1.GetOptions{})
k8sDeployment.Kind = "Deployment"
k8sDeployment.APIVersion = "apps/v1"
k8sDeployment.SetManagedFields(nil) // 不顯示管理欄位,至於什麼是管理欄位? 請繼續閱讀後面的內容.
runtimeWorkload = k8sDeployment
yamlPrinter := printers.YAMLPrinter{}
buffers := bytes.NewBufferString("")
yamlErr := yamlPrinter.PrintObj(runtimeWorkload, buffers)
YAML := buffers.String()
效果:
之前一直有這樣一個困擾,例如這樣的一個Pod資源,可以看到像kubectl.kubernetes.io/last-applied-configuration annotation和 managedFields 這兩個欄位,並不是我們手寫YAML中存在的,他們是什麼呢?
用一個例子展現:
kubectl get pods demo -o yaml
apiVersion: v1
kind: Pod
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{},"creationTimestamp":null,"labels":{"run":"demo"},"name":"demo","namespace":"default"},"spec":{"containers":[{"image":"nginx","name":"demo","resources":{}}],"dnsPolicy":"ClusterFirst","restartPolicy":"Always"},"status":{}}
creationTimestamp: "2022-05-28T07:28:51Z"
labels:
run: demo
managedFields:
- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:metadata:
f:annotations:
.: {}
f:kubectl.kubernetes.io/last-applied-configuration: {}
f:labels:
.: {}
f:run: {}
....
由這兩個欄位,引出本文的兩位主角,Client-Side Apply(以下簡稱CSA)和Server-Side Apply(以下簡稱SSA)
kubectl apply是一種宣告示的K8S物件管理方式,是我們最常用的應用部署,升級方式之一。
需要特別指出的是,kubectl apply宣告的僅僅是它關心的欄位的狀態,而不是整個物件的真實狀態。apply表達的意思是:此次提交管理的欄位應該和apply的組態檔一致,其它欄位並不關心。
比如首次部署時,K8S會將replicas值設定為預設1,隨後由HPA控制器擴容到合適的副本數。
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: nginx
name: nginx
spec:
# replicas: 1 不要設定replicas
selector:
matchLabels:
app: nginx
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: nginx
spec:
containers:
- image: nginx:latest
name: nginx
resources: {}
當升級應用時(修改映象版本),修改組態檔中的image欄位,再次執行kubectl apply。此時kubectl apply只會影響映象版本 ,而不會影響HPA控制器設定的副本數。在這個例子中,replicas欄位不是kubectl apply管理的欄位,因此更新映象時不會被刪除,避免了每次應用升級時,副本數都會被重置。
在上述例子中,為了能識別出replicas不是kubectl管理的欄位,kubectl需要一個標識,用來追蹤物件中哪些欄位是由kubectl apply管理的,而這個標識就是last-applied-configuration。
該annotation是在kubectl apply時,由kubectl使用者端自行填充——每次執行kubectl apply時(未啟用SSA),kubectl會將本次apply的組態檔全量的記錄在last-applied-configurationannotation中,用於追蹤哪些欄位由kubectl apply管理。
Apply一個資源物件時,如果該物件不存在,則建立它(同時寫入last-applied-configuration)。如果物件已經存在,則kubectl需要根據以下三個狀態:
通過patch方式進行更新(而不是將組態檔全量的傳送到伺服器端)。patch報文的計算方法如下:
詳細的patch計算範例可參考K8S檔案中給出的詳細範例。
由此可見,last-applied-configuration體現的是一種ownership的關係,表示哪些欄位是由kubectl管理,它是kubectl apply時,計算patch報文的依據。
Server-Side Apply 是另一種宣告式的物件管理方式,和CSA的作用是基本一致的。SSA始於從1.14開始釋出alpha版本,到1.16beta,到1.18beta2,終於在1.22升級為GA。
它協助使用者、控制器通過宣告式設定的方式管理他們的資源。 使用者端可以傳送完整描述的目標(A fully specified intent), 宣告式地建立和修改物件。
顧名思義,SSA將物件合併的邏輯轉移到了伺服器端(APIServer),使用者端只需提交完整的組態檔,剩下的工作交給伺服器端處理。 在kubectl中使用SSA,只需在kubectl apply時加上--server-side引數即可,例如這樣:
kubectl apply --server-side=true -f - <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
name: test-server-side-apply
data:
a: "a"
b: "b"
EOF
部署成功後,檢視物件會發現該物件中不再存在之前CSA中的last-applied-configuration,取而代之的是metadata.managedFields。檢視上面apply建立的資源:
apiVersion: v1
data:
a: a
b: b
kind: ConfigMap
metadata:
creationTimestamp: "2022-12-04T07:59:24Z"
managedFields:
- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:data:
f:a: {}
f:b: {}
manager: kubectl
operation: Apply
time: "2022-12-04T07:59:24Z"
name: test-server-side-apply
namespace: default
resourceVersion: "1304750"
uid: d265df3d-b9e9-4d0f-91c2-e654f850d25a
欄位管理(field management)機制追蹤物件欄位的變化。 當一個欄位值改變時,其所有權從當前管理器(manager)轉移到施加變更的管理器。 當嘗試將新設定應用到一個物件時,如果欄位有不同的值,且由其他管理器管理, 將會引發衝突。 衝突引發警告訊號:此操作可能抹掉其他共同作業者的修改。 衝突可以被刻意忽略,這種情況下,值將會被改寫,所有權也會發生轉移。
在介紹SSA的合併策略前,我們先了解一下CSA的合併策略。CSA的合併規則是基於Kubernetes的strategic merge patch方式,不同的欄位型別分別有各自不同的合併策略,規則比較複雜。這也導致了CSA容易產生更多的Bug。SSA針對這個問題做了優化,相較於CSA,SSA定義了更加規範和準確的合併規則。 這裡抄錄檔案中的一段表格加以說明:
簡化使用者端邏輯,CSA是一個很重的使用者端邏輯,裡面有複雜的物件合併操作,這意味著apply這項操作和kubectl是深度繫結的,使用其他使用者端或者在控制器(Controller)中難以使用apply方式來設定物件。而SSA將這些合併的邏輯轉移到了伺服器端,提供單一的API,使用者端實現方式得以簡化。這讓apply的能力得以整合到client-go中,讓應用可以通過client-go來使用apply的能力。
更細粒度的欄位所有權管理,減少錯誤覆蓋設定的可能性
相比於last-applied-configuration,SSA使用managedFields來管理每個欄位的ownership,這是一種更細粒度的欄位管理方式。這使得多個管理者之間能更好的共同作業,且其自帶衝突檢測,能很大程度避免錯誤覆蓋設定的發生。
當使用SSA時,dry-run的邏輯也放在伺服器端執行。相比CSA,伺服器端dry-run可以真實的經過validating/mutating admission webhooks的校驗,從而獲取最準確的返回結果。這是CSA無法實現的。
總之CSA和SSA是兩種不同實現的宣告示管理Kubernetes物件的方式。SSA的出現是為了解決了CSA中存在的一些挑戰與問題,如apply邏輯和kubectl深度繫結、strategic merge patch複雜多bug等等。SSA發展至今已是Kubernetes中的一個關鍵特性,相信其最終的目標將會是完全取代CSA,成為Kubernetes中唯一的apply方式。
作者: YOYOFx
出處:https://www.cnblogs.com/maxzhang1985/p/12673160.html
版權:本文采用「署名-非商業性使用-相同方式共用 4.0 國際」知識共用許可協定進行許可。