微服务之部署

2019-11-04 16:36:35 浏览数 (1)

如何在细粒度的架构中更好的微服务。这里会从持续集成和持续交付说起。

1.持续集成简介

CI(Continuous Integration , 持续集成)

CI能够保证新提交的代码与已有的代码进行集成,从而保证所有人保持同步。CI服务器会检测到

代码已提交并签出,然后花些时间来验证代码是否通过编译以及测试能否通过。

作为这个流程的一部分,我们经常会生成一些构建物(artifact)以供后续验证使用。

理想情况下,这些构建物应该只生成一次,然后在本次提交所对应的所有部署环节中使用。

CI的好处很多。通过它,我们能够得到关于代码质量的某种程度的快速反馈。

CI可以自动化生成二进制文件。用于生成这些构建物的所有代码都在版本的控制之下,

所以如果需要的话,可以重新生成这个版本的构建物。通过CI我们能够从已部署的构建物回溯到

相应的代码,有些CI工具,还可以使这些代码和构建物上运行过的测试可视化。

持续集成允许我们更快速,更容易的修改代码。

有人任务使用了CI工具就算采用了CI这个实践,事实上,只有工具是远远不够的。

测试别人是否真正理解CI的三个问题?

  • 你是否每天签入代码到主线?

你应该保证代码能够与已有代码进行集成

  • 你是否有一组测试来验证修改?

如果没有测试,我们只能知道集成后没有语法错误,但无法知道系统的行为是否已经被破坏。

没有对代码行为进行验证的CI不是真正的CI。

  • 当构建失败后,团队是否把修复CI当做第一优先级的事情来做?

绿色的构建意味着,我们的修改已经安全地和已有代码集成在了一起。红色的构建意味着,最后一次修改很可能有问题,

这时只能提交修复构建的代码。

2.把持续集成映射到微服务

前面已经提到过,每个微服务应该能够独立于其他服务进行部署。

所以如何在微服务、CI构建及源代码三者之间,建立起合适的映射呢?

最简单的做法,如下

图 6-1 把所有微服务放在同一个代码库中,并且只有一个构建

这种方法从表面上看比其他方法要简单的多:因为你需要关心的代码库比较少,

而且从概念上来讲,这种构建也比较简单。开发者的工作也得到了简化:

我们只需要提交代码即可,如果需要同时在多个服务上工作的话,一个提交就能搞定。

在同步发布(lock - step release)中,你需要一次性部署多个服务。

一般来讲,我们绝对应该避免这个模式,但是在项目初期是个例外。

当仅有一个团队在所有的服务上工作时,这种模式在短时间内是可接受的。

这种模式存在很多明显的缺点。

如果我仅修改了图6-1中用户服务中的一行代码,所有其他服务都需要进行验证和构建,

而事实上它们或许并不需要重新进行验证和构建,所以这里我们花费了不必要的时间。

更糟糕的是,我不知道那些构建物应该被重新部署,哪些不应该。

使用这种方式的组织,往往都会退回到同时部署所有代码的模式,而这也正是我们非常不想看到的。

很不幸,如果这一行的修改导致构建失败,那么在构建得到修复之前,其他服务相关的代码也无法提交。

这种方法的一个变体是保留一个代码库,但是存在多个CI会分别映射到代码库的不同部分。

如图 6-2

这种模式是个双刃剑。

一方面它会简化检出/检入的流程,但是另一方面,它会让你觉得同时提交对多个服务的修改

是一件简单的事情,从而做出将多个服务耦合在一起的修改。

但是相对于只有一个构建的多个服务来说,这个方式已经好很多了。

还有一种比较好的方式,每个微服务都有自己的CI,这样就可以将该微服务部署到生产环境之前做一个快速的验证。

如图6-3

这里的每个微服务都有自己的代码库,分别于相应的CI绑定。

当对代码库进行修改时,可以只运行相关的构建以及其中的测试。

每个微服务都有自己的代码库和构建流程。

我们也会使用CI构建流程,全自动话的创建出用于部署的构建物。

3. 构建流水线和持续交付

把一个构建分成多个阶段是很有价值的。

因为有的测试运行快,涉及范围小,有的测试运行耗时,涉及范围广,

如果放一起,快速如果失败,还会接着运行耗时的测试,这样就不合理。

解决这个问题的一个方案是,将构建分解成为多个阶段,从而得到我们熟知的构建流水线。

在第一个阶段运行快速测试,在第二个阶段运行耗时测试。

构建流水线可以很好的跟踪软件构建进度:每完成一个阶段,就离终点更近一步。

流水线也能够可视化本次构建物的软件质量。构建物会在整个构建的第一个环节生成,

然后会被用在整个流水线中。

CD(Continuous Delivery, 持续交付)基于上述概念,并在此之上有所发展。

CD能够检查每次提交是否达到了部署生成环境的要求,并持续地把这些信息反馈给我们,

