使用配置文件对 Kubernetes 对象进行声明式管理

你可以通过在一个目录中存储多个对象配置文件、并使用 kubectl apply 来递归地创建和更新对象来创建、更新和删除 Kubernetes 对象。 这种方法会保留对现有对象已作出的修改,而不会将这些更改写回到对象配置文件中。 kubectl diff 也会给你呈现 apply 将作出的变更的预览。

准备开始

安装 kubectl

你必须拥有一个 Kubernetes 的集群,同时你的 Kubernetes 集群必须带有 kubectl 命令行工具。 如果你还没有集群,你可以通过 Minikube 构建一 个你自己的集群,或者你可以使用下面任意一个 Kubernetes 工具构建:

要获知版本信息,请输入 kubectl version.

权衡取舍

kubectl 工具能够支持三种对象管理方式:

  • 指令式命令
  • 指令式对象配置
  • 声明式对象配置

关于每种对象管理的优缺点的讨论,可参见 Kubernetes 对象管理

概览

声明式对象管理需要用户对 Kubernetes 对象定义和配置有比较深刻的理解。 如果你还没有这方面的知识储备,请先阅读下面的文档:

以下是本文档中使用的术语的定义:

  • 对象配置文件/配置文件:一个定义 Kubernetes 对象的配置的文件。 本主题展示如何将配置文件传递给 kubectl apply。 配置文件通常存储于类似 Git 这种源码控制系统中。
  • 现时对象配置/现时配置:由 Kubernetes 集群所观测到的对象的现时配置值。 这些配置保存在 Kubernetes 集群存储(通常是 etcd)中。
  • 声明式配置写者/声明式写者:负责更新现时对象的人或者软件组件。 本主题中的声明式写者负责改变对象配置文件并执行 kubectl apply 命令 以写入变更。

如何创建对象

使用 kubectl apply 来创建指定目录中配置文件所定义的所有对象,除非对应对象已经存在:

  1. kubectl apply -f <目录>/

此操作会在每个对象上设置 kubectl.kubernetes.io/last-applied-configuration: '{...}' 注解。注解值中包含了用来创建对象的配置文件的内容。

说明: 添加 -R 标志可以递归地处理目录。

下面是一个对象配置文件示例:

application/simple_deployment.yaml 使用配置文件对 Kubernetes 对象进行声明式管理 - 图1

  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: nginx-deployment
  5. spec:
  6. selector:
  7. matchLabels:
  8. app: nginx
  9. minReadySeconds: 5
  10. template:
  11. metadata:
  12. labels:
  13. app: nginx
  14. spec:
  15. containers:
  16. - name: nginx
  17. image: nginx:1.14.2
  18. ports:
  19. - containerPort: 80

执行 kubectl diff 可以打印出将被创建的对象:

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

说明:

diff 使用服务器端试运行(Server-side Dry-run) 功能特性;而该功能特性需要在 kube-apiserver 上启用。

由于 diff 操作会使用试运行模式执行服务器端 apply 请求,因此需要为 用户配置 PATCHCREATEUPDATE 操作权限。 参阅试运行授权 了解详情。

使用 kubectl apply 来创建对象:

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

使用 kubectl get 打印其现时配置:

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

输出显示注解 kubectl.kubernetes.io/last-applied-configuration 被写入到 现时配置中,并且其内容与配置文件相同:

  1. kind: Deployment
  2. metadata:
  3. annotations:
  4. # ...
  5. # This is the json representation of simple_deployment.yaml
  6. # It was written by kubectl apply when the object was created
  7. kubectl.kubernetes.io/last-applied-configuration: |
  8. {"apiVersion":"apps/v1","kind":"Deployment",
  9. "metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"},
  10. "spec":{"minReadySeconds":5,"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}},
  11. "spec":{"containers":[{"image":"nginx:1.14.2","name":"nginx",
  12. "ports":[{"containerPort":80}]}]}}}}
  13. # ...
  14. spec:
  15. # ...
  16. minReadySeconds: 5
  17. selector:
  18. matchLabels:
  19. # ...
  20. app: nginx
  21. template:
  22. metadata:
  23. # ...
  24. labels:
  25. app: nginx
  26. spec:
  27. containers:
  28. - image: nginx:1.14.2
  29. # ...
  30. name: nginx
  31. ports:
  32. - containerPort: 80
  33. # ...
  34. # ...
  35. # ...
  36. # ...

