Kubernetes 对象管理的三种方式

Kubernetes 中文文档

1. Kubernetes 对象管理的三种方式对比

Kubernetes 中的对象管理方式,根据对象配置信息的位置不同可以分为两大类:

  • 命令式:对象的参数通过命令指定
  • 配置式:对象的参数通过 YAML 配置文件指定

其中,对于配置式对象管理方式,根据在执行 kubectl 命令时是否指定具体操作,又可以分为两类:

  • 命令式对象配置:命令中指定具体操作
  • 声明式对象配置:命令中不指定具体操作,通过 kubectl 自动检测对象并自动进行创建、更新和删除操作

总结一下,Kubernetes 中对象管理的方式有:

  • 命令式对象管理:直接在命令行中指定操作和配置
  • 对象配置式
    • 命令式对象配置:在命令行中指定操作,对象配置信息在配置文件中指定
    • 声明式对象配置:由 kubectl 自动检测对象并执行操作,对象配置信息在配置文件中指定
类型 操作对象 适用环境 优点 缺点
命令式对象管理 活动对象 测试 简单易学 只能操作活动对象。无法审计、跟踪
命令式对象配置 单个文件 开发 配置文件可以使用版本控制,可以审计、跟踪 项目大时,配置文件多,操作麻烦。只能通过配置文件更新活动对象
声明式对象配置 目录 开发 支持目录操作 意外情况下难以调试。可以直接更改活动对象并保留信息

1.1 命令式对象管理

命令参考手册
官方文档

语法

Kubernetes 中,操作以及对象定义都可以直接在 kubectl 命令行中通过参数指定,这时采用的对象管理方式就是命令式对象管理。例如创建一个 Deployment:

kubectl create deployment nginx --image nginx

kubectl run nginx --image nginx

可以查看每个命令的帮助信息,只要去掉参数并在后面加 help-h 即可,例如:

kubectl run -h
kubectl create deployment -h
kubectl create service nodeport -h

创建对象

使用动词创建常用的对象
  • run:创建 Deployment 对象,用于在 Pod 中运行容器。
  • expose:创建 Service 对象,用于在 Pod 之间负载均衡流量。
  • autoscale:创建 Autoscaler 对象,用于自动水平伸缩控制器,例如 Deployment。
使用 create 命令创建指定类型的对象

语法:

create <objecttype> [<subtype>] <instancename>

部分对象有子类型,例如 Service 对象的子类型包括:ClusterIP、LoadBalancer 和 NodePort。示例:

kubectl create service nodeport <myservicename>

更新对象

使用动词更新对象
  • scale:水平伸缩控制器,通过更新控制器的副本数量来添加或删除 Pod。
  • annotate:添加或删除对象的注释。
  • label:添加或删除对象的标签。
使用 set 命令更新对象的指定方面,不同对象的字段可能不同
  • set:设置对象的一个方面。
其他方式

kubectl 工具支持直接更新活动对象,但需要更好地理解 Kubernetes 对象模型。

  • edit:通过在编辑器中打开其配置文件,直接编辑活动对象的原始配置。
  • patch:使用 patch 命令指定的字符串直接修改活动对象的特定字段。

删除对象

使用 delete 命令从集群中删除对象
delete <type>/<name>

命令式命令和命令式对象配置都可以使用 kubectl delete。示例:

kubectl delete deployment/nginx # 命令式命令
kubectl delete -f nginx.yaml    # 命令式对象配置

查看对象

  • get:打印对象基本信息。
  • describe:打印对象详细信息。
  • logs:打印 Pod 中运行的容器的 STDOUT 和 STDERR 信息。

1.2 命令式对象配置

命令参考手册 - 同上
官方文档

语法

Kubernetes 中,kubectl 命令行参数指定操作和定义对象的文件,这时采用的对象管理方式就是命令式对象配置。例如创建一个 Deployment:

kubectl create -f nginx.yaml

删除配置文件中定义的对象:

kubectl delete -f nginx.yaml

更新配置文件中定义的对象(可以改副本数量、Label、镜像版本、端口,但是不能改名字,且如果改 Label 会创建新 Pod,但不会删除原 Pod)。注意,replace 操作会替换整个对象的配置,其他对象所做的更改都会丢失:

