我们说了一个重要的设计思想 控制器模式

那么按照这个思想,我们介绍第一个控制器思想完整实现 Deployment

Deployment实现了K8S的基础功能,就是对Pod的水平扩展/收缩 这个功能,是从PAAS时代开始,一个平台及项目必须要有的编排能力

我们更新了Deployment的Pod模板,那么Deployment会按照一种滚动更新(rolling update)的方式,来升级现有的容器

这个能力的实现,依赖于另一种重要的API对象,ReplicaSet

ReplicaSet的结构很简单,我们通过YAML文件来直接查看

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一样,就是一个Deployment的子集

但是Deployment控制器实际操控的,正是这样的ReplicatSet对象,而不是Pod对象

同样的,一个Pod的实际ownerReference其实就是ReplicaSet

那么再回过头去看那个Deployment

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.7.9

ports:

– containerPort: 80

那我们看一下Pod副本数在其中定义的是3个,spec.replicas=3

具体的Deployment和ReplicaSet以及Pod的关系如下所示

图片

这样就是一种层层控制的关系

ReplicaSet通过控制器模式,保证系统中Pod的个数等于指定的个数

也正是如此,所以Deployment只允许设置容器的restartPolicy=Always

只有保持自己为Running的状态前提下,ReplicaSet调整Pod的个数才有意义

而在此基础上,Deployment可以通过控制器模式,来操作ReplicaSet的个数和属性,进而实现水平扩展/收缩 滚动更新的编排动作

对于水平扩展/收缩很容易实现,Deployment Controller只需要修改所控制的ReplicaSet的Pod个数就可以了

比如将上面的值从3改为4,那么Deployment所对应的ReplicaSet,就会根据修改的值去创建一个新的Pod,这就是水平扩展

比如使用指令kubectl scale deployment nginx-deployment –replicas=4

然后其次是滚动更新,我们依次讲解这个流程

kubectl create -f nginxk8s.yaml –record

然后查看这个deployment的状态

kubectl get deployment

图片

上面的有四个字段

DESIRED:用户期望的Pod副本个数

CURRENT 当前处于Running的Pod个数

UP-TO-DATE:处于最新版本的个数,就是最新版本要求的镜像个数

AVAILABLE:当前可用的Pod个数,就是即是Running状态,又是最新版本,并且处于Ready状态的Pod个数

所以,我们最后要满足AVAILABLE字段的要求

而且,Kubectl指令可以帮助我们看Deployment对象的状态,

kubectl rollout status

例如

kubectl rollout status deployment/nginx-deployment

deployment “nginx-deployment” successfully rolled out

然后查看对应的的ReplicaSet的状态

在这个Rs中,是由Deployment的名字和一个随机字符串共同组成的

这个随机字符串还会加在这个RS控制下的所有Pod中

保证这些Pod不会和集群中其他的Pod混淆

那么,如果我们修改了Deployment的Pod模板,滚动更新就会被触发

修改可以直接去考虑编辑Etcd中的API对象

$ kubectl edit deployment/nginx-deployment

spec:

containers:

– name: nginx

image: nginx:1.9.1 # 1.7.9 -> 1.9.1

ports:

– containerPort: 80

deployment.extensions/nginx-deployment edited

这个命令会打开ETCD中的对象Yaml,这样我们将其修改了镜像版本

保存退出,就会触发滚动更新的流程

我们通过kubectl rollout status查看nginx-deployment的状态变化

图片

然后看下整体的Deployment的Events,整个的滚动更新的流程如下

图片

当修改了这个里面的Pod定义,首先会创建一个新的Pod,然后Deployment Controller会使用这个修改后的Pod模板,创建一个新的ReplicaSet,新的RS的初始副本数是0

然后会新增一个Pod,然后减少一个Pod

然后重复上述流程

直到全部替换完成

完成了一组的Pod版本升级流程

这样滚动更新完成了,可以看一下存在的ReplicaSet

旧的版本已经被水平收缩为0个副本

而且,为了保证更新过程中,服务的可用,Deploymeng Controller还能保证,在任何时间内,只有指定比例的Pod可以离线,而且也是只有指定比例的新Pod,会被创建出来,这两个比例的值都是坑以配置的,都是DESIRED值的25%

