【微服务架构】 8 部署与配置

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gdp12315/article/details/90321372

8 从开发环境到生产环境

本节讨论如何构建微服务、部署微服务。通过这节内容,促进开发环境到生产环境的平滑移动:

  1. 一个微服务必须独立部署
  2. 一个新版本的微服务应该在一分钟内部署完毕,而不是一个小时
  3. 一个微服务应该具有容错能力,能够避免雪崩式的失败
  4. 一个微服务可以不改变代码就部署到不同目标环境

微服务架构需要很好的自动部署工具,帮助管理部署、测试和提升服务跨目标环境。

本章主要包括以下几个主题:

  1. 部署模式
  2. 部署管线和工具集
  3. 打包方式
  4. 跨阶段的应用配置

8.1 部署模式

传统单块构建和部署于一个单元上。单块应用构建时包括多个部分、多个Java平台、企业编辑文档。微服务与此相反,每个微服务独立部署。这样微服务配置提供更具弹性的消费者应对服务改变:

  1. 蓝/绿部署
    蓝绿部署时一种很常见的单块应用部署模式。这个方法的关键点在于我们需要维护两个工作环境的版本,例如一个可以作为另一个回滚,如果部署失败的话。这个过程包类似:
    a. 一个蓝系统正在运行
    b. 启动一个绿系统,测试该系统,验证它是否工作正常,然后将流量切换到绿系统。蓝系统作为独立运行以便发生绿系统发生失败的时候回滚
    c. 当绿系统运行稳定后,蓝系统将准备下一次更新,这个过程由相关替换,从绿到蓝

    在微服务环境下,一个备份的环境经常应用。每个系统(蓝、绿)包含所有运行稳定的微服务版本,由配置管理进管理。以这种方式周期性、协调的更新非耦合的服务。

  2. 金丝雀发布

金丝雀发布也被认为是阶段或增长首次增长。一个金丝雀发行目标是发现部署新版服务时的失败。类似蓝/绿部署,金丝雀发布作为备份环境的起始。

  1. 开关(toggling)

toggle 特性是服务内部用于启动或关闭新功能的标识。这个过程可以有效的服务消费者选择性的测试新功能(可能使用自定义的Http头),而不需要维护不同的服务版本或工程师首次协作。开关在单块应用中很常见,作为一种保护机制,避免变化对已有的消费者产生影响。

开关可以在环境中设置,一个服务可以打开或关闭某个功能。这些功能可以动态配置,以二进制的方式对服务运行,或者可以结合每个请求机制,例如HTTP头,来打开或关闭每个请求上的新功能。

toggles可以用于路由决策,可以作为中心服务的金丝雀版本的选择器。他们可以用作A/B测试,即一对一的进行可选实现方式的对比。

8.2 部署管线和工具集

微服务架构另一个优势就是敏捷性,由于每个独立的服务可以聚焦到一个范围,一个独立的生命周期,这使得可以花费更少的时间,将新的功能以微服务的方式实现,测试这些服务,部署这些功能到生产环境。微服务经常迭代构建,这意味着支持持续集成或部署。

待部署的artifacts经常运行在不同的系统,因此,为了使服务可以工作,很多系统需要进行配置。手动维护微服务到生产环境的部署步骤越多,越容易引起异常的错误,由于坏的输入或忘记某个步骤。人为干涉应该尽可能少。例如,在一个新版的服务部署到生产环境,由于某些原因可能手动方法,然而,都可以自动化。

根据Martin Fowler描述的部署管线(Deployment Pipeline)作为持续交付(Continuous Delivery)环境的一部分。部署管线描述了一个微服务到生产环境的步骤集合,通常将这个过程分为多个阶段,包含构建(building)、部署(deploying)和每个阶段的测试。DevOps技术可以简化部署管线的创建,相关的工具链支持将应用从开发环境部署到生产环境。

前面提到过,微服务的源码应该放到代码管理(SCM,Source Code Management)工具中,例如 Subversion,Git。微服务应该使用依赖管理工具,例如 Maven、Gradle。选择一个仓库来存储我们共享的代码库,例如,NexusArtifactory。使用持续集成工具(CI-Tools),例如,JenkinsBamboo

在自动化构建完微服务后,需要进行测试,有些测试需要部署微服务,这意味着在自动预分配的系统中设置好正确的配置。我们可以使用经典的脚本工具来正确的配置目标,但是我们可能管理平台或系统不同的脚本。脚本框架,例如 ChefPuppetAnsibleSalt能很好地处理这些情景。这些框架也可以用来初始化安装、修改已有的系统。我们描述配置文件的目标内容,这些工具可以处理不同目标系统变化产生的差异。