如何更新对象

你也可以使用 kubectl apply 来更新某个目录中定义的所有对象,即使那些对象已经存在。 这一操作会隐含以下行为:

  1. 在现时配置中设置配置文件中出现的字段;
  2. 在现时配置中清除配置文件中已删除的字段。
  1. kubectl diff -f <目录>/
  2. kubectl apply -f <目录>/

说明: 使用 -R 标志递归处理目录。

下面是一个配置文件示例:

application/simple_deployment.yaml 使用配置文件对 Kubernetes 对象进行声明式管理 - 图2

  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: nginx-deployment
  5. spec:
  6. selector:
  7. matchLabels:
  8. app: nginx
  9. minReadySeconds: 5
  10. template:
  11. metadata:
  12. labels:
  13. app: nginx
  14. spec:
  15. containers:
  16. - name: nginx
  17. image: nginx:1.14.2
  18. ports:
  19. - containerPort: 80

使用 kubectl apply 来创建对象:

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

说明: 出于演示的目的,上面的命令引用的是单个文件而不是整个目录。

使用 kubectl get 打印现时配置:

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

输出显示,注解 kubectl.kubernetes.io/last-applied-configuration 被写入到 现时配置中,并且其取值与配置文件内容相同。

  1. kind: Deployment
  2. metadata:
  3. annotations:
  4. # ...
  5. # 此为 simple_deployment.yaml 的 JSON 表示
  6. # 在对象创建时由 kubectl apply 命令写入
  7. kubectl.kubernetes.io/last-applied-configuration: |
  8. {"apiVersion":"apps/v1","kind":"Deployment",
  9. "metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"},
  10. "spec":{"minReadySeconds":5,"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}},
  11. "spec":{"containers":[{"image":"nginx:1.14.2","name":"nginx",
  12. "ports":[{"containerPort":80}]}]}}}}
  13. # ...
  14. spec:
  15. # ...
  16. minReadySeconds: 5
  17. selector:
  18. matchLabels:
  19. # ...
  20. app: nginx
  21. template:
  22. metadata:
  23. # ...
  24. labels:
  25. app: nginx
  26. spec:
  27. containers:
  28. - image: nginx:1.14.2
  29. # ...
  30. name: nginx
  31. ports:
  32. - containerPort: 80
  33. # ...
  34. # ...
  35. # ...
  36. # ...

通过 kubeclt scale 命令直接更新现时配置中的 replicas 字段。 这一命令没有使用 kubectl apply

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

使用 kubectl get 来打印现时配置:

  1. kubectl get deployment nginx-deployment -o yaml

输出显示,replicas 字段已经被设置为 2,而 last-applied-configuration 注解中 并不包含 replicas 字段。

  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. annotations:
  5. # ...
  6. # 注意注解中并不包含 replicas
  7. # 这是因为更新并不是通过 kubectl apply 来执行的
  8. kubectl.kubernetes.io/last-applied-configuration: |
  9. {"apiVersion":"apps/v1","kind":"Deployment",
  10. "metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"},
  11. "spec":{"minReadySeconds":5,"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}},
  12. "spec":{"containers":[{"image":"nginx:1.14.2","name":"nginx",
  13. "ports":[{"containerPort":80}]}]}}}}
  14. # ...
  15. spec:
  16. replicas: 2 # written by scale
  17. # ...
  18. minReadySeconds: 5
  19. selector:
  20. matchLabels:
  21. # ...
  22. app: nginx
  23. template:
  24. metadata:
  25. # ...
  26. labels:
  27. app: nginx
  28. spec:
  29. containers:
  30. - image: nginx:1.14.2
  31. # ...
  32. name: nginx
  33. ports:
  34. - containerPort: 80
  35. # ...

现在更新 simple_deployment.yaml 配置文件,将镜像文件从 nginx:1.14.2 更改为 nginx:1.16.1,同时删除minReadySeconds 字段:

application/update_deployment.yaml 使用配置文件对 Kubernetes 对象进行声明式管理 - 图3

  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: nginx-deployment
  5. spec:
  6. selector:
  7. matchLabels:
  8. app: nginx
  9. template:
  10. metadata:
  11. labels:
  12. app: nginx
  13. spec:
  14. containers:
  15. - name: nginx
  16. image: nginx:1.16.1 # update the image
  17. ports:
  18. - containerPort: 80

