vlambda博客
学习文章列表

详解 K8s 作业副本控制器 Deployment

1. 引言

上一篇文章中,我们详细介绍了 Kubernetes 中的 Pod 的原理和配置:


我们看到,Pod 仅仅是对若干容器进行的封装和加强,在实际的场景下,光是有 Pod 是不够的,我们还需要考虑 Pod 之间的相互关系,这个时候,我们就需要更高一层的抽象,这就是 Kubernetes 中的控制器思想,本文,我们就来详细介绍一下 Kubernetes 中最基本的控制器 -- Deployment。

2. 控制器

在 Kubernetes 中,有着许多的控制器组件,他们都是由 kube-controller-manager 组件管理的,包括:

  1. deployment

  2. job

  3. cronjob

  4. podautoscaller

  5. cloud

  6. volume

  7. replication

  8. ...

控制器在配置时,我们会通过 spec 字段定义我们希望的期望状态。

控制器通过“控制编排模式”让集群最终达到期望状态。所谓的“控制编排模式”主要就是定时执行以下步骤:

  1. 统计当前实际状态;

  2. 对比实际状态与期望状态;

  3. 如果实际状态与期望状态不符,则对实际状态进行调整,以达期望状态。

3. 控制器的配置

下面就是一个典型的 Deployment 的配置:

详解 K8s 作业副本控制器 Deployment

  • 需要注意的是,Deployment 中的容器必须配置 restartPolicy=Always

4. 作业副本与水平扩展

从 PaaS 时代开始,水平扩展/收缩都是容器编排平台必须具备的功能。在 Kubernetes 中也自然是如此,但 Kubernetes 中,容器的水平扩展与收缩是通过 ReplicaSet 对象实现的。

ReplicaSet 具有两个基本属性:副本数与 Pod 模板,这是一个典型的 ReplicaSet 示例:

apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: nginx-set
labels:
  app: nginx
spec:
replicas: 3
selector:
  matchLabels:
    app: nginx
template:
  metadata:
    labels:
      app: nginx
  spec:
    containers:
    - name: nginx
      image: nginx:1.7.9

而 Deployment 对集群的控制正是通过 ReplicaSet 实现的:

当用户修改副本数后,Deployment 就会与其控制的 ReplicaSet 交互,ReplicaSet 得到新的副本数后,通过对它所控制的 Pod 进行增减从而实现水平扩展或收缩。

5. 滚动更新

当用户修改 Pod 模板,就会自动触发 Deployment 的滚动更新。

Deployment 的滚动更新是通过创建一个新的 ReplicaSet,并且周期性的在旧的 ReplicaSet 中删除 Pod 节点,在新的 ReplicaSet 中创建新的 Pod 节点实现的。

通过配置 Deployment 的 spec.strategy 中的 RollingUpdateStrategy 可以设置滚动更新的策略:

spec:
minReadySeconds: 5
strategy:
  type: RollingUpdate
  rollingUpdate:
    maxUnavailable: 1
    maxSurge: 1
replicas: 2
revisionHistoryLimit: 10
template:
...

maxSurge 指定了除了用户期望创建的 Pod 外,在一次滚动更新中,Deployment 控制器最多可以创建的新 Pod 节点数;maxUnavailable 指的是一次滚动更新中 Deployment 最多可以删除的旧 Pod 节点数。我们可以配置百分比,例如 maxUnvailable: 50%,表示一次最多可以删除期望数量的 50%。

通过滚动更新,新版本的 ReplicaSet 中所有 Running 状态的节点数最终达到用户期望,而旧版本的 ReplicaSet 中的 Pod 节点则全部被删除后,滚动更新就这样完成了。

而最终,我们看到,一个应用的版本与 ReplicaSet 的版本是一一对应的。

6. 滚动更新的回滚

通过 kubectl rollout undo 命令就可以实现滚动更新的反向操作 -- 回滚:

$ kubectl rollout undo deployment/nginx-deployment --to-version=2

可想而知,Deployment 必须保存相应的历史版本的 ReplicaSet 才能够实现对应版本的回滚,但保存所有历史版本往往是不现实的,Deployment 支持通过 spec.revisionHistoryLimit 字段指定保留的历史版本数,特殊的,如果它设置为 0,就相当于关闭了回滚功能。