容器化技术也可以帮助我们获得正确配置的系统,对于动态预分配下,我们可以使用云主机服务基础设施即服务(IaaS),或者平台即服务(PaaS)。由于在我们系统中可以有大量的微服务,因此 一个Release Management 工具对我们来说很有用。我们必须监控大部分部署的系统,收集日志,用于审计或诊断。此外,还应该扫描微服务中的安全漏洞。

Maven和Gradle作为很好的构建工具,可用来创建微服务,并且能够管理JAR文件的依赖。两个工具都可以列出构建微服务所需要的Jar包,一般来说一个有区别的版本指示器可以让每次构建变得清晰,那个版本是部署的。

微服务模板可以帮助需要开发相同实现的微服务,例如 日志管理,在微服务系统中是必要的一部分,跟踪微服务之间的请求也很有用。如果每个微服务的开发团队,编写自己的日志管理模块,使用自己的日志格式,那么不同服务的日志文件的聚合将变得非常困难。安全也是基本需求,应该在统一的规范下进行开发。模板可以编写这些公共关注点,将这些写入惯例中,便于遵从。然而,有些开发者觉得这些模板太过约束,他们想忽视或摆脱这些要求。因此,规范在开发者之间需要进行权衡,避免构建的模板带来不好的影响。

8.3 打包选择

当我们构建完微服务后,选择哪种打包格式进行部署需要考虑。根据12要素以及云原生应用定义,创建不可变的artifact,运行到不同的环境而不需要更改代码。

部署到中的变化越多,风险越高,由于缺失配置,缺少依赖。

构建不可变、自包含的artifact,引入部署和的一致性,产品环境。

对于Java,一下是多种打包选择:

  1. WAR/EAR 文件部署到预安装的中间件上
  2. 可执行JAR文件
  3. 容器
  4. 每个微服务拥有自己的虚拟服务器

一般而言,我们可能想最小化服务部署、测试的环境差异,以及生产环境。有些打包和部署方式,使得分离部署和生产环境十分容易。以下是每种打包方式的优点和缺点。

JAR、 WAR、 EAR

这些都是Java EE 应用默认的打包形式,WAR 或 EAR 需要部署到一个专门的系统中,例如 Tomcat容器。

应用从开发环境到生产环境可能经历不同临时环境、不同的宿主机。虽然每个环境有相同的配置,但不是所有的情况都是这样。例如,相比开发或测试环境,生产环境可能需要更加保守的方法来进行更新。

这种方法可能产生以下几种问题:

  1. 很难实现短时间频率高的部署,例如,一天活一小时。有些应用需要重启应用服务器,这样可能影响同一个服务器上的其它服务。这种影响不可能每天或每小时执行。
  2. 如果不同的应用都部署在同一个应用服务器,那么一个应用可能的加载可能会影响另一个应用的性能
  3. 基础设施很难适应,应用程序加载时的高速变化
  4. 开发者和运营管理者的责任分离,可能导致很难确定部署问题由谁造成

如果这种模式在微服务中使用,那么服务和服务器应该是一对一的关系。这种配置很常见,例如,PaaS环境下,我只需要推送WAR 或 EAR来刷新构建的服务。在中间件实例中部署隔离的服对保持服务的独立伸缩性是非常重要的,并且这有利于降低服务升级对其他服务的影响。

正如上面提到的,这种部署方法仍然有风向,即便服务已经隔离,但是仍然运行在中间件中以提供服务运行所需的依赖。然而,因为反映了大部分传统单块应用的经验,当我开始一个新的微服务应用时可以作为第一步来进行。

可执行JAR文件

为了避免依赖部署环境以提供包所需要依赖,我们可以将应用打包至可执行的JAR包来运行服务。这个大的JAR文件包含服务类文件,以及支持的共享库,包括Servlet容器或其他java EE中间件。这种技术消除由于开发和测试环境与生产环境依赖不一致造成的部署问题。

自包含JAR文件一帮容易处理,可以创建一致、可复写的环境,用于本地开发是测试,以及部署到目标环境中。

容器

前面的阶段需要部署到操作系统中才能运行,即必须安装操作系统,并且需要配置运行JAR的环境。为了避免不同操作系统配置不同的所引发的问题,我们需要一种策略保持微服务在操作系统中有相同环境配置。

容器是一种轻量的虚拟化技术,能够在一个操作系统上运行。Docker提供一个基础设施,能构建完整的文件系统,包含所有用于原型应用所需的操作系统的元素(工具、库),以及应用自身。Docker引擎是一个轻量的运行时,提供容器、宿主机以及支持工具之间的抽象和隔离。