它会把每次提交当成候选发布版本来对待。

为了更好的理解这些概念,我们需要对从代码提交及部署到生产环境这个过程中,所需要经历的流程进行建模,

并知道哪些版本的软件时可发布的。

UAT(User Acceptance Testing, 用户验收测试)流程。

通过对整个软件上线过程进行建模,软件质量的可视化得到了极大改善,这可以大大减少发布之间的间隔,

因为可以在一个集中的地方看到构建和发布流程,这也是可以引入改进的一个焦点。

在微服务的世界,我们想要保证服务之间可以独立于彼此进行部署,所以每个服务都有自己独立的CI.

不可避免的例外

所有好的规则都需要考虑例外。

当一个团队刚开始启动一个新项目时,尤其是什么都没有的情况下,你可能会花很多时间来识别出服务的边界。

所以在你识别出稳定的领域之前,可以把初始服务都放在一起。

在最开始的阶段,经常会发生跨服务边界的修改,所以时常会有些内容移入或者移出某个服务。

在这个阶段,把所有的服务都放在一个单独的构建中,可以减轻跨服务修改所带来的代价。

当然,在这个阶段你必须把所有服务打包发布,但这应该是一个过渡步骤。

4.平台特定的构建物

大多数技术栈都有相应的构建物类型,同时也有相关的工具来创建和安装这些构建物。

Ruby中有gem,Java中有JAR包和WAR包,Python中有egg。

但是,从微服务部署的角度来看,在有些技术栈中只有构建物本身是不够的。

所以为了部署和启动这些构建物,需要安装和配置一些其他软件,再启动这些构建物。

自动化可以对不同构建物的底层部署机制进行屏蔽。

5.操作系统构建物

有一种方法可以避免多种技术栈下的构建物所带来的问题,那就是使用操作系统支持的构建物。

举个例子,对基于RedHat或者CentOS的系统来说,可以使用RPM;对于Ubuntu来说,可以使用deb包;

对于Windows来说,可以使用MSI。

使用OS特定构建物的好处是,在做部署时不需要考虑底层使用的是什么技术。只需要简单使用内置的工具就可以完成软件的安装。

OS包管理工具,可以帮你完成很多原本需要使用Chef或者Puppet来完成的工作。

其缺点是,刚开始编写构建脚本的过程可能会比较困难。

当然,还有另一个缺点,即如果你需要部署到多个操作系统的话,维护不同版本构建物的开销就会很大。

但如果软件时部署在你可控的机器上,那么建议,尽量减少需要维护的操作系统的数量,最好只维护一种。

它可以大大减少不同机器之间可能存在的不同之处,并减小部署和维护的工作量。

特别是如果你在linux上工作,而且采用多种技术栈来部署微服务,那么这种方法就很合适。

6.定制化镜像

使用类似Puppet、Chef及Ansible这些自动化配置管理工具的一个问题是,需要花费大量时间在机器上运行这些脚本。

什么是蓝绿部署?

蓝绿部署允许我们在老版本服务不下线的同时,去部署新版本的服务。可以减少在部署时,服务停止的时间增加。

一种减少启动时间的方法是创建一个虚拟机镜像,其中包含一些常用的依赖。

现在你可以把公共的工具安装在镜像上,然后在部署软件时,只需要根据该镜像创建一个实例,

之后在其上安装最新的服务版本即可。

你只需要构建一次镜像,然后根据这些镜像启动虚拟机,不需要再花费时间来安装相应的依赖,

因为它们已经在镜像中安装好了,这样就可以节省很多时间。如果你的核心依赖没有改变,

那么新版本的服务就可以继续使用相同的基础镜像。

这个方法也有些缺点。

首先,构建镜像会花费大量的时间。

其次,产生的镜像可能会很大。例如,当你创建VMWare镜像时,在网络上传送一个20GB的镜像文件会怎么样。

由于历史原因,构建不同平台上的镜像所需的工具是不一样的。

6.1 将镜像作为构建物

我们可以把服务本身也包含在镜像中,这样就把镜像变成了构建物。

就像使用OS特定软件包那样,可以认为这些VM镜像时对不同技术栈的一层抽象。

我们不需要关心运行在镜像中的服务,所使用的语言是Ruby还是Java,最终构建物是gem还是JAR包,

我们唯一需要关心的就是它能否工作。

这个简洁的方法有助于我们实现另一个部署概念:不可变服务器。

6.2 不可变服务器

通过把配置都存到版本控制中,我们可以自动化重建服务,甚至重建整个环境。

但是如果部署完成后,有人登陆到机器上修改了一些东西呢?

这就会导致机器上的实际配置和源代码管理中的配置不再一致,这个问题叫做配置漂移。

为了避免这个问题,可以禁止对任何运行的服务器做手动修改。

相反,无论修改多么小,都需要经过构建流水线来创建新的机器。

7.环境

0 人点赞