云计算之k8s系列_第十二回

上一回讲解了控制器,这一回详细看看控制器中Deployment控制器,k8s中,Deployment实现了一个非常重要的功能:pod的水平扩展与收缩。如果我们更新了Deployment的pod模板,那么deployment就需要”滚动更新(rolling update)“,来升级现有的容器。

上述功能依赖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

从上可以看出,一个ReplicaSet对象,其实就是由副本数目的定义和一个pod模板组成的。它的定义其实是Deployment的一个子集。Deployment控制器实际操作的,正是这样的ReplicaSet对象,而不是Pod对象。

接下来,我们一起看看Deployment这个YAML文件:

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

可以看到,这就是一个我们常用的nginx-deployment,他定义的Pod副本个数是3(spec.replicas=3)。

那么,在具体的实现上,这个Deployment,与ReplicaSet,以及Pod的关系是怎样的呢?Deployment -->ReplicaSet(v1)--->Pods,通过箭头,我们可以看到,一个定义了replicas=3的Deployment,与它的ReplicaSet,以及Pod的关系,实际上是一种“层控制”的关系。

其中,ReplicaSet负责通过“控制器模式”,保证系统中Pod的个数永远等于指定的个数。这也正是Deployment只允许容器的restartPolicy=Always的主要原因:只有在容器能保证自己始终是Running的状态的前提下,ReplicaSet调整Pod个数才有意义。

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

其中,“水平扩展/收缩”非常容易实现,Deployment Controller只需要修改它所控制的ReplicaSet的Pod副本个数就可以了。ReplicaSet就会根据修改后的值自动创建一个新的Pod。这就是“水平扩展”,反之,则是“水平收缩”。使用kubectl scale就可以实现这个操作。

$ kubectl scale deployment nginx-deployment --replicas=4
deployment.apps/nginx-deployment scaled

接下来,看看“滚动更新”的过程,

$ kubectl create -f nginx-deployment.yaml --record

这边添加了--record参数:作用是记录下你每次操作所执行的命令,方便后面查看。然后查看一下状态信息:

$ kubectl get deployments
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3         0         0            0           1s

在返回结果中,我们可以看到四个状态字段:

1. DESIRED: 用户期望的Pod副本个数(spec.replicas);
2. CURRENT: 当前处于running状态的Pod个数;
3. UP-TO-DATE:当前处于最新版本的Pod的个数,Pod的spec部分与Deployment里Pod模块里定义的完全一致;
4. AVAILABLE:当前已经可用的Pod的个数,即:Running状态,又是最新版本,并且已经处于ready(健康检查正确)状态的Pod的个数。

可以看到,只有这个AVAILABLE字段,描述的才是用户所期望的最终状态。

而k8s项目还为我们提供了一条指令,让我们可以实时查看Deployment对象的状态变化。这个指令就是kubectl rollout status:

$ kubectl rollout status deployment/nginx-deployment
Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
deployment.apps/nginx-deployment successfully rolled out

在这个返回结果中,“2 out of 3 new replicas have been updated”意味着已经有2个Pod进入了UP-TO-DATE状态。过一会儿,我们就能看到这个Deployment的3个Pod,就进入到了AVAILABLE状态:

NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3         3         3            3           20s

此时,我们可以查看这个Deployment所控制的ReplicaSet:

$ kubectl get rs
NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-3167673210   3         3         3       20s

从上面结果可知,在用户提交了一个Deployment对象后,Deployment Controller 就会立即创建一个Pod副本个数为3的ReplicaSet。这个ReplicaSet的名字,由Deployment的名字和一个随机字符串共同组成。这个随机字符串叫作pod-template-hash,如我们这里的3167673210。ReplicaSet把这个随机字符串加在它所控制的所有Pod的标签里,从而保证这些Pod不会与集群里的其他Pod混淆。

而ReplicaSet的DESIRED、CURRENT和READY字段的含义,和Deployment中是一致的。所以,相比之下,Deployment只是在ReplicaSet的基础上,添加了UP-TO-DATE这个跟版本有关的状态字段。