应用对配置文件所作更改:

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

使用 kubectl get 打印现时配置:

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

输出显示现时配置中发生了以下更改:

  • 字段 replicas 保留了 kubectl scale 命令所设置的值:2; 之所以该字段被保留是因为配置文件中并没有设置 replicas
  • 字段 image 的内容已经从 nginx:1.14.2 更改为 nginx:1.16.1
  • 注解 last-applied-configuration 内容被更改为新的镜像名称。
  • 字段 minReadySeconds 被移除。
  • 注解 last-applied-configuration 中不再包含 minReadySeconds 字段。
  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. annotations:
  5. # ...
  6. # 注解中包含更新后的镜像 nginx 1.16.1
  7. # 但是其中并不包含更改后的 replicas 值 2
  8. kubectl.kubernetes.io/last-applied-configuration: |
  9. {"apiVersion":"apps/v1","kind":"Deployment",
  10. "metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"},
  11. "spec":{"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}},
  12. "spec":{"containers":[{"image":"nginx:1.16.1","name":"nginx",
  13. "ports":[{"containerPort":80}]}]}}}}
  14. # ...
  15. spec:
  16. replicas: 2 # 由 `kubectl scale` 设置,被 `kubectl apply` 命令忽略
  17. # minReadySeconds 被 `kubectl apply` 清除
  18. # ...
  19. selector:
  20. matchLabels:
  21. # ...
  22. app: nginx
  23. template:
  24. metadata:
  25. # ...
  26. labels:
  27. app: nginx
  28. spec:
  29. containers:
  30. - image: nginx:1.16.1 # 由 `kubectl apply` 设置
  31. # ...
  32. name: nginx
  33. ports:
  34. - containerPort: 80
  35. # ...
  36. # ...
  37. # ...
  38. # ...

警告:kubectl apply 与指令式对象配置命令 kubectl createkubectl replace 混合使用是不受支持的。这是因为 createreplace 命令都不会保留 kubectl apply 用来计算更新内容所使用的 kubectl.kubernetes.io/last-applied-configuration 注解值。

如何删除对象

有两种方法来删除 kubectl apply 管理的对象。

建议操作:kubectl delete -f <文件名>

使用指令式命令来手动删除对象是建议的方法,因为这种方法更为明确地给出了 要删除的内容是什么,且不容易造成用户不小心删除了其他对象的情况。

  1. kubectl delete -f <文件名>

替代方式:kubectl apply -f <目录名称/> --prune -l your=label

只有在充分理解此命令背后含义的情况下才建议这样操作。

警告: kubectl apply --prune 命令本身仍处于 Alpha 状态,在后续发布版本中可能会 引入一些向后不兼容的变化。

警告: 在使用此命令时必须小心,这样才不会无意中删除不想删除的对象。

作为 kubectl delete 操作的替代方式,你可以在目录中对象配置文件被删除之后, 使用 kubectl apply 来辩识要删除的对象。 带 --prune 标志的 apply 命令会首先查询 API 服务器,获得与某组标签相匹配 的对象列表,之后将返回的现时对象配置与目录中的对象配置文件相比较。 如果某对象在查询中被匹配到,但在目录中没有文件与其相对应,并且其中还包含 last-applied-configuration 注解,则该对象会被删除。

  1. kubectl apply -f <directory/> --prune -l <labels>

警告: 带剪裁(prune)行为的 apply 操作应在包含对象配置文件的目录的根目录运行。 如果在其子目录中运行,可能导致对象被不小心删除。 因为某些对象可能与 -l <标签> 的标签选择算符匹配,但其配置文件不在当前 子目录下。

如何查看对象

你可以使用 kubectl get 并指定 -o yaml 选项来查看现时对象的配置:

  1. kubectl get -f <文件名 | URL> -o yaml

apply 操作是如何计算配置差异并合并变更的?

注意: patch 是一种更新操作,其作用域为对象的一些特定字段而不是整个对象。 这使得你可以更新对象的特定字段集合而不必先要读回对象。

kubectl apply 更新对象的现时配置,它是通过向 API 服务器发送一个 patch 请求 来执行更新动作的。 所提交的补丁中定义了对现时对象配置中特定字段的更新。 kubectl apply 命令会使用当前的配置文件、现时配置以及现时配置中保存的 last-applied-configuration 注解内容来计算补丁更新内容。