kubectl replace -f nginx.yaml

创建对象

通过 kubectl create -f 从配置文件创建对象。

kubectl create -f <filename|url>

更新对象

使用 replace 命令更新对象将删除所有没有在配置文件中指定 spec 部分。这不应用于其 spec 由集群管理的对象,例如类型为 LoadBalancer 的 Services,其中 externalIPs 字段独立管理,与配置文件无关。必须将独立管理的字段复制到配置文件,以防止替换丢弃它们。

使用 kubectl replace -f 根据配置文件更新活动对象:

kubectl replace -f <filename|url>

删除对象

使用 kubectl delete -f 删除在配置文件中描述的对象:

kubectl delete -f <filename|url>

查看对象

使用 kubectl get -f 查看配置文件中描述的对象的信息:

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

-o yaml 选项表示打印完整的对象配置信息。

限制

当每个对象的配置完全定义且记录在其配置文件中时,create、replace 和 delete 命令可以很好地工作。但是,当活动对象被更新过,并且更新未合并到其配置文件中时,更新将在执行 replace 命令时丢失。如果控制器(如 HorizontalPodAutoscaler)直接更新活动对象,就会发生这种情况。例子:

  1. 通过配置文件创建了一个对象。
  2. 另一个源通过更改某个字段来更新对象。
  3. 通过配置文件为该对象执行 replace 命令。步骤 2 中其他来源所做的更改将丢失。

如果需要支持多个 writer 操作同一个对象,可以使用 kubectl apply 来管理这个对象。

从 URL 创建和编辑对象而不保存配置

kubectl create --edit 可以在创建对象前修改对象。适合练习者需要自定义配置的教程及任务。不适合生产及测试环境。

kubectl create -f <url> --edit

从命令性命令迁移到命令性对象配置

从命令行命令迁移到命令式对象配置:

  • 将活动对象导出到本地对象配置文件:
kubectl get / -o yaml –export > _.yaml
  • 手动从对象配置文件中删除状态字段。
  • 对于后续的对象管理,可以使用 replace
kubectl replace -f _.yaml

1.3 声明式对象配置

只有一个命令 - apply
官方文档

语法

声明式对象配置保留其他对象所做的更改,即使这些更改未记录到对象的配置文件中。可以使用 patch 操作来更新对象。

处理 configs 目录中的所有对象配置文件,create(对象不存在时)或 patch(对象已经存在时)活动对象:

kubectl apply -f configs/

递归处理目录:

kubectl apply -R -f configs/

三个概念

  • object configuration file / configuration file:定义 Kubernetes 对象配置的文件,通常存储在源代码控制中,比如 Git。
  • live object configuration / live configuration:对 Kubernetes 集群可见的对象的实时配置值。保存在 Kubernetes 集群存储中,通常是 etcd。
  • declarative configuration writer / declarative writer:对活动对象进行更新的人员或软件组件。本主题中提到的 live writer 会更改对象的配置文件,并运行 kubectl apply 来写入更改。

创建对象

使用 kubectl apply 创建所有对象(除了那些已经存在的对象),由指定目录中的配置文件定义:

kubectl apply -f <directory>/

这会为每个对象设置 kubectl.kubernetes.io/last-applied-configuration: '{...}' 注释。注释包含用于创建对象的对象配置文件的内容。

可以通过 -R 选项递归处理目录。

simple_deployment.yaml docs/concepts/overview/object-management-kubectl 对象配置文件示例:

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.7.9
        ports:
        - containerPort: 80

通过 kubectl apply -f URL 创建对象:

kubectl apply -f https://k8s.io/docs/concepts/overview/object-management-kubectl/simple_deployment.yaml

通过 kubectl get -f URL 查看对象的实时配置:

kubectl get -f https://k8s.io/docs/concepts/overview/object-management-kubectl/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.7.9","name":"nginx",
      "ports":[{"containerPort":80}]}]}}}}
  # ...
