极简容器化交付 | 部署组件分析

之前给大家讲了构建镜像的原理,那么有了镜像之后,我们怎么样能将它快速的部署到kuberentes集群上呢? 

早期,大家都习惯于使用kubernetes接口,或者cli命令行来完成这些操作,但是yaml文件语法的复杂性、大量容器和kuernetes的概念,让人难以理解,无疑成为容器化交付路上的又不障碍。

为了解决这些问题,华为云容器服务推出了向导式镜像部署,通过一步步引导、对复杂概念的屏蔽或抽象,在一定程度上降低了用户首次部署镜像的难度,但是灵活度相对较差,对于经常变更版本的场景,还是需要使用原生接口进行操作,使得整体研发交付流程的复杂度并未降低太多。

鉴于此现状华为云容器团队又推出了一款易用性更好、自动化程度更高的服务产品——容器交付流水线,它以容器技术为基础,践行DevOps的理念,围绕容器业务端到端场景提供持续集成和持续交付能力,包括代码编译、镜像构建与交付、自动化部署、升级回滚等能力,并提供全量能力接口,便于与企业已有流程相结合,同时接口屏蔽底层容器概念,对接口进行了二次封装,接口定义更贴近于CI/CD业务概念,使得熟悉CI/CD流程的用户能快速切换。

发布组件作为该产品的一个重要组成部分,其直接影响了整个发布周期,我们今天就跟大家一起聊聊该部件的一些实现原理。

01

Kubernetes Deployment

Kubernetes Deployment提供了官方的用于更新Pod和ReplicaSet(下一代的Replication Controller)的方法,我们可以在Deployment对象中填写我们需要用到的配置和版本信息,Deployment控制器将现在的实际状态转换成我们所期望的状态,例如,将nginx:v1.0升级成nginx:v2.0,我们只需创建一个Deployment,Kubernetes会按照Deployment自动进行升级。而随着Kubernetes的迭代更新,目前云容器引擎服务提供了几个版本的集群,那么如何使得部署组件支持不同的集群版本呢?由于我们的CI/CD的工具提供了deployment的yaml页面编辑,部署组件会根据deployment中apiversion即:apps/v1, apps/v1beta1, extensions/v1beta1。提供不同版本的API接口。自行封装接口使得发布组件自主能动性强,免于受制于第三方库。

02

如何判断发布成功?

解决了版本问题,还有一个最重要的问题,是如何判断组件发布结果呢?对于一个CI/CD工具,我们判断工作负载运行成功的标准并不仅仅是Pod处于running状态又或者工作负载处于可用状态。我们需要保证的是工作负载运行的镜像或者配置是新版本的。因此我们判断成功的标志应该是新起的pod处于running状态,那如何找到这些新起的pod呢?下图展示了Deployment,ReplicaSet和Pod之间的关系,以无状态工作负载为例,我们通过查询deployment中Annotations的"deployment.kubernetes.io/revision",根据这个revision寻找Deployment控制的ReplicaSet。最后根据ReplicaSet的label去寻找这些新起的pod。我们已经找到这些新起的Pod了,那么现在就需要对pod的状态进行分析了。

0130_14.jpg

在K8s源码中PodPhase字段表示了pod的不同阶段:

  • Pending: k8s已经接受创建pod的请求,但是容器并没有启动成功,这个阶段包括调度之前的,下载镜像等。 

  • Running: pod已经绑定到node节点,并且所有的容器已经启动成功,或者至少有一个容器在运行,或者在重启中。 

  • Succeeded: pod中的所有的容器已经正常的自行退出,并且k8s永远不会自动重启这些容器。 

  • Failed: pod中的所有容器已经终止,并且至少有一个容器已经终止于失败(退出非零退出代码或被系统停止)。 

  • Unknown: 由于一些未知的原因,无法获得pod的状态,通常是因为pod的主机通信错误。

而以上四个阶段只是一个粗略状态阶段。而对于每一个阶段都有更详细的pod conditions信息,podcondition数组的元素都包含了类型和状态字段,这个类型分为以下四种:

  • "Ready":Pod能够提供服务

  • "PodScheduled":pod正处于调度中

  • "Unschedulable":调度程序现在无法调度Pod,例如由于缺乏资源或其他限制;

  • "Initialized":所有pod的容器初始化已经完成。

  • "ContainersReady":pod中的容器都已准备好。

其中状态字段用"true" 表示处于,"false"表示不处于,我们发现当阶段为Running, condition中Ready状态为True时, 即表示pod中的容器可以提供服务了。因此,我们现在就可以依次判断新起的pod是否升级成功了。

03

如何判断发布失败?

现在我们能够判断成功了,下一步就需要对失败的状态进行一个判断,其实理论上负载的成功或者失败不是一个确定性的东西,k8s本身具有重试机制,也存在概率性调度失败的情况。所以一开始我们考虑的是通过一个超时机制来判断发布失败的情况。但后面分析考虑到,发布作为一个重要的组件,需要第一时间知道问题所在,以致可以快速进行版本回退等操作。

对于容器的状态,pod中的containerstatus包含了更详细的当前容器的状态。其state字段表示容器当前的状态,state分为三种状态:等待中,运行中和已终止,对应三种不同类型的结构体。等待中的结构体包含了处于非运行状态的原因和信息。其他结构体就不在此一一赘述。结合podphase、podcondition、以及containerstatus,我们总结了如下几个升级异常状态(如有不全,欢迎补充):

  • PodPhase == Failed

  • PodPhase == Succeeded

  • ContainerStatuses 中有一项为state.waiting 并且 waiting 的原因不是ContainerCreating 或者 PodInitializing(ContainerCreating表示容器创建中,PodInitializing表示pod初始化中)

  • ContainerStatuses 中有一项为state.running 并且 containerStatuses.ready == false

  • Conditions中存在一项的type为Ready且status为False且Containerstatues存在

  • Conditions中存在一项的type为PodScheduled且status为False:调度失败

由于pod的状态是动态变化的,为了保证发布结果的准确性,我们选择pod超过3次时置为失败状态,即发布失败。

看完上面这些原理解析,您是不是有什么收获,或者有什么想法与我们交流呢?请在下方留下您宝贵意见和建议吧?说不定下个版本,您的创意就会出现在我们的产品里。

相关服务请访问https://www.huaweicloud.com/product/swr.html?cce_helpcenter_2019

猜你喜欢

转载自www.cnblogs.com/CCE-SWR/p/10339288.html