合并补丁计算

kubectl apply 命令将配置文件的内容写入到 kubectl.kubernetes.io/last-applied-configuration 注解中。 这些内容用来识别配置文件中已经移除的、因而也需要从现时配置中删除的字段。 用来计算要删除或设置哪些字段的步骤如下:

  1. 计算要删除的字段,即在 last-applied-configuration 中存在但在 配置文件中不再存在的字段。
  2. 计算要添加或设置的字段,即在配置文件中存在但其取值与现时配置不同的字段。

下面是一个例子。假定此文件是某 Deployment 对象的配置文件:

application/update_deployment.yaml 使用配置文件对 Kubernetes 对象进行声明式管理 - 图4

  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: nginx-deployment
  5. spec:
  6. selector:
  7. matchLabels:
  8. app: nginx
  9. template:
  10. metadata:
  11. labels:
  12. app: nginx
  13. spec:
  14. containers:
  15. - name: nginx
  16. image: nginx:1.16.1 # update the image
  17. ports:
  18. - containerPort: 80

同时假定同一 Deployment 对象的现时配置如下:

  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. annotations:
  5. # ...
  6. kubectl.kubernetes.io/last-applied-configuration: |
  7. {"apiVersion":"apps/v1","kind":"Deployment",
  8. "metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"},
  9. "spec":{"minReadySeconds":5,"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}},
  10. "spec":{"containers":[{"image":"nginx:1.14.2","name":"nginx",
  11. "ports":[{"containerPort":80}]}]}}}}
  12. # ...
  13. spec:
  14. replicas: 2
  15. # ...
  16. minReadySeconds: 5
  17. selector:
  18. matchLabels:
  19. # ...
  20. app: nginx
  21. template:
  22. metadata:
  23. # ...
  24. labels:
  25. app: nginx
  26. spec:
  27. containers:
  28. - image: nginx:1.14.2
  29. # ...
  30. name: nginx
  31. ports:
  32. - containerPort: 80
  33. # ...

下面是 kubectl apply 将执行的合并计算:

  1. 通过读取 last-applied-configuration 并将其与配置文件中的值相比较, 计算要删除的字段。 对于本地对象配置文件中显式设置为空的字段,清除其在现时配置中的设置, 无论这些字段是否出现在 last-applied-configuration 中。 在此例中,minReadySeconds 出现在 last-applied-configuration 注解中,但 并不存在于配置文件中。 动作: 从现时配置中删除 minReadySeconds 字段。
  2. 通过读取配置文件中的值并将其与现时配置相比较,计算要设置的字段。 在这个例子中,配置文件中的 image 值与现时配置中的 image 不匹配。 动作:设置现时配置中的 image 值。
  3. 设置 last-applied-configuration 注解的内容,使之与配置文件匹配。
  4. 将第 1、2、3 步骤得出的结果合并,构成向 API 服务器发送的补丁请求内容。

下面是此合并操作之后形成的现时配置:

  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. annotations:
  5. # ...
  6. # 注解中包含更新后的 image,nginx 1.11.9,
  7. # 但不包含更新后的 replicas
  8. kubectl.kubernetes.io/last-applied-configuration: |
  9. {"apiVersion":"apps/v1","kind":"Deployment",
  10. "metadata":{"annotations":{},"name":"nginx-deployment","namespace":"default"},
  11. "spec":{"selector":{"matchLabels":{"app":nginx}},"template":{"metadata":{"labels":{"app":"nginx"}},
  12. "spec":{"containers":[{"image":"nginx:1.16.1","name":"nginx",
  13. "ports":[{"containerPort":80}]}]}}}}
  14. # ...
  15. spec:
  16. selector:
  17. matchLabels:
  18. # ...
  19. app: nginx
  20. replicas: 2
  21. # minReadySeconds 此字段被清除
  22. # ...
  23. template:
  24. metadata:
  25. # ...
  26. labels:
  27. app: nginx
  28. spec:
  29. containers:
  30. - image: nginx:1.16.1
  31. # ...
  32. name: nginx
  33. ports:
  34. - containerPort: 80
  35. # ...
  36. # ...
  37. # ...
  38. # ...

不同类型字段的合并方式

