使用 kubectl patch 更新 API 对象

    这个任务展示如何使用 就地更新 API 对象。 这个任务中的练习演示了一个策略性合并 patch 和一个 JSON 合并 patch。

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

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

    使用策略合并 patch 更新 Deployment

    下面是具有两个副本的 Deployment 的配置文件。每个副本是一个 Pod,有一个容器:

    application/deployment-patch.yaml

    创建 Deployment:

    1. kubectl create -f https://k8s.io/examples/application/deployment-patch.yaml

    查看与 Deployment 相关的 Pod:

    1. kubectl get pods

    输出显示 Deployment 有两个 Pod。1/1 表示每个 Pod 有一个容器:

    1. NAME READY STATUS RESTARTS AGE
    2. patch-demo-28633765-670qr 1/1 Running 0 23s
    3. patch-demo-28633765-j5qs3 1/1 Running 0 23s

    把运行的 Pod 的名字记下来。稍后,你将看到这些 Pod 被终止并被新的 Pod 替换。

    此时,每个 Pod 都有一个运行 nginx 镜像的容器。现在假设你希望每个 Pod 有两个容器:一个运行 nginx,另一个运行 redis。

    创建一个名为 patch-file-containers.yaml 的文件。内容如下:

    1. spec:
    2. template:
    3. spec:
    4. containers:
    5. - name: patch-demo-ctr-2
    6. image: redis

    修补你的 Deployment:

    1. kubectl patch deployment patch-demo --patch "$(cat patch-file-containers.yaml)"

    查看修补后的 Deployment:

    1. kubectl get deployment patch-demo --output yaml

    输出显示 Deployment 中的 PodSpec 有两个容器:

    1. containers:
    2. - image: redis
    3. imagePullPolicy: Always
    4. name: patch-demo-ctr-2
    5. ...
    6. - image: nginx
    7. imagePullPolicy: Always
    8. name: patch-demo-ctr
    9. ...

    查看与 patch Deployment 相关的 Pod:

    1. kubectl get pods

    输出显示正在运行的 Pod 与以前运行的 Pod 有不同的名称。Deployment 终止了旧的 Pod,并创建了两个 符合更新的部署规范的新 Pod。2/2 表示每个 Pod 有两个容器:

    1. NAME READY STATUS RESTARTS AGE
    2. patch-demo-1081991389-2wrn5 2/2 Running 0 1m
    3. patch-demo-1081991389-jmg7b 2/2 Running 0 1m

    仔细查看其中一个 patch-demo Pod:

    1. kubectl get pod <your-pod-name> --output yaml

    输出显示 Pod 有两个容器:一个运行 nginx,一个运行 redis:

    1. containers:
    2. - image: redis
    3. ...
    4. - image: nginx
    5. ...

    对于策略性合并 patch,列表可以根据其 patch 策略进行替换或合并。 patch 策略由 Kubernetes 源代码中字段标记中的 patchStrategy 键的值指定。 例如,PodSpec 结构体的 Containers 字段的 patchStrategymerge

    1. type PodSpec struct {
    2. ...
    3. Containers []Container `json:"containers" patchStrategy:"merge" patchMergeKey:"name" ...`

    你还可以在 规范中看到 patch 策略:

    你可以在 Kubernetes API 文档 中看到 patch 策略。

    创建一个名为 patch-file-tolerations.yaml 的文件。内容如下:

    1. spec:
    2. spec:
    3. tolerations:
    4. - effect: NoSchedule
    5. key: disktype
    6. value: ssd

    对 Deployment 执行 patch 操作:

    1. kubectl patch deployment patch-demo --patch "$(cat patch-file-containers.yaml)"

    查看修补后的 Deployment:

    1. kubectl get deployment patch-demo --output yaml

    输出结果显示 Deployment 中的 PodSpec 只有一个容忍度设置:

    1. containers:
    2. - image: redis
    3. name: patch-demo-ctr-2
    4. ...
    5. - image: nginx
    6. imagePullPolicy: Always
    7. name: patch-demo-ctr
    8. ...
    1. tolerations:
    2. - effect: NoSchedule
    3. key: disktype
    4. value: ssd

    请注意,PodSpec 中的 tolerations 列表被替换,而不是合并。这是因为 PodSpec 的 tolerations 的字段标签中没有 patchStrategy 键。所以策略合并 patch 操作使用默认的 patch 策略,也就是 replace

    1. type PodSpec struct {
    2. ...
    3. Tolerations []Toleration `json:"tolerations,omitempty" protobuf:"bytes,22,opt,name=tolerations"`

    策略性合并 patch 不同于 。 使用 JSON 合并 patch,如果你想更新列表,你必须指定整个新列表。新的列表完全取代现有的列表。

    kubectl patch 命令有一个 type 参数,你可以将其设置为以下值之一:

    有关 JSON patch 和 JSON 合并 patch 的比较,查看 JSON patch 和 JSON 合并 patch

    type 参数的默认值是 strategic。在前面的练习中,我们做了一个策略性的合并 patch。

    下一步,在相同的 Deployment 上执行 JSON 合并 patch。创建一个名为 patch-file-2 的文件。内容如下:

    1. spec:
    2. template:
    3. spec:
    4. containers:
    5. - name: patch-demo-ctr-3
    6. image: gcr.io/google-samples/node-hello:1.0

    在 patch 命令中,将 type 设置为 merge

    1. kubectl patch deployment patch-demo --type merge --patch "$(cat patch-file-2.yaml)"

    查看修补后的 Deployment:

    1. kubectl get deployment patch-demo --output yaml

    patch 中指定的containers列表只有一个 Container。 输出显示你所给出的 Contaier 列表替换了现有的 containers 列表。

    1. spec:
    2. containers:
    3. - image: gcr.io/google-samples/node-hello:1.0
    4. ...
    5. name: patch-demo-ctr-3

    列表中运行的 Pod:

    1. kubectl get pods

    在输出中,你可以看到已经终止了现有的 Pod,并创建了新的 Pod。1/1 表示每个新 Pod只运行一个容器。

    1. NAME READY STATUS RESTARTS AGE
    2. patch-demo-1307768864-69308 1/1 Running 0 1m
    3. patch-demo-1307768864-c86dc 1/1 Running 0 1m

    使用带 retainKeys 策略的策略合并 patch 更新 Deployment

    创建 Deployment:

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

    这时,Deployment 被创建,并使用 RollingUpdate 策略。

    创建一个名为 patch-file-no-retainkeys.yaml 的文件,内容如下:

    1. spec:
    2. strategy:
    3. type: Recreate

    修补你的 Deployment:

    1. kubectl patch deployment retainkeys-demo --patch "$(cat patch-file-no-retainkeys.yaml)"
    1. kubectl patch deployment retainkeys-demo --patch $(Get-Content patch-file-no-retainkeys.yaml -Raw)

    在输出中,你可以看到,当 spec.strategy.rollingUpdate 已经拥有取值定义时, 将其 type 设置为 Recreate 是不可能的。

    更新 type 取值的同时移除 spec.strategy.rollingUpdate 现有值的方法是 为策略性合并操作设置 retainKeys 策略:

    创建另一个名为 patch-file-retainkeys.yaml 的文件,内容如下:

    1. spec:
    2. strategy:
    3. $retainKeys:
    4. - type
    5. type: Recreate

    使用此 patch,我们表达了希望只保留 strategy 对象的 type 键。 这样,在 patch 操作期间 rollingUpdate 会被删除。

    使用新的 patch 重新修补 Deployment:

    1. kubectl patch deployment retainkeys-demo --patch "$(cat patch-file-retainkeys.yaml)"
    1. kubectl patch deployment retainkeys-demo --patch $(Get-Content patch-file-retainkeys.yaml -Raw)

    检查 Deployment 的内容:

    1. kubectl get deployment retainkeys-demo --output yaml

    输出显示 Deployment 中的 strategy 对象不再包含 rollingUpdate 键:

    1. spec:
    2. strategy:
    3. type: Recreate
    4. template:

    关于使用 retainKeys 策略的策略合并 patch 操作的说明

    在前文练习中所执行的称作 retainKeys 策略的策略合并 patch(Strategic Merge Patch with retainKeys Strategy)。 这种方法引入了一种新的 $retainKey 指令,具有如下策略:

    • 其中包含一个字符串列表;
    • 所有需要被保留的字段必须在 $retainKeys 列表中给出;
    • 对于已有的字段,会和对象上对应的内容合并;
    • 在修补操作期间,未找到的字段都会被清除;
    • 列表 $retainKeys 中的所有字段必须 patch 操作所给字段的超集,或者与之完全一致。

    策略 retainKeys 并不能对所有对象都起作用。它仅对那些 Kubernetes 源码中 patchStrategy 字段标志值包含 retainKeys 的字段有用。 例如 DeploymentSpec 结构的 Strategy 字段就包含了 patchStrategyretainKeys 的标志。

    1. type DeploymentSpec struct {
    2. ...
    3. // +patchStrategy=retainKeys
    4. Strategy DeploymentStrategy `json:"strategy,omitempty" patchStrategy:"retainKeys" ...`

    你也可以查看 OpenAPI 规范中的 retainKeys 策略:

    1. "io.k8s.api.apps.v1.DeploymentSpec": {
    2. ...
    3. "strategy": {
    4. "$ref": "#/definitions/io.k8s.api.apps.v1.DeploymentStrategy",
    5. "description": "The deployment strategy to use to replace existing pods with new ones.",
    6. "x-kubernetes-patch-strategy": "retainKeys"
    7. },

    而且你也可以在 . 中看到 retainKey 策略。

    kubectl patch 命令使用 YAML 或 JSON。它可以接受以文件形式提供的补丁,也可以 接受直接在命令行中给出的补丁。

    创建一个文件名称是 patch-file.json 内容如下:

    以下命令是等价的:

    1. kubectl patch deployment patch-demo --patch "$(cat patch-file.yaml)"
    2. kubectl patch deployment patch-demo --patch 'spec:\n template:\n spec:\n containers:\n - name: patch-demo-ctr-2\n image: redis'
    3. kubectl patch deployment patch-demo --patch "$(cat patch-file.json)"
    4. kubectl patch deployment patch-demo --patch '{"spec": {"template": {"spec": {"containers": [{"name": "patch-demo-ctr-2","image": "redis"}]}}}}'

    总结