我们说了一个重要的设计思想 控制器模式
那么按照这个思想,我们介绍第一个控制器思想完整实现 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项目,提供了一些有状态应用的管理
思考题
听说过金丝雀发布和蓝绿发布吗?
听上面讲解
金丝雀发布:先发布一台机器或者少量机器,做流量验证,如果新版没问题,则将剩余机器全部更新
蓝绿部署,实现准备一台机器全部更新,然后将所有流量切换到更新好的机器上