管理 Kubernetes 对象
1 - 使用配置文件对 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 <目录>
此操作会在每个对象上设置 kubectl.kubernetes.io/last-applied-configuration: '{...}'
注解。注解值中包含了用来创建对象的配置文件的内容。
说明:
添加 -R
标志可以递归地处理目录。
下面是一个对象配置文件示例:
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
说明:
diff
使用服务器端试运行(Server-side Dry-run)
功能特性;而该功能特性需要在 kube-apiserver
上启用。
由于 diff
操作会使用试运行模式执行服务器端 apply 请求,因此需要为用户配置
PATCH
、CREATE
和 UPDATE
操作权限。
参阅试运行授权了解详情。
使用 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:
# ...
# 此为 simple_deployment.yaml 的 JSON 表示
# 在对象创建时由 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
来更新某个目录中定义的所有对象,即使那些对象已经存在。
这一操作会隐含以下行为:
- 在现时配置中设置配置文件中出现的字段;
- 在现时配置中清除配置文件中已删除的字段。
kubectl diff -f <目录>
kubectl apply -f <目录>
说明:
使用 -R
标志递归处理目录。
下面是一个配置文件示例:
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:
# ...
# 此为 simple_deployment.yaml 的 JSON 表示
# 在对象创建时由 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
命令直接更新现时配置中的 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:
# ...
# 注意注解中并不包含 replicas
# 这是因为更新并不是通过 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:
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
# ...
现在更新 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 # 更新该镜像
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; 之所以该字段被保留是因为配置文件中并没有设置replicas
。 - 字段
image
的内容已经从nginx:1.14.2
更改为nginx:1.16.1
。 - 注解
last-applied-configuration
内容被更改为新的镜像名称。 - 字段
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` 设置,被 `kubectl apply` 命令忽略
# minReadySeconds 被 `kubectl apply` 清除
# ...
selector:
matchLabels:
# ...
app: nginx
template:
metadata:
# ...
labels:
app: nginx
spec:
containers:
- image: nginx:1.16.1 # 由 `kubectl apply` 设置
# ...
name: nginx
ports:
- containerPort: 80
# ...
# ...
# ...
# ...
警告:
将 kubectl apply
与指令式对象配置命令 kubectl create
或 kubectl replace
混合使用是不受支持的。这是因为 create
和 replace
命令都不会保留
kubectl apply
用来计算更新内容所使用的
kubectl.kubernetes.io/last-applied-configuration
注解值。
如何删除对象
有两种方法来删除 kubectl apply
管理的对象。
建议操作:kubectl delete -f <文件名>
使用指令式命令来手动删除对象是建议的方法,因为这种方法更为明确地给出了要删除的内容是什么, 且不容易造成用户不小心删除了其他对象的情况。
kubectl delete -f <文件名>
替代方式:kubectl apply -f <目录> --prune
作为 kubectl delete
操作的替代方式,你可以在本地文件系统的目录中的清单文件被删除之后,
使用 kubectl apply
来辩识要删除的对象。
在 Kubernetes 1.32 中,kubectl apply
可使用两种剪裁模式:
- 基于 Allowlist 的剪裁:这种模式自 kubectl v1.5 版本开始就存在, 但由于其设计存在易用性、正确性和性能问题,因此仍处于 Alpha 阶段。 基于 ApplySet 的模式设计用于取代这种模式。
- 基于 ApplySet 的剪裁:apply set 是一个服务器端对象(默认是一个 Secret), kubectl 可以使用它来在 apply 操作中准确高效地跟踪集合成员。 这种模式在 kubectl v1.27 中以 Alpha 引入,作为基于 Allowlist 剪裁的替代方案。
Kubernetes v1.5 [alpha]
警告:
在 Allowlist 模式下使用 kubectl apply
命令时要小心使用 --prune
标志。
哪些对象被剪裁取决于 --prune-allowlist
、--selector
和 --namespace
标志的值,
并且依赖于作用域中对象的动态发现。特别是在调用之间更改标志值时,这可能会导致对象被意外删除或保留。
要使用基于 Allowlist 的剪裁,可以添加以下标志到你的 kubectl apply
调用:
--prune
:删除之前应用的、不在当前调用所传递的集合中的对象。--prune-allowlist
:一个需要考虑进行剪裁的组-版本-类别(group-version-kind, GVK)列表。 这个标志是可选的,但强烈建议使用,因为它的默认值是同时作用于命名空间和集群的部分类型列表, 这可能会产生令人意外的结果。--selector/-l
:使用标签选择算符以约束要剪裁的对象的集合。此标志是可选的,但强烈建议使用。--all
:用于替代--selector/-l
以显式选择之前应用的类型为 Allowlist 的所有对象。
基于 Allowlist 的剪裁会查询 API 服务器以获取与给定标签(如果有)匹配的所有允许列出的 GVK 对象,
并尝试将返回的活动对象配置与对象清单文件进行匹配。如果一个对象与查询匹配,并且它在目录中没有对应的清单,
但它有一个 kubectl.kubernetes.io/last-applied-configuration
注解,则它将被删除。
kubectl apply -f <目录> --prune -l <标签> --prune-allowlist=<gvk 列表>
警告:
带剪裁(prune)行为的 apply
操作应在包含对象清单的根目录运行。
如果对象之前被执行了 apply
操作,具有给定的标签(如果有)且未出现在子目录中,
在其子目录中运行可能导致对象被不小心删除。
Kubernetes v1.27 [alpha]
注意:
kubectl apply --prune --applyset
目前处于 Alpha 阶段,在后续的版本中可能引入向后不兼容的变更。
要使用基于 ApplySet 的剪裁,请设置 KUBECTL_APPLYSET=true
环境变量,
并添加以下标志到你的 kubectl apply
调用中:
--prune
:删除之前应用的、不在当前调用所传递的集合中的对象。--applyset
:是 kubectl 可以使用的对象的名称,用于在apply
操作中准确高效地跟踪集合成员。
KUBECTL_APPLYSET=true kubectl apply -f <目录> --prune --applyset=<名称>
默认情况下,所使用的 ApplySet 父对象的类别是 Secret。
不过也可以按格式 --applyset=configmaps/<name>
使用 ConfigMap。
使用 Secret 或 ConfigMap 时,如果对应对象尚不存在,kubectl 将创建这些对象。
还可以使用自定义资源作为 ApplySet 父对象。
要启用此功能,请为定义目标资源的 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
标志是必需的。这意味着跨越多个命名空间的 ApplySet 必须使用集群作用域的自定义资源作为父对象。 - 要安全地在多个目录中使用基于 ApplySet 的剪裁,请为每个目录使用唯一的 ApplySet 名称。
如何查看对象
你可以使用 kubectl get
并指定 -o yaml
选项来查看现时对象的配置:
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
注解中。
这些内容用来识别配置文件中已经移除的、因而也需要从现时配置中删除的字段。
用来计算要删除或设置哪些字段的步骤如下:
- 计算要删除的字段,即在
last-applied-configuration
中存在但在配置文件中不再存在的字段。 - 计算要添加或设置的字段,即在配置文件中存在但其取值与现时配置不同的字段。
下面是一个例子。假定此文件是某 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
同时假定同一 Deployment 对象的现时配置如下:
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
# ...
# 注意注解中并不包含 replicas
# 这是因为更新并不是通过 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:
replicas: 2 # 按规模填写
# ...
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
将执行的合并计算:
- 通过读取
last-applied-configuration
并将其与配置文件中的值相比较, 计算要删除的字段。 对于本地对象配置文件中显式设置为空的字段,清除其在现时配置中的设置, 无论这些字段是否出现在last-applied-configuration
中。 在此例中,minReadySeconds
出现在last-applied-configuration
注解中, 但并不存在于配置文件中。 动作: 从现时配置中删除minReadySeconds
字段。 - 通过读取配置文件中的值并将其与现时配置相比较,计算要设置的字段。
在这个例子中,配置文件中的
image
值与现时配置中的image
不匹配。 动作:设置现时配置中的image
值。 - 设置
last-applied-configuration
注解的内容,使之与配置文件匹配。 - 将第 1、2、3 步骤得出的结果合并,构成向 API 服务器发送的补丁请求内容。
下面是此合并操作之后形成的现时配置:
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` 设置,被 `kubectl apply` 命令忽略
# minReadySeconds 此字段被 `kubectl apply` 清除
# ...
template:
metadata:
# ...
labels:
app: nginx
spec:
containers:
- image: nginx:1.16.1 # 由 `kubectl apply` 设置
# ...
name: nginx
ports:
- containerPort: 80
# ...
# ...
# ...
# ...
不同类型字段的合并方式
配置文件中的特定字段与现时配置合并时,合并方式取决于字段类型。 字段类型有几种:
基本类型:字段类型为
string
、integer
或boolean
之一。 例如:image
和replicas
字段都是基本类型字段。动作: 替换。
map:也称作 object。类型为
map
或包含子域的复杂结构。例如,labels
、annotations
、spec
和metadata
都是 map。动作: 合并元素或子字段。
list:包含元素列表的字段,其中每个元素可以是基本类型或 map。 例如,
containers
、ports
和args
都是 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
元素的顺序在被添加到现时配置中时保持不变。
# last-applied-configuration 值
args: ["a", "b"]
# 配置文件值
args: ["a", "c"]
# 现时配置
args: ["a", "b", "d"]
# 合并结果
args: ["a", "c"]
解释: 合并操作将配置文件中的值当做新的 list 值。
如果 list 中元素为复合类型则逐个执行合并
此操作将 list 视为 map,并将每个元素中的特定字段当做其主键。 逐个元素地执行添加、删除或更新操作。结果顺序无法得到保证。
此合并策略会使用每个字段上的一个名为 patchMergeKey
的特殊标签。
Kubernetes 源代码中为每个字段定义了 patchMergeKey
:
types.go。
当合并由 map 组成的 list 时,给定元素中被设置为 patchMergeKey
的字段会被当做该元素的 map 键值来使用。
例如: 使用 kubectl apply
来更新 Pod 规约中的 containers
字段。
此操作会将 containers
列表视作一个映射来执行合并,每个元素的主键为 name
。
# last-applied-configuration 值
containers:
- name: nginx
image: nginx:1.16
- name: nginx-helper-a # 键 nginx-helper-a 会被删除
image: helper:1.3
- name: nginx-helper-b # 键 nginx-helper-b 会被保留
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 会被添加
image: helper:1.3
# 现时配置
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"] # 字段会被保留
- name: nginx-helper-d # 键 nginx-helper-d 会被保留
image: helper:1.3
# 合并结果
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" 的容器被删除,因为配置文件中不存在同名的容器。
- 名为 "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
:
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 # API 服务器所设默认值
strategy:
rollingUpdate: # API 服务器基于 strategy.type 所设默认值
maxSurge: 1
maxUnavailable: 1
type: RollingUpdate # API 服务器所设默认值
template:
metadata:
creationTimestamp: null
labels:
app: nginx
spec:
containers:
- image: nginx:1.14.2
imagePullPolicy: IfNotPresent # API 服务器所设默认值
name: nginx
ports:
- containerPort: 80
protocol: TCP # API 服务器所设默认值
resources: {} # API 服务器所设默认值
terminationMessagePath: /dev/termination-log # API 服务器所设默认值
dnsPolicy: ClusterFirst # API 服务器所设默认值
restartPolicy: Always # API 服务器所设默认值
securityContext: {} # API 服务器所设默认值
terminationGracePeriodSeconds: 30 # API 服务器所设默认值
# ...
在补丁请求中,已经设置了默认值的字段不会被重新设回其默认值, 除非在补丁请求中显式地要求清除。对于默认值取决于其他字段的某些字段而言, 这可能会引发一些意想不到的行为。当所依赖的其他字段后来发生改变时, 基于它们所设置的默认值只能在显式执行清除操作时才会被更新。
为此,建议在配置文件中为服务器设置默认值的字段显式提供定义, 即使所给的定义与服务器端默认值设定相同。 这样可以使得辩识无法被服务器重新基于默认值来设置的冲突字段变得容易。
示例:
# 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
# 现时配置
spec:
strategy:
type: RollingUpdate # 默认设置的值
rollingUpdate: # 基于 type 设置的默认值
maxSurge : 1
maxUnavailable: 1
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
# 合并后的结果 - 出错!
spec:
strategy:
type: Recreate # 更新的值:与 rollingUpdate 不兼容
rollingUpdate: # 默认设置的值:与 "type: Recreate" 冲突
maxSurge : 1
maxUnavailable: 1
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
解释:
- 用户创建 Deployment,未设置
strategy.type
。 - 服务器为
strategy.type
设置默认值RollingUpdate
,并为strategy.rollingUpdate
设置默认值。 - 用户改变
strategy.type
为Recreate
。字段strategy.rollingUpdate
仍会取其默认设置值,尽管服务器期望该字段被清除。 如果strategy.rollingUpdate
值最初于配置文件中定义, 则它们需要被清除这一点就更明确一些。 apply
操作失败,因为strategy.rollingUpdate
未被清除。strategy.rollingupdate
在strategy.type
为Recreate
不可被设定。
建议:以下字段应该在对象配置文件中显式定义:
- 如 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 对象在同一时刻应该只用一种方法来管理。 从一种方法切换到另一种方法是可能的,但这一切换是一个手动过程。
说明:
在声明式管理方法中使用指令式命令来删除对象是可以的。
从指令式命令管理切换到声明式对象配置
从指令式命令管理切换到声明式对象配置管理的切换包含以下几个手动步骤:
将现时对象导出到本地配置文件:
kubectl get <kind>/<name> -o yaml > <kind>_<name>.yaml
手动移除配置文件中的
status
字段。说明:
这一步骤是可选的,因为
kubectl apply
并不会更新 status 字段, 即便配置文件中包含 status 字段。
设置对象上的
kubectl.kubernetes.io/last-applied-configuration
注解:kubectl replace --save-config -f <kind>_<name>.yaml
更改过程,使用
kubectl apply
专门管理对象。
从指令式对象配置切换到声明式对象配置
在对象上设置
kubectl.kubernetes.io/last-applied-configuration
注解:kubectl replace --save-config -f <kind>_<name>.yaml
自此排他性地使用
kubectl apply
来管理对象。
定义控制器选择算符和 PodTemplate 标签
警告:
强烈不建议更改控制器上的选择算符。
建议的方法是定义一个不可变更的 PodTemplate 标签, 仅用于控制器选择算符且不包含其他语义性的含义。
示例:
selector:
matchLabels:
controller-selector: "apps/v1/deployment/nginx"
template:
metadata:
labels:
controller-selector: "apps/v1/deployment/nginx"
接下来
2 - 使用 Kustomize 对 Kubernetes 对象进行声明式管理
Kustomize 是一个独立的工具,用来通过 kustomization 文件 定制 Kubernetes 对象。
从 1.14 版本开始,kubectl
也开始支持使用 kustomization 文件来管理 Kubernetes 对象。
要查看包含 kustomization 文件的目录中的资源,执行下面的命令:
kubectl kustomize <kustomization_directory>
要应用这些资源,使用 --kustomize
或 -k
参数来执行 kubectl apply
:
kubectl apply -k <kustomization_directory>
准备开始
安装 kubectl
。
你必须拥有一个 Kubernetes 的集群,且必须配置 kubectl 命令行工具让其与你的集群通信。 建议运行本教程的集群至少有两个节点,且这两个节点不能作为控制平面主机。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:
要获知版本信息,请输入kubectl version
.Kustomize 概述
Kustomize 是一个用来定制 Kubernetes 配置的工具。它提供以下功能特性来管理应用配置文件:
- 从其他来源生成资源
- 为资源设置贯穿性(Cross-Cutting)字段
- 组织和定制资源集合
生成资源
ConfigMap 和 Secret 包含其他 Kubernetes 对象(如 Pod)所需要的配置或敏感数据。
ConfigMap 或 Secret 中数据的来源往往是集群外部,例如某个 .properties
文件或者 SSH 密钥文件。
Kustomize 提供 secretGenerator
和 configMapGenerator
,可以基于文件或字面值来生成 Secret 和 ConfigMap。
configMapGenerator
要基于文件来生成 ConfigMap,可以在 configMapGenerator
的 files
列表中添加表项。
下面是一个根据 .properties
文件中的数据条目来生成 ConfigMap 的示例:
# 生成一个 application.properties 文件
cat <<EOF >application.properties
FOO=Bar
EOF
cat <<EOF >./kustomization.yaml
configMapGenerator:
- name: example-configmap-1
files:
- application.properties
EOF
所生成的 ConfigMap 可以使用下面的命令来检查:
kubectl kustomize ./
所生成的 ConfigMap 为:
apiVersion: v1
data:
application.properties: |
FOO=Bar
kind: ConfigMap
metadata:
name: example-configmap-1-8mbdf7882g
要从 env 文件生成 ConfigMap,请在 configMapGenerator
中的 envs
列表中添加一个条目。
下面是一个用来自 .env
文件的数据生成 ConfigMap 的例子:
# 创建一个 .env 文件
cat <<EOF >.env
FOO=Bar
EOF
cat <<EOF >./kustomization.yaml
configMapGenerator:
- name: example-configmap-1
envs:
- .env
EOF
可以使用以下命令检查生成的 ConfigMap:
kubectl kustomize ./
生成的 ConfigMap 为:
apiVersion: v1
data:
FOO: Bar
kind: ConfigMap
metadata:
name: example-configmap-1-42cfbf598f
说明:
.env
文件中的每个变量在生成的 ConfigMap 中成为一个单独的键。这与之前的示例不同,
前一个示例将一个名为 application.properties
的文件(及其所有条目)嵌入到同一个键的值中。
ConfigMap 也可基于字面的键值偶对来生成。要基于键值偶对来生成 ConfigMap,
在 configMapGenerator
的 literals
列表中添加表项。下面是一个例子,
展示如何使用键值偶对中的数据条目来生成 ConfigMap 对象:
cat <<EOF >./kustomization.yaml
configMapGenerator:
- name: example-configmap-2
literals:
- FOO=Bar
EOF
可以用下面的命令检查所生成的 ConfigMap:
kubectl kustomize ./
所生成的 ConfigMap 为:
apiVersion: v1
data:
FOO: Bar
kind: ConfigMap
metadata:
name: example-configmap-2-g2hdhfc6tk
要在 Deployment 中使用生成的 ConfigMap,使用 configMapGenerator 的名称对其进行引用。 Kustomize 将自动使用生成的名称替换该名称。
这是使用生成的 ConfigMap 的 deployment 示例:
# 创建一个 application.properties 文件
cat <<EOF >application.properties
FOO=Bar
EOF
cat <<EOF >deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
labels:
app: my-app
spec:
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: app
image: my-app
volumeMounts:
- name: config
mountPath: /config
volumes:
- name: config
configMap:
name: example-configmap-1
EOF
cat <<EOF >./kustomization.yaml
resources:
- deployment.yaml
configMapGenerator:
- name: example-configmap-1
files:
- application.properties
EOF
生成 ConfigMap 和 Deployment:
kubectl kustomize ./
生成的 Deployment 将通过名称引用生成的 ConfigMap:
apiVersion: v1
data:
application.properties: |
FOO=Bar
kind: ConfigMap
metadata:
name: example-configmap-1-g4hk9g2ff8
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: my-app
name: my-app
spec:
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- image: my-app
name: app
volumeMounts:
- mountPath: /config
name: config
volumes:
- configMap:
name: example-configmap-1-g4hk9g2ff8
name: config
secretGenerator
你可以基于文件或者键值偶对来生成 Secret。要使用文件内容来生成 Secret,
在 secretGenerator
下面的 files
列表中添加表项。
下面是一个根据文件中数据来生成 Secret 对象的示例:
# 创建一个 password.txt 文件
cat <<EOF >./password.txt
username=admin
password=secret
EOF
cat <<EOF >./kustomization.yaml
secretGenerator:
- name: example-secret-1
files:
- password.txt
EOF
所生成的 Secret 如下:
apiVersion: v1
data:
password.txt: dXNlcm5hbWU9YWRtaW4KcGFzc3dvcmQ9c2VjcmV0Cg==
kind: Secret
metadata:
name: example-secret-1-t2kt65hgtb
type: Opaque
要基于键值偶对字面值生成 Secret,先要在 secretGenerator
的 literals
列表中添加表项。下面是基于键值偶对中数据条目来生成 Secret 的示例:
cat <<EOF >./kustomization.yaml
secretGenerator:
- name: example-secret-2
literals:
- username=admin
- password=secret
EOF
所生成的 Secret 如下:
apiVersion: v1
data:
password: c2VjcmV0
username: YWRtaW4=
kind: Secret
metadata:
name: example-secret-2-t52t6g96d8
type: Opaque
与 ConfigMap 一样,生成的 Secret 可以通过引用 secretGenerator 的名称在 Deployment 中使用:
# 创建一个 password.txt 文件
cat <<EOF >./password.txt
username=admin
password=secret
EOF
cat <<EOF >deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
labels:
app: my-app
spec:
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: app
image: my-app
volumeMounts:
- name: password
mountPath: /secrets
volumes:
- name: password
secret:
secretName: example-secret-1
EOF
cat <<EOF >./kustomization.yaml
resources:
- deployment.yaml
secretGenerator:
- name: example-secret-1
files:
- password.txt
EOF
generatorOptions
所生成的 ConfigMap 和 Secret 都会包含内容哈希值后缀。
这是为了确保内容发生变化时,所生成的是新的 ConfigMap 或 Secret。
要禁止自动添加后缀的行为,用户可以使用 generatorOptions
。
除此以外,为生成的 ConfigMap 和 Secret 指定贯穿性选项也是可以的。
cat <<EOF >./kustomization.yaml
configMapGenerator:
- name: example-configmap-3
literals:
- FOO=Bar
generatorOptions:
disableNameSuffixHash: true
labels:
type: generated
annotations:
note: generated
EOF
运行 kubectl kustomize ./
来查看所生成的 ConfigMap:
apiVersion: v1
data:
FOO: Bar
kind: ConfigMap
metadata:
annotations:
note: generated
labels:
type: generated
name: example-configmap-3
设置贯穿性字段
在项目中为所有 Kubernetes 对象设置贯穿性字段是一种常见操作。 贯穿性字段的一些使用场景如下:
- 为所有资源设置相同的名字空间
- 为所有对象添加相同的前缀或后缀
- 为对象添加相同的标签集合
- 为对象添加相同的注解集合
下面是一个例子:
# 创建一个 deployment.yaml
cat <<EOF >./deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
EOF
cat <<EOF >./kustomization.yaml
namespace: my-namespace
namePrefix: dev-
nameSuffix: "-001"
labels:
- pairs:
app: bingo
includeSelectors: true
commonAnnotations:
oncallPager: 800-555-1212
resources:
- deployment.yaml
EOF
执行 kubectl kustomize ./
查看这些字段都被设置到 Deployment 资源上:
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
oncallPager: 800-555-1212
labels:
app: bingo
name: dev-nginx-deployment-001
namespace: my-namespace
spec:
selector:
matchLabels:
app: bingo
template:
metadata:
annotations:
oncallPager: 800-555-1212
labels:
app: bingo
spec:
containers:
- image: nginx
name: nginx
组织和定制资源
一种常见的做法是在项目中构造资源集合并将其放到同一个文件或目录中管理。 Kustomize 提供基于不同文件来组织资源并向其应用补丁或者其他定制的能力。
组织
Kustomize 支持组合不同的资源。kustomization.yaml
文件的 resources
字段定义配置中要包含的资源列表。
你可以将 resources
列表中的路径设置为资源配置文件的路径。
下面是由 Deployment 和 Service 构成的 NGINX 应用的示例:
# 创建 deployment.yaml 文件
cat <<EOF > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
selector:
matchLabels:
run: my-nginx
replicas: 2
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- name: my-nginx
image: nginx
ports:
- containerPort: 80
EOF
# 创建 service.yaml 文件
cat <<EOF > service.yaml
apiVersion: v1
kind: Service
metadata:
name: my-nginx
labels:
run: my-nginx
spec:
ports:
- port: 80
protocol: TCP
selector:
run: my-nginx
EOF
# 创建 kustomization.yaml 来组织以上两个资源
cat <<EOF >./kustomization.yaml
resources:
- deployment.yaml
- service.yaml
EOF
kubectl kustomize ./
所得到的资源中既包含 Deployment 也包含 Service 对象。
定制
补丁文件(Patches)可以用来对资源执行不同的定制。
Kustomize 通过 StrategicMerge
和 Json6902
使用 patches
字段支持不同的打补丁机制。
patches
字段包含了一列按指定顺序应用的补丁。
补丁目标通过 group
、version
、kind
、name
、namespace
、labelSelector
和
annotationSelector
来选择资源。
建议使用只做一件事的小补丁。例如,创建一个用于增加部署副本数量的补丁,以及另一个用于设置内存限制的补丁。
目标资源是通过补丁文件中的 group
、version
、kind
和 name
字段进行匹配的。
# 创建 deployment.yaml 文件
cat <<EOF > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
selector:
matchLabels:
run: my-nginx
replicas: 2
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- name: my-nginx
image: nginx
ports:
- containerPort: 80
EOF
# 生成一个补丁 increase_replicas.yaml
cat <<EOF > increase_replicas.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
replicas: 3
EOF
# 生成另一个补丁 set_memory.yaml
cat <<EOF > set_memory.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
template:
spec:
containers:
- name: my-nginx
resources:
limits:
memory: 512Mi
EOF
cat <<EOF >./kustomization.yaml
resources:
- deployment.yaml
patches:
- path: increase_replicas.yaml
- path: set_memory.yaml
EOF
执行 kubectl kustomize ./
来查看 Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
replicas: 3
selector:
matchLabels:
run: my-nginx
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- image: nginx
name: my-nginx
ports:
- containerPort: 80
resources:
limits:
memory: 512Mi
并非所有资源或者字段都支持策略性合并(strategicMerge
)补丁。为了支持对任何资源的任何字段进行修改,
Kustomize 提供通过 Json6902
来应用 JSON 补丁的能力。
为了给 Json6902
补丁找到正确的资源,必须在 kustomization.yaml
文件中设置 target
字段。
例如,可以通过 Json6902
补丁来增加 Deployment 对象的副本数量。
目标资源是通过 target
字段中的 group
、version
、kind
和 name
进行匹配的。
# 创建一个 deployment.yaml 文件
cat <<EOF > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
selector:
matchLabels:
run: my-nginx
replicas: 2
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- name: my-nginx
image: nginx
ports:
- containerPort: 80
EOF
# 创建一个 JSON 补丁文件
cat <<EOF > patch.yaml
- op: replace
path: /spec/replicas
value: 3
EOF
# 创建一个 kustomization.yaml
cat <<EOF >./kustomization.yaml
resources:
- deployment.yaml
patches:
- target:
group: apps
version: v1
kind: Deployment
name: my-nginx
path: patch.yaml
EOF
执行 kubectl kustomize ./
以查看 replicas
字段被更新:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
replicas: 3
selector:
matchLabels:
run: my-nginx
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- image: nginx
name: my-nginx
ports:
- containerPort: 80
除了补丁之外,Kustomize 还提供定制容器镜像或者将其他对象的字段值注入到容器中的能力,并且不需要创建补丁。
例如,你可以通过在 kustomization.yaml
文件的 images
字段设置新的镜像来更改容器中使用的镜像。
cat <<EOF > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
selector:
matchLabels:
run: my-nginx
replicas: 2
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- name: my-nginx
image: nginx
ports:
- containerPort: 80
EOF
cat <<EOF >./kustomization.yaml
resources:
- deployment.yaml
images:
- name: nginx
newName: my.image.registry/nginx
newTag: 1.4.0
EOF
执行 kubectl kustomize ./
以查看所使用的镜像已被更新:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
replicas: 2
selector:
matchLabels:
run: my-nginx
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- image: my.image.registry/nginx:1.4.0
name: my-nginx
ports:
- containerPort: 80
有些时候,Pod 中运行的应用可能需要使用来自其他对象的配置值。
例如,某 Deployment 对象的 Pod 需要从环境变量或命令行参数中读取读取
Service 的名称。
由于在 kustomization.yaml
文件中添加 namePrefix
或 nameSuffix
时
Service 名称可能发生变化,建议不要在命令参数中硬编码 Service 名称。
对于这种使用场景,Kustomize 可以通过 replacements
将 Service 名称注入到容器中。
# 创建一个 deployment.yaml 文件(引用此处的文档分隔符)
cat <<'EOF' > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
selector:
matchLabels:
run: my-nginx
replicas: 2
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- name: my-nginx
image: nginx
command: ["start", "--host", "MY_SERVICE_NAME_PLACEHOLDER"]
EOF
# 创建一个 service.yaml 文件
cat <<EOF > service.yaml
apiVersion: v1
kind: Service
metadata:
name: my-nginx
labels:
run: my-nginx
spec:
ports:
- port: 80
protocol: TCP
selector:
run: my-nginx
EOF
cat <<EOF >./kustomization.yaml
namePrefix: dev-
nameSuffix: "-001"
resources:
- deployment.yaml
- service.yaml
replacements:
- source:
kind: Service
name: my-nginx
fieldPath: metadata.name
targets:
- select:
kind: Deployment
name: my-nginx
fieldPaths:
- spec.template.spec.containers.0.command.2
EOF
执行 kubectl kustomize ./
以查看注入到容器中的 Service 名称是 dev-my-nginx-001
:
apiVersion: apps/v1
kind: Deployment
metadata:
name: dev-my-nginx-001
spec:
replicas: 2
selector:
matchLabels:
run: my-nginx
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- command:
- start
- --host
- dev-my-nginx-001
image: nginx
name: my-nginx
基准(Bases)与覆盖(Overlays)
Kustomize 中有 基准(bases) 和 覆盖(overlays) 的概念区分。
基准 是包含 kustomization.yaml
文件的一个目录,其中包含一组资源及其相关的定制。
基准可以是本地目录或者来自远程仓库的目录,只要其中存在 kustomization.yaml
文件即可。
覆盖 也是一个目录,其中包含将其他 kustomization 目录当做 bases
来引用的
kustomization.yaml
文件。
基准不了解覆盖的存在,且可被多个覆盖所使用。
kustomization.yaml
文件位于 overlay 目录中,可以引用多个 bases
,
将这些 base 中定义的所有资源合并为一个统一的配置。
此外,它还可以在这些资源之上应用定制化修改以满足特定需求。
以下是 base 的一个示例:
# 创建一个包含基准的目录
mkdir base
# 创建 base/deployment.yaml
cat <<EOF > base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
selector:
matchLabels:
run: my-nginx
replicas: 2
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- name: my-nginx
image: nginx
EOF
# 创建 base/service.yaml 文件
cat <<EOF > base/service.yaml
apiVersion: v1
kind: Service
metadata:
name: my-nginx
labels:
run: my-nginx
spec:
ports:
- port: 80
protocol: TCP
selector:
run: my-nginx
EOF
# 创建 base/kustomization.yaml
cat <<EOF > base/kustomization.yaml
resources:
- deployment.yaml
- service.yaml
EOF
此基准可在多个覆盖中使用。你可以在不同的覆盖中添加不同的 namePrefix
或其他贯穿性字段。
下面是两个使用同一基准的覆盖:
mkdir dev
cat <<EOF > dev/kustomization.yaml
resources:
- ../base
namePrefix: dev-
EOF
mkdir prod
cat <<EOF > prod/kustomization.yaml
resources:
- ../base
namePrefix: prod-
EOF
如何使用 Kustomize 来应用、查看和删除对象
在 kubectl
命令中使用 --kustomize
或 -k
参数来识别被 kustomization.yaml
所管理的资源。
注意 -k
要指向一个 kustomization 目录。例如:
kubectl apply -k <kustomization 目录>/
假定使用下面的 kustomization.yaml
:
# 创建 deployment.yaml 文件
cat <<EOF > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
selector:
matchLabels:
run: my-nginx
replicas: 2
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- name: my-nginx
image: nginx
ports:
- containerPort: 80
EOF
# 创建 kustomization.yaml
cat <<EOF >./kustomization.yaml
namePrefix: dev-
labels:
- pairs:
app: my-nginx
includeSelectors: true
resources:
- deployment.yaml
EOF
执行下面的命令来应用 Deployment 对象 dev-my-nginx
:
> kubectl apply -k ./
deployment.apps/dev-my-nginx created
运行下面的命令之一来查看 Deployment 对象 dev-my-nginx
:
kubectl get -k ./
kubectl describe -k ./
执行下面的命令来比较 Deployment 对象 dev-my-nginx
与清单被应用之后集群将处于的状态:
kubectl diff -k ./
执行下面的命令删除 Deployment 对象 dev-my-nginx
:
> kubectl delete -k ./
deployment.apps "dev-my-nginx" deleted
Kustomize 功能特性列表
字段 | 类型 | 解释 |
---|---|---|
bases | []string | 列表中每个条目都应能解析为一个包含 kustomization.yaml 文件的目录 |
commonAnnotations | map[string]string | 要添加到所有资源的注解 |
commonLabels | map[string]string | 要添加到所有资源和选择算符的标签 |
configMapGenerator | []ConfigMapArgs | 列表中的每个条目都会生成一个 ConfigMap |
configurations | []string | 列表中每个条目都应能解析为一个包含 Kustomize 转换器配置 的文件 |
crds | []string | 列表中每个条目都应能够解析为 Kubernetes 类别的 OpenAPI 定义文件 |
generatorOptions | GeneratorOptions | 更改所有 ConfigMap 和 Secret 生成器的行为 |
images | []Image | 每个条目都用来更改镜像的名称、标记与/或摘要,不必生成补丁 |
labels | map[string]string | 添加标签而不自动注入对应的选择器 |
namePrefix | string | 此字段的值将被添加到所有资源名称前面 |
nameSuffix | string | 此字段的值将被添加到所有资源名称后面 |
patchesJson6902 | []Patch | 列表中每个条目都能解析为一个 Kubernetes 对象和一个 JSON 补丁 |
patchesStrategicMerge | []string | 列表中每个条目都能解析为某 Kubernetes 对象的策略性合并补丁 |
replacements | []Replacements | 将 resource 字段的值复制到任意数量的指定目标 |
resources | []string | 列表中的每个条目都必须能够解析为现有的资源配置文件 |
secretGenerator | []SecretArgs | 列表中的每个条目都会生成一个 Secret |
vars | []Var | 每个条目用来从某资源的字段来析取文字 |
接下来
3 - 使用指令式命令管理 Kubernetes 对象
使用构建在 kubectl
命令行工具中的指令式命令可以直接快速创建、更新和删除
Kubernetes 对象。本文档解释这些命令的组织方式以及如何使用它们来管理活跃对象。
准备开始
安装kubectl
。
你必须拥有一个 Kubernetes 的集群,且必须配置 kubectl 命令行工具让其与你的集群通信。 建议运行本教程的集群至少有两个节点,且这两个节点不能作为控制平面主机。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:
要获知版本信息,请输入kubectl version
.权衡取舍
kubectl
工具能够支持三种对象管理方式:
- 指令式命令
- 指令式对象配置
- 声明式对象配置
关于每种对象管理的优缺点的讨论,可参见 Kubernetes 对象管理。
如何创建对象
kubectl
工具支持动词驱动的命令,用来创建一些最常见的对象类别。
命令的名称设计使得不熟悉 Kubernetes 对象类型的用户也能做出判断。
run
:创建一个新的 Pod 来运行一个容器。expose
:创建一个新的 Service 对象为若干 Pod 提供流量负载均衡。autoscale
:创建一个新的 Autoscaler 对象来自动对某控制器(例如:Deployment) 执行水平扩缩。
kubectl
命令也支持一些对象类型驱动的创建命令。
这些命令可以支持更多的对象类别,并且在其动机上体现得更为明显,
不过要求用户了解它们所要创建的对象的类别。
create <对象类别> [<子类别>] <实例名称>
某些对象类别拥有自己的子类别,可以在 create
命令中设置。
例如,Service 对象有 ClusterIP、LoadBalancer 和 NodePort 三种子类别。
下面是一个创建 NodePort 子类别的 Service 的示例:
kubectl create service nodeport <服务名称>
在前述示例中,create service nodeport
命令也称作 create service
命令的子命令。
可以使用 -h
标志找到一个子命令所支持的参数和标志。
kubectl create service nodeport -h
如何更新对象
kubectl
命令也支持一些动词驱动的命令,用来执行一些常见的更新操作。
这些命令的设计是为了让一些不了解 Kubernetes 对象的用户也能执行更新操作,
但不需要了解哪些字段必须设置:
scale
:对某控制器进行水平扩缩以便通过更新控制器的副本个数来添加或删除 Pod。annotate
:为对象添加或删除注解。label
:为对象添加或删除标签。
kubectl
命令也支持由对象的某一方面来驱动的更新命令。
设置对象的这一方面可能对不同类别的对象意味着不同的字段:
set <字段>
:设置对象的某一方面。
说明:
在 Kubernetes 1.5 版本中,并非所有动词驱动的命令都有对应的方面驱动的命令。
kubectl
工具支持以下额外的方式用来直接更新活跃对象,不过这些操作要求
用户对 Kubernetes 对象的模式定义有很好的了解:
edit
:通过在编辑器中打开活跃对象的配置,直接编辑其原始配置。patch
:通过使用补丁字符串(Patch String)直接更改某活跃对象的特定字段。 关于补丁字符串的更详细信息,参见 API 约定 的 patch 节。
如何删除对象
你可以使用 delete
命令从集群中删除一个对象:
delete <类别>/<名称>
说明:
你可以使用 kubectl delete
来执行指令式命令或者指令式对象配置。不同之处在于传递给命令的参数。
要将 kubectl delete
作为指令式命令使用,将要删除的对象作为参数传递给它。
下面是一个删除名为 nginx
的 Deployment 对象的命令:
kubectl delete deployment/nginx
如何查看对象
用来打印对象信息的命令有好几个:
get
:打印匹配到的对象的基本信息。使用get -h
可以查看选项列表。describe
:打印匹配到的对象的详细信息的汇集版本。logs
:打印 Pod 中运行的容器的 stdout 和 stderr 输出。
使用 set
命令在创建对象之前修改对象
有些对象字段在 create
命令中没有对应的标志。
在这些场景中,你可以使用 set
和 create
命令的组合来在对象创建之前设置字段值。
这是通过将 create
命令的输出用管道方式传递给 set
命令来实现的,最后执行 create
命令来创建对象。
下面是一个例子:
kubectl create service clusterip my-svc --clusterip="None" -o yaml --dry-run=client | kubectl set selector --local -f - 'environment=qa' -o yaml | kubectl create -f -
- 命令
kubectl create service -o yaml --dry-run=client
创建 Service 的配置, 但将其以 YAML 格式在标准输出上打印而不是发送给 API 服务器。 - 命令
kubectl set selector --local -f - -o yaml
从标准输入读入配置, 并将更新后的配置以 YAML 格式输出到标准输出。 - 命令
kubectl create -f -
使用标准输入上获得的配置创建对象。
在创建之前使用 --edit
更改对象
你可以用 kubectl create --edit
来在对象被创建之前执行任意的变更。
下面是一个例子:
kubectl create service clusterip my-svc --clusterip="None" -o yaml --dry-run=client > /tmp/srv.yaml
kubectl create --edit -f /tmp/srv.yaml
- 命令
kubectl create service
创建 Service 的配置并将其保存到/tmp/srv.yaml
文件。 - 命令
kubectl create --edit
在创建 Service 对象打开其配置文件进行编辑。
接下来
4 - 使用配置文件对 Kubernetes 对象进行命令式管理
可以使用 kubectl
命令行工具以及用 YAML 或 JSON 编写的对象配置文件来创建、更新和删除 Kubernetes 对象。
本文档说明了如何使用配置文件定义和管理对象。
准备开始
安装 kubectl
。
你必须拥有一个 Kubernetes 的集群,且必须配置 kubectl 命令行工具让其与你的集群通信。 建议运行本教程的集群至少有两个节点,且这两个节点不能作为控制平面主机。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:
要获知版本信息,请输入kubectl version
.权衡
kubectl
工具支持三种对象管理:
- 命令式命令
- 命令式对象配置
- 声明式对象配置
参见 Kubernetes 对象管理 中关于每种对象管理的优缺点的讨论。
如何创建对象
你可以使用 kubectl create -f
从配置文件创建一个对象。
更多细节参阅 kubernetes API 参考。
kubectl create -f <filename|url>
如何更新对象
警告:
使用 replace
命令更新对象会删除所有未在配置文件中指定的规范的某些部分。
不应将其规范由集群部分管理的对象使用,比如类型为 LoadBalancer
的服务,
其中 externalIPs
字段独立于配置文件进行管理。
必须将独立管理的字段复制到配置文件中,以防止 replace
删除它们。
你可以使用 kubectl replace -f
根据配置文件更新活动对象。
kubectl replace -f <filename|url>
如何删除对象
你可以使用 kubectl delete -f
删除配置文件中描述的对象。
kubectl delete -f <filename|url>
说明:
如果配置文件在 metadata
节中设置了 generateName
字段而非 name
字段,
你无法使用 kubectl delete -f <filename|url>
来删除该对象。
你必须使用其他标志才能删除对象。例如:
kubectl delete <type> <name>
kubectl delete <type> -l <label>
如何查看对象
你可以使用 kubectl get -f
查看有关配置文件中描述的对象的信息。
kubectl get -f <filename|url> -o yaml
-o yaml
标志指定打印完整的对象配置。使用 kubectl get -h
查看选项列表。
局限性
当完全定义每个对象的配置并将其记录在其配置文件中时,create
、 replace
和delete
命令会很好的工作。
但是,当更新一个活动对象,并且更新没有合并到其配置文件中时,下一次执行 replace
时,更新将丢失。
如果控制器,例如 HorizontalPodAutoscaler ,直接对活动对象进行更新,则会发生这种情况。
这有一个例子:
- 从配置文件创建一个对象。
- 另一个源通过更改某些字段来更新对象。
- 从配置文件中替换对象。在步骤2中所做的其他源的更改将丢失。
如果需要支持同一对象的多个编写器,则可以使用 kubectl apply
来管理该对象。
从 URL 创建和编辑对象而不保存配置
假设你具有对象配置文件的 URL。
你可以在创建对象之前使用 kubectl create --edit
对配置进行更改。
这对于指向可以由读者修改的配置文件的教程和任务特别有用。
kubectl create -f <url> --edit
从命令式命令迁移到命令式对象配置
从命令式命令迁移到命令式对象配置涉及几个手动步骤。
将活动对象导出到本地对象配置文件:
kubectl get <kind>/<name> -o yaml > <kind>_<name>.yaml
- 从对象配置文件中手动删除状态字段。
对于后续的对象管理,只能使用
replace
。kubectl replace -f <kind>_<name>.yaml
定义控制器选择器和 PodTemplate 标签
警告:
不建议在控制器上更新选择器。
推荐的方法是定义单个不变的 PodTemplate 标签,该标签仅由控制器选择器使用,而没有其他语义。
标签示例:
selector:
matchLabels:
controller-selector: "apps/v1/deployment/nginx"
template:
metadata:
labels:
controller-selector: "apps/v1/deployment/nginx"
接下来
5 - 使用 kubectl patch 更新 API 对象
这个任务展示如何使用 kubectl patch
就地更新 API 对象。
这个任务中的练习演示了一个策略性合并 patch 和一个 JSON 合并 patch。
准备开始
你必须拥有一个 Kubernetes 的集群,且必须配置 kubectl 命令行工具让其与你的集群通信。 建议运行本教程的集群至少有两个节点,且这两个节点不能作为控制平面主机。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:
要获知版本信息,请输入kubectl version
.使用策略合并 patch 更新 Deployment
下面是具有两个副本的 Deployment 的配置文件。每个副本是一个 Pod,有一个容器:
apiVersion: apps/v1
kind: Deployment
metadata:
name: patch-demo
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: patch-demo-ctr
image: nginx
tolerations:
- effect: NoSchedule
key: dedicated
value: test-team
创建 Deployment:
kubectl apply -f https://k8s.io/examples/application/deployment-patch.yaml
查看与 Deployment 相关的 Pod:
kubectl get pods
输出显示 Deployment 有两个 Pod。1/1
表示每个 Pod 有一个容器:
NAME READY STATUS RESTARTS AGE
patch-demo-28633765-670qr 1/1 Running 0 23s
patch-demo-28633765-j5qs3 1/1 Running 0 23s
把运行的 Pod 的名字记下来。稍后,你将看到这些 Pod 被终止并被新的 Pod 替换。
此时,每个 Pod 都有一个运行 nginx 镜像的容器。现在假设你希望每个 Pod 有两个容器:一个运行 nginx,另一个运行 redis。
创建一个名为 patch-file.yaml
的文件。内容如下:
spec:
template:
spec:
containers:
- name: patch-demo-ctr-2
image: redis
修补你的 Deployment:
kubectl patch deployment patch-demo --patch-file patch-file.yaml
查看修补后的 Deployment:
kubectl get deployment patch-demo --output yaml
输出显示 Deployment 中的 PodSpec 有两个容器:
containers:
- image: redis
imagePullPolicy: Always
name: patch-demo-ctr-2
...
- image: nginx
imagePullPolicy: Always
name: patch-demo-ctr
...
查看与 patch Deployment 相关的 Pod:
kubectl get pods
输出显示正在运行的 Pod 与以前运行的 Pod 有不同的名称。Deployment 终止了旧的 Pod,
并创建了两个符合更新后的 Deployment 规约的新 Pod。2/2
表示每个 Pod 有两个容器:
NAME READY STATUS RESTARTS AGE
patch-demo-1081991389-2wrn5 2/2 Running 0 1m
patch-demo-1081991389-jmg7b 2/2 Running 0 1m
仔细查看其中一个 patch-demo Pod:
kubectl get pod <your-pod-name> --output yaml
输出显示 Pod 有两个容器:一个运行 nginx,一个运行 redis:
containers:
- image: redis
...
- image: nginx
...
策略性合并类的 patch 的说明
你在前面的练习中所做的 patch 称为 策略性合并 patch(Strategic Merge Patch)
。
请注意,patch 没有替换 containers
列表。相反,它向列表中添加了一个新 Container。换句话说,
patch 中的列表与现有列表合并。当你在列表中使用策略性合并 patch 时,并不总是这样。
在某些情况下,列表是替换的,而不是合并的。
对于策略性合并 patch,列表可以根据其 patch 策略进行替换或合并。
patch 策略由 Kubernetes 源代码中字段标记中的 patchStrategy
键的值指定。
例如,PodSpec
结构体的 Containers
字段的 patchStrategy
为 merge
:
type PodSpec struct {
...
Containers []Container `json:"containers" patchStrategy:"merge" patchMergeKey:"name" ...`
...
}
你还可以在 OpenApi 规范中看到 patch 策略:
"io.k8s.api.core.v1.PodSpec": {
...,
"containers": {
"description": "List of containers belonging to the pod. ...."
},
"x-kubernetes-patch-merge-key": "name",
"x-kubernetes-patch-strategy": "merge"
}
你可以在 Kubernetes API 文档 中看到 patch 策略。
创建一个名为 patch-file-tolerations.yaml
的文件。内容如下:
spec:
template:
spec:
tolerations:
- effect: NoSchedule
key: disktype
value: ssd
对 Deployment 执行 patch 操作:
kubectl patch deployment patch-demo --patch-file patch-file-tolerations.yaml
查看修补后的 Deployment:
kubectl get deployment patch-demo --output yaml
输出结果显示 Deployment 中的 PodSpec 只有一个容忍度设置:
tolerations:
- effect: NoSchedule
key: disktype
value: ssd
请注意,PodSpec 中的 tolerations
列表被替换,而不是合并。这是因为 PodSpec 的 tolerations
的字段标签中没有 patchStrategy
键。所以策略合并 patch 操作使用默认的 patch 策略,也就是 replace
。
type PodSpec struct {
...
Tolerations []Toleration `json:"tolerations,omitempty" protobuf:"bytes,22,opt,name=tolerations"`
...
}
使用 JSON 合并 patch 更新 Deployment
策略性合并 patch 不同于 JSON 合并 patch。 使用 JSON 合并 patch,如果你想更新列表,你必须指定整个新列表。新的列表完全取代现有的列表。
kubectl patch
命令有一个 type
参数,你可以将其设置为以下值之一:
参数值 | 合并类型 |
---|---|
json | JSON Patch, RFC 6902 |
merge | JSON Merge Patch, RFC 7386 |
strategic | 策略合并 patch |
有关 JSON patch 和 JSON 合并 patch 的比较,查看 JSON patch 和 JSON 合并 patch。
type
参数的默认值是 strategic
。在前面的练习中,我们做了一个策略性的合并 patch。
下一步,在相同的 Deployment 上执行 JSON 合并 patch。创建一个名为 patch-file-2
的文件。内容如下:
spec:
template:
spec:
containers:
- name: patch-demo-ctr-3
image: gcr.io/google-samples/hello-app:2.0
在 patch 命令中,将 type
设置为 merge
:
kubectl patch deployment patch-demo --type merge --patch-file patch-file-2.yaml
查看修补后的 Deployment:
kubectl get deployment patch-demo --output yaml
patch 中指定的 containers
列表只有一个 Container。
输出显示你所给出的 Container 列表替换了现有的 containers
列表。
spec:
containers:
- image: gcr.io/google-samples/hello-app:2.0
...
name: patch-demo-ctr-3
列出正运行的 Pod:
kubectl get pods
在输出中,你可以看到已经终止了现有的 Pod,并创建了新的 Pod。1/1
表示每个新 Pod 只运行一个容器。
NAME READY STATUS RESTARTS AGE
patch-demo-1307768864-69308 1/1 Running 0 1m
patch-demo-1307768864-c86dc 1/1 Running 0 1m
使用带 retainKeys 策略的策略合并 patch 更新 Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: retainkeys-demo
spec:
selector:
matchLabels:
app: nginx
strategy:
rollingUpdate:
maxSurge: 30%
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: retainkeys-demo-ctr
image: nginx
创建 Deployment:
kubectl apply -f https://k8s.io/examples/application/deployment-retainkeys.yaml
这时,Deployment 被创建,并使用 RollingUpdate
策略。
创建一个名为 patch-file-no-retainkeys.yaml
的文件,内容如下:
spec:
strategy:
type: Recreate
修补你的 Deployment:
kubectl patch deployment retainkeys-demo --type strategic --patch-file patch-file-no-retainkeys.yaml
在输出中,你可以看到,当 spec.strategy.rollingUpdate
已经拥有取值定义时,
将其 type
设置为 Recreate
是不可能的。
The Deployment "retainkeys-demo" is invalid: spec.strategy.rollingUpdate: Forbidden: may not be specified when strategy `type` is 'Recreate'
更新 type
取值的同时移除 spec.strategy.rollingUpdate
现有值的方法是为策略性合并操作设置 retainKeys
策略:
创建另一个名为 patch-file-retainkeys.yaml
的文件,内容如下:
spec:
strategy:
$retainKeys:
- type
type: Recreate
使用此 patch,我们表达了希望只保留 strategy
对象的 type
键。
这样,在 patch 操作期间 rollingUpdate
会被删除。
使用新的 patch 重新修补 Deployment:
kubectl patch deployment retainkeys-demo --type strategic --patch-file patch-file-retainkeys.yaml
检查 Deployment 的内容:
kubectl get deployment retainkeys-demo --output yaml
输出显示 Deployment 中的 strategy
对象不再包含 rollingUpdate
键:
spec:
strategy:
type: Recreate
template:
关于使用 retainKeys 策略的策略合并 patch 操作的说明
在前文练习中所执行的称作 带 retainKeys
策略的策略合并 patch(Strategic Merge
Patch with retainKeys Strategy)。
这种方法引入了一种新的 $retainKey
指令,具有如下策略:
- 其中包含一个字符串列表;
- 所有需要被保留的字段必须在
$retainKeys
列表中给出; - 对于已有的字段,会和对象上对应的内容合并;
- 在修补操作期间,未找到的字段都会被清除;
- 列表
$retainKeys
中的所有字段必须 patch 操作所给字段的超集,或者与之完全一致。
策略 retainKeys
并不能对所有对象都起作用。它仅对那些 Kubernetes 源码中
patchStrategy
字段标志值包含 retainKeys
的字段有用。
例如 DeploymentSpec
结构的 Strategy
字段就包含了 patchStrategy
为
retainKeys
的标志。
type DeploymentSpec struct {
...
// +patchStrategy=retainKeys
Strategy DeploymentStrategy `json:"strategy,omitempty" patchStrategy:"retainKeys" ...`
...
}
你也可以查看
OpenAPI 规范中的
retainKeys
策略:
"io.k8s.api.apps.v1.DeploymentSpec": {
...,
"strategy": {
"$ref": "#/definitions/io.k8s.api.apps.v1.DeploymentStrategy",
"description": "The deployment strategy to use to replace existing pods with new ones.",
"x-kubernetes-patch-strategy": "retainKeys"
},
....
}
而且你也可以在
Kubernetes API 文档中看到
retainKey
策略。
kubectl patch 命令的其他形式
kubectl patch
命令使用 YAML 或 JSON。它可以接受以文件形式提供的补丁,也可以接受直接在命令行中给出的补丁。
创建一个文件名称是 patch-file.json
内容如下:
{
"spec": {
"template": {
"spec": {
"containers": [
{
"name": "patch-demo-ctr-2",
"image": "redis"
}
]
}
}
}
}
以下命令是等价的:
kubectl patch deployment patch-demo --patch-file patch-file.yaml
kubectl patch deployment patch-demo --patch 'spec:\n template:\n spec:\n containers:\n - name: patch-demo-ctr-2\n image: redis'
kubectl patch deployment patch-demo --patch-file patch-file.json
kubectl patch deployment patch-demo --patch '{"spec": {"template": {"spec": {"containers": [{"name": "patch-demo-ctr-2","image": "redis"}]}}}}'
使用 kubectl patch
和 --subresource
更新一个对象的副本数
Kubernetes v1.24 [alpha]
使用 kubectl 命令(如 get、patch、edit 和 replace)时带上 --subresource=[subresource-name]
标志,
可以获取和更新资源的 status
和 scale
子资源(适用于 kubectl v1.24 或更高版本)。
这个标志可用于带有 status
或 scale
子资源的所有 API 资源 (内置资源和 CR 资源)。
Deployment 是支持这些子资源的其中一个例子。
下面是有两个副本的 Deployment 的清单。
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 2 # 告知 Deployment 运行 2 个与该模板匹配的 Pod
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
创建 Deployment:
kubectl apply -f https://k8s.io/examples/application/deployment.yaml
查看与 Deployment 关联的 Pod:
kubectl get pods -l app=nginx
在输出中,你可以看到此 Deployment 有两个 Pod。例如:
NAME READY STATUS RESTARTS AGE
nginx-deployment-7fb96c846b-22567 1/1 Running 0 47s
nginx-deployment-7fb96c846b-mlgns 1/1 Running 0 47s
现在用 --subresource=[subresource-name]
标志修补此 Deployment:
kubectl patch deployment nginx-deployment --subresource='scale' --type='merge' -p '{"spec":{"replicas":3}}'
输出为:
scale.autoscaling/nginx-deployment patched
查看与你所修补的 Deployment 关联的 Pod:
kubectl get pods -l app=nginx
在输出中,你可以看到一个新的 Pod 被创建,因此现在你有 3 个正在运行的 Pod。
NAME READY STATUS RESTARTS AGE
nginx-deployment-7fb96c846b-22567 1/1 Running 0 107s
nginx-deployment-7fb96c846b-lxfr2 1/1 Running 0 14s
nginx-deployment-7fb96c846b-mlgns 1/1 Running 0 107s
查看所修补的 Deployment:
kubectl get deployment nginx-deployment -o yaml
...
spec:
replicas: 3
...
status:
...
availableReplicas: 3
readyReplicas: 3
replicas: 3
说明:
如果你运行 kubectl patch
并指定 --subresource
标志时,所针对的是不支持特定子资源的资源,
则 API 服务器会返回一个 404 Not Found 错误。
总结
在本练习中,你使用 kubectl patch
更改了 Deployment 对象的当前配置。
你没有更改最初用于创建 Deployment 对象的配置文件。
用于更新 API 对象的其他命令包括
kubectl annotate
、
kubectl edit
、
kubectl replace
、
kubectl scale
和
kubectl apply
。
说明:
定制资源不支持策略性合并 patch。
接下来
6 - 使用存储版本迁移功能来迁移 Kubernetes 对象
Kubernetes v1.30 [alpha]
(enabled by default: false)Kubernetes 依赖主动重写的 API 数据来支持与静态存储相关的一些维护活动。 两个著名的例子是已存储资源的版本化模式(即针对给定资源的首选存储模式从 v1 更改为 v2) 和静态加密(即基于数据加密方式的变化来重写过时的数据)。
准备开始
安装 kubectl
。
你必须拥有一个 Kubernetes 的集群,且必须配置 kubectl 命令行工具让其与你的集群通信。 建议运行本教程的集群至少有两个节点,且这两个节点不能作为控制平面主机。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:
你的 Kubernetes 服务器版本必须不低于版本 v1.30. 要获知版本信息,请输入kubectl version
.确保你的集群启用了 StorageVersionMigrator
和 InformerResourceVersion
特性门控。
你需要有控制平面管理员权限才能执行此项变更。
在 API 服务器上将运行时配置 storagemigration.k8s.io/v1alpha1
设为 true
,启用存储版本迁移 REST API。
有关如何执行此操作的更多信息,请阅读启用或禁用 Kubernetes API。
使用存储版本迁移来重新加密 Kubernetes Secret
首先配置 KMS 驱动, 以便使用如下加密配置来加密 etcd 中的静态数据。
kind: EncryptionConfiguration apiVersion: apiserver.config.k8s.io/v1 resources: - resources: - secrets providers: - aescbc: keys: - name: key1 secret: c2VjcmV0IGlzIHNlY3VyZQ==
确保通过将
--encryption-provider-config-automatic-reload
设置为 true,允许自动重新加载加密配置文件。
使用 kubectl 创建 Secret。
kubectl create secret generic my-secret --from-literal=key1=supersecret
验证该 Secret 对象的序列化数据带有前缀
k8s:enc:aescbc:v1:key1
。按照以下方式更新加密配置文件,以轮换加密密钥。
kind: EncryptionConfiguration apiVersion: apiserver.config.k8s.io/v1 resources: - resources: - secrets providers: - aescbc: keys: - name: key2 secret: c2VjcmV0IGlzIHNlY3VyZSwgaXMgaXQ/ - aescbc: keys: - name: key1 secret: c2VjcmV0IGlzIHNlY3VyZQ==
要确保之前创建的 Secret
my-secret
使用新密钥key2
进行重新加密,你将使用存储版本迁移。创建以下名为
migrate-secret.yaml
的 StorageVersionMigration 清单:kind: StorageVersionMigration apiVersion: storagemigration.k8s.io/v1alpha1 metadata: name: secrets-migration spec: resource: group: "" version: v1 resource: secrets
使用以下 kubectl 命令创建对象:
kubectl apply -f migrate-secret.yaml
通过检查 StorageVersionMigration 的
.status
来监控 Secret 的迁移。 成功的迁移应将其Succeeded
状况设置为 true。 获取 StorageVersionMigration 对象的方式如下:kubectl get storageversionmigration.storagemigration.k8s.io/secrets-migration -o yaml
输出类似于:
kind: StorageVersionMigration apiVersion: storagemigration.k8s.io/v1alpha1 metadata: name: secrets-migration uid: 628f6922-a9cb-4514-b076-12d3c178967c resourceVersion: "90" creationTimestamp: "2024-03-12T20:29:45Z" spec: resource: group: "" version: v1 resource: secrets status: conditions: - type: Running status: "False" lastUpdateTime: "2024-03-12T20:29:46Z" reason: StorageVersionMigrationInProgress - type: Succeeded status: "True" lastUpdateTime: "2024-03-12T20:29:46Z" reason: StorageVersionMigrationSucceeded resourceVersion: "84"
- 验证存储的
Secret 现在带有前缀
k8s:enc:aescbc:v1:key2
。
更新 CRD 的首选存储模式
考虑这样一种情况: 用户创建了 CustomResourceDefinition (CRD) 来提供自定义资源 (CR),并将其设置为首选的存储模式。 当需要引入 CRD 的 v2 版本时,只需提供转换 Webhook 就可以为 v2 版本提供服务。 基于转换 Webhook 的方式能够实现更平滑的过渡,用户可以使用 v1 或 v2 模式创建 CR,并通过合适的 Webhook 执行必要的模式转换。 在将 v2 设置为首选的存储模式版本之前,重要的是要确保将当前已存储为 v1 的所有 CR 已被迁移到 v2。 这种迁移可以通过使用存储版本迁移将所有 CR 从 v1 迁移到 v2 来达成。
如下针对名为
test-crd.yaml
的 CRD 创建一个清单:apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: name: selfierequests.stable.example.com spec: group: stable.example.com names: plural: SelfieRequests singular: SelfieRequest kind: SelfieRequest listKind: SelfieRequestList scope: Namespaced versions: - name: v1 served: true storage: true schema: openAPIV3Schema: type: object properties: hostPort: type: string conversion: strategy: Webhook webhook: clientConfig: url: https://127.0.0.1:9443/crdconvert caBundle: <CABundle info> conversionReviewVersions: - v1 - v2
使用 kubectl 创建 CRD:
kubectl apply -f test-crd.yaml
为 testcrd 示例创建一个清单。命名为
cr1.yaml
并使用以下内容:apiVersion: stable.example.com/v1 kind: SelfieRequest metadata: name: cr1 namespace: default
使用 kubectl 创建 CR:
kubectl apply -f cr1.yaml
通过从 etcd 获取对象来验证 CR 是否以 v1 格式被写入和存储。
ETCDCTL_API=3 etcdctl get /kubernetes.io/stable.example.com/testcrds/default/cr1 [...] | hexdump -C
其中
[...]
包含连接到 etcd 服务器的额外参数。
如下更新 CRD
test-crd.yaml
,将 v2 版本设置为 served 和 storage,并将 v1 设置为仅 served:apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: name: selfierequests.stable.example.com spec: group: stable.example.com names: plural: SelfieRequests singular: SelfieRequest kind: SelfieRequest listKind: SelfieRequestList scope: Namespaced versions: - name: v2 served: true storage: true schema: openAPIV3Schema: type: object properties: host: type: string port: type: string - name: v1 served: true storage: false schema: openAPIV3Schema: type: object properties: hostPort: type: string conversion: strategy: Webhook webhook: clientConfig: url: "https://127.0.0.1:9443/crdconvert" caBundle: <CABundle info> conversionReviewVersions: - v1 - v2
使用 kubectl 更新 CRD:
kubectl apply -f test-crd.yaml
如下创建名为
cr2.yaml
的 CR 资源文件:apiVersion: stable.example.com/v2 kind: SelfieRequest metadata: name: cr2 namespace: default
使用 kubectl 创建 CR:
kubectl apply -f cr2.yaml
通过从 etcd 获取对象来验证 CR 是否以 v2 格式被写入和存储。
ETCDCTL_API=3 etcdctl get /kubernetes.io/stable.example.com/testcrds/default/cr2 [...] | hexdump -C
其中
[...]
包含连接到 etcd 服务器的额外参数。
如下创建名为
migrate-crd.yaml
的 StorageVersionMigration 清单:kind: StorageVersionMigration apiVersion: storagemigration.k8s.io/v1alpha1 metadata: name: crdsvm spec: resource: group: stable.example.com version: v1 resource: SelfieRequest
使用如下 kubectl 命令创建此对象:
kubectl apply -f migrate-crd.yaml
使用 status 监控 Secret 的迁移。 若迁移成功,应在 status 字段中将
Succeeded
状况设置为 "True"。 获取迁移资源的方式如下:kubectl get storageversionmigration.storagemigration.k8s.io/crdsvm -o yaml
输出类似于:
kind: StorageVersionMigration apiVersion: storagemigration.k8s.io/v1alpha1 metadata: name: crdsvm uid: 13062fe4-32d7-47cc-9528-5067fa0c6ac8 resourceVersion: "111" creationTimestamp: "2024-03-12T22:40:01Z" spec: resource: group: stable.example.com version: v1 resource: testcrds status: conditions: - type: Running status: "False" lastUpdateTime: "2024-03-12T22:40:03Z" reason: StorageVersionMigrationInProgress - type: Succeeded status: "True" lastUpdateTime: "2024-03-12T22:40:03Z" reason: StorageVersionMigrationSucceeded resourceVersion: "106"
通过从 etcd 获取对象来验证之前创建的 cr1 是否现在以 v2 格式被写入和存储。
ETCDCTL_API=3 etcdctl get /kubernetes.io/stable.example.com/testcrds/default/cr1 [...] | hexdump -C
其中
[...]
包含连接到 etcd 服务器的额外参数。