那么就是在3个Pod滚动刷新的过程中,保证至少有2个Pod处于可用状态,至多有4个Pod同时在集群中,这个策略是Deployment对象的一个字段

RollingUpdateStrategy

apiVersion: apps/v1

kind: Deployment

metadata:

name: nginx-deployment

labels:

app: nginx

spec:

strategy:

type: RollingUpdate

rollingUpdate:

maxSurge: 1

maxUnavailable: 1

上面配置的rollingUpdate中的字段

maxSurge指的是在一次滚动中,Deployment中还能创建多少个Pod

maxUnavailable指的是,再一次滚动中,还能删除多少个旧的Pod

这两个配置可以用我们说的百分比来进行配置

这样,我们的Deployement ReplicaSet和Pod的关系图如下

图片

因为Deployment的控制器,本质上控制的是RS的数目和RS的属性

每一次修改了应用,都会产生一个新的RS,这个版本应用的POD数量,则和RS通过RS Controller来进行保证

这样,就实现了对多个应用版本的描述

这样,应用版本和ReplicaSet对应后,我们说下版本控制的具体流程

我们使用一个kubectl set image的指令,修改nginx-deployment所使用的镜像,这样,就可以不用像kubectl edit那样打开编辑器

我们尝试将镜像的名称修改为一个错误的值

kubectl set image deployment/nginx-deployment nginx=nginx:1.91

但是这个镜像并不存在,所以这个滚动更新会被报错并停止

然后我们检查一下ReplicaSet的状态,

图片

这时候我们看到,创建第一个镜像就已经失败了

但没有影响到旧版本

那么我们如何回滚到旧版本呢?

只需要指定一条kubectl rollout undo命令,就能将整个Deployment进行回滚

其实就是将上一个RS进行重新扩展,这一个RS进行收缩为0个

如果是更加早的版本,需要怎么办呢?

可以使用kubectl rollout history命令,查看每次Deployment对应的版本,由于创建的时候指定了-record,我们修改版本的kubectl命令,都会被记录下来,这些操作的输出

[root@dev-code2 app]# kubectl rollout history deployment/nginx-deployment

deployments “nginx-deployment”

REVISION  CHANGE-CAUSE

1         kubectl create –filename=nginxk8s.yaml –record=true

2         kubectl create –filename=nginxk8s.yaml –record=true

3         kubectl create –filename=nginxk8s.yaml –record=true

这样,我们每次修改的命令都对应着一个版本

那么我们可以查看每一个版本的详细信息.

kubectl rollout history deployment/nginx-deployment –revision=2

然后,我们就可以回滚到指定版本了

回滚命令如下

$ kubectl rollout undo deployment/nginx-deployment –to-revision=2

deployment.extensions/nginx-deployment

这个降级操作,也是滚动更新

但是,我们每一次的更新操作,都会生成一个RS对象,是不是有点浪费资源呢?

我们可以使用

kubectl rollout pause命令和kubectl rollout resume命令来进行,一次RS中,暂停和开启修改

最后说下如何控制历史版本RS的数量呢?

在Deployment之中,有一个字段,叫做spec.revisionHistoryLimit,这个字段可以用来控制Deployment的历史版本的个数,将其设置为0,就不能做回滚操作了

总结一下,我们详细讲解了Deployment这个K8s中基本的编排控制器的实现原理和使用方式

Deployment实际上是一个两层控制器,其控制RS的个数来描述对应的版本,然后通过RS来保证Pod的副本个数

不过,Deployment利用RS的不同来实现了版本控制,使得我们可以使用这个Deployment来描述应用,使用kubectl rollout实现应用的版本

但是,实际的场景中,很多的场景不能简单的回滚,有些Pod的上下线,不是随便控制的

于是Kubernetes项目,提供了一些有状态应用的管理

思考题

听说过金丝雀发布和蓝绿发布吗?

听上面讲解

金丝雀发布:先发布一台机器或者少量机器,做流量验证,如果新版没问题,则将剩余机器全部更新

蓝绿部署,实现准备一台机器全部更新,然后将所有流量切换到更新好的机器上

发表评论

邮箱地址不会被公开。 必填项已用*标注