一. 简介
Deployment是Kubernetes“控制器模式”的最基础的完整实现之一,但是却提供了Pod 的“水平扩展 / 收缩”(horizontal scaling out/in)的能力,这是非常核心的编排能力。
关于本文的代码,都在如下的地址中:Github资源
二. 核心
2.1 架构设计
Deployment 实际上是一个两层控制器。首先,它通过 ReplicaSet 的个数来描述应用的版本;然后,它再通过 ReplicaSet 的属性(比如 replicas 的值),来保证 Pod 的副本数量。
具体的deployment-replicaset-pod之间的关系可以如下图展示:
2.2 ReplicaSet
ReplicaSet 对象,其实就是由副本数目的定义和一个 Pod 模板组成的,其实就是是 Deployment 的一个子集。Deployment 的控制器,实际上控制的是 ReplicaSet 的数目,以及每个 ReplicaSet 的属性。
2.3 滚动更新
将一个集群中正在运行的多个 Pod 版本,交替地逐一升级的过程,就是“滚动更新”。这两个比例的值都是可以配置的,默认都是 DESIRED 值的 25%。
三. 创建Deployment流程
关于基于Deployment的自动扩容流程可以借助一个完整案例展示出来。
3.1 创建Depolyment YAML
定义一个 demo-deployment,它定义的 Pod 副本个数是 5 (spec.replicas=5)
。
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-deployment
labels:
app: nginx
spec:
replicas: 5
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.8.0
ports:
- containerPort: 80
3.2 水平扩展/收缩分析
ReplicaSet 负责通过“控制器模式”,保证系统中 Pod 的个数永远等于指定的个数(5 个)。 所以,Deployment 只允许容器的 restartPolicy=Always
的主要原因:只有在容器能保证自己始终是 Running 状态的前提下,ReplicaSet 调整 Pod 的个数才有意义。
Deployment 同样通过“控制器模式”,来操作 ReplicaSet 的个数和属性,进而实现“水平扩展 / 收缩”和“滚动更新”这两个编排动作。
- 水平扩展
Deployment Controller 只需要修改它所控制的 ReplicaSet 的 Pod 副本个数就可以了。比如,把这个值从 5 改成 6,那么 Deployment 所对应的 ReplicaSet,就会根据修改后的值自动创建一个新的 Pod。
- 水平收缩
按照上面流程按序反向删除即可。
3.3 创建
执行如下的创建指令即可:
kubectl apply -f demo-deployment.yaml --record
关于–record
参数,可与记录下每次操作所执行的命令,以方便后面查看。
3.4 检查Deployment状态
执行如下指令可以查看deployments的整体状态:
kubectl get deployments
# result
NAME READY UP-TO-DATE AVAILABLE AGE
demo-deployment 5/5 5 5 24s
关于图中的3种状态,详细含义如下:
- READY:分子代表当前处于 Running 状态的 Pod 的个数,分母代表用户期望的 Pod 副本个数(spec.replicas 的值)。
- UP-TO-DATE:当前处于最新版本的 Pod 的个数,所谓最新版本指的是 Pod 的 Spec 部分与 Deployment 里 Pod 模板里定义的完全一致。
- AVAILABLE:当前已经可用的 Pod 的个数,即:既是 Running 状态,又是最新版本,并且已经处于 Ready(健康检查正确)状态的 Pod 的个数。
所以, AVAILABLE 字段,描述的才是用户所期望的最终状态。
3.5 实时滚动状态
实时查看 Deployment 对象的状态变化,执行如下指令:
kubectl rollout status deployment/demo-deployment
关于真实的滚动状态,手速要快,执行完apply后立即查看,结果如下:
3.6 查看ReplicaSet状态
执行如下指令:
kubectl get rs
在用户提交了一个 Deployment 对象后,Deployment Controller
就会立即创建一个 Pod 副本个数为 5 的 ReplicaSet。这个 ReplicaSet 的名字,则是由 Deployment 的名字和一个随机字符串共同组成。
随机规则叫作pod-template-hash
,ReplicaSet 会把这个随机字符串加在它所控制的所有 Pod 的标签里,从而保证这些 Pod 不会与集群里的其他 Pod 混淆。
3.7 检查Events
为了产生事件,我在demo-deployment-v1.yaml
里面调整了nginx的版本,并执行创建了。所以执行如下指令,可以看到这5个Pod的滚动更新状态。
kubectl describe deployment demo-deployment
通过查看 Deployment 的 Events,看到这个“滚动更新”的流程:
像这样,将一个集群中正在运行的多个 Pod 版本,交替地逐一升级的过程,就是“滚动更新”。
3.8 ReplicaSet最终状态
执行如下指令,获取ReplicaSet的最终状态:
kubectl get rs
# result
NAME DESIRED CURRENT READY AGE
demo-deployment-5d59d67564 5 5 5 7m22s
demo-deployment-64c9d67564 0 0 0 13m
结果如下,旧 ReplicaSet(hash=64c9d67564)已经被“水平收缩”成了 0 个副本。
四. 拓展
4.1 历史回滚
我们可以通过kubectl rollout undo命令,就能把整个 Deployment 进行回滚。
检查历史版本
我需要使用 kubectl rollout history 命令,查看每次 Deployment 变更对应的版本。而由于我们在创建这个 Deployment 的时候,指定了–record 参数,所以我们创建这些版本时执行的 kubectl 命令,都会被记录下来。
指令如下:
kubectl rollout history deployment/demo-deployment
deployment.apps/demo-deployment
REVISION CHANGE-CAUSE
1 kubectl apply --filename=demo-deployment.yaml --record=true
2 kubectl apply --filename=demo-deployment-v2.yaml --record=true
结果如下图:
注意:关于这个结果,是从上向下查看,版本号从Index 1开始。
查看指定版本内容
根据上图,我们打算回查看版本1内容,所以我们执行如下指令:
kubectl rollout history deployment/demo-deployment --revision=1
具体内容如下:
回滚到指定版本
执行如下的回滚指令:
kubectl rollout undo deployment/demo-deployment --to-revision=1
Deployment Controller 还会按照“滚动更新”的方式,完成对 Deployment 的降级操作。
- 设置历史版本数量
spec.revisionHistoryLimit,就是 Kubernetes 为 Deployment 保留的“历史版本”个数。如果把它设置为 0,就不支持回滚操作。
4.2 发布方式区别
关于‘金丝雀发布’,‘蓝绿发布’和‘滚动发布’之间的区别,就以如下简单的总结。
- 金丝雀部署
金丝雀发布的好处在于可以用真实环境测试新版本,当新版本存在问题时最多只影响部分用户,且支持安全快速的回滚策略(将路由到新版本上的流量切换到其它的老版本机器上即可)
- 蓝绿发布
目的是减少因发布导致的服务中断时间,同时它也支持发布失败时的快速回滚。
蓝绿部署需要在发布过程中,同时运行两套程序。对硬件的要求也是当前所需的2倍,比如当前运行时,需要10台服务器支撑业务,那么使用蓝绿部署,你就需要购置20台服务器。
- 滚动升级
滚动发布能够解决掉蓝绿部署时对硬件要求增倍的问题。
所谓滚动发布,就是在发布过程中,并不一下子启动所有新版本,而是先启动一台新版本,再停止一台老版本,然后再启动一台新版本,再停止一台老版本,直到全部发布完成,这样的话,如果当前需要10台服务器支撑服务,那么升级过程中一共只需要11台就行了。
五. 总结
Kubernetes 项目对 Deployment 的设计,实际上是代替我们完成了对“应用”的抽象,使得我们可以使用这个 Deployment 对象来描述应用,使用 kubectl rollout 命令控制应用的版本。
关于Deployment是日常无状态应用中最常见的组件,使用很简单,但是灵活使用很难。毕竟,工具始终是死的,主要还是看个人的符合场景的架构设计。
转自:https://blog.wyatt.plus/archives/150