spec:
  # ...
  minReadySeconds: 5
  selector:
    matchLabels:
      # ...
      app: nginx
  template:
    metadata:
      # ...
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.7.9
        # ...
        name: nginx
        ports:
        - containerPort: 80
        # ...
      # ...
    # ...
  # ...

更新对象

可以用 kubectl apply 更新一个目录中定义的所有对象,即使对象已经存在。该方法实现以下内容:

  • 设置实时配置中出现在配置文件中的字段。
  • 清除实时配置中从配置文件中删除的字段。
kubectl apply -f <directory> /

注意:添加 -R 标志可以递归处理目录。

simple_deployment.yaml docs/concepts/overview/object-management-kubectl 示例文件:

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.7.9
        ports:
        - containerPort: 80

创建及查看对象的实时配置参考上一步。现在,通过 kubectl scale 更新动态配置的 replicas 字段:

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

通过 kubectl get -f URL 查看对象的实时配置:

kubectl get -f https://k8s.io/docs/concepts/overview/object-management-kubectl/simple_deployment.yaml -o yaml

输出显示 replicas 字段已经变为 2,并且 kubectl.kubernetes.io/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.7.9","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.7.9
        # ...
        name: nginx
        ports:
        - containerPort: 80
      # ...

更新 simple_deployment.yaml 配置文件,将镜像从 nginx:1.7.9 变为 nginx:1.11.9,并删除 minReadySeconds 字段:

update_deployment.yaml docs/concepts/overview/object-management-kubectl 文件:

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.11.9 # update the image
        ports:
        - containerPort: 80

通过 apply 应用对配置文件所做的更改:

kubectl apply -f https://k8s.io/docs/concepts/overview/object-management-kubectl/update_deployment.yaml

查看实时配置:

kubectl get -f https://k8s.io/docs/concepts/overview/object-management-kubectl/simple_deployment.yaml -o yaml

输出显示对实时配置发生以下更改:

  • replicas 字段保留了由 kubectl scale 设置的值 2
  • image 字段已经从 nginx:1.7.9 更新为 nginx:1.11.9
  • 注释 last-applied-configuration 已更新为新镜像
  • minReadySeconds 字段已被清除
  • 注释 last-applied-configuration 不再包含 minReadySeconds 字段
apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    # ...
    # The annotation contains the updated image to nginx 1.11.9,
    # 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.11.9","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.11.9 # Set by `kubectl apply`
        # ...
        name: nginx
        ports:
        - containerPort: 80
        # ...
      # ...
    # ...
  # ...

警告:不支持混合使用 kubectl apply 和命令式对象配置中的 create 和 replace 命令。这是因为 create 和 replace 不会保留 kubectl apply 用于计算更新的 kubectl.kubernetes.io/last-applied-configuration 注释。

删除对象

有两种方式删除 kubectl apply 管理的对象:

推荐:kubectl delete -f <filename>

推荐的方法是使用命令式命令手动删除对象,因为它更清晰地表明要删除的内容,并且不太可能导致用户无意中删除某些内容:

kubectl delete -f <filename>

可选:kubectl apply -f <directory/> --prune -l your=label

只有当你知道在做什么时再用。

警告:kubectl apply --prune 是 alpha 版本,后续版本中可能会引入向后不兼容的更改。

警告:使用此命令时必须小心,以免意外删除对象。

作为 kubectl delete 的替代方法,在配置文件从目录中删除后,可以使用 kubectl apply 识别要删除的对象。--prune 用于查询 API 服务器以查找匹配一组标签的所有对象,并尝试将返回的实时对象配置与对象配置文件进行匹配。如果某个对象与该查询匹配,并且该目录中没有配置文件,并且该对象具有 last-applied-configuration 注释,则该对象将被删除。

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

重要提示:使用 prune 只能在包含对象配置文件的根目录下运行。针对子目录运行可能会导致对象被无意中删除(如果它们是由 -l <labels> 指定的标签选择器查询返回的并且不出现在子目录中)。

查看对象

查看有效对象的配置:

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

apply 如何计算差异并合并更改

定义:patch 是一种更新操作,它的作用范围是对象的特定字段而不是整个对象。这允许只更新对象上的一组特定字段,而无需先读取对象。

