使用配置檔案宣告式管理 Kubernetes 物件

透過將多個物件配置檔案儲存在一個目錄中並使用 kubectl apply 來根據需要遞迴建立和更新這些物件,可以建立、更新和刪除 Kubernetes 物件。此方法會保留對活動物件所做的寫入,而不會將更改合併回物件配置檔案中。kubectl diff 還會提供 apply 將進行的更改的預覽。

準備工作

安裝 kubectl

你需要擁有一個 Kubernetes 叢集,並且 kubectl 命令列工具必須配置為與你的叢集通訊。建議在至少有兩個不作為控制平面主機的節點的叢集上執行本教程。如果你還沒有叢集,可以使用 minikube 建立一個,或者使用以下 Kubernetes 演練環境之一:

要檢查版本,請輸入 kubectl version

權衡

kubectl 工具支援三種物件管理方式

  • 命令式命令
  • 命令式物件配置
  • 宣告式物件配置

有關每種物件管理的優缺點討論,請參閱Kubernetes 物件管理

概述

宣告式物件配置要求對 Kubernetes 物件定義和配置有深入的理解。如果你尚未閱讀,請閱讀並完成以下文件:

以下是本文件中使用的術語的定義:

  • 物件配置檔案 / 配置檔案:定義 Kubernetes 物件配置的檔案。本主題演示如何將配置檔案傳遞給 kubectl apply。配置檔案通常儲存在原始碼管理中,例如 Git。
  • 即時物件配置 / 即時配置:Kubernetes 叢集觀察到的物件的即時配置值。這些值儲存在 Kubernetes 叢集儲存中,通常是 etcd。
  • 宣告式配置編寫器 / 宣告式編寫器:對即時物件進行更新的人員或軟體元件。本主題中提及的即時編寫器會修改物件配置檔案並執行 kubectl apply 來寫入更改。

如何建立物件

使用 kubectl apply 建立指定目錄中所有由配置檔案定義的物件,除了那些已經存在的物件之外:

kubectl apply -f <directory>

這將在每個物件上設定 kubectl.kubernetes.io/last-applied-configuration: '{...}' 註解。該註解包含用於建立物件的物件配置檔案的內容。

這是一個物件配置檔案的示例:

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 diff 以列印將要建立的物件

kubectl diff -f https://k8s.io/examples/application/simple_deployment.yaml

使用 kubectl apply 建立物件

kubectl apply -f https://k8s.io/examples/application/simple_deployment.yaml

使用 kubectl get 列印即時配置

kubectl get -f https://k8s.io/examples/application/simple_deployment.yaml -o yaml

輸出顯示 kubectl.kubernetes.io/last-applied-configuration 註解已寫入即時配置,並且與配置檔案匹配:

kind: Deployment
metadata:
  annotations:
    # ...
    # This is the json representation of simple_deployment.yaml
    # It was written by kubectl apply when the object was created
    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 更新目錄中定義的所有物件,即使這些物件已經存在。這種方法實現了以下目的:

  1. 在即時配置中設定配置檔案中出現的欄位。
  2. 清除即時配置中已從配置檔案中移除的欄位。
kubectl diff -f <directory>
kubectl apply -f <directory>

這是一個配置檔案的示例:

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 建立物件

kubectl apply -f https://k8s.io/examples/application/simple_deployment.yaml

使用 kubectl get 列印即時配置

kubectl get -f https://k8s.io/examples/application/simple_deployment.yaml -o yaml

輸出顯示 kubectl.kubernetes.io/last-applied-configuration 註解已寫入即時配置,並且與配置檔案匹配:

kind: Deployment
metadata:
  annotations:
    # ...
    # This is the json representation of simple_deployment.yaml
    # It was written by kubectl apply when the object was created
    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 直接更新即時配置中的 replicas 欄位。這不使用 kubectl apply

kubectl scale deployment/nginx-deployment --replicas=2

使用 kubectl get 列印即時配置

kubectl get deployment nginx-deployment -o yaml

