Kubernetes - Deployments

Table of Contents

Deployment 为 Pods 和 ReplicaSets 提供了声明式的更新。

你描述 Deployment 的 期望状态 ,然后 Deployment Controller 修改实际的状态到达期望的状态。 你可以定义 Deployments 来创建新的 ReplicaSet,或者删除已存在的 Deployments 然后根据新的 Deployments 分配资源。

1 使用场景

下面是 Deployments 典型用例:

  • 创建一个 Deployment 以 ReplicaSet 展开 ReplicaSet 在后台创建 Pods。检查部署的状态,查看是否成功。
  • 通过更新 Deployment 的 PodTemplateSpec 声明 Pods 的新状态 。将创建一个新的 ReplicaSet,Deployment 负责 以受控的速率将 Pod 从旧的 ReplicaSet 迁移到新的 ReplicaSet。每个新的 ReplicaSet 都会更新 Deployments 的修订版。
  • 如果当前的 Deployment 状态不是稳定的, 回滚到之前的 Deployment 修订版 。每次回滚都会更新 Deployments 的修订版。
  • 水平扩展 Deployment (以提升负载能力)。
  • 暂停 Deployment (以对 PodTemplateSpec 进行修改,然后恢复它开始新的部署)。
  • 使用 Deployment 状态 作为 rollout 暂停指示器。
  • 如果你不再需要 清空旧的 ReplicaSets

2 创建 Deployment

下面是 Deployment 的一个例子。它创建了一个 ReplicaSet 包含 3 个 nginx Pods:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80
  • Deployment 名字为 nginx-deployment ,由 .metadata.name 指定
  • Deployment 创建了 3 个 Pod 副本,由 replicas 指定
  • selector 字段定义了 Deployment 如何找到自己管理的 Pods。在本例中,使用 Pod 模板中的 app:nginx 标签。 然而,只要 Pod 模板本身满足该规则,就可以使用更复杂的选择规则。

    注意 matchLabels 是 map 类型的 {key, value} 对。简单的 {key, value} 等价于 matchExpressions , 表示键字段为 "key" 运算符为 "in",而值数组只包含 "value"。为了匹配,必须要满足 matchLabelsmatchExpressions 的所有要求。

  • template 字段包含如下的子字段:
    • 使用 labels 字段Pod 标记为 app: nginx
    • Pod 模板规范 .template.spec 字段,指示 Pods 运行的容器名字是 nginx 镜像是 nginx:1.14.2

命名格式

由 Deployment 关联生成的 ReplicaSet 和 Pods 的命名格式是这样的:

  • ReplicaSet: [DEPLOYMENT-NAME]-[RANDOM-STRING]
  • Pod: [RS-NAME]-[RANDOM-STRING]

RANDOM-STRING 是随机生成的,使用 pod-template-hash 作为种子(seed)。

在 Deployment Controller 中创建的 ReplicaSet 都会自动加上一个 pod-template-hash 标签。

3 更新 Deployment

注意 只有也仅只有 Deployment 的 Pod 模板(也就是 .spec.template )发生改变时,比如模板标签、容器镜像更新, 才会触发推出(rollout)。其它的更新,比如扩展实例不会触发。

更新 Deployment 时,它只会每次更新只更新一定数量的 Pods:

  • 最大不可用(unavailable) 默认,它确保至少有期望数量的 75% Pod 正常运行(最大 25% 不可用) 保证服务整体可用性
  • 最大浪涌(surge) 默认,它确保最大容纳所需 Pod 数量的 125%(25% 最大浪涌) 保证更新时的集群资源占用

而且,确保新的 Pod 创建之后,然后才删除旧的 Pod,在一(批)次完成之前,不会再新建 Pod 或者删除 Pod。/整体的更新比较平滑。/

更新 Deployment,并不会更新旧的 ReplicaSet,而是创建一个新的 ReplicaSet,更新过程:

  • 逐渐增加新的 RS 副本数量
  • 逐渐减少旧的 RS 副本数量

按照比例(受 max-unavailable 和 max-surge 约束)持续扩大和缩放新旧 RS,直到旧的 RS 副本数量为 0 ,新的副本数量为 Deployment 的 replica 为止,更新结束。

上面的整个更新过程,我们称之为滚动更新(rolling-update)。

3.1 滚动更新更新比例说明

滚动更新允许设置 maxSurgemaxUnavaible 来限制更新比例。上面已经说过了,

  • maxSurge 表示集群中最大的副本数
  • maxUnavaible 表示最大不可用的副本数。

