原文链接:https://blogs.oracle.com/developers/getting-started-with-microservices-part-four

到了结束“从微服务开始”这一系列文章的时候了。第一部分讨论了一些微服务的主要优势,并且接触到一些在使用微服务时需要考虑的问题;第二部分考虑了容器与微服务结合;第三部分讲到实施微服务的一些基本模式和最佳实践。

在本文中,我们将探讨通过容器化微服务使用DevOps原则和实践的关键环节。

简介

采用微服务架构最重要的一个原因是加快交付速度。为了实现高速的交付,高效的DevOps流程非常重要。在微服务架构中有很多细节需要在实现DevOps流程时考虑。在本文中我们将涉及一些容器化微服务的模式和流程。很多内容都是我们在创建Oracle Cloud云服务的时候与客户一起获得的经验和教训。

CI/CD和CD

可能你对于缩略词CI/CD和CD还有一些困惑,所以在我们深入流程本身之前,我们先明确一下这些缩略词的意思。表1描述了这些缩略词。

表1: CI/CD和CD的定义

缩略词

定义

CI——持续集成

通过自动化构建向共享代码库当中频繁的进行代码检入。能够帮助团队在代码开发的早期捕捉到潜在的集成问题。

CD——持续交付

确保代码总是处于生产就绪状态。这种就绪状态通常是通过持续集成(CI)和高级测试(比如负载测试和/或压力测试)来保证的。一旦声明为“就绪”,部署到生产系统中的过程就由DevOps人员来手工完成。

CD——持续部署

与持续交付相同,不同之处是部署到生产环境是由CI/CD系统自动化完成的。

DevOps流程

部署

DevOps流程是从开发人员开始的。当使用容器工作时,作为开发人员,你会有多种选择。我们不会在这方面涉及太多的细节,因为它高度依赖于你的开发偏好和公司策略,但是列出最常用的选择还是很有帮助的。表2提供了它们的概览。

表2: 几种常用的部署选项

选择

描述

本地化开发

在本地使用你所偏好的IDE进行代码的构建和调试。作为CI流程的一部分,代码是编译过的,并且容器镜像创建后上传到一个容器注册库当中。

注意:作为镜像创建的一部分,对镜像设置版本非常重要。在本文中你会看到更多关于版本控制的特性。

开发容器

开发容器镜像包含应用以及应用诊断/测试所需要的工具和组件。在你开发主机上的源代码目录会作为卷映射到容器,应用在本地容器环境中的容器里面进行运行来进行测试,比如Docker for Mac。开发容器本身通常只会在开发阶段使用,不会被用于生产环境。

应用容器

服务运行在一个类生产容器中进行测试。能够让开发人员在真实环境下测试他们的代码。通常你要将开发和运行容器分离。这种方式需要许多重要的额外计划和协调,比如维护一个类生产环境的容器,并且确保每一个团队成员都使用的是相同的版本。

CI/CD管道

在Oracle,大多数的微服务平台开发团队都在使用本地化开发方式,因为通过它能够继续使用它们选择的的构建工具和系统,为每一个团队带来了灵活性。有的团队使用Maven,有的使用Gradle。他们甚至使用不同的工具和构建系统,每一个团队产出一个容器镜像,作为我们持续集成的一部分。另一方面,每一个服务开发团队都有他们自己的CI/CD管道,来实现独立部署。图1为一个从本地化开发方式开始的DevOps流程的高阶概览。


1:使用本地化开发的微服务DevOps流程

以下是管道中每一个阶段所发生的高阶步骤:

  1. 服务开发团队的开发人员检入源代码。
  1. 构建被触发。在该阶段,单元测试和消费者契约测试将针对构建进行执行。
  1. 如果测试成功,将会执行一个命令,来创建一个服务的容器镜像。镜像的标记/版本非常重要。
  1. 一旦镜像创建完成,它就会被推送到容器注册库。
  1. 最后一个步骤,镜像以手工(持续交付)或者自动(持续部署)的拉式或者推送方式,送入目标环境,比如,一个Kubernetes集群。一旦镜像在集群中可用,容器就会启动,并且服务启动并运行。