kubectl apply 更新对象的实时配置时,它通过向 API 服务器发送 patch 程序请求来实现此目的。该 patch 将更新范围定义为活动对象配置的特定字段。kubectl apply 命令使用配置文件,实时配置和存储在实时配置中的 last-applied-configuration 注释来计算此 patch 请求。

合并 patch 计算

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.11.9 # 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.7.9","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.7.9
        # ...
        name: nginx
        ports:
        - containerPort: 80
      # ...

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

  1. 通过读取 last-applied-configuration 中的值并将它们与配置文件中的值进行比较来计算要删除的字段。示例中,minReadySeconds 显示在 last-applied-configuration 中,但未出现在配置文件中。操作:清除实时配置中的 minReadySeconds
  2. 通过读取配置文件中的值并将它们与实时配置中的值进行比较来计算要设置的字段。示例中,配置文件中 image 的值与实时配置中的值不匹配。操作:在实时配置中设置 image 的值。
  3. 设置 last-applied-configuration 注释以匹配配置文件的值。
  4. 将前面 3 个步骤的结果合并到 API 服务器的单个 patch 请求中。

以下是合并后的实时配置:

apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    # ...
    # The annotation contains the updated image to nginx 1.11.9,
    # 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.11.9","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.11.9 # Set by `kubectl apply`
        # ...
        name: nginx
        ports:
        - containerPort: 80
        # ...
      # ...
    # ...
  # ...

如何合并不同类型的字段

配置文件中的特定字段如何与实时配置合并取决于字段的类型。有几种类型的字段:

  • primitive:字符串、整数或布尔类型的字段。例如,image 和 replicas 是 primitive 字段。行为:更换 Replace。
  • map,也称为 object:map 类型或包含子字段的复杂类型。例如,label、annotation、spec 和 metadata 都是 map。行为:合并元素或子字段。
  • list:包含 primitive 或 map 的条目列表的字段。例如,container、port 和 args 都是列表。行为:各不相同。

kubectl apply 更新 map 或 list 字段时,通常不会替换所有字段,而是更新单个子元素。例如,在合并 Deployment 的 spec 规范时,不会替换整个规范,而是比较并合并规范的子字段(例如 replicas)。

将更改合并到原始字段

原始字段被替换或清除。

注意:’ - ‘用于“不适用”,因为该值未被使用。

Field in object configuration file Field in live object configuration Field in last-applied-configuration Action
Yes Yes - Set live to configuration file value.
Yes No - Set live to local configuration.
No - Yes Clear from live configuration.
No - No Do nothing. Keep live value.

Merging changes to map fields

Fields that represent maps are merged by comparing each of the subfields or elements of the map:

Note: ‘-’ is used for “not applicable” because the value is not used.

Key in object configuration file Key in live object configuration Field in last-applied-configuration Action
Yes Yes - Compare sub fields values.
Yes No - Set live to local configuration.
No - Yes Delete from live configuration.
No - No Do nothing. Keep live value.

Merging changes for fields of type list

Merging changes to a list uses one of three strategies:

  • Replace the list.
  • Merge individual elements in a list of complex elements.
  • Merge a list of primitive elements.

The choice of strategy is made on a per-field basis.

替换列表 list

将列表视为 primitive 原始字段。替换或删除整个列表。这保留了 ordering。

示例:使用 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"]

说明:合并使用配置文件值作为新的列表值。

合并一个复杂元素列表中的单个元素:

将列表视为 map,并将每个元素的特定字段视为 key。添加,删除或更新单个元素。不保留排序。

此合并策略在每个字段上使用一个名为 patchMergeKey 的特殊标记。 patchMergeKey 是为 Kubernetes 源代码中的每个字段定义的:types.go。合并 map 列表时,为给定元素指定为 patchMergeKey 的字段将用作该元素的 map key(the field specified as the patchMergeKey for a given element is used like a map key for that element)。

示例:使用 kubectl apply 来更新 PodSpec 的 containers 字段。这会合并列表,就好像它是一个 map,其中每个元素都是按名称键入的。