看着简单,其实有点绕,举个例子。假设一个 Deployment 有 10 个副本,maxSurge=3 maxUnavaible=2 进行滚动更新,实际的限制为:

  • 集群中最大的副本数量为 13
  • 集群中至少要有 8 个副本可用

滚动更新会创建一个新的 RS,假设旧的名字为 nginx-deployment-1989198191 新的为 nginx-deployment-618515232

那么第一批更新的副本数为 13 - 8 = 5 。此时的 ReplicaSet 是这样:

NAME                          DESIRED   CURRENT   READY     AGE
nginx-deployment-1989198191   5         5         0         9s
nginx-deployment-618515232    8         8         8         1m

而此时的 Deployment :

NAME                 DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment     15        18        7            8           7m

等新的 Pod READY 之后,进行第二批的更新,以此类推。

可以看出包含 maxSurge 和 maxUnavaible 限制的滚动更新策略还是有些复杂的,实际更新数量的跟想像的可能不同。 实际使用过程中,可以简化这个策略: maxUnavaible=0 当最大不可用设置为 0 时,maxSurge 的含义就自动变成了每次更新的比例或者数量。

对应到上面的例子,第一批更新的副本数为 3,而 nginx-deployment-618515232 的DESIRED 依旧为 10。

按比例缩放

接上面的例子,如果在第一批更新时出现新的扩展需求,自动缩放器将 Deployment 副本增加到了 15。 控制器需要确定把新增的 5 个副本添加到何处。如果没有使用按比例缩放的话,会将新的 5 个全部加到新的 ReplicaSet 中。 通过按比例缩放,可以将其它副本分布在所有的 ReplicaSet 上。大比例的放到副本数多的 ReplicaSet, 较低比例的副本放到较少的 ReplicaSet。

4 回滚 Deployment

默认情况下,所有 Deployment 的 rollout 记录都会保存在系统中,保证任何时候你想回滚的时候都可以回滚(你可以修改修订 历史记录限制)。

通过 kubectl rollout history 可以查看修订历史,然后通过 kubectl rollout undo 来回滚。

注意 修订记录只有 Deployment rollout 才会创建修订记录,也就是说只有 Pod template 变更了之后才会记录(比如更新镜像地址)。 其它的更新,比如缩容扩容都不会触发。所以,回滚仅限于 Pod template 部分的更新。

但实际使用过程中,仅回滚 Pod template 部分是远远不够的,为了保证一致性,所有对于应用的操作都应该记录下来,这样的回滚才有意义。 所以,回滚功能还是比较鸡肋的。

5 弹性伸缩(Scaling)

Deployment 允许弹性伸缩(增加或者减少副本数量),命令是 kubectl scale

如果集群中开启了水平 Pod 自动缩放功能,你可以为 Deployment 设置自动缩放器,基于现有 Pod 的 CPU 利用率设置应用最小、 最大的副本个数。比如:

kubectl autoscale deployment.v1.apps/nginx-deployment --min=10 --max=15 --cpu-percent=80

注意 Deployment 弹性伸缩时不会创建新的 ReplicaSet,而是修改之前 ReplicaSet 的数量。

6 暂停和恢复 Deployment

当有多个更新的时候,先暂停,后修改,然后恢复可以声调很多次不必要的 rollout。

暂停命令: kubectl rollout pause

暂停之后修改 Pod template 不会产生新的修订版本。也就是说在 rollout history 中不包含这期间的修改记录。 而且已经暂停的 Deployment 不可以被回滚。

恢复命令: kubectl rollout resume

7 Deployment 状态

在 Deployment 的生命周期会有不同的状态。

7.1 进行中(Processing)

执行下面的操作时,Deployment 会被标记为 processing

  • Deployment 创建新的 ReplicaSet
  • Deployment 扩容它新的 ReplicaSet
  • Deployment 缩容它旧的 ReplicaSet
  • 新的 Pod 准备就绪或者可用

对于扩容缩容的说法其实有歧义,实际上不会创建新的 ReplicaSet,而是对已有的进行修改,不同于 Pod Template 中的字段

可以使用 kubectl rollout status 监控状态。

7.2 完成(complete)

当 Deployment 有下面的特点时,会被标记为 complete

  • 与 Deployment 关联的所有副本都已经更新为最新的版本,意味着所有的更新请求都已经完成了
  • 与 Deployment 关联的所有副本均可用;
  • 没有旧的副本正在运行。

kubectl rollout status 返回码为 0 表示 complete。

7.3 失败(Failed)