这种DevOps流程的最后一方面是一旦服务部署到一个目标环境,就开始收集遥测和诊断数据。数据的收集不止包括“通常”的遥测数据,比如资源消耗、错误和警告,还包括请求消息的端到端追踪,在某些情况下,甚至是使用的功能。收集的所有数据不仅能够帮助你更高的理解服务运行时的行为,还能够给你一些从最终用户角度如何使用你的服务的有价值的洞察,所以你能够基于数据,而不是基于意见,做出产品决策。

这里描述了一个简化的流程,但是它应该能够给你一个在服务开发过程方面很好的想法,是你能够在最终环境中使用容器化服务。

测试

在大多数的企业当中,一个月最多会有一次交付,每一次交付通常都会需要大量的计划与交付会议。在企业需要对bug、客户反馈或者新的市场需求快速响应的敏捷世界当中,缓慢的行动是一个明显的弱点。部署的自动化是微服务DevOps实践的一个关键支柱,但是要使用一个完全自动化的DevOps管道,你需要能够相信你的服务能够满足你的质量标准。所以,在交付管道中的每一个阶段中进行彻底的测试是非常重要的。测试是由你特定的应用需求驱动的,比如SLA,对于微服务应用的不同的测试方法、测试自动化和测试方法已经远超过了本文的范畴。也就是说,值得指出的是两种主要的方法。第一种是使用阶段式的环境,第二种是在生产环境中测试。

阶段式的环境

下面展示了在阶段式环境中如何实现不同的测试阶段的例子。实际上,下面的例子从更高的层面,描述了我们的一个使用微服务架构设计的内部服务是如何处理交付和测试的。图2展示了交付管道期间的测试阶段。


图2:交付管道测试阶段

  1. 测试代码质量:在这个阶段,在服务级别上有大量的单元和集成测试需要被执行,以便在早期捕捉到问题。
  1. 沙箱:在这个阶段,我们将服务部署到一个隔离的沙箱中,比如一个虚拟机,托管当前部署的所有其他服务。这也是服务升级的第一个检查点。升级后,我们运行自动化的功能验收测试。因为功能验收测试要花费很长的时间,我们将它分成每个10分钟的组块,这样我们就可以并行的执行这些测试。底线是那些开发人员只需要等待10分钟就可以知道他们的服务能否工作。
  1. 预阶段:在这个阶段,我们使用一个类生产环境(在我们的例子里,我们的例子中它运行在我们的Oracle Bare Metal Cloud当中)。在这个环境中,我们运行额外的端到端测试来代表用户环境,以便检测是否所有的服务能够在一个类生产的目标环境中一起工作。
  1. 预产品阶段:这是最终测试阶段,来运行非功能性测试,比如压力和性能测试、疲劳测试,再次进行升级和功能验收测试。这些测试每天晚上都会执行。根据测试的输出结果,我们声明服务交付的候选版本。

在我们的案例中,我们会自动化的将一个候选版本发布到产品中;我们基本上只做持续交付。主要原因,除了商业决策,是我们必须要说明性能与疲劳测试的结果,在这些点上我们无法进行自动化。

如果你对我们如何处理产品级别的容器CI/CD和测试细节感兴趣,可以参考OracleCode发布的“Show and Tell Session onHandling Production Grade Containers”。

产品环境中的测试

对很多企业来说,自动化部署到生产环境当中是非常可怕的想法。那就是说,那些掌握了持续部署的公司会有一些被证明很成功的测试技术。

滚动升级

滚动升级是一种通常用于平台级别迁移的的功能。也就是说,当你在你的环境中部署了一些新版本的服务时,系统处于从之前的服务版本升级到新版本的过程中,健康检测会被执。如果部署升级失败,会进行回滚。它的优势是在部署的过程当中,用户只会感受到很小的影响。前面提过,根据平台的不同,你可能有多种滚动升级的实施方法。以Kubernetes为例,它在系统中保存着整个部署的历史,让你能够很容易的回滚到之前的版本。

金丝雀部署/测试

金丝雀测试是将一个新版本的微服务部署到一个使用用户比较少的环境当中,来确保新版本服务的适当的行为。比如,你可能需要确保新版本服务与其他微服务的集成工作正常,并且资源消耗,例如CPU、内存、硬盘空间等,都在预期的范围之内。

比如,我们有一个新版本的前端微服务要进行金丝雀测试。新版本的服务部署到生产环境当中,流量分割如图3所示,有99%的流量分流到当前版本(v1.0),1%的流量分流到新版本(v1.1)。如果新版本满足你的需求,那么你就可以增加流量的比例,知道100%的流量都使用新的版本。

