美文网首页微服务
《微服务设计》——部署

《微服务设计》——部署

作者: 王勇1024 | 来源:发表于2019-10-06 20:47 被阅读0次

    部署一个单块系统的流程非常简单。然而在众多相互依赖的微服务中,部署却是完全不同 的情况。如果部署的方法不合适,那么其带来的复杂程度会让你很痛苦。

    1. 持续集成简介

    CI(Continuous Integration,持续集成)能够保证新提交的代码与已有代码进行集成,从而让所有人保持同步。 CI 服务器会检 测到代码已提交并签出,然后花些时间来验证代码是否通过编译以及测试能否通过。

    作为这个流程的一部分,我们经常会生成一些构建物(artifact)以供后续验证使用,比如 启动一个服务并对其运行测试。理想情况下,这些构建物应该只生成一次,然后在本次提 交所对应的所有部署环节中使用。 这不仅可以避免多次重复做一件事情, 还可以保证部 署上线的构建物与测试通过的那个是同一个。为了重用构建物,需要把它们放在某个仓储 中。 CI 本身会提供这样的仓储,你也可以使用一个独立系统来做这件事情。

    CI 的好处有很多。通过它,我们能够得到关于代码质量的某种程度的快速反馈。 CI 可以 自动化生成二进制文件。用于生成这些构建物的所有代码都在版本的控制之下,所以如果 需要的话,可以重新生成这个版本的构建物。通过 CI 我们能够从已部署的构建物回溯到 相应的代码,有些 CI 工具,还可以使在这些代码和构建物上运行过的测试可视化。正是 因为上述这些好处, CI 才会成为一项如此成功的实践。

    1.1. 你真的在做CI吗

    我很喜欢 Jez Humble 用来测试别人是否真正理解 CI 的三个问题。

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

    你应该保证代码能够与已有代码进行集成。如果你的代码和其他人的代码没被频繁地放 在一起,那么将来的集成就会非常困难。即使你只使用生命周期很短的分支来管理这些 修改,也要尽可能频繁地把代码检入到单个主线分支中。

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

    如果没有测试,我们只能知道集成后没有语法错误,但无法知道系统的行为是否已经被 破坏。没有对代码行为进行验证的 CI 不是真正的 CI。

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

    绿色的构建意味着,我们的修改已经安全地和已有代码集成在了一起。红色的构建意味 着,最后一次修改很可能有问题,这时只能提交修复构建的代码。如果你允许别人在构 建失败时提交更多的修改,用于修复构建的时间就会大大增加。我见过在一个团队中构 建失败持续了好几天,最后花了很长时间才修复这个构建。

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

    2.1 把所有微服务放在同一个代码库中

    如果把所有微服务放在同一个代码库中,并且只有一个 CI 构建,从表面上看比其他方法要简单得多:因为你需要关心的代码库比较少,而且从概 念上来讲,这种构建也比较简单。开发者的工作也得到了简化:我只需要提交代码即可, 如果需要同时在多个服务上工作的话,一个提交就能搞定。

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

    但这种方式有明显的缺陷:

    1. 构建效率低下;只修改了一行代码,所有其他的服务都需要进行验证和构建,而事实上它们或许并不需要重新进行验证和构建,所 以这里我们花费了不必要的时间。
    2. 产生过多的构建物;每次构建都会生成一个构建物,致使我们不知道哪些构建物应该被重新部署,哪些不应该。
    3. 代码的管理会成为噩梦;很可能会频繁出现代码冲突,如果冲突解决过程有问题,还可能导致代码被覆盖,需要花大量时间解决冲突。
    4. 降低工作效率;如果这一行的修改导致构建失败, 那么在构建得到修复之前, 与其他服务相关 的代码也无法提交。

    2.2 将一个代码库的子目录映射到不同的构建中

    这种方法的一个变体是保留一个代码库,但是存在多个 CI 会分别映射到代码库的不同部 分,如下图所示。如果代码库的目录结构定义得合理,就会很容易把其中一部分映射到 一个构建中。总的来说我不太喜欢这个方法,因为这个模式可能是把双刃剑。一方面它会简化检出 / 检入的流程,但另一方面,它会让你觉得同时提交对多个服务的修改是一件很 容易的事情,从而做出将多个服务耦合在一起的修改。但是相对于只有一个构建的多个服务来说,这个方法已经好很多了。

    将一个代码库的子目录映射到不同的构建中

    2.3 每个微服务有一个源代码库和 CI 构建

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

    每个微服务都有 自己的代码库,分别与相应的 CI 绑定。当对代码库进行修改时,可以只运行相关的构建 以及其中的测试。我只会得到一个需要部署的构建物,代码库与团队所有权的匹配程度也 更高了。

    每个微服务有一个源代码库和 CI 构建

    每个与微服务相关的测试也应该和其本身的代码放在一起,这样就很容易知道对于某个服 务来说应该运行哪些测试。

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

    构建流水线可以很好地跟踪软件构建进度:每完成一个阶段,就离终点更近一步。流水线 也能够可视化本次构建物的软件质量。构建物会在整个构建的第一个环节生成,然后它会 被用在整个流水线中。随着构建物通过不同的阶段,我们越来越能确定该软件能够在生产 环境下正常工作。

    CD(Continuous Delivery, 持续交付)基于上述的这些概念,并在此之上有所发展。正如 Jez Humble 和 Dave Farley 的同名著作中提到的, CD 能够检查每次提交是否达到了部署到 生产环境的要求,并持续地把这些信息反馈给我们,它会把每次提交当成候选发布版本来 对待。

    在 CD 中,我们会把多阶段构 建流水线的概念进行扩展,从而覆盖软件通过的所有阶段,无论是手动的还是自动的。在下图中,我们可以看到一个熟悉的示例流水线。

    一个使用构建流水线建模的标准发布流程

    通过对整个软件上线过程进行建模,软件质量的可视化得到了极大改善,这可以大大减少 发布之间的间隔,因为可以在一个集中的地方看到构建和发布流程,这也是可以引入改进 的一个焦点。

    在微服务的世界,我们想要保证服务之间可以独立于彼此进行部署,所以每个服务都有自 己独立的 CI。 在流水线中,构建物会沿着上线方向进行移动。构建物的大小和形态可能会 有很大差别。

    4. 平台特定的构建物

    大多数技术栈都有相应的构建物类型, 同时也有相关的工具来创建和安装这些构建物。 Ruby 中有 gem,Java 中有 JAR 包和 WAR 包, Python 中有 egg。

    但是从微服务部署的角度来看, 在有些技术栈中只有构建物本身是不够的。 虽然可以把 Java 的 JAR 包做成可执行文件, 并在其中运行一个嵌入式的 HTTP 进程, 但对于类似于 Ruby 和 Python 这样的应用程序来说, 你需要使用一个运行在 Apache 或者 Nginx 中的进 程管理器。所以为了部署和启动这些构建物,需要安装和配置一些其他软件,然后再启动 这些构建物。类似于 Puppet 和 Chef 这样的自动化配置管理工具,就可以很好地解决这个 问题。

    另一个问题是,不同技术栈生成的构建物各不相同,所以混合不同的构建物进行部署就会 很复杂。可以尝试从某人想要同时部署多个服务的角度来考虑,比如,某个开发或者测试人员想要测试一些功能,或者做一次生产环境的部署。现在想象一下,所要部署的服务使 用了三种完全不同的部署机制,比如 Ruby 的 Gem、JAR 包和 Node.js 的 NPM 包,你会有 什么感觉?

    5. 操作系统构建物

    有一种方法可以避免多种技术栈下的构建物所带来的问题,那就是使用操作系统支持的构 建物。举个例子,对基于 RedHat 或者 CentOS 的系统来说,可以使用 RPM;对 Ubuntu 来 说,可以使用 deb 包;对 Windows 来说,可以使用 MSI。

    使用 OS 特定构建物的好处是,在做部署时不需要考虑底层使用的是什么技术。只需要简 单使用内置的工具就可以完成软件的安装。这些操作系统工具也可以进行软件的卸载及查 询,甚至还可以把 CI 生成的构建物推送到软件包仓库中。 OS 包管理工具,可以帮你完成 很多原本需要使用 Chef 或者 Puppet 来完成的工作。

    其缺点是,刚开始编写构建脚本的过程可能会比较困难。需要熟悉每个操作系统软件包的构建方式,并编写对应的构建脚本。
    另一个缺点,即如果你需要部署到多种操作系统的话,维护不同版本构建物 的开销就会很大。

    6. 定制化镜像

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

    和 Ansible 这类的工具, 能够很智能地避免重复安装已安装的软件。 但不幸 的是,这并不意味着在已经存在的机器上运行这些脚本总会很快,因为仅仅是做这些检查 就会花费很多时间。

    一种减少启动时间的方法是创建一个虚拟机镜像,其中包含一些常用的依赖。你可以把公共的工具安装在镜像上, 然后在部署软件时, 只需要根据该镜像创建一个实 例,之后在其之上安装最新的服务版本即可。

    你只需要构建一次镜像,然后根据这些镜像启动虚拟机,不需要再花费时间来安装相应的 依赖,因为它们已经在镜像中安装好了,这样就可以节省很多时间。如果你的核心依赖没 有改变,那么新版本的服务就可以继续使用相同的基础镜像。

    这个方法也有一些缺点。首先,构建镜像会花费大量的时间。其次,产生 的镜像可能会很大。

    6.1 将镜像作为构建物

    把服务本身也包含在镜像中,这样就把镜像变成了构建物。现在当你启动 镜像时,服务就已经就绪了。

    6.2 不可变服务器

    如果 部署完成后,有人登录到机器上修改了一些东西呢?这就会导致机器上的实际配置和源代 码管理中的配置不再一致,这个问题叫作 配置漂移 。

    为了避免这个问题,可以禁止对任何运行的服务器做手动修改。相反,无论修改多么小, 都需要经过构建流水线来创建新的机器。事实上,即使不使用镜像,你也可以实现类似的 模式,但它是把镜像作为构建物的一个非常合理的扩展。你甚至可以在镜像的创建过程中 禁止 SSH, 以确保没有人能够登录到机器上做任何修改。

    7. 服务与主机之间的映射

    每台机器(machine)应该有多少个服务?

    7.1 单主机多服务

    在每个主机上部署多个服务是很有吸引力的。
    首先,从主机管理的角度来看它更简单。在一个团队管理基础设施,另一个团队管理软件的模式下,管理基础设施团 队的工作量通常与所要管理的主机量成正比。如果单个主机包含更多的服务,那么主机管 理的工作量不会随着服务数量的增加而增加。

    其次是关于成本。很多关于部署和主机管理的工作实践都是为了优化稀缺资源 的利用。即使你有一个能够提供一 些配置和更改虚拟主机大小等服务的虚拟化平台,虚拟化的基础设施本身也会占用一部分 资源,从而减少服务可用的资源。

    每个主机多个微服务

    但这个模型也有一些挑战。
    首先, 它会使监控变得更加困难。 举个例子, 当监控 CPU 使 用率时, 应该监控每个单独的服务还是整个机器呢?

    服务之间的相互影响也是不可避免的。如果一个服务的负载很高,那么它有可能会过多占用系统其他部分的资源。

    服务的部署也会变得更复杂,因为很难保证对一个服务的部署不会影响其他的服务。是每个服务的依赖是不同的,甚至可能存在冲突。

    这个模型对团队的自治性也不利。如果不同团队所维护的服务安装在了同一台主机上,那 么谁来配置这些服务所在的主机呢?很有可能最后有一个专门的团队来做这些事情,这就 意味着,需要和更多人协调才能完成服务的部署。

    在单个主机上部署多个服务,会增加对单个服务进行扩展的复杂性。如果一个微服务处理 的数据很敏感,底层主机的配置也可能会有所不同,或者干脆把这台主机放置在不同的网 络中。把所有东西放在一台主机上意味着,即使每个服务的需求是不一样的,我们也不得不对它们一视同仁。

    按需计算平台的出现大 大降低了计算资源的成本,而虚拟化技术的革新,也使得内部基础设施的搭建更加灵活。

    7.2 应用程序容器

    如果你对基于 IIS 的 .NET 应用程序部署,或者基于 servlet 容器的 Java 应用程序部署比较 熟悉的话,那么应该非常了解把不同的服务放在同一个容器中,再把容器放置到单台主机 上的模式,如图 6-7 所示。这么做的初衷是使用容器来简化管理,比如对多实例提供集群 支持、监控等。

    单台主机多个微服务

    这种设置也可以节省语言运行时的开销。 比如, 在一个 Java servlet 容器中部署五个 Java 服务的话, 只需要启动一个 JVM 即可。 而如果在同一个主机上使用嵌入式容器的方式, 启动五个独立的 JVM 的话,开销就会相对较大。

    第一个缺点是,它会不可避免地限制技术栈的选择。你只能使用一种技术栈。

    除此之外, 它还会限制自动化和系统管理技术的选择

    因为它会影响服务的可伸缩性

    当我们考虑在微服务世界中使用的聚合监控时, 它们提供的监控能力又难以支撑

    其中的很多容器启动时间也特别长,这会影响开发人员的反馈周期。

    因为你的多个应用程序 都处在同一个进程中,所以分析资源的使用和线程也非常复杂

    7.3 每个主机一个服务

    每个主机一个服务的模型,这种模型避免了单主机多服务的问题,并简化 了监控和错误恢复。这种方式也可以减少潜在的单点故障。一台主机宕机只会影响一个服 务.

    我们也可以独立于其他服务很容易地对某一个服务进行扩展,安全性措施也可以更有 目的性地在更小范围内进行。

    每个主机一个微服务

    更重要的是,这样做之后我们才有可能采用一些不同的部署技术,比如前面提到的基于镜 像的部署或者不可变服务器模式。

    但主机数量的增加也可能是个问题。管理更多的服务器,运行更多不同的主机也会引入很多的隐式代价。

    7.4 平台即服务

    好的 PaaS 解决方案已经为你做了很多,它们能够很好地帮你管理数量众多的组件。

    8. 自动化

    如果服务器的数量翻倍,你的工作量也会翻倍!但是如果 我们将主机控制、服务部署等工作自动化,那么工作量肯定就不会随着主机数量的增加而 线性增长。

    但即使我们控制了主机的数量,还是会有很多服务。这就意味着有更多的部署要处理、更 多的服务要监控、更多的日志要收集,所以自动化很关键。

    自动化还能够帮助开发人员保持工作效率。自助式配置单个服务或者一组服务的能力,会 大大简化开发人员的工作。理想情况下,开发人员使用的工具链应该和部署生产环境时使 用的完全一样,这样就可以及早发现问题。

    相关文章

      网友评论

        本文标题:《微服务设计》——部署

        本文链接:https://www.haomeiwen.com/subject/hgaupctx.html