輸出顯示 replicas 欄位已設定為 2,並且 last-applied-configuration 註解不包含 replicas 欄位。

apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    # ...
    # note that the annotation does not contain replicas
    # because it was not updated through 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:
  replicas: 2 # written by scale
  # ...
  minReadySeconds: 5
  selector:
    matchLabels:
      # ...
      app: nginx
  template:
    metadata:
      # ...
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.14.2
        # ...
        name: nginx
        ports:
        - containerPort: 80
      # ...

更新 simple_deployment.yaml 配置檔案,將映象從 nginx:1.14.2 更改為 nginx:1.16.1,並刪除 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 # update the image
        ports:
        - containerPort: 80

應用對配置檔案所做的更改

kubectl diff -f https://k8s.io/examples/application/update_deployment.yaml
kubectl apply -f https://k8s.io/examples/application/update_deployment.yaml

使用 kubectl get 列印即時配置

kubectl get -f https://k8s.io/examples/application/update_deployment.yaml -o yaml

輸出顯示即時配置發生以下更改:

  • replicas 欄位保留了由 kubectl scale 設定的值 2。這是因為該欄位已從配置檔案中省略。
  • image 欄位已從 nginx:1.14.2 更新為 nginx:1.16.1
  • last-applied-configuration 註解已更新為新的映象。
  • minReadySeconds 欄位已清除。
  • last-applied-configuration 註解不再包含 minReadySeconds 欄位。
apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    # ...
    # The annotation contains the updated image to nginx 1.16.1,
    # but does not contain the updated replicas to 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 # Set by `kubectl scale`.  Ignored by `kubectl apply`.
  # minReadySeconds cleared by `kubectl apply`
  # ...
  selector:
    matchLabels:
      # ...
      app: nginx
  template:
    metadata:
      # ...
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.16.1 # Set by `kubectl apply`
        # ...
        name: nginx
        ports:
        - containerPort: 80
        # ...
      # ...
    # ...
  # ...

如何刪除物件

有兩種方法可以刪除由 kubectl apply 管理的物件。

使用命令式命令手動刪除物件是推薦的方法,因為它更明確地說明了要刪除的內容,並且不太可能導致使用者意外刪除某些內容。

kubectl delete -f <filename>

替代方法:kubectl apply -f <directory> --prune

作為 kubectl delete 的替代方法,你可以使用 kubectl apply 在清單從本地檔案系統中的目錄中移除後識別要刪除的物件。

在 Kubernetes 1.34 中,kubectl apply 中有兩種可用的修剪模式:

  • 基於白名單的修剪:此模式自 kubectl v1.5 以來一直存在,但由於其設計存在可用性、正確性和效能問題,仍處於 Alpha 階段。基於 ApplySet 的模式旨在取代它。
  • 基於 ApplySet 的修剪:ApplySet 是一個伺服器端物件(預設是 Secret),kubectl 可以使用它在 apply 操作中準確高效地跟蹤集合成員。此模式在 kubectl v1.27 中作為基於白名單的修剪的替代方案以 Alpha 階段引入。

功能狀態: Kubernetes v1.5 [alpha]

要使用基於允許列表的修剪,請在 kubectl apply 呼叫中新增以下標誌:

  • --prune:刪除先前應用但不在當前呼叫傳入的集合中的物件。
  • --prune-allowlist:要考慮修剪的組-版本-種類 (GVK) 列表。此標誌是可選的,但強烈建議使用,因為其預設值是名稱空間和叢集範圍型別的部分列表,這可能導致意外結果。
  • --selector/-l:使用標籤選擇器來限制選擇用於修剪的物件集合。此標誌是可選的,但強烈建議使用。
  • --all:代替 --selector/-l 使用,以顯式選擇所有已列入允許列表型別的先前應用物件。

基於允許列表的修剪會查詢 API 伺服器,以獲取所有匹配給定標籤(如果有)的允許列表 GVK 物件,並嘗試將返回的即時物件配置與物件清單檔案進行匹配。如果物件與查詢匹配,並且目錄中沒有清單,並且具有 kubectl.kubernetes.io/last-applied-configuration 註解,則會將其刪除。