如果在新版本发布期间的任何时间点发生失败,你都可以轻松的将流量切回到v1.0,而不需要回滚或者重新部署。


图3:金丝雀部署

蓝绿部署

蓝绿部署的概念与金丝雀部署很相似,不同之处是这种方式假设你有两种生产环境,其中一个环境处理100%的流量。当你为服务准备一个新的交付时,你在蓝色环境当中进行最终阶段的测试。一旦你的测试和遥测数据显示你的新版本在蓝色环境当中工作正常,你的路由或者网关就可以将所有的流量定向到蓝色环境,绿色环境将会空闲。蓝绿部署是一种非常强大的方式来推出一个新的版本,但是它也有特定的缺点。比如,它需要额外的基础架构环境。也就是说,虽然现代的平台,比如Kubernetes允许你实现蓝绿部署,而不需要额外的集群,你可能会考虑他们的开箱即用的测试与部署到生产环境的功能,比如滚动升级和金丝雀交付。

A/B测试

虽然A/B测试能够与金丝雀或者蓝绿部署结合,但它却是一个非常不同的东西。A/B测试的目标是服务和特性的使用行为,通常用来验证一个假设或者度量两个版本的服务或者特性,以及他们在性能、可发现性和可用性方面的相互关系。A/B测试通常理由特性标记(特性开关),能够让你动态的打开和关闭特性。

之前提到过,好消息是很多容器化的平台,比如Kubernetes提供了对在生产环境当中进行测试的开箱即用的支持。如果开箱即用的功能不满足你的需要,你可以考虑使用比如ISTIO或者Linkerd这样的服务。我们将会在后续的面向关于现代API/服务方式创建微服务的文章中,详细介绍这些服务。

容器

版本控制

容器的版本控制可以通过对镜像添加标签来实现。默认情况下,当时使用“docker build”创建一个容器镜像,不会有版本控制,Docker会给他增加一个“:lastest”标签。这可能会引起困惑,因为“latest”并不意味着最后创建的镜像,它实际上以为这没有一个明显的定义标签的最新的镜像。对于这个和其他版本控制的好处,你应该总是使用适当的容器镜像版本控制,比如fe-commerce:1.0.0-b21,来为你的镜像添加标签。

镜像大小

通常的设想中,容器都很小,但是这并不一定正确。有些大的容器镜像的大小与小的VM的相当。镜像大小在很多场景中都很重要,扩展场景是一个很明显的例子。假设你的集群在满负荷运行,你需要增加其他的主机到集群当中(扩展)来分担服务A的负载;在这个例子当中,在编排器为服务A启动一个容器之前,镜像需要从注册库当中下载。根据镜像的延迟和大小,这需要花费一点时间,所以作为一个规则,你应该总是考虑使用尺寸更小的基于比如Oracle Linux 7.1的镜像。通过将你的容器注册库尽可能的与托管服务的集群靠近,会在一定程度上缓解这个问题。

编排

在很多情况下,当容器中实际的服务仍然在启动时,编排器就报告容器运行的状态。如果这个级别的时间对你很重要,你可以为服务配置一个健康检查URL,这样就能够确切的知道容器和里面的处理流程是启动和运行的了。这是进行健康检查的一个最佳实践,而不仅仅是检查它是否是活动状态,并且能够返回200OK。健康检查应该被设计成能够从功能的角度报告服务的实际状态。如果服务没有处于健康状态,编排器需要采取适当的行动,比如重启容器。

测试

下面是一些从测试中获得的经验教训

  • 为开发、调试和测试分别准备隔离的沙箱环境
    • 团队成员应该很容易的建立他们自己的隔离环境
  • 单元测试/集成测试/端到端测试的敏捷“测试金字塔”
    • 更多高质量的单元测试;更少的端到端测试来降低CI周期时间
  • 在可能的地方进行并行测试
    • 用更少的事件执行更多的测试
  • 在CI/CD管道的早期测试升级
    • 如果升级失败/引入回归,能够在其他的昂贵测试上节省时间
  • 立即解决端到端测试中的间歇性故障
    • 优先处理这些故障,识别根本原因/错误组件并进行修复

总结

在本系列博客的第四部分,我们了解了容器化微服务DevOps的基础。这是本系列文章的结论。希望你已经了解了那些开发人员进入容器化微服务世界时需要了解事情。


Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