这个时候,如果我们修改了Deployment的Pod模板,“滚动更新”就会被自动触发。

修改Deployment有很多方法。比如,我们可以用kubectl edit 指令编辑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

kubectl edit 这个命令只不过是把API对象内容下载到本地修改后再提交上去。kubectl edit保存退出,k8s就会立刻触发“滚动更新”的过程。可以用kubectl rollout status 查看nginx-deployment的状态变化:

$ kubectl rollout status deployment/nginx-deployment
Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
deployment.extensions/nginx-deployment successfully rolled out

这时,你可以通过查看Deployment的Events,看到这个“滚动更新”的流程:

$ kubectl describe deployment nginx-deployment
...
Events:
  Type    Reason             Age   From                   Message
  ----    ------             ----  ----                   -------
...
  Normal  ScalingReplicaSet  24s   deployment-controller  Scaled up replica set nginx-deployment-1764197365 to 1
  Normal  ScalingReplicaSet  22s   deployment-controller  Scaled down replica set nginx-deployment-3167673210 to 2
  Normal  ScalingReplicaSet  22s   deployment-controller  Scaled up replica set nginx-deployment-1764197365 to 2
  Normal  ScalingReplicaSet  19s   deployment-controller  Scaled down replica set nginx-deployment-3167673210 to 1
  Normal  ScalingReplicaSet  19s   deployment-controller  Scaled up replica set nginx-deployment-1764197365 to 3
  Normal  ScalingReplicaSet  14s   deployment-controller  Scaled down replica set nginx-deployment-3167673210 to 0

当你修改Deployment里的Pod定义之后,Deployment Controller 会使用这个修改后的Pod模板,创建一个新的ReplicaSet(has=1764197365),这个新的ReplicaSet的初始Pod副本数是:0。

然后,在Age=24s的位置,Deployment Controller开始将这个新的ReplicaSet所控制的Pod副本数从0个变成1个,即水平扩展出一个副本。在Age=22s的位置,Deployment Controller又将旧的ReplicaSet(hash=3167673210)所控制的旧Pod副本数减少一个,即水平收缩成两个副本。如此交替进行,新ReplicaSet管理的Pod副本数,从0个变成1个,再变成2个,最后变成3个。而旧的ReplicaSet管理的Pod副本数则从3个变成2个,再变成1个,最后变成0个。这样,就完成了这一组Pod的版本升级过程。将一个集群中正在运行的多个Pod版本,交替地逐一升级的过程,就是“滚动更新”。 

在这个“滚动更新”过程完成之后,可以查看一下新旧两个ReplicaSet的最终状态:

$ kubectl get rs
NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-1764197365   3         3         3       6s
nginx-deployment-3167673210   0         0         0       30s

其中,旧ReplicaSet(hash=3167673210)已经被“水平收缩”成了0个副本。

滚动更新的好处:在升级刚开始的时候,集群中只有1个新版本的Pod。如果这时,新版本Pod有问题启动不起来,那么“滚动更新”就会停止,从而允许开发和运维人员介入。而在这个过程中,由于应用本身还有两个旧版本的Pod在线,所以服务并不会受到太大的影响。

当然,这也就要求你一定要使用Pod的Health Check机制检查应用的运行状态,而不是简单地依赖于容器Running状态。要不然的话,虽然容器已经变成Running了,但服务很有可能尚未启动,“滚动更新”的效果也就达不到了。

而为了进一步保证服务的连续性,Deployment Controller还会确保,在任何时间窗口内,只有指定比例的Pod处于离线状态。同时,它也会确保,在任何时间窗口内,只有指定比例的新Pod被创建出来。这两个比例的值都是可以配置的,默认都是DESIRED值的25%。

所以,在上面这个Deployment中,它有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

在上面这个RollingUpdateStrategy的配置中,maxSurge指定的是除了DESIRED数量之外,在一次“滚动”中,Deployment控制器还可以创建多个新Pod;而maxUnavailable指定的是,在一次“滚动”中,Deployment控制器可以删除多少个旧Pod。