kubectl apply -f <directory> --prune -l <labels> --prune-allowlist=<gvk-list>

功能狀態: Kubernetes v1.27 [alpha]

要使用基於 ApplySet 的修剪,請設定 KUBECTL_APPLYSET=true 環境變數,並在 kubectl apply 呼叫中新增以下標誌:

  • --prune:刪除先前應用但不在當前呼叫傳入的集合中的物件。
  • --applyset:kubectl 可用於在 apply 操作中準確高效地跟蹤集合成員的物件名稱。
KUBECTL_APPLYSET=true kubectl apply -f <directory> --prune --applyset=<name>

預設情況下,使用的 ApplySet 父物件的型別是 Secret。但是,也可以使用 ConfigMap,格式為:--applyset=configmaps/<name>。當使用 Secret 或 ConfigMap 時,如果物件不存在,kubectl 將建立該物件。

還可以使用自定義資源作為 ApplySet 父物件。為此,請用以下標籤標記定義要使用的資源的 Custom Resource Definition (CRD):applyset.kubernetes.io/is-parent-type: true。然後,建立要用作 ApplySet 父級的物件(kubectl 不會自動為自定義資源執行此操作)。最後,在 applyset 標誌中按如下方式引用該物件:--applyset=<resource>.<group>/<name>(例如,widgets.custom.example.com/widget-name)。

透過基於 ApplySet 的修剪,kubectl 在將集合中的每個物件傳送到伺服器之前,為其新增 applyset.kubernetes.io/part-of=<parentID> 標籤。出於效能原因,它還會收集集合中包含的資源型別和名稱空間列表,並將這些資訊作為註解新增到即時父物件中。最後,在 apply 操作結束時,它會查詢 API 伺服器,查詢屬於該集合的、位於這些名稱空間中(或在叢集範圍內,視情況而定)的那些型別物件,這些物件由 applyset.kubernetes.io/part-of=<parentID> 標籤定義。

注意事項和限制

  • 每個物件最多隻能是一個集合的成員。
  • 當使用任何名稱空間父級(包括預設 Secret)時,需要 --namespace 標誌。這意味著跨多個名稱空間的 ApplySets 必須使用叢集範圍的自定義資源作為父物件。
  • 要安全地將基於 ApplySet 的修剪與多個目錄一起使用,請為每個目錄使用唯一的 ApplySet 名稱。

如何檢視物件

您可以使用 kubectl get 配合 -o yaml 檢視即時物件的配置。

kubectl get -f <filename|url> -o yaml

應用如何計算差異併合並更改

kubectl apply 更新物件的即時配置時,它透過向 API 伺服器傳送一個補丁請求來完成。該補丁定義了針對即時物件配置的特定欄位的更新。kubectl apply 命令使用配置檔案、即時配置以及儲存在即時配置中的 last-applied-configuration 註解來計算此補丁請求。

合併補丁計算

kubectl apply 命令將配置檔案內容寫入 kubectl.kubernetes.io/last-applied-configuration 註解。這用於識別已從配置檔案中移除並需要從即時配置中清除的欄位。以下是用於計算應刪除或設定哪些欄位的步驟:

  1. 計算要刪除的欄位。這些欄位存在於 last-applied-configuration 中,但配置檔案中缺少。
  2. 計算要新增或設定的欄位。這些欄位存在於配置檔案中,但其值與即時配置不匹配。

這是一個例子。假設這是一個 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 # update the image
        ports:
        - containerPort: 80

此外,假設這是同一個 Deployment 物件的即時配置:

apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    # ...
    # note that the annotation does not contain replicas
    # because it was not updated through 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:
  replicas: 2 # written by scale
  # ...
  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 將執行的合併計算:

  1. 透過讀取 last-applied-configuration 中的值並將其與配置檔案中的值進行比較來計算要刪除的欄位。無論 last-applied-configuration 中是否出現,清除本地物件配置檔案中明確設定為 null 的欄位。在此示例中,minReadySeconds 出現在 last-applied-configuration 註解中,但未出現在配置檔案中。操作:從即時配置中清除 minReadySeconds
  2. 透過讀取配置檔案中的值並將其與即時配置中的值進行比較來計算要設定的欄位。在此示例中,配置檔案中 image 的值與即時配置中的值不匹配。操作:設定即時配置中 image 的值。
  3. last-applied-configuration 註解設定為與配置檔案值匹配。
  4. 將 1、2、3 的結果合併為對 API 伺服器的單個補丁請求。

以下是合併後的即時配置:

apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    # ...
    # The annotation contains the updated image to nginx 1.16.1,
    # but does not contain the updated replicas to 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 # Set by `kubectl scale`.  Ignored by `kubectl apply`.
  # minReadySeconds cleared by `kubectl apply`
  # ...
  template:
    metadata:
      # ...
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.16.1 # Set by `kubectl apply`
        # ...
        name: nginx
        ports:
        - containerPort: 80
        # ...
      # ...
    # ...
  # ...

不同型別欄位的合併方式

配置檔案中特定欄位與即時配置的合併方式取決於該欄位的型別。有幾種欄位型別:

  • 原始型別:字串、整數或布林型別的欄位。例如,imagereplicas 都是原始欄位。操作:替換。

  • 對映,也稱為 物件:對映型別或包含子欄位的複雜型別的欄位。例如,labelsannotationsspecmetadata 都是對映。操作:合併元素或子欄位。

  • 列表:包含原始型別或對映的列表的欄位。例如,containersportsargs 都是列表。操作:因情況而異。

kubectl apply 更新對映或列表欄位時,它通常不會替換整個欄位,而是更新單個子元素。例如,在合併 Deployment 上的 spec 時,不會替換整個 spec。相反,會比較和合並 spec 的子欄位,例如 replicas

合併原始欄位的更改

原始欄位被替換或清除。

物件配置檔案中的欄位即時物件配置中的欄位last-applied-configuration 中的欄位行動
-將即時配置設定為配置檔案值。
-將即時配置設定為本地配置。
-從即時配置中清除。
-不執行任何操作。保留即時值。

合併對映欄位的更改

表示對映的欄位透過比較對映的每個子欄位或元素來合併。

物件配置檔案中的鍵即時物件配置中的鍵last-applied-configuration 中的欄位行動
-比較子欄位值。
-將即時配置設定為本地配置。
-從即時配置中刪除。
-不執行任何操作。保留即時值。

合併列表型別欄位的更改

合併對列表的更改使用以下三種策略之一:

  • 如果列表的所有元素都是原始型別,則替換該列表。
  • 合併複雜元素列表中的單個元素。
  • 合併原始元素列表。

策略的選擇是針對每個欄位進行的。

如果列表的所有元素都是原始型別,則替換該列表。

將列表視為與原始欄位相同。替換或刪除整個列表。這會保留順序。

示例:使用 kubectl apply 更新 Pod 中容器的 args 欄位。這會將即時配置中 args 的值設定為配置檔案中的值。先前已新增到即時配置中的任何 args 元素都將丟失。配置檔案中定義的 args 元素的順序將在即時配置中保留。

# last-applied-configuration value
    args: ["a", "b"]

# configuration file value
    args: ["a", "c"]

# live configuration
    args: ["a", "b", "d"]

# result after merge
    args: ["a", "c"]

解釋:合併使用配置檔案值作為新的列表值。

合併複雜元素列表中的單個元素

將列表視為一個對映,並將每個元素的特定欄位視為鍵。新增、刪除或更新單個元素。這不保留順序。

此合併策略對每個欄位使用一個特殊標籤,稱為 patchMergeKeypatchMergeKey 在 Kubernetes 原始碼中為每個欄位定義:types.go。當合並對映列表時,為給定元素指定為 patchMergeKey 的欄位將用作該元素的對映鍵。