Deployment 更新未完成,更新卡住了。可能是以下因素引起的:

  • 资源配额不足
  • 可用探针(Readiness)失败
  • 镜像拉取失败
  • 权限不足
  • Limit ranges
  • 应用程序运行时配置错误

当出现问题时,可以用 kubectl describe deployment 查看 Conditions 中的输出来定位错误:

<...>
Conditions:
  Type            Status  Reason
  ----            ------  ------
  Available       True    MinimumReplicasAvailable
  Progressing     True    ReplicaSetUpdated
  ReplicaFailure  True    FailedCreate
<...>

也可以使用 kubectl get deployment ,包含更详细的信息。

还可以使用 kubectl get event 获取异常事件信息,Kubernetes 通过事件来记录异常。

8 清理策略

你可以设置 .spec.revisionHistoryLimit 字段设置旧的 ReplicaSet 最多保留多少份,多余的部分会被后台的垃圾收集回收掉。 默认情况下是 10。如果设置为 0,将会完全清除 Deployment 的所有历史记录,因此没法回滚。

9 金丝雀部署(灰度发布)

如果你想不同的版本给部分用户使用。你可以按照 管理资源 中的描述创建多个 Deployment,每个版本一个 Deployment。

灰度发布的 Kubernetes 实现方法是同一个应用多个 deployment。

10 书写 Deployment 规范

10.1 Pod Template

.spec.template.spec.selector.spec 唯二的必填字段。

.spec.template 是一个 Pod template,与 Pod 的书写规范相同。

除了 Pod 的必填字段外,Pod template 还必须指定适当的标签和合适的重启策略。

允许将 .spec.template.spec.restartPolicy 设置为 Always (只有 Deployment 允许),如果没有指定使用默认值。

10.2 Replicas

.spec.replicas 表示期望的副本数量,不填写默认是 1。

10.3 Selector

.spec.selector 是必填字段,用于标识此 Deployment 部署的 Pod。

.spec.selector 必须与 .spec.template.metadata.labels 匹配,否则会被 API 拒绝,而且是不可变的( apps/v1 )。

10.4 Strategy

.spec.strategy 指定新 Pod 替换旧 Pod 的策略。 .spec.strategy.type 可以是 "Recreate" 或者 "RollingUpdate", 默认是 "RollingUpdate"。

ReCreate
重新创建:先把所有的副本的杀掉,然后重新创建 .spec.strategy.type==Recreate
RollingUpdate

滚动更新 .spec.strategy.type==RollingUpdate 此时,你可以设置:

  • maxUnavailable .spec.strategy.rollingUpdate.maxUnavailable 可选的,用来指定最大不可用数量。 其值可以是数值或者百分比,数值是通过百分比四舍五入中得出的。默认值是 25%。
  • maxSurge .spec.strategy.rollingUpdate.maxSurge 可选的,用来表示更新过程中最大的副本数。 其值可以是数值或者百分比,数值是通过百分比四舍五入中得出的。默认值是 25%。如果是 25%,那么最大的副本数为 125%。

注意: maxUnavaible 和 maxSurge 不可以同时为 0。

10.5 Progress Deadline Seconds

.spec.progressDeadlineSeconds 可选的字段,用来指定等待部署进度,然后系统报告 Deployment 失败的秒数。 最终以 Type=Progressing, Status=False 表达出来,且在资源状态中 Reason=ProgressDeadlineExceeded 。 Deployment 控制器将继续重试部署。在未来,等自动回滚实现了,Deployment 在遇到这种情况时会立刻自动回滚。

如果指定了,该字段的值需要大于 .spec.minReadySeconds 的值。

10.6 Min Ready Seconds

最小就绪(ready)秒, .spec.minReadySeconds 是一个可选的字段,用来表示新创建的 Pod 在容器不会崩溃的的情况下准备就绪 所需要的秒数,会视为可用。默认是 0(准备就绪后,该 Pod 既被视为可用)。

10.7 Revision History Limit

.spec.revisionHistoryLimit 可选字段,表明要保留的允许回滚的旧的 ReplicaSets 数量。这些旧的 ReplicaSets 会消耗 etcd 中的资源,在 kubectl get rs 中聚合。默认保存 10 个,但其理想值取决于部署频率和稳定性。 值为 0 表示所有的旧 RS 都会被清除。

10.8 Paused

.spec.paused 是一个可选的字段,用于暂停和恢复 Deployment。

First created: 2020-04-07 11:51:47
Last updated: 2020-04-08 Wed 11:43
Power by Emacs 26.3 (Org mode 9.3.6)