配置文件中的特定字段与现时配置合并时,合并方式取决于字段类型。 字段类型有几种:

  • 基本类型:字段类型为 stringintegerboolean 之一。 例如:imagereplicas 字段都是基本类型字段。

    动作: 替换。

  • map:也称作 object。类型为 map 或包含子域的复杂结构。例如,labelsannotationsspecmetadata 都是 map。

    动作: 合并元素或子字段。

  • list:包含元素列表的字段,其中每个元素可以是基本类型或 map。 例如,containersportsargs 都是 list。

    动作: 不一定。

kubectl apply 更新某个 map 或 list 字段时,它通常不会替换整个字段,而是会 更新其中的各个子元素。例如,当合并 Deployment 的 spec 时,kubectl 并不会 将其整个替换掉。相反,实际操作会是对 replicas 这类 spec 的子字段来执行比较和更新。

合并对基本类型字段的更新

基本类型字段会被替换或清除。

说明: - 表示的是“不适用”,因为指定数值未被使用。

字段在对象配置文件中字段在现时对象配置中字段在 last-applied-configuration动作
-将配置文件中值设置到现时配置上。
-将配置文件中值设置到现时配置上。
-从现时配置中移除。
-什么也不做。保持现时值。

合并对 map 字段的变更

用来表示映射的字段在合并时会逐个子字段或元素地比较:

说明: - 表示的是“不适用”,因为指定数值未被使用。

键存在于对象配置文件中键存在于现时对象配置中键存在于 last-applied-configuration动作
-比较子域取值。
-将现时配置设置为本地配置值。
-从现时配置中删除键。
-什么也不做,保留现时值。

合并 list 类型字段的变更

对 list 类型字段的变更合并会使用以下三种策略之一:

  • 如果 list 所有元素都是基本类型则替换整个 list。
  • 如果 list 中元素是复合结构则逐个元素执行合并操作。
  • 合并基本类型元素构成的 list。

策略的选择是基于各个字段做出的。

如果 list 中元素都是基本类型则替换整个 list

将整个 list 视为一个基本类型字段。或者整个替换或者整个删除。 此操作会保持 list 中元素顺序不变

示例: 使用 kubectl apply 来更新 Pod 中 Container 的 args 字段。此操作会 将现时配置中的 args 值设为配置文件中的值。 所有之前添加到现时配置中的 args 元素都会丢失。 配置文件中的 args 元素的顺序在被添加到现时配置中时保持不变。

  1. # last-applied-configuration 值
  2. args: ["a", "b"]
  3. # 配置文件值
  4. args: ["a", "c"]
  5. # 现时配置
  6. args: ["a", "b", "d"]
  7. # 合并结果
  8. args: ["a", "c"]

解释: 合并操作将配置文件中的值当做新的 list 值。

如果 list 中元素为复合类型则逐个执行合并

此操作将 list 视为 map,并将每个元素中的特定字段当做其主键。 逐个元素地执行添加、删除或更新操作。结果顺序无法得到保证。

此合并策略会使用每个字段上的一个名为 patchMergeKey 的特殊标签。 Kubernetes 源代码中为每个字段定义了 patchMergeKeytypes.go 当合并由 map 组成的 list 时,给定元素中被设置为 patchMergeKey 的字段会被 当做该元素的 map 键值来使用。

例如: 使用 kubectl apply 来更新 Pod 规约中的 containers 字段。 此操作会将 containers 列表视作一个映射来执行合并,每个元素的主键为 name

  1. # last-applied-configuration 值
  2. containers:
  3. - name: nginx
  4. image: nginx:1.16
  5. - name: nginx-helper-a # 键 nginx-helper-a 会被删除
  6. image: helper:1.3
  7. - name: nginx-helper-b # 键 nginx-helper-b 会被保留
  8. image: helper:1.3
  9. # 配置文件值
  10. containers:
  11. - name: nginx
  12. image: nginx:1.16
  13. - name: nginx-helper-b
  14. image: helper:1.3
  15. - name: nginx-helper-c # 键 nginx-helper-c 会被添加
  16. image: helper:1.3
  17. # 现时配置
  18. containers:
  19. - name: nginx
  20. image: nginx:1.16
  21. - name: nginx-helper-a
  22. image: helper:1.3
  23. - name: nginx-helper-b
  24. image: helper:1.3
  25. args: ["run"] # 字段会被保留
  26. - name: nginx-helper-d # 键 nginx-helper-d 会被保留
  27. image: helper:1.3
  28. # result after merge
  29. containers:
  30. - name: nginx
  31. image: nginx:1.16
  32. # 元素 nginx-helper-a 被删除
  33. - name: nginx-helper-b
  34. image: helper:1.3
  35. args: ["run"] # 字段被保留
  36. - name: nginx-helper-c # 新增元素
  37. image: helper:1.3
  38. - name: nginx-helper-d # 此元素被忽略(保留)
  39. image: helper:1.3

