简介:为了能够有效且高效地同时管理SaaS版本和私有化版本的发布过程,云效团队也结合云原生的基础设施和标准化工具(比如helm)进行了一系列的探索和实践,并将其中一些通能的能力进行了产品化。本文从问题本身出发,讲解解决问题的思路,及如何通过“DIY”的方式来实现这套思路。
大家好,我是崔力强,我在云效负责Flow流水线的开发工作。近年来,SaaS化部署形态的产品的私有化部署需求越来越多,比如云效自身就有私有化部署的版本。为了能够有效且高效地同时管理SaaS版本和私有化版本的发布过程,云效团队也结合云原生的基础设施和标准化工具(比如helm)进行了一系列的探索和实践,并将其中一些通能的能力进行了产品化。本文会从问题本身出发,讲解解决问题的思路,及如何通过“DIY”的方式来实现这套思路。最终讲解云效AppStack产品是如何对这些实践进行产品化,并使其更容易规模化。
SaaS服务在版本化上的先天不足
软件交付有两种基本场景:面向大版本的交付和面向SaaS的升级更新。
通常来讲,提供本地或私有化部署的软件都属于第一种。比如Jenkins刚刚发布了2.319.2版本,那么这个版本里包含了什么样的特性就是明确的。你拿着这个安装包在任何一台机器上都可以从头安装得到这些功能。
而互联网产品很大一部分是SaaS化的,即只有一套部署,供所有用户使用。软件的维护者更关心的并不是我的产品是否可以在任何一个数据中心从头搭建出来,而是如何在现有的这个运行中的系统上通过更新某个组件或者服务来快速的交付一个特性。
图1:SaaS服务交付和大版本交付的交付节奏
从上述的示意图,可以形象地看到两种交互方式的差异。
面向大版本的交付会明确该版本中包含的特性以及交付时间, 版本的发布时间间隔通常比较长,需要对版本的全新安装以及不同版本之间的升级安装进行详尽的测试。
面向SaaS的升级更新,交付的频率比较高,可以快速响应市场上的需求,但相应的规划性比较差。同时因为“可重复安装能力”的优先级要低于“快速利用已有的服务和能力交付新特性”,因此在架构上可能会逐步产生复杂的依赖,从而进一步地使得全新部署这套服务变的越来越困难。
然而现实并不是非黑即白的。有可能一套互联网产品在发展了若干年之后有了进军海外的需求,就需要同时部署海外站, 或者需要做私有化部署。此时该怎么办呢?是牺牲效率全部改成版本化的交付,还是以SaaS服务的交付节奏为主?如果是后者,那么每个私有化大版本发布前的几天,团队需要从纷乱的SaaS部署中厘清需要将哪些服务的什么版本(比如镜像版本)纳入到这个大版本中,进行版本验证,以及潜在的可能要对代码和配置进行调整。
图2:同时兼顾SaaS服务和大版本交付两种交付方式
假设一个月出一个大版本,那么在上图的2月1号到2月7号这七天里都可能发生了什么呢?
- 可能在对焦,大版本里要求的功能是否都完成了,如果没有就要拉分支继续做。
- SaaS化版本里面的一些功能可能是私有化部署不需要的,这时需要加一些开关使其不可见,需要改代码。
- 在这一个月的迭代里,技术架构发生的调整,删除了一个微服务,又新加了一个微服务,大版本需要做相应的调整。
- 在这一个月的迭代里,应用的配置项也发生了变更,需要在大版本中做相应调整。
其中:
1和2属于版本规划和测试左移的问题。本文暂时不聊。
3和4就是可以通过技术来解决的问题了,本文接下来的部分会重点讨论如何高效的解决这两类问题。
统一版本格式
解决上述问题的核心技术就是要有一个统一的版本格式,无论是SaaS版本还是大版本都应该使用相同的版本格式。
在此基础之上,要做到
1、版本应该是一个完整的系统描述,包含了所有的镜像,配置等一切启动服务所需要的描述。从而能够实现基于某个版本,就可以一键拉起一个新的可用的环境。
2、每个环境有一个基线的概念,也就是和环境的当前运行态保持一致的那个版本。
图3:版本中包含的内容
3、在环境中,每个服务还是可以独立更新的。每一次某个服务在某个环境上(比如服务A的生产环境)的发布,尽管只修改了系统中的一个服务,但也应该自动生成整个环境的一个新的版本。
4、每个环境的配置应该集中化起来,而不是在各个服务中分别维护。在服务数量比较多的情况下,这种方式可以大大地降低版本维护的成本。尤其是在新建环境的场景下,由于配置集中化了,需要修改什么就更加的一目了然。通常在配置项集中化之后,还会看到另一个好处,那就是重复配置少了,因为一个系统中的不同服务多多少少都会共用一些配置,如果要单独在服务中维护,就不可避免的出现重复。
图4:任何制品和配置的变更都引起大版本的更新
5、对环境的任何变更都应该最终反映在版本中。只有这样才能保证你做的变更,可以原样的在另一个环境,另外一家客户的机房中被正确的执行,比如DDL和DML。
6、所有的日常发布行为,本质上就是针对版本变更这个动作的一些场景化封装。比如对某一个服务做变更,那就可以创建一个独立的CD流水线进行镜像构建,创建临时版本,更新环境,将临时版本写入基线。而进行某个配置变更,就是修改基线,然后应用基线到环境。
图5:围绕版本构建日常构建发布等工作流
围绕Helm进行版本管理和构建部署
在不同的基础设施之上,上述的思路可以有不同的实现方式。
而在K8S基础设施上,Helm Chart就是版本格式的不二之选。
Helm的核心概念包括:
- 一套K8S资源文件的组织方式,资源文件中可以使用变量占位符
- 变量管理机制,使用helm提供的机制,可以很容易的将整个大版本的变量提取出来放到统一的文件来维护,这就契合了我们前面提到的需求
- 一个渲染引擎,在运行时,将变量替换到文件中,并进一步应用到集群中
- 一套部署历史管理的机制,比如update/rollback等
下面看一个典型的例子:
图6:基于Helm构建版本
得益于K8S资源的强大描述能力,形成一个“版本”的各种组成部分都可以很好的描述,比如:
- 系统的域名是什么?
- 不同的URL应该路由到哪个服务?
- 可以将Flyway和相关的SQL迁移脚本打包成一个Job,来做DDL。
- 可以将其他的需要对系统进行数据初始化的任务打包成一个Job。
在此之上,再加上helm提供的模板化能力,就可以清楚的将对一个环境的描述分为两个部分:
- 不变的部分,也就是那些模板化的资源文件,不同的环境会共用这部分描述。
- 抽取出来的属于某个环境的变量。
因此上图中的蓝色的框内的就是“测试环境”的一个版本。
helm chart作为版本,可以看到,本质上就是一堆描述文件。这些描述文件可以以目录的形式存在,也可以以tgz包的形式存在。因为面向SaaS的交付的变更频率会非常高,因此每次打一个tgz包就会显得非常的臃肿。所以笔者会采取目录的形式,那么什么是承载目录,并且还能实现版本序列技能力的技术呢,很显然就是Git啦。
我们把上面思路中的那个围绕版本进行一系列研发活动那种图翻译到Helm和Git上,就是这样:
图7:围绕helm chart构建日常构建发布等工作流
至此,利用现成的一些标准化工具,就实现了一套版本机制,及围绕版本机制的开发流程。
在这套流程下:
- 面向SaaS的交付流程,仍然非常敏捷,且同时会自动的维护好各个环境的基线。
- 由于各个环境都通过helm chart中的模板文件“耦合”在了一起,当你修改一个环境时候,自然就需要考虑其他环境怎么办,因此一致性也很好的得到了保证。任何时刻,我都可以使用某个环境的基线来重建这个环境。
- 也可以基于一个环境的基线,快速地创建出另一个环境的基线,只需要简单的修改一下环境的变量文件即可。
一些小细节
在实际使用这套方案的时候,其实还是很多小细节,需要慢慢优化。这里就简单列两个:
- 所有的镜像的tag包含日期和commitId,在后续定位问题时候,可以通过这些信息快速的找到对应的代码,进行排查。
- 在上述的CD流水线中更新一个环境之前,确保基线与运行态的一致性,如果不一致,则不进行更新,避免有人修改了基线的代码库,意外的被你捎带上了环境。
规模化的采纳最佳实践
上述方案最大的好处,就是采纳的都是标准的组件,具有很大的灵活性,和可定制性。
但这同时也是这个方案的坏处,就是太灵活了,各种最佳实践也需要慢慢摸索和调整。在调整的过程中可能会发现很多类似上面提到的“小细节”,需要进行规范或者封装。如果进行规模化的推广,那么就要求每个团队都有一个很熟悉这些工具的同事。如果无法找到这么多熟悉工具的同是,那就可以考虑对上述的思路进行产品化,使得大部分的开发人员都可以低成本的follow最佳实践。
云效的AppStack,就着眼解决这个问题,通过白屏化应用编排、版本管理、以及企业级应用编排模板等产品能力帮助降低开源工具使用门槛,提供了开箱即用的最佳实践。
- 应用编排。即上述的基于helm来描述多环境配置的产品化实现。
- 版本和基线。有了版本和基线,就可以快速地进行回滚和基于某个版本一键拉起环境等操作。
- 集成发布流水线。将上文中提到的常见的日常工作流程和版本结合在一起,避免每个团队分别配置。
原文链接
本文为阿里云原创内容,未经允许不得转载。