Docker容器可以隔离多个应用,分离宿主操作上的应用。容器隔离和轻量化属性使得在一个操作系统中可以运行不止一个Docker容器。这个配置意味着我们不需要很多虚拟服务来按照微服务所需的操作系统。

在这里插入图片描述

Docker镜像是不可变的artifact。当一个应用编译成Docker镜像后,这些镜像可以用于启动不同部署环境下的容器,确保相同的技术栈用于所有环境中。使用Docker容器后,应用以怎样的打包格式就不重要了,这些镜像的配置来决定容器如何启动。微服务的配置包含在容器中,可以使用环境变量或者挂在的volumes。

微服务所需的Data store是或其他后端不应该包在同一个容器中。这些后端也作为服务,是微服务的一个重要特征。数据存储,例如也可以有Docker容器来管理。一般而言,我们不应该将同一个服务的多个实例安装到同一个宿主机内。

一个微服务独立一个服务器

为了保持多个阶段的操作系统统一,需要使用相同虚拟化的服务,作为每个阶段的基础。为了实现这个目标,安装操作系统和其他所需的工具,然而创建一个服务模板。

使用这个基础模板来创建每个微服务的系统服务,是另一个不可变服务。

我们可以使用虚拟工具、云平台和第三方选择来管理这些服务和服务模板。某些第三方选择可用,包括Vagrant、VMware、VirtualBox。当使用软件配置和自动化技术,例如Salt和Ansible,使独立服务上的微服务虚拟变得更具弹性。

这种方式可以产生两种:

  1. 将微服务打包到单个虚拟服务器上
  2. 一个虚拟服务部署多个微服务包

在这里插入图片描述

当每个虚拟服务器上不是一个微服务,这种方式很好的隔离多个虚拟服务器,运行多个微服务。针对特定微服务的需要可以修改系统配置。当错误发生时,可以很容易的分离不相关的信息或其他服务的影响,来定位问题。这种不相关的应用减少影响可以有利提升性能。一个应用不能占用另一个隔离的服务的资源。

这种方式的缺点是,每个虚拟的服务器需要统一和协调管理。所有的资源消耗也很高,因为每个虚拟服务器需要自己独立的操作系统。某些虚拟解决方法有对这个问题做优化,可以减少多个操作系统处理器的使用。

只有当多个微服务有相似的资源使用模式,才适合部署在一个虚拟服务器上,这样可以降低服务之间的影响。

操作系统应该具有将多个微服务部署在同一个虚拟服务器上的配置。不同的配置之间不应该相互影响或在多个阶段与操作的的配置发生冲突。从共享系统配置的成本上考虑,这种方式能有减少部署微服务所需的虚拟系统数量。在这个配置下,我们可以使用JVM中的共享类缓存,这能减少启动微服务的次数。

一台服务器运行多个微服务

本节的内容,适合一个服务器上部署多个微服务,无论使用容器或虚拟系统的方式实现。

如果在服务器上部署多个微服务,那么需要注意Java运行时的线程池优化。JVM查询操作系统,获取服务器的CPU核数,根据这个信息定义管理线程池的策略。当使用虚拟硬盘时,JVM通常会获取错误的可用资源的信息,这是由于服务器上每个JVM所看到的相同可用CPU信息,甚至很少考虑一些特殊进程对于可用CPU的数量。这种情景容易在某些线程池切换算法中引起异常,由于无法获取预期中的CPU个数,可能触发错误的优化。

从其他方面上看,我们应该考虑微服务如何在相同的服务器上共享或竞争服务器上的资源。提取了解每个微服务的资源需要,或获取解决这个问题的数据,避免这类问题发生。例如,随着缓存的增长,微服务内部的缓存可能引起问题。因此,一般在系统外部实现缓存。外部的缓存服务应该看字服务的附属,就像数据存储(data store)一样。

最佳实践

抢眼一看,在同一个服务器上运行多个Docker容器打包方式的微服务与在同一个服务器上运行多个微服务包是一样的。但是深入分析后,这两种是显然不同的部署模式。同一个服务器上运行多个非Docker容器化的微服务,只能在所有微服务需要相同配置的操作系统条件下进行。然而,容器提供更加高级隔离机制,同时重复利用宿主机的资源与公共的底层操作系统。微服务需要不同或冲突的操作系统配置可以很开心的在Docker容器中运行。