解释:

  • 名为 “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 中没有与之同名的元素。

合并基本类型元素 list

在 Kubernetes 1.5 中,尚不支持对由基本类型元素构成的 list 进行合并。

说明: 选择上述哪种策略是由源码中给定字段的 patchStrategy 标记来控制的: types.go 如果 list 类型字段未设置 patchStrategy,则整个 list 会被替换掉。

默认字段值

API 服务器会在对象创建时其中某些字段未设置的情况下在现时配置中为其设置默认值。

下面是一个 Deployment 的配置文件。文件未设置 strategy

application/simple_deployment.yaml 使用配置文件对 Kubernetes 对象进行声明式管理 - 图5

  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: nginx-deployment
  5. spec:
  6. selector:
  7. matchLabels:
  8. app: nginx
  9. minReadySeconds: 5
  10. template:
  11. metadata:
  12. labels:
  13. app: nginx
  14. spec:
  15. containers:
  16. - name: nginx
  17. image: nginx:1.14.2
  18. ports:
  19. - containerPort: 80

使用 kubectl apply 创建对象:

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

使用 kubectl get 打印现时配置:

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

输出显示 API 在现时配置中为某些字段设置了默认值。 这些字段在配置文件中并未设置。

  1. apiVersion: apps/v1
  2. kind: Deployment
  3. # ...
  4. spec:
  5. selector:
  6. matchLabels:
  7. app: nginx
  8. minReadySeconds: 5
  9. replicas: 1 # API 服务器所设默认值
  10. strategy:
  11. rollingUpdate: # API 服务器基于 strategy.type 所设默认值
  12. maxSurge: 1
  13. maxUnavailable: 1
  14. type: RollingUpdate # API 服务器所设默认值
  15. template:
  16. metadata:
  17. creationTimestamp: null
  18. labels:
  19. app: nginx
  20. spec:
  21. containers:
  22. - image: nginx:1.14.2
  23. imagePullPolicy: IfNotPresent # API 服务器所设默认值
  24. name: nginx
  25. ports:
  26. - containerPort: 80
  27. protocol: TCP # API 服务器所设默认值
  28. resources: {} # API 服务器所设默认值
  29. terminationMessagePath: /dev/termination-log # API 服务器所设默认值
  30. dnsPolicy: ClusterFirst # API 服务器所设默认值
  31. restartPolicy: Always # API 服务器所设默认值
  32. securityContext: {} # API 服务器所设默认值
  33. terminationGracePeriodSeconds: 30 # API 服务器所设默认值
  34. # ...

在补丁请求中,已经设置了默认值的字段不会被重新设回其默认值,除非 在补丁请求中显式地要求清除。对于默认值取决于其他字段的某些字段而言, 这可能会引发一些意想不到的行为。当所依赖的其他字段后来发生改变时, 基于它们所设置的默认值只能在显式执行清除操作时才会被更新。

为此,建议在配置文件中为服务器设置默认值的字段显式提供定义,即使所 给的定义与服务器端默认值设定相同。这样可以使得辩识无法被服务器重新 基于默认值来设置的冲突字段变得容易。