同时,这两个配置还可以用前面我们介绍的百分比形式来表示,比如:maxUnavailable=50%,指的是我们最多可以一次删除“50%*DESIRED 数量”个Pod。

现在我们可以扩展一下上面的箭头图了:

          Deployment
               |
      _________|________
     |                 |
    ReplicaSet(v1)    ReplicaSet(v2)
     |                 |
    pods              pods

Deployment的控制器,实际上控制的是ReplicaSet的数目,以及每个ReplicaSet的属性。

而一个应用的版本,对应的正是一个ReplicaSet;这个版本应用的Pod数量,则是由ReplicaSet通过它自己的控制器(ReplicaSet Controller)来保证。通过这样的多个ReplicaSet对象,k8s项目就实现了对多个“应用版本”的描述。也就是说,应用版本和ReplicaSet是一一对应的。

Deployment对应用进行版本控制的具体原理:

这一次使用kubectl set image直接修改nginx-deployment所使用的的镜像。这一次写一个错误的镜像名字,这样,Deployment就会出现一个升级失败的版本。

$ kubectl set image deployment/nginx-deployment nginx=nginx:1.91
deployment.extensions/nginx-deployment image updated

因为nginx:1.91镜像在docker hub中并不存在,所以这个Deployment的“滚动更新”被触发后,会立刻报错并停止。这时看一下ReplicaSet的状态:

$ kubectl get rs
NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-1764197365   2         2         2       24s
nginx-deployment-3167673210   0         0         0       35s
nginx-deployment-2156724341   2         2         0       7s

通过返回结果可以看出,新版本的ReplicaSet(hash=2156724341)的“水平扩展”已经停止。而且此时,它已经创建了两个Pod,但是他们都没有进入READY状态。这当然是因为这两个Pod都拉取不到有效的镜像。与此同时,旧版本的ReplicaSet(hash=1764197365)的“水平收缩”,也自动停止了。此时,已经有一个旧Pod被删除,还剩下两个旧Pod。那么如何让这个Deployment的3个Pod,都回滚到以前的就版本呢?

我们只需要执行一条kubectl rollout undo命令,就能把整个Deployment回滚到上一个版本:

$ kubectl rollout undo deployment/nginx-deployment
deployment.extensions/nginx-deployment

这个过程,就是让旧的ReplicaSet再次“扩展”到3个Pod,新的ReplicaSet重新“收缩”到0个Pod。那么,如果我想回滚到更早之前的版本,要怎么办呢?

首先,我们需要使用kubectl rollout history命令,查看每次Deployment变更对应的版本。而由于我们在创建这个Deployment的时候,指定了--record参数,所以我们创建这些版本时执行的kubectl命令,都会被记录下来:

$ kubectl rollout history deployment/nginx-deployment
deployments "nginx-deployment"
REVISION    CHANGE-CAUSE
1           kubectl create -f nginx-deployment.yaml --record
2           kubectl edit deployment/nginx-deployment
3           kubectl set image deployment/nginx-deployment nginx=nginx:1.91

可以看到,我们前面执行的创建和更新操作,分别对应了版本1和版本2,而那次失败的更新操作,则对应的是版本3。这个命令还可以看API对象的细节:

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

然后,我们就可以在kubectl  rollout  undo 命令行最后,加上要回滚到的指定版本的版本号,就可以回滚到指定版本了:

 

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

这样,Deployment Controller还会按照“滚动更新”的方式,完成对Deployment的降级操作。

不过,你可能已经想到了一个问题:我们对Deployment进行的每一次更新操作,都会生成一个新的ReplicaSet对象,是不是有些多余?

没错,是多余的,所以,k8s项目还提供了一个指令,使得我们对Deployment的多次更新操作,最后只生成一个ReplicaSet。在更新Deployment之前,要先执行一条kubectl rollout pause指令。

$ kubectl rollout pause deployment/nginx-deployment
deployment.extensions/nginx-deployment paused

