导读
本文针对 企业级分布式应用服务( EDAS ) 应用生命周期管理所使用的发布单系统进行介绍,包括背景、设计目标、设计方案、功能介绍等,新开发的发布单系统实现了变更过程的流程化、任务化、可视化,发布流程可定制,支持多种变更策略,自2017年8月上线以来,承担着 EDAS 生命周期管理操作。
背景
EDAS 是引入淘宝中间件整套成熟的分布式计算框架,以应用为中心的大型分布式应用服务。应用生命周期管理功能是EDAS最基础,也是用户最直接的需求,之前的生命周期管理功能有下面一些遗憾:
- 分批策略单一;
- 发布过程不能可视化,对应用变更操作,不能反馈变更进度,也不利于问题排查;
- 发布系统的可扩展性、可维护性差;
新开发的发布单系统为了解决以上的问题而进行的,下面详细介绍。
设计目标
- EDAS 应用发布过程的流程化、任务化、可视化;
- 支持分批发布/全部发布的发布策略,支持优雅上下线;
- 发布任务可扩展,发布流程可定制,提升可扩展性;
EDAS 发布单系统设计方案
发布单系统设计包括流程方案设计和系统架构设计,流程方案设计是指发布单系统的任务是如何被编排,又如何把流程模板化的,系统架构主要关注发布单系统根据用户应用配置和流程模板是如何触发执行的。
流程概念和流程方案设计
流程概念
- 发布单(ChangeOrder) 用户的每次生命周期操作(启动/停止/扩容/下线/重置/部署/回滚等)会创建一个发布单;
- 流程(Pipeline) 发布单中应用实例可以分批执行,每一批为一个流程;
- 阶段(Stage) 流程有阶段组成,阶段通常由几个耦合度高的任务组成;
- 任务(Task) 执行的最小单元,任务分为两种类型,一种是与Tab服务、SLB服务等外部服务交互的任务(service任务),另一种是与ECS交互的任务(agent任务),例如拉去镜像、启动应用等;
- 流程模版(Template) 根据应用发布的系统行为固化下来的流程定义,流程模版的内容与应用版本(EDAS版本)挂钩,应用发布单创建时根据模版生成具体的流程实例,流程流转时会根据流程定义及当前任务执行情况计算下一步将要采取的行为;
- 事件(Event)与监听器(Listener) 支持流程事件与事件监听器的绑定,当特定的流程事件发生时将触发所绑定的监听器的执行,目前只支持了Pipeline流程级别事件,事件具有扩展到Task、Stage、ChangeOrder级别的能力;
流程方案设计
针对生命周期管理的每一个操作,我们都要提前设计一个特定的流程并且固化下来,形成模板文件,这个特定的流程就是发布单系统执行生命周期管理功能操作的具体步骤,下面介绍下采用的流程编排方案。
流程编排方案
这部分介绍我们如何编排stage、task来形成特定流程的模板文件,流程编排方案这部分我们考虑过三种选型,如下图所示:
注:图中的每个节点是一个stage,stage中会包含一个或多个task。
- model A:对于需要在ECS上执行的任务,采用子流程(子发布单)来完成,这种类型的好处是主流程简单了,但引入子流程其实对流程复杂度是有害的;
- model B:把流程拆成最细粒度的pipeline,每个ECS的部署对应一个Pipeline,这种在图形表现上非常方便;但是这种情况下,对于需要Join操作的操作是支持不好的,需要生成的流程stage实例节点也最多,在效率上不太好;
- model C:这种流程实现上,有分有合,在 ECS 上执行的任务采用并行任务来执行,在服务端执行的任务一般是需要批量实例数据,需要待流程聚合后执行;
综合考虑后,我们采用 model C 方式来实现我们的流程编排,在并行之后的第一个串行节点(通常是service类型任务所在的stage)需要Join并行节点的数据来做批量操作, Join的实现通过并行任务的技术countdown来做。model C中,串行stage失败,流程失败;并行stage都失败,整个流程才会失败,单个并行stage失败,只影响单个 ECS 机器;Join操作时,不会合并失败的并行节点的数据。
流程模板举例
根据上面的流程编排方案,我们采用yaml文件来描述一个发布流程,以下是一个真实的应用启动流程模版,当用户创建一个发布单时,发布单系统需要根据相应的流程模板和应用配置来生成流程实例数据。
coSchema: '2.15.2'
pipelineName: edas-app-start
metadata:
name: 'start instance'
description: 'A generic start instance in pipeline.'
owner: edas-dev@alibaba-inc.com
stages:
-
stageName: startPipeline
executorType: 0
tasks:
-
taskName: startPipeline
taskType: 0 # 任务类型:0、service类型,1、agent类型
-
stageName: startAppInstance
executorType: 1 # 0、串行,1、并行;
retryType: 2 # 重试类型:0、不重试,1、自动,2、手动;
retryCount: 3 # 重试次数;
tasks:
-
taskName: updateTomcatConfig
taskType: 1
-
taskName: startInstance
taskType: 1
-
taskName: startTengineInstance
taskType: 1
-
taskName: updateTengineConfig
taskType: 0
executeMode: 1
taskTimeout: 60000 # 超时时间
-
stageName: healthCheck
executorType: 1
retryType: 2
retryCount: 3
tasks:
-
taskName: healthCheckWithPort
taskType: 1
executeMode: 2 # 执行方式:0、同步(默认),1、异步,2、异步执行;
taskTimeout: 60000
-
taskName: healthCheckWithURL
taskType: 1
executeMode: 2
taskTimeout: 1800000
-
stageName: slbOnline
executorType: 0
barricade: 1
retryType: 2
retryCount: 3
tasks:
-
taskName: reopenSLBInstance
taskType: 0
-
stageName: endPipeline
executorType: 0
tasks:
-
taskName: endPipeline
taskType: 0
应用启动的流程模板中包含5个stage,其中startAppInstance、healthCheck为并行stage,在流程实例生成时一个并行stage可以生成和变更实例相同数量的stage实例,而串行stage只生成一个stage实例,执行时串行stage实例会使用join后的实例数据;每个stage中有一个或多个task,task实例是任务执行的最小单元。
发布系统改造
基于流程的设计方案,我们对整个发布单系统的功能进行梳理、改造,主要有以下几个方面的工作。
发布过程任务化
通过生命周期管理逻辑分析,我们把管理逻辑分为13种阶段(stage),28种任务(task),任务是编码和运行的基本实体单元,是构成流程(pipeline)和发布单的基础;通过管理逻辑任务化,代码变得易维护、易扩展,也是发布过程可视化的前提。
发布过程流程化
EDAS 应用生命周期管理主要包括应用创建、删除、部署、启动、停止、重置、下线、扩容、更换分组、软件版本升降级等操作,对于这些操作我们都会创建一个发布单,发布单可以是分批执行,每一批就对应一个流程(pipeline),是关于一个或一批应用实例的变更过程;
发布流程可定制
流程(pipeline)是可以通过流程模版(Template)来定制,流程模板就是满足一定规则的yaml文件,可以在通过对task、stage编排来组装流程,通过模板文件固化下来,供特定的应有生命周期管理操作使用。
支持分批发布/全部发布的发布策略
支持分组内分批变更,分批的策略有自动分批和手动分批,自动分批用户可以选择是直接运行还是等待一段时间运行下一个分批。
功能架构设计
整体架构
- 在EDAS-console上创建发布单,根据生命周期管理操作和应用版本所对应的流程模版创建相应的流程实例;
- Redis保存发布单的执行的task信息和上下文信息
- Agent-server中worker拉取任务,根据不同的任务类型采取不同方式执行,如果是service类型任务调用外部系统执行任务,如果是agent类型任务通过 api server 下发应用管控命令到ECS上,利用ECS上预先下发的Python脚本执行任务,执行结果同步返回Agent-server,或者异步回调到Agent-server或EDAS-admin
- 执行结果通过Agent-server中worker汇总、触发下一个任务执行
系统模块之间交互
- 发布单管理模块,主要包括流程实例生成功能、对外接口(发布单详情查询、日志查询、发布单列表查询等)、驱动流程引擎和任务引擎执行;
- 流程执行引擎,根据发布单管理模块生成的流程实例根据条件顺序执行;
- 任务执行引擎,由流程引擎驱动,执行具体的任务,根据执行结果判断触发流程引擎继续执行;
- 事件由是流程执行引擎或任务执行引擎触发,事件引擎根据事件类型执行相应的动作;
- 日志管理模块,支持用户查看具体的任务执行情况,service类型的任务执行日志存储在数据库,agent类型的任务执行日志存储在用户ECS上;
- 数据存储,流程数据存储在mysql中,任务执行的上线问信息,用redis来存储,agent类型任务的执行日志,存储在用户ECS的文件系统上。
任务执行
本部分说明任务如何在发布单系统中被执行,下图显示了agent类型任务的执行过程,service类型的任务执行过程类似,只是交互对象不再是 用户ECS,而是外部系统(SLB、Tab等)。
如以上时序图所示,worker将从任务队列中取出任务进行执行,如果任务类型是Agent,将通过api server 将任务下发到ECS来执行,在ECS有预先发现的Python脚本,Python脚本根据下发的命令,路由到相应的功能模块执行任务,再同步或者异步的方式把执行结果反馈给发布单系统。
关键点的处理
服务优雅下线
服务优雅下线功能根据用户选用的 EDAS 软件版本不同,所涉及的组件也不同,一般是SLB下线、Tegine下线(流量管理,支持应用灰度发布)和RPC服务上下线的一种或多种功能的组合。在服务下线具体流程中,SLB首先下线,接着是Tegine下线,最后是RPC服务下线。
- SLB下线和Tegine下线是和外部系统交互,一般是批量操作,并且一般如果任务失败,会中断流程,变更失败;
- EDAS RPC下线是调用本地接口从configserver上下线服务,调用返回成功后等待15秒,继续后面流程,如果调用返回时失败,则设置RPC下线任务失败,但流程继续进行,不中断流程,这样的设计,主要是为了避免用户因RPC下线一直失败而无法对应用进行生命周期管理的问题。
健康检查
健康检查是指对用户应用实例启动后,通过健康检查来保证用户应用实例确实启动成功,然后再做流程后续工作,如Tegine上线、SLB上线等,如果健康检查没有成功,则流程失败。健康检查是一个定时的agent任务,每3/6分钟执行一次,直到整个健康检查成功或者失败。健康检查是发布单功能上线以来,失败率最高的任务,原因大多是用户应用程序启动失败造成的,还有一部分是用户健康检查参数配置不当引起的。健康检查健康检查包括端口健康检查和URL健康检查,关于这两种健康检查的方案下面详细介绍。
- 端口的健康检查 , 端口健康检查是通过api server下发应用管控命令到用户ECS上检查用户应用所设置的http端口号是否可用,分为两个步骤,一是检查端口是否处于监听状态,二是如果端口处于监听状态,检查在本地是否可以连接,这个两个步骤都成功,才说明这个端口的健康检查是成功,端口是可用的;一般检查的端口是用户Tomcat端口,如果用户应用软件版本支持流程管理功能(支持流程管理功能,用户需要在本地启动一个Tegine实例)要同时检查Tegine端口是否可用。端口的健康检查我们设定超时时间是60秒,如果轮询检查端口一分钟还是失败,则端口健康检查失败;
-
URL健康检查,URL健康检查是对用户配置的健康检查URL进行检查,确保用户业务可用。在用户不配置健康检查URL的时候,我们会为用户应用默认配置一个URL。发布单系统上线后关于URL健康检查逻辑做了很多优化,具体逻辑如下:
- 如果用户配置健康检查URL为空,则会跳过健康检查URL,但这样就不能从业务上来保证应用实例是否启动成功;
- 在URL健康检查任务执行的过程中,用户可以手动选择跳过URL健康检查,除非确认业务可用了,一般不建议这么操作;
- 经我们实验验证,如果Tomcat正在启动的时候,访问健康检查URL,会得到超时异常,基于此,我们采取的方案是:如果URL健康检查得到的是一个超时异常(或是python抛出的其他异常)则继续URL健康检查(30分钟超时);如果URL健康检查,返回结果 HTTP Response 状态码为200,则说明成功;如果URL健康检查返回结果 HTTP Response 状态码为4xx、5xx,则认为此次URL健康检查任务失败,一分钟内所有URL健康检查任务都失败,则URL健康检查失败;
- URL健康检查默认会有一个比较长的健康检查时间(30分钟),满足了应用实例启动时间比较长的情况;对于URL健康检查持续失败的情况,会持续检查一分钟,是为了避免由于网络抖动造成个别URL健康检查任务失败的情况。
功能介绍
创建发布单
在应用详情页,用户的每次生命周期操作启动/停止/扩容/下线/重置/部署/回滚和创建应用等都会创建一个相应类型的发布单,并且可以在变更记录中查询发布单列表,也可以进入发布单详情查询更详细的信息,下面用部署应用操作来说明发布单系统功能。
- 下图是测试应用部署实例信息,有两个分组,共3个应用实例,应用实例运行正常;
-
点击右上方的 部署应用 按钮,弹出应用部署配置的交互框,如下图;
- 文件上传方式,普通应用有上传WAR包、WAR地址、使用历史版本等选项,docker应用另外会有镜像部署的选项;两种类型的应用如果是首次部署并且软件版本支持fatjar的时,有jar部署的选项;
- 发布版本,此次应用部署的版本号,建议用户设置明确的版本号;
- 发布目标分组,可以选择所用分组和单独的分组进行发布,如果选择所有分组,则每个分组是一个分批,不能对单个分组内的机器做分批部署;
- 批次,当单独部署一个分组时,可以配置分批数;
- 分批方式,有自动下一个分批、手动下一个分批和自动等待一段时间继续下一个分批三种分批方式;
- 分批等待时间,分批方式为自动等待一段时间继续下一个分批时,允许用户设置分批间自动等待时间,单位为分钟。
本次部署我们选择了文件上传方式为war上传,发布目标为所有分组,分批方式为自动,分批等待时间为不等待的发布策略,点击 发布,创建发布单完成。
查看发布单信息
发布单列表信息
- 通过应用详情页的左面的菜单栏中 变更记录,可以查看发布单列表;
发布单列表也包括创建时间、结束时间、变更类型、描述、变更状态、变更人、来源、操作等信息。
发布单详情
-
点击发布单列表中查看,可以查看发布单发布详情,如下图,
发布单详情页包括两部分,一部分发布单概要信息,另一部分为发布流程和任务执行信息- 发布单概要信息,包括发布单Id、发布分批数、分批间处理方式、执行状态、变更类型、变更对象等信息;
- 发布流程和任务执行信息,从图中可以看出,此次部署分为两批部署,目前正在执行的是第二批,第二批有部署了两个应用实例,可以清晰的看出每个应用实例的部署步骤和部署进度;
- 点击每个stage下面的链接 查看日志,可以查看stage中具体的task执行的详细情况,如下图,
常见问题排查
在使用发布单的过程中会遇到一些问题,排查的基本思路是首先进入发布单详情页面,查看失败任务的执行日志,确定任务失败原因,根据原因给出解决办法,下面就几类常见的问题给出以下解决思路。
- 应用部署信息页面看到应用实例一直在执行中,一般是该任务执行时报错,而流程模板对该任务设置为可以重试造成(通常设置为可手动重试3次),在发布单详情页应用实例列表中可以进行重试操作,如果重试不成功,再具体排查;
-
命令通道不通,这个直接可以通过发布单详情页中任务执行的日志来定位问题
- 普通应用,现象一般任务执行日志有 “please check if service is available”字样,可以登录机器,admin权限 执行 pkill -9 staragent && /home/staragent/bin/agent.sh start, 然后重置该应用实例;
- Docker应用,现象一般是第一个agent类型的任务就因为超时执行失败,登录应用实例所在机器,重启edas-agent容器, 命令如docker restart {containerId},如果宿主机没有edas-agent容器,docker ps -a 找出该容器,docker start {containerId}即可;然后重置应用实例;
-
健康检查失败
- 端口健康检查失败,应用实例启动后会检查tomcat的后端端口,如果应用软件版本支持Tengine还会检查Tegine的转发端口号,端口健康检查失败,一般为tomcat或Tengine启动失败造成的;
- URL健康检查,是上线以来失败率最高的任务,URL健康检查失败时,请检查URL是否配置正确,如果配置没问题,请检查业务代码日志是否有报错造成应用启动失败,URL健康检查设置相关信息可参考 关键点处理 -> URL健康检查中的介绍;
- 普通应用用户 ECS 目录或文件权限为root,无法操作相关文件, 一般在任务执行日志中会有显示权限不够的信息,登录机器root权限执行chown admin:admin {要修改的目录或文件},然后重试该任务;
- SLB上下线失败、权重设置失败, 请检查该SLB是否过期,过期的SLB实例上下线和权重设置都会失败;
- Tegine启动、停止失败,在客户支持的过程中,遇到几次这种情况,应用为普通应用,用户第三方插件修改/etc/sudoers文件,造成机器上admin用户无权限操作Tegine启动/停止脚本;
- 应用实例陆续多次发布单失败,这种可能是因为之前的发布单失败造成后续的发布单都失败;因为 ECS 需要初始化(安装脚本、安装tomcat、安装tengine等)后才能通过发布单进行管控,初始化的过程可能会因为网络、机器环境不匹配等原因造成失败的,初始化一般发生在三个地方,创建应用、重置应用实例、软件版本升降级,如果发生陆续多次发布单失败,请着重排查这个三种类型的发布单,看是否是环境初始化失败导致;
后续将上线的特性
- 发布单系统的可扩展性;
- 发布单模板、任务,用户可定制;
- 多应用编排,支持实例级别变量自定义;
- 配置版本化,只有在用户修改应用配置、应用配置版本改变的时候才会下发配置;