示例:

  1. # last-applied-configuration
  2. spec:
  3. template:
  4. metadata:
  5. labels:
  6. app: nginx
  7. spec:
  8. containers:
  9. - name: nginx
  10. image: nginx:1.14.2
  11. ports:
  12. - containerPort: 80
  13. # 配置文件
  14. spec:
  15. strategy:
  16. type: Recreate # 更新的值
  17. template:
  18. metadata:
  19. labels:
  20. app: nginx
  21. spec:
  22. containers:
  23. - name: nginx
  24. image: nginx:1.14.2
  25. ports:
  26. - containerPort: 80
  27. # 现时配置
  28. spec:
  29. strategy:
  30. type: RollingUpdate # 默认设置的值
  31. rollingUpdate: # 基于 type 设置的默认值
  32. maxSurge : 1
  33. maxUnavailable: 1
  34. template:
  35. metadata:
  36. labels:
  37. app: nginx
  38. spec:
  39. containers:
  40. - name: nginx
  41. image: nginx:1.14.2
  42. ports:
  43. - containerPort: 80
  44. # 合并后的结果 - 出错!
  45. spec:
  46. strategy:
  47. type: Recreate # 更新的值:与 rollingUpdate 不兼容
  48. rollingUpdate: # 默认设置的值:与 "type: Recreate" 冲突
  49. maxSurge : 1
  50. maxUnavailable: 1
  51. template:
  52. metadata:
  53. labels:
  54. app: nginx
  55. spec:
  56. containers:
  57. - name: nginx
  58. image: nginx:1.14.2
  59. ports:
  60. - containerPort: 80

解释:

  1. 用户创建 Deployment,未设置 strategy.type
  2. 服务器为 strategy.type 设置默认值 RollingUpdate,并为 strategy.rollingUpdate 设置默认值。
  3. 用户改变 strategy.typeRecreate。字段 strategy.rollingUpdate 仍会取其 默认设置值,尽管服务器期望该字段被清除。 如果 strategy.rollingUpdate 值最初于配置文件中定义,则它们需要被清除 这一点就更明确一些。
  4. apply 操作失败,因为 strategy.rollingUpdate 未被清除。 strategy.rollingupdatestrategy.typeRecreate 不可被设定。

建议:以下字段应该在对象配置文件中显式定义:

  • 如 Deployment、StatefulSet、Job、DaemonSet、ReplicaSet 和 ReplicationController 这类负载的选择算符和 PodTemplate 标签
  • Deployment 的上线策略

如何清除服务器端按默认值设置的字段或者被其他写者设置的字段

没有出现在配置文件中的字段可以通过将其值设置为 null 并应用配置文件来清除。 对于由服务器按默认值设置的字段,清除操作会触发重新为字段设置新的默认值。

如何将字段的属主在配置文件和直接指令式写者之间切换

更改某个对象字段时,应该采用下面的方法:

  • 使用 kubectl apply.
  • 直接写入到现时配置,但不更改配置文件本身,例如使用 kubectl scale

将属主从直接指令式写者更改为配置文件

将字段添加到配置文件。针对该字段,不再直接执行对现时配置的修改。 修改均通过 kubectl apply 来执行。

将属主从配置文件改为直接指令式写者

在 Kubernetes 1.5 中,将字段的属主从配置文件切换到某指令式写者需要手动 执行以下步骤:

  • 从配置文件中删除该字段;
  • 将字段从现时对象的 kubectl.kubernetes.io/last-applied-configuration 注解 中删除。

更改管理方法

Kubernetes 对象在同一时刻应该只用一种方法来管理。 从一种方法切换到另一种方法是可能的,但这一切换是一个手动过程。

说明: 在声明式管理方法中使用指令式命令来删除对象是可以的。

从指令式命令管理切换到声明式对象配置

从指令式命令管理切换到声明式对象配置管理的切换包含以下几个手动步骤:

  1. 将现时对象导出到本地配置文件:

    1. kubectl get <kind>/<name> -o yaml > <kind>_<name>.yaml
  2. 手动移除配置文件中的 status 字段。

    说明: 这一步骤是可选的,因为 kubectl apply 并不会更新 status 字段,即便 配置文件中包含 status 字段。

  3. 设置对象上的 kubectl.kubernetes.io/last-applied-configuration 注解:

    1. kubectl replace --save-config -f <kind>_<name>.yaml

从指令式对象配置切换到声明式对象配置

  1. 在对象上设置 kubectl.kubernetes.io/last-applied-configuration 注解:

    1. kubectl replace -save-config -f <kind>_<name>.yaml
  2. 自此排他性地使用 kubectl apply 来管理对象。

定义控制器选择算符和 PodTemplate 标签

警告: 强烈不建议更改控制器上的选择算符。

建议的方法是定义一个不可变更的 PodTemplate 标签,仅用于控制器选择算符且 不包含其他语义性的含义。

示例:

  1. selector:
  2. matchLabels:
  3. controller-selector: "apps/v1/deployment/nginx"
  4. template:
  5. metadata:
  6. labels:
  7. controller-selector: "apps/v1/deployment/nginx"

接下来