以下是比较喜欢的打包实践:

  1. 虚拟化或云服务一般作为基础设施,由于他们提供动态的资源来帮助系统或微服务处理容量上的需求变化。
  2. 在每个部署阶段应该保持系统级别的配置一样,或者应用之间应该隔离,例如使用容器技术
  3. 微服务应该打包成不可变的包(Immutable artifact),能促进各个阶段的不可变能力
  4. 对于微服务的Immutable artifact,应该独立运行并且自包含(self-contained)。例如可执行的Jar包或Docker容器。
  5. 每个Immutable artifact应该包含一个微服务应用,以及他所支持的运行时和库。
  6. 当主持共享资源来避免自己竞争引起的冲突,应该考虑资源需要
  7. 共享库应该作为应用的一部分,而不是有宿主机提供共享或依赖。

在这里插入图片描述

上图显示的是Java EE的微服务,以Docker容器的方式打包。

8.4 跨阶段应用配置

每个微服务应该是一个不可变的部署包(immutable deployment artifact)。在它构建完成之后,不同的部署环境不应该改变其中的内容。因此,各个部署环境下,环境变量可能不一样,这些需要再部署时注入到artifact中。此外,使用不可变服务器(immutable server)来部署,应用配置应该尽可能的保持不变。只需要改变不同服务器上的配置的值,例如(端口,IP地址,数据库用户名,线程池大小),其他应用的配置应该尽可能保持一致。我们改变的值越多,那么我们在部署到下一个环境中的风险越高。如果这些值在后续阶段中需要改变,我们应该测试这些改变后服务表现的行为。

微服务的配置可以使用不同的方式:

  1. 环境变量
  2. 配置文件
  3. 配置系统

环境变量

一种获取微服务外部配置的方式是,使用环境变量,这是一种比较常用的方法来向运行的进程中传递数值。

环境变量都是基于字符的Key-value键值对的形式,这种方式设置不同操作系统的值。

使用环境变量相比传递系统属性(Java command line option -D)至微服务更好,因为在云环境下,我们一般不能直接访问进程的命令行。云环境通常使用环境变量。

例如 Cloud Foundry提供一个VCAP_SERVICES的环境变量,它是一个JSON文档,包含服务实例的信息,
https://docs.cloudfoundry.org/devguide/deploy-apps/environment-variable.html#VCAP-SERVICES

Spring boot中有个Connector可以访问这个变量

https://spring.io/blog/2015/04/27/binding-to-data-services-with-spring-boot-in-cloud-foundry

配置文件

在Java应用中,另一个可选的获取配置数据的方式是使用JNDI(Java Naming and Directory Interface)API来查询目录服务。每个JavaEE应用必须JNDI能力,每个在JNDI命名空间中定义的元素可以注入到java CDI,这样访问这些数值就很容易了。

Spring Boot提供应用外部的配置文件,一般使用YAML文件来进行配置。

https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html

配置系统

第三种可选的方法来配置微服务系统,使用特点的服务器。这些配置系统与Key-value存储一样可以持久化配置不同微服务。

根据CAP原则,我们必须选中其中的两项。在微服务系统中,缺少配置系统将无法工作,配置系统必须是分离的,这意味着我们配置系统只能是具有不一致的数据但可用,或者具有限制性的可用但是数据是一致的。

以下是微服务体系典型的配置系统:

  1. Apache Zookeeper http://zookeeper.apache.org/
  2. etcd https://coreos.com/etcd/
  3. Netflix archaius https://github.com/Netflix/archaius
  4. Consul http://www.consul.io/

根据CAP原则,ZooKeeper 和 etcd 是CP系统, Archaius 和 Consul是 AP属性,每个系统都可以再Java应所有中使用。根据具体的微服务应用需求可以选择不同的配置系统。

使用配置系统的优势是配置数据可以在运行时进行修改,这些改变不是由环境变量或配置文件实现的。

程序

微服务的配置代码不应该在业务逻辑中完成,最好在中心的位置进行这些配置编码。在大多数情景下,访问配置数据是基于技术决定,这不会影响业务逻辑。

如果配置系统使用CDI(Contexts and Dependency Injection),那么我们必须处理运行时的数据值改变。在这种情况下,CDI Bean的注解 @RequestScoped(或者 @Dependent)需要确保每次请求时这些数值被刷新。某些配置系统提供回调机制来获取客户端的信息,当发生数值改变时。结合CDI的producer(@Produces),我们可以很容易的附加配置系统到我们应用中。

最佳实践

综上所述,以下是配置的最佳实践:

  1. 配置应该保持到部署包的外部,可以采用三种方式进行配置(环境变量、配置文件、配置系统)。这几种配置方式取决于应用的需求。

  2. 动态配置数据应用有配置系统进行管理,但是应用必须对配置改变进处理

  3. 尽可能的保持系统是不可变的,这条准则可适用于配置上以降低风险

  4. 微服务的业务逻辑不应该处理配置数据,这些应该在中心helper 类或包中进行。

猜你喜欢

转载自blog.csdn.net/gdp12315/article/details/90321372