上面命令是让这个Deployment进入了一个“暂停”状态。接下来,可以随意使用kubectl edit 或者kubectl set image命令,修改这个Deployment的内容了。由于此时Deployment正处于“暂停”状态,所以我们对Deployment的所有修改,都不会触发新的“滚动更新”,也不会创建新的ReplicaSet。当修改操作完成后,只需要执行一条kubectl rollout resume指令,就可以把这个Deployment“恢复”过来:

$ kubectl rollout resume deploy/nginx-deployment
deployment.extensions/nginx-deployment resumed

在resume和pause之间,我们对Deployment进行的所有修改,最后只会触发一次“滚动更新”。可以检查ReplicaSet状态的变化,来验证一下kubectl rollout pause 和kubectl rollout resume 指令的执行效果:

$ kubectl get rs
NAME               DESIRED   CURRENT   READY     AGE
nginx-1764197365   0         0         0         2m
nginx-3196763511   3         3         3         28s

通过返回结果,我们可以看到,只有一个hash=3196763511的ReplicaSet被创建出来。不过,即使我们小心控制ReplicaSet的生成数量,随着应用版本的不断增加,k8s中还是会为同一个Deployment保存很多很多不同的ReplicaSet。那么,我们又该如何控制这些“历史”ReplicaSet的数量呢?

很简单,Deployment对象有一个字段,叫作spec.revisionHistoryLimit,就是k8s为Deployment保留的“历史版本”个数。所以,如果把它设置为0,你就再也不能做回滚操作了。

总结:

综上所述,我们应该了解到:

1. Deployment实际上是一个两层控制器。首先,它通过ReplicaSet的个数来描述应用的版本;然后,通过ReplicaSet的属性(如replicas的值),来保证Pod的副本数量。

2. 我们可以使用这个Deployment对象来描述应用,使用kubectl rollout命令控制应用的版本。

在实际使用场景中,应用发布的流程往往是千差万别,可能有定制化需求,各个Pod之间有关联,哪些Pod能下线,不能随便选择,那么这种场景,单靠Deployment就很难实现了。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
云计算K8s运维作为毕业设计项目,可以设计和实现一套完整的Kubernetes集群的运维解决方案。具体的内容可以包括以下几个方面: 1. 集群架构设计:设计一个高可用、可扩展的K8s集群架构,包括Master节点和Worker节点的数量和配置、网络拓扑、负载均衡等,并考虑安全性、可靠性和性能方面的要求。 2. 部署和管理工具:选用适合的部署和管理工具,如Kubernetes自带的工具(kubeadm、kubelet、kubectl)、Helm、Kops等,设计并实现集群的快速部署、节点管理、服务编排等功能,简化运维流程。 3. 高可用和故障处理:设计并实现K8s集群的高可用解决方案,包括Master节点的冗余备份、节点故障自动恢复、服务自动重启等机制,确保集群具备高可用性;同时,也需设计并实现故障自愈的监控和告警系统,能够及时发现和处理集群中的异常情况。 4. 系统监控和日志管理:实现集群的实时监控和日志管理功能,包括节点资源的监控、应用服务的运行状态监控、日志的收集与分析等,便于运维人员对集群进行性能评估、故障排查和优化。 5. 安全策略与权限管理:设计并实现K8s集群的安全策略和权限管理,包括集群的网络隔离、安全升级、访问控制等,保障集群和应用的安全性。 6. 弹性和自动化扩展:实现集群的弹性扩展功能,根据负载情况自动进行节点的动态扩缩容,并实现自动集群的水平扩展,提高集群的资源利用率和整体性能。 7. 持续集成和部署:设计并实现集群的持续集成和持续部署解决方案,结合Jenkins等工具,实现代码构建、打包、镜像构建和部署的自动化流程,提高开发和部署效率。 总之,云计算K8s运维的毕业设计可以从架构设计、部署管理、高可用故障处理、监控日志、安全策略、弹性扩展以及持续集成等方面来展开,通过设计和实现一套完整的Kubernetes集群运维解决方案,提高云原生应用的可靠性、安全性和可扩展性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值