# last-applied-configuration value
    containers:
    - name: nginx
      image: nginx:1.10
    - 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.10
    - 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.10
    - 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.10
      # 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 的更改。即使其配置文件中的字段具有不同的值(配置文件中没有 args),kubectl apply 也能识别出实时配置中的“nginx-helper-b”与配置文件中的“nginx-helper-b”相同。这是因为 patchMergeKey 字段值(名称)在两者中都是相同的。
  • 添加了一个名为“nginx-helper-c”的容器,因为实时配置中没有带有该名称的容器出现,但具有该名称的容器出现在配置文件中。
  • 名为“nginx-helper-d”的容器被保留,因为 last-applied-configuration 中没有出现具有该名称的元素。

合并一系列 primitive 原始元素

合并 primitive 元素列表

从 Kubernetes 1.5 开始,不支持合并原始元素列表。

注意:为给定字段选择上述哪种策略是由 types.go 中的 patchStrategy 标记控制的。如果未为类型列表字段指定 patchStrategy,则会替换该列表。

默认的字段值

如果在创建对象时未指定字段值,则 API 服务器会将实时配置中的某些字段设置默认值。

这是 Deployment 的配置文件。该文件不指定策略或选择器:

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.7.9
        ports:
        - containerPort: 80

使用 kubectl apply 创建对象:

kubectl apply -f https://k8s.io/docs/concepts/overview/object-management-kubectl/simple_deployment.yaml

使用 kubectl get 查看实时配置:

kubectl get -f https://k8s.io/docs/concepts/overview/object-management-kubectl/simple_deployment.yaml -o yaml

输出显示,API 服务器为实时配置中的几个字段设置了默认值。这些字段没有在配置文件中声明。

apiVersion: apps/v1
kind: Deployment
# ...
spec:
  selector:
    matchLabels:
      app: nginx
  minReadySeconds: 5
  replicas: 1 # defaulted by apiserver
  selector:
    matchLabels: # defaulted by apiserver - derived from template.metadata.labels
      app: nginx
  strategy:
    rollingUpdate: # defaulted by apiserver - derived from strategy.type
      maxSurge: 1
      maxUnavailable: 1
    type: RollingUpdate # defaulted apiserver
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.7.9
        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
# ...

注意:某些字段的默认值是从配置文件中指定的其他字段的值派生的,例如 selector 字段。

在 patch 请求中,默认字段不会被重新默认,除非它们作为 patch 请求的一部分被明确地清除。这可能会导致某些依赖其他字段值的字段发生非预期的行为。当其他字段稍后更改时,除非明确清除,否则不会更新其默认值。

因此,建议在配置文件中明确定义服务器默认的字段,即使所需值与服务器默认值匹配。

示例:

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

# configuration file
spec:
  strategy:
    type: Recreate # updated value
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        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.7.9
        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.7.9
        ports:
        - containerPort: 80

说明:

  1. 用户创建一个没有定义 strategy.type 的 Deployment。
  2. 服务器将 strategy.type 默认为 RollingUpdate,并默认使用 strategy.rollingUpdate 值。
  3. 用户将 strategy.type 更改为 Recreate。尽管服务器期望它们被清除,但 strategy.rollingUpdate 值仍保持默认值。如果 strategy.rollingUpdate 值最初是在配置文件中定义的,那么它会更清楚地理解它们需要被删除。
  4. 由于 strategy.rollingUpdate 未清除,因此应用失败。strategy.rollingupdate 字段不能使用 Recreate 的 strategy.type 来定义。

建议:这些字段应该在对象配置文件中明确定义:

  • 工作负载上的选择器和 PodTemplate 标签,例如 Deployment,StatefulSet,Job,DaemonSet,ReplicaSet 和 ReplicationController
  • Deployment rollout strategy(推出策略)

如何清除服务器默认字段或由其他 writer 设置的字段

截至 Kubernetes 1.5,配置文件中的字段都能被合并操作清除。以下是一些解决方法:

选项 1:通过直接修改活动对象来删除字段。

注意:截至 Kubernetes 1.5,kubectl edit 不适用于 kubectl apply。一起使用会导致意想不到的行为。

