Kubectl patch 命令允许用户对运行在 Kubernetes 集群中的资源进行局部更新。相较于我们经常使用的 kubectl apply 命令,kubectl patch 命令在更新时无需提供完整的资源文件,只需要提供要更新的内容即可。
Kubectl patch 支持以下 3 种 patch 类型:
接下来对 Kubectl patch 的 3 种类型进行介绍。
下面是具有 2 个副本的 Deployment 的配置文件。
# deployment-patch.yaml
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 deployment-patch.yaml
此时,每个 Pod 都有一个运行 nginx 镜像的容器。
> kubectl get pod -l app=nginx
NAME READY STATUS RESTARTS AGE
patch-demo-54975b655f-csk9g 1/1 Running 0 50s
patch-demo-54975b655f-qvv59 1/1 Running 0 50s
现在假设你希望每个 Pod 有两个容器:一个运行 nginx,另一个运行 redis。可以执行以下命令更新,不通过 --type
参数指定 patch 类型时,默认使用 strategic 策略进行更新。
kubectl patch deployment patch-demo --patch '
{
"spec": {
"template": {
"spec": {
"containers": [
{
"name": "patch-demo-ctr-2",
"image": "redis"
}
]
}
}
}
}'
除了通过 --patch(-p)
参数在命令中指定更新的 JSON 内容,也可以使用 --patch-file
参数指定更新的 patch 文件。创建一个名为 patch-file.yaml 的文件,内容如下:
# patch/strategic-patch-file.yaml
spec:
template:
spec:
containers:
- name: patch-demo-ctr-2
image: redis
然后执行以下命令更新。
kubectl patch deployment patch-demo --patch-file patch-file.yaml
查看 patch-demo Deployment,输出显示 Pod 有两个容器:一个运行 nginx,一个运行 redis。
kubectl get deployment patch-demo -o yaml
前面提到默认的 strategic patch 类型是根据不同字段 patchStrategy
决定具体的合并 patch 策略。patch 策略由 Kubernetes 源代码中字段标记中的 patchStrategy
键的值指定。patchStrategy
总共有以下 3 种:
patchStrategy
,则整个 list 会被替换掉。例如,PodSpec 结构体[3] 的 Containers 字段的 patchStrategy
为 merge:
```go
type PodSpec struct {
...
Containers []Container `json:"containers" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,2,rep,name=containers"`
...
}
你可以在 Kubernetes API 文档[4] 中看到字段的 patch 策略。
可以看到其中有两个关键信息:patchStrategy:"merge" patchMergeKey:"name"
。这就代表了,containers 列表使用 strategic merge patch 策略更新时,会把下面每个元素中的 name 字段看作 key。
简单来说,在我们 patch 更新 containers 不再需要指定下标序号了,而是指定 name 来修改,Kubernetes 会把 name 作为 key 来计算合并。如果 K8s 发现当前 containers 中已经有名字为 nginx 的容器,则只会把 image 更新上去;而如果当前 containers 中没有 nginx 容器,K8s 会把这个容器插入 containers 列表。
从 Kubernetes API 文档中可以看到 PodSpec 的 Tolerations 字段在其字段标签中没有键 patchStrategy
,因此 patch 合并策略使用默认的 patch 策略,即 replace
,也就是说当更新 Tolerations 字段时,整个 list 会被替换。
接下来我们尝试更新 Tolerations 字段的内容。
kubectl patch deployment patch-demo --patch '
{
"spec": {
"template": {
"spec": {
"tolerations": [
{
"effect": "NoSchedule",
"key": "disktype",
"value": "ssd"
}
]
}
}
}
}'
查看 patch 后的 Deployment 资源发现 PodSpec 中只有一个 Toleration,也就是说 Toleration 中原来的内容被覆盖了。
kubectl get deployment patch-demo -o yaml
Deployment strategy 用于定义在升级应用时对 Pod 进行替换的策略,其中有 2 种 策略:
我们之前创建的 patch-demo Deployment 使用的是默认的 RollingUpdate 策略。
kubectl get deployment patch-demo -o yaml
如果我们尝试使用 patch 命令将 Deployment strategy 更新为 Recreate :
kubectl patch deployment patch-demo --patch '
{
"spec": {
"strategy": {
"type": "Recreate"
}
}
}'
会看到以下报错:
The Deployment "patch-demo" is invalid: spec.strategy.rollingUpdate: Forbidden: may not be specified when strategy `type` is 'Recreate'
这是由于 spec.strategy.rollingUpdate
字段中已经定义了 rollingUpdate 类型更新策略相关的配置参数,然而 Recreate 类型的策略是不存在这些配置参数的。我们需要再更新 ``spec.strategy.type为 Recreate 的同时,移除
spec.strategy.rollingUpdate`。
查看 Kubernetes API 文档可以发现 Deployment strategy 的 patch 策略为retainKeys
,我们可以在 spec.strategy
字段下配置 $retainKeys
需要保留的字段 type,未列出的字段 rollingUpdate
将会被移除。
使用新的 patch 命令更新 Deployment。
kubectl patch deployment patch-demo --patch '
{
"spec": {
"strategy": {
"$retainKeys": [
"type"
],
"type": "Recreate"
}
}
}'
检查 Deployment 的内容。
kubectl get deployment patch-demo -o yaml
输出显示 Deployment 中的 strategy
对象不再包含 rollingUpdate
键。
JSON merge patch 遵循 JSON Merge Patch, RFC 7386 规范,根据 patch 中提供的期望更改的字段及其对应的值,更新到目标中。具体规则如下:
让我们看一个例子,源文件如下:
{
"title": "Goodbye!",
"author": {
"givenName": "John",
"familyName": "Doe"
},
"tags": [
"example",
"sample"
],
"content": "This will be unchanged"
}
如果我们的 patch 内容如下,JSON merge patch 将会对源文件进行以下更改:
title
字段的值。author.familyName
字段。phoneNumber
字段。tags
列表中的 example。{
"title": "Hello!",
"author": {
"familyName": null
},
"phoneNumber": "+01-123-456-7890",
"tags": [
"example"
]
}
那么最终的文件将会变成:
{
"title": "Hello!",
"author": {
"givenName": "John"
},
"tags": [
"example"
],
"content": "This will be unchanged",
"phoneNumber": "+01-123-456-7890"
}
我们使用 kubectl patch 时,只需要在 --type
参数设置 merge
就可以通过 JSON merge patch 的方式更新资源了。JSON merge patch 同样支持指定文件的方式更新 patch。例如执行以下命令分别更新 patch-demo Deployment 的 replicas 副本数以及 containers 容器列表。
kubectl patch deployment --type merge patch-demo --patch '
{
"spec": {
"replicas": 5
}
}'
kubectl patch deployment --type merge patch-demo --patch '
{
"spec": {
"template": {
"spec": {
"containers": [
{
"name": "patch-demo-ctr-3",
"image": "nginx"
}
]
}
}
}
}'
查看更新后的 Deployment 资源。
kubectl get deployment patch-demo -o yaml
可以看到 replicas 字段被改为了 5,而 containers 列表被完全替换成新的了。
JSON merge patch 无法单独更新一个列表中的某个元素,因此不管我们是要在 containers 里新增容器、还是修改已有容器的 image、env 等字段,都要用整个 containers 列表来提交 patch。
kubectl patch deployment --type merge patch-demo --patch '
{
"spec": {
"template": {
"spec": {
"containers": [
{
"name": "patch-demo-ctr-1",
"image": "nginx"
},
{
"name": "patch-demo-ctr-2",
"image": "redis"
},
{
"name": "patch-demo-ctr-3",
"image": "nginx"
}
]
}
}
}
}'
查看更新后的 Deployment 资源。
kubectl get deployment patch-demo -o yaml
JSON patch 遵循 JSON Patch, RFC 6902 规范,通过明确的指令表示具体的操作,指令在 op 字段中设置,包含:
还是用上面相同的例子对比使用 JSON patch,源文件如下:
{
"title": "Goodbye!",
"author": {
"givenName": "John",
"familyName": "Doe"
},
"tags": [
"example",
"sample"
],
"content": "This will be unchanged"
}
JSON patch 的内容如下,和前面 JSON merge patch 实现的效果一样,将会对源文件进行以下更改:
title
字段的值。author.familyName
字段。phoneNumber
字段。tags
列表中的 example。[
{ "op": "replace", "path": "/title", "value": "Hello!"},
{ "op": "remove", "path": "/author/familyName"},
{ "op": "add", "path": "/phoneNumber", "value": "+01-123-456-7890"},
{ "op": "replace", "path": "/tags", "value": ["example"]}
]
为了方便对比我把前面 JSON merge patch 的内容也放在这里。
{
"title": "Hello!",
"author": {
"familyName": null
},
"phoneNumber": "+01-123-456-7890",
"tags": [
"example"
]
}
可以看出 JSON merge patch 和 JSON patch 相比,最大的优势就是简单,但这种简单性同样带来了一些限制:
JSON Merge Patch 是一种简单的格式,它的应用范围相对有限。当你只需要更新非常简单的 JSON Schema 时,使用 JSON Merge Patch 可能是一个不错的选择。然而,对于更复杂的用例,我会选择使用 JSON Patch,因为它适用于任何 JSON 文档,并且该规范还确保原子执行和可靠的错误报告。
我们使用 kubectl patch 时,只需要在 --type
参数设置 json
就可以通过 JSON patch 的方式更新资源了。JSON patch 同样支持指定文件的方式更新 patch。例如执行以下命令分别更新 patch-demo Deployment 的 replicas 副本数以及 containers 容器列表。
kubectl patch deployment --type json patch-demo --patch '
[
{
"op": "replace",
"path": "/spec/replicas",
"value": 2
}
]'
kubectl patch deployment --type json patch-demo --patch '
[
{
"op": "add",
"path": "/spec/template/spec/containers/3",
"value": {
"name": "patch-demo-ctr-4",
"image": "redis"
}
}
]'
查看更新后的 Deployment 资源。
kubectl get deployment patch-demo -o yaml
本文介绍了使用 kubectl patch 命令更新 Kubernetes 集群中资源对象的方法。Kubectl patch 命令允许用户对资源对象的指定字段进行局部更新,提高了更新操作的灵活性。文章介绍了 kubectl patch 命令的语法和使用方法,包括三种 strategic patch, JSON merge patch, JSON patch 3 种类型,并结合了具体的示例进行说明。