示例:使用 kubectl apply 更新 PodSpec 的 containers 欄位。這將合併列表,就像它是一個對映,其中每個元素都以 name 為鍵。

# last-applied-configuration value
    containers:
    - name: nginx
      image: nginx:1.16
    - name: nginx-helper-a # key: nginx-helper-a; will be deleted in result
      image: helper:1.3
    - name: nginx-helper-b # key: nginx-helper-b; will be retained
      image: helper:1.3

# configuration file value
    containers:
    - name: nginx
      image: nginx:1.16
    - name: nginx-helper-b
      image: helper:1.3
    - name: nginx-helper-c # key: nginx-helper-c; will be added in result
      image: helper:1.3

# live configuration
    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"] # Field will be retained
    - name: nginx-helper-d # key: nginx-helper-d; will be retained
      image: helper:1.3

# result after merge
    containers:
    - name: nginx
      image: nginx:1.16
      # Element nginx-helper-a was deleted
    - name: nginx-helper-b
      image: helper:1.3
      args: ["run"] # Field was retained
    - name: nginx-helper-c # Element was added
      image: helper:1.3
    - name: nginx-helper-d # Element was ignored
      image: helper:1.3

說明

  • 名為 "nginx-helper-a" 的容器被刪除了,因為配置檔案中沒有名為 "nginx-helper-a" 的容器。
  • 名為 "nginx-helper-b" 的容器保留了即時配置中 args 的更改。kubectl apply 能夠識別即時配置中的 "nginx-helper-b" 與配置檔案中的 "nginx-helper-b" 是相同的,儘管它們的欄位值不同(配置檔案中沒有 args)。這是因為 patchMergeKey 欄位值(name)在兩者中都相同。
  • 名為 "nginx-helper-c" 的容器被添加了,因為即時配置中沒有同名容器,但配置檔案中有一個同名容器。
  • 名為 "nginx-helper-d" 的容器被保留,因為 last-applied-configuration 中沒有同名元素。

合併原始元素列表

自 Kubernetes 1.5 起,不支援合併原始元素列表。

預設欄位值

如果物件建立時未指定某些欄位,API 伺服器會在即時配置中將其設定為預設值。

這是一個 Deployment 的配置檔案。該檔案未指定 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 建立物件

kubectl apply -f https://k8s.io/examples/application/simple_deployment.yaml

使用 kubectl get 列印即時配置

kubectl get -f https://k8s.io/examples/application/simple_deployment.yaml -o yaml

輸出顯示 API 伺服器在即時配置中將多個欄位設定為預設值。這些欄位未在配置檔案中指定。

apiVersion: apps/v1
kind: Deployment
# ...
spec:
  selector:
    matchLabels:
      app: nginx
  minReadySeconds: 5
  replicas: 1 # defaulted by apiserver
  strategy:
    rollingUpdate: # defaulted by apiserver - derived from strategy.type
      maxSurge: 1
      maxUnavailable: 1
    type: RollingUpdate # defaulted by apiserver
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.14.2
        imagePullPolicy: IfNotPresent # defaulted by apiserver
        name: nginx
        ports:
        - containerPort: 80
          protocol: TCP # defaulted by apiserver
        resources: {} # defaulted by apiserver
        terminationMessagePath: /dev/termination-log # defaulted by apiserver
      dnsPolicy: ClusterFirst # defaulted by apiserver
      restartPolicy: Always # defaulted by apiserver
      securityContext: {} # defaulted by apiserver
      terminationGracePeriodSeconds: 30 # defaulted by apiserver
# ...

在補丁請求中,預設欄位不會被重新預設,除非它們作為補丁請求的一部分被明確清除。這可能導致基於其他欄位值預設的欄位出現意外行為。當其他欄位隨後更改時,從中預設的值不會更新,除非它們被明確清除。

因此,建議在配置檔案中明確定義伺服器預設的某些欄位,即使所需值與伺服器預設值匹配。這使得更容易識別不會被伺服器重新預設的衝突值。

示例

# last-applied-configuration
spec:
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

