3.5 Spinnaker架构
Spinnaker采用微服务的思想,整个系统一共由10个微服务组件构成,主要由Java语言开发,这些微服务以“职责或功能”的原则进行拆分,不同的服务负责不同的功能,微服务之间使用HTTP协议进行调用。微服务组件如下。
• Deck:用户操作界面。
• Gate:微服务API网关。
• Orca:流水线阶段编排引擎。
• Clouddriver:负责与不同的云提供商交互。
• Front50:存储控制。
• Rosco:生成VM镜像。
• Igor:调用外部CI系统。
• Echo:事件总线。
• Fiat:认证授权中心。
• Kayenta:自动金丝雀分析。
• Halyard:配置中心。
这些微服务组件对外提供服务的监听端口如表3-4所示。
表3-4 不同微服务的监听端口
各微服务之间的调用关系如图3-6所示。
图3-6 Spinnaker微服务间的调用关系
在一次完整部署生命周期的部分,微服务间的调用时序如图3-7所示。
图3-7 Spinnaker部分微服务间的调用时序
接下来对各个微服务组件进行简单介绍。
3.5.1 Deck
Deck是Spinnaker的UI组件。它为用户提供流水线的可视化界面,使用TypeScript编写,并使用React框架,在9000端口提供服务,主要提供以下功能。
• 应用管理。
• 部署流水线管理。
• 集群管理。
• 防火墙管理。
• 负载均衡器管理。
由于对不同的云提供商构建基础环境所需的表单不同,Deck内置了所有云提供商创建基础设施所需的表单,并通过配置文件对这些功能进行启用和禁用。在所有微服务组件中,Deck是一个集成度和复杂度较高的组件。
Deck体现了Spinnaker对云原生和混合云下应用或服务的抽象设计哲学,所有的基础设施均基于应用对粒度进行管理,Deck的首页即为应用列表。
Deck首页的创建应用是所有功能的开始,即一切的流水线、集群、服务器组等操作都是基于应用进行的。
Spinnaker Deck首页如图3-8所示,进入应用(Application)后,首页将展示应用信息。
图3-8 Spinnaker Deck首页
在这种组织方式下,应用代表一个系统或一套微服务,Deck下涉及的基础设施包括服务组、集群和防火墙均一目了然。Deck屏蔽了“多云”的概念,统一了不同云平台之间的展示和操作。
3.5.2 Gate
Gate是Spinnaker的API网关,主要为Deck提供REST API,也是所有外部请求的入口。服务启动后,Gate监听在8184端口。
用户在Deck界面进行操作时,会对Gate发起请求。Gate收到请求后,将请求转发到对应的某个微服务上进行处理。此外,Gate不会充当微服务内部调用的网关,微服务之间的内部调用一般由请求方直接对被调用方发起请求。
作为API网关,Gate需要解决服务发现的问题。当使用推荐的Kubernetes部署方式运行Spinnaker时,每个微服务都将部署在独立的Pod内工作,此时可以使用Kubernetes的Service作为服务发现的方式。如果采用VM的部署方式,那么Spinnaker的微服务会运行在独立的服务器组中,因此需要借助负载均衡器或服务发现系统(如Eureka)解决服务发现问题。
3.5.3 Clouddriver
Clouddriver组件采用Java语言开发,负责与不同的云提供商API进行交互,并对已部署的资源进行索引和缓存,为Deck提供资源的数据源。
Clouddriver包含非常多的模块,部分模块如图3-9所示。
图3-9 Clouddriver的部分模块
例如,对Kubernetes、腾讯云和AWS等的支持都是Clouddriver针对不同的部署阶段调用相应的模块(Provider)来完成的。Clouddriver最初只提供了部分国外云提供商的Provider,其他的Provider均为各厂商主动实现对Spinnaker的扩展支持。同时,Clouddriver良好的设计为不同的云提供商提供了友好的扩展性,每个云提供商的扩展都能标准化地实现Provider所需提供的功能。
3.5.4 Orca
Spinnaker因其部署流水线高度可配置和可组合而闻名,这很大程度上归功于Orca对流水线阶段的编排能力。
Orca是Spinnaker的编排引擎,当部署流水线启动后,它负责从流水线中同步或异步执行阶段和任务,并协调其他的微服务组件。
Orca是一个无状态的服务,这意味着它可以进行水平扩展。Spinnaker将任务执行状态持久化到后端存储系统(例如Redis或MySQL),并通过工作队列均匀地分配任务,只要后端存储层具有足够的容量和性能,就可以对Orca服务进行自由伸缩,以此满足Spinnaker的编排需求。
通过编排一条流水线内的所有阶段,可以形成一个DAG(有向无环图),实现复杂的部署工作逻辑。Orca最重要的功能就是控制何时从DAG内取出一个节点(阶段)、何时执行节点的内容、何时执行下一个节点。这些节点可能会被相互依赖,Orca负责它们的上下文传递,以便判断执行依赖的情况,如图3-10所示。
图3-10 流水线的阶段组成DAG
Orca使用分布式队列Keiko来管理阶段的任务。Keiko类似于一种延迟队列,意味着Orca可以立即或在将来的特定时间传递消息。在内部实现中,每种消息类型都对应一种Handler,这些Handler负责处理阶段的逻辑。Orca处理消息的能力在一定程度上依赖线程池的大小,根据负载情况的不同,线程池可以进行调整以满足需求。在默认情况下,消息处理器会以默认10毫秒的间隔轮询消息队列,并尝试最大化填充线程池。如果线程池的大小设置为20,并且已有5个线程处于繁忙状态,那么消息处理器会尝试从队列中取出15条消息进行处理,而不是每次取出1条消息。
3.5.5 Echo
Echo在Spinnaker中主要有如下3个作用。
• 事件路由,例如Igor轮询到了新的镜像版本产生的触发事件。
• CRON任务计划触发流水线。
• 外部消息通知。
事件在Echo中的流向如图3-11所示。
图3-11 Echo中事件流向
① Igor在检测到外部有增量更新时,例如Dokcer Registry中镜像的变更或者SCM(源代码管理器)的变更,会向Echo发送事件。
② 从Gate发送的事件,例如用户在Deck中手动启动流水线或通过API调用启动流水线。
③ Echo将流水线事件提交给Orca执行。
④ 当有以下情形时,Orca会发送事件给Echo。
• 阶段(Stage)开始或完成,此时Echo可以发出外部通知(如果定义了通知)。
• 流水线(Pipeline)正在启动或已完成,Echo发出通知(如果定义了通知)。
• 正处于人工确认的阶段,Echo发出外部通知(如果定义了通知)。
⑤ Echo使用外部服务(例如电子邮件)发送通知。
⑥ Echo还可以通过Webhook的方式调用外部URL。
Echo内置的外部通知支持Email、Slack、Bearychat、Microsoft Teams、SMS(Twilio)、PagerDuty类型。
除此之外,我们还可以使用Webhook阶段实现更多的外部通知。
Echo可以在两种模式下工作,分别是内存模式和SQL模式。内存模式将所有CRON计划时间存放在内存中,这意味着如果有多个Echo实例,CRON类型触发的流水线可能会被执行多次。SQL模式能够将CRON触发信息保存在SQL数据库中,这样即便在有多个Echo实例的情况下,也不会出现重复运行的情况,所以生产环境部署Spinnaker建议采用SQL的方式运行Echo。
3.5.6 Front50
Front50[1]是Spinnaker中负责存储数据的组件,包括应用信息、流水线和其他元数据。
所有的数据都将被持久化存储,其他组件通过Front50提供的对外接口获取存储的信息。在对外提供数据时,Front50将优先从应用运行时的内存缓存中读取数据。
Front50支持Redis、SQL、Amazon S3存储系统持久化数据。其中,推荐SQL的存储方式。
Front50持久化数据的主要类型如表3-5所示。
表3-5 Front50持久化数据
为了方便存储和屏蔽存储系统差异,在Front50中,所有数据都被统一格式化为Json并落地到持久化存储中。
3.5.7 Igor
Igor是Spinnaker与外部持续集成(CI)和源代码管理器(SCM)的集成和管理中心。
Igor的基本运行单位是轮询器,在其服务内部运行着许多不同类型的轮询器,但它们的工作流程保持一致。
• 定期从外部系统获取项目资源(例如Jenkins的构建记录)。
• 将获取到的项目与自身存储缓存进行比较。
• 当外部系统产生新的变更时,向Echo发送事件。
• 对新项目进行缓存。
Igor默认的轮询周期是60秒,此配置项可被修改。Igor还支持分布式锁(依赖于Redis),开启后,能够对Igor进行高可用部署,使其支持横向扩容。
Igor对SCM的支持主要有GitHub、GitLab、Bitbucket、Stash类型。
对于CI系统的支持有Jenkins、Travis、GitLab CI、Nexus、Concourse、Artifactory、Wercker类型。
Igor可以为以上的CI系统启用轮询器。启用后,轮询器会定时获取构建/Jenkins流水线/部署制品的变更,缓存数据,并向Echo发送事件。此外,还能够与Docker Registry进行集成,并监听Docker镜像的变更。
Igor对SCM和CI系统的支持类型丰富,例如,比较典型的是选择Gitlab(SCM)+ Jenkins(CI)实现GitOps。
3.5.8 Fiat
Fiat是Spinnaker的认证和授权中心,负责Spinnaker用户的权限控制。
Fiat对外提供了Rest接口,以便其他的微服务进行查询,并对以下3种粒度的资源类型进行权限控制。
• 云账户。
• 应用权限。
• 服务账户(Service Accounts,用于触发器的权限控制)。
需要注意的是,Fiat的授权是基于角色(Role)实现的,其本身并不重新创造角色和授权,而是通过集成外部服务来提供角色信息,目前支持的类型有GitHub Teams、LDAP、SAML Groups、File Based Role Provider。
Fiat基于角色的权限控制的具体实现为:记录Spinnaker的应用分别具有读(Read)、写(Write)和运行(Execute)的角色,当产生对应的读、写和运行的行为时,对当前用户的角色进行判断,并对比是否具有相关权限。
利用Fiat的特性,我们可以实现诸如控制用户级的Spinnaker应用权限与读写权限隔离,也可以使用LDAP和现有用户系统对接。
3.5.9 Rosco
Rosco组件是Spinnaker云主机实例部署中最重要的一环,该组件负责生成云主机的镜像,生成的过程称为构建(Bake)。
Rosco使用开源工具Packer[2]生成镜像,以腾讯云CVM云主机为例,以下template.json定义了CVM镜像模板。
该模板定义了在ap-guangzhou地域ap-guangzhou-4可用区,使用S4.SMALL1配置的CVM以镜像ID为img-oikl1tzv为基础构建CVM镜像,命名为PackerTest。
接下来,只需要运行packer build template.json即可构建镜像,构建完成后,在CVM控制台即可找到该镜像。
当Spinnaker启动Bake阶段时,Rosco创建并运行一台VM实例用于构建,构建完成后产生镜像并自动销毁实例。该镜像用于云主机的部署,并可以用于云主机服务器组的横向扩容。
目前Rosco支持的云提供商有腾讯云、华为云、AWS和Azure。
3.5.10 Kayenta
Kayenta组件是用于金丝雀分析的工具,Spinnaker根据它的分析结果控制自动金丝雀发布。
Kayenta通过比较新版本和旧版本的关键指标来评估金丝雀发布的质量。如果指标质量相对于旧版有所下降,那么金丝雀会被终止,所有流量将被路由到稳定版本,最大程度降低意外带来的影响。
Kayenta的金丝雀分析需要收集新老版本的指标数据,它依赖于外部的遥测系统提供,目前支持的遥测系统有Prometheus、Newrelic、Stackdriver、Datadog。
Kayenta在实现自动金丝雀发布时主要分为两个阶段,首先是从外部的遥测系统获取指标数据,通常是时间序列的数据,该阶段包括处理输入数据中一些缺失的值及NaN值。接着,Kayenta会对这些指标进行比较,并输出可视化的图形,用来展示这些指标比旧版本升高或降低了。整个过程如图3-12所示。
图3-12 Kayenta金丝雀分析
最后,Kayenta会使用一种评分机制(默认为NetflixACAJudge)对本次金丝雀发布进行评分,当评分高于金丝雀配置的分数时,则认为金丝雀发布通过,如图3-13所示。
图3-13 Kayenta金丝雀分数计算