选项 2:通过配置文件删除字段。

  1. 将字段添加到配置文件以匹配活动对象。
  2. 应用配置文件,这会更新注释以包含该字段。
  3. 从配置文件中删除该字段。
  4. 应用配置文件,这会从活动对象和注释中删除该字段。

如何在配置文件和直接命令式 writer 之间更改字段的所有权

这些是用来更改单个对象字段的唯一方法:

  • 使用 kubectl apply
  • 直接写入实时配置而不修改配置文件:例如,使用 kubectl scale

将所有权从直接命令式 writer 改为配置文件

将该字段添加到配置文件。对于该字段,停止直接更新不经过 kubectl apply 的实时配置(discontinue direct updates to the live configuration that do not go through kubectl apply)。

将所有权从配置文件改为直接命令式 writer

截至 Kubernetes 1.5,需要手动步骤:

  • 从配置文件中删除该字段。
  • 从实时对象上的 kubectl.kubernetes.io/last-applied-configuration annotation 中删除该字段。

改变管理方法

每次只应该使用一种方法来管理 Kubernetes 对象。从一种方法切换到另一种方法是可能的,但是是手动过程。

例外:声明式管理可以使用命令式删除。

从命令式命令管理迁移到声明式对象配置

从命令式命令管理迁移到声明式对象配置需要几个手工步骤:

  • 将实时对象导出到本地配置文件:
kubectl get / -o yaml –export > _.yaml
  • 手动从配置文件中删除状态字段。
    注意:此步骤是可选的,因为即使 kubectl apply 存在于配置文件中,它也不会更新状态字段。
  • 在对象上设置 kubectl.kubernetes.io/last-applied-configuration 注释:
kubectl replace –save-config -f _.yaml
  • 更改进程以只使用 kubectl apply 管理对象。

从命令式对象配置迁移到声明式对象配置

  • 设置对象上的 kubectl.kubernetes.io/last-applied-configuration 注释:
kubectl replace –save-config -f _.yaml
  • 更改进程以只使用 kubectl apply 管理对象。

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

警告:强烈建议不要更新控制器上的选择器。

推荐的方法是定义一个单一的、不可变的 PodTemplate 标签,仅由控制器选择器使用,没有其他语义含义。

示例:

selector:
  matchLabels:
      controller-selector: "extensions/v1beta1/deployment/nginx"
template:
  metadata:
    labels:
      controller-selector: "extensions/v1beta1/deployment/nginx"

2. replace 和 patch

replace 替换

使用配置文件(JSON 或 YAML 格式)或 STDIN 来替换资源。如果要替换的资源已经存在,则必须提供完整的资源规范。可以通过以下命令获取:

$ kubectl get TYPE NAME -o yaml

请参考 这里 的模型,查找字段是否可变。

replace 命令执行后,对配置文件中缺少的对象的所有更改都将丢弃,所以不应更新与配置文件无关的资源类型。

要修改资源属性时,直接修改原 YAML 配置文件即可,然后执行 replace 命令更新配置文件中定义的对象(可以改副本数量、Label、镜像版本、端口,但是不能改名字,且如果改 Label 会创建新 Pod,但不会删除原 Pod)。注意,replace 操作会替换整个对象的配置,其他对象所做的更改都会丢失。

patch 修补

patch 命令直接在命令行中通过 -p 参数指定要执行修补替换的操作,可以精确操作,不影响之前通过 YAML 配置文件设置的参数,也不影响其他对象所做的修改。

声明式对象配置保留其他对象所做的更改,即使这些更改未记录到对象的配置文件中。所以需要使用 patch 操作来更新对象,而不能用 replace。

语法:

语法
$ patch (-f FILENAME | TYPE NAME) -p PATCH

更新 Node 节点示例:

kubectl patch node k8s-node-1 -p '{"spec":{"unschedulable":true}}'

更新 Pod 中容器名字及镜像:

kubectl patch pod valid-pod -p '{"spec":{"containers":[{"name":"kubernetes-serve-hostname","image":"new image"}]}}'

posted on 2018-05-06 17:53  kikajack  阅读(1394)  评论(0编辑  收藏  举报