# configuration file
spec:
  strategy:
    type: Recreate # updated value
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

# live configuration
spec:
  strategy:
    type: RollingUpdate # defaulted value
    rollingUpdate: # defaulted value derived from type
      maxSurge : 1
      maxUnavailable: 1
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

# result after merge - ERROR!
spec:
  strategy:
    type: Recreate # updated value: incompatible with rollingUpdate
    rollingUpdate: # defaulted value: incompatible with "type: Recreate"
      maxSurge : 1
      maxUnavailable: 1
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

說明

  1. 使用者在未定義 strategy.type 的情況下建立了一個 Deployment。
  2. 伺服器將 strategy.type 預設為 RollingUpdate 並預設 strategy.rollingUpdate 值。
  3. 使用者將 strategy.type 更改為 Recreatestrategy.rollingUpdate 值保持其預設值,儘管伺服器期望它們被清除。如果 strategy.rollingUpdate 值最初在配置檔案中定義,那麼它們需要被刪除會更清楚。
  4. 由於 strategy.rollingUpdate 未清除,應用失敗。strategy.rollingupdate 欄位不能與 Recreatestrategy.type 一起定義。

建議:這些欄位應在物件配置檔案中明確定義:

  • 工作負載(例如 Deployment、StatefulSet、Job、DaemonSet、ReplicaSet 和 ReplicationController)上的選擇器和 PodTemplate 標籤。
  • 部署回滾策略

如何清除伺服器預設欄位或由其他寫入器設定的欄位

未在配置檔案中出現的欄位可以透過將其值設定為 null 然後應用配置檔案來清除。對於伺服器預設的欄位,這將觸發重新預設這些值。

如何在配置檔案和直接命令式寫入器之間更改欄位的所有權

這些是您應該用於更改單個物件欄位的唯一方法:

  • 使用 kubectl apply
  • 直接寫入即時配置,而不修改配置檔案:例如,使用 kubectl scale

將所有者從直接命令式寫入器更改為配置檔案

將欄位新增到配置檔案中。對於該欄位,停止對不透過 kubectl apply 的即時配置進行直接更新。

將所有者從配置檔案更改為直接命令式寫入器

自 Kubernetes 1.5 起,將欄位的所有權從配置檔案更改為命令式寫入器需要手動步驟:

  • 從配置檔案中刪除該欄位。
  • 從即時物件上的 kubectl.kubernetes.io/last-applied-configuration 註解中刪除該欄位。

管理方法變更

Kubernetes 物件應一次只使用一種方法進行管理。從一種方法切換到另一種方法是可能的,但這是一個手動過程。

從命令式管理遷移到宣告式物件配置

從命令式管理遷移到宣告式物件配置涉及幾個手動步驟:

  1. 將即時物件匯出到本地配置檔案

    kubectl get <kind>/<name> -o yaml > <kind>_<name>.yaml
    
  2. 手動從配置檔案中刪除 status 欄位。

  3. 在物件上設定 kubectl.kubernetes.io/last-applied-configuration 註解

    kubectl replace --save-config -f <kind>_<name>.yaml
    
  4. 更改流程以獨佔使用 kubectl apply 管理物件。

從命令式物件配置遷移到宣告式物件配置

  1. 在物件上設定 kubectl.kubernetes.io/last-applied-configuration 註解

    kubectl replace --save-config -f <kind>_<name>.yaml
    
  2. 更改流程以獨佔使用 kubectl apply 管理物件。

定義控制器選擇器和 PodTemplate 標籤

推薦的方法是定義一個唯一的、不可變的 PodTemplate 標籤,該標籤僅由控制器選擇器使用,沒有其他語義含義。

示例

selector:
  matchLabels:
      controller-selector: "apps/v1/deployment/nginx"
template:
  metadata:
    labels:
      controller-selector: "apps/v1/deployment/nginx"

下一步

最後修改於 2023 年 8 月 24 日下午 6:38 PST:使用 code_sample 簡碼代替 code 簡碼 (e8b136c3b3)