《微服务设计》,Building Microservices,作者Sam Newman,译者崔力强、张骏,人民邮电出版社,2016年。
笔记中有些内容直接引用原书。
================================================================
第六章部署
1.持续集成(CI)。好处:能够得到关于代码质量的某种程度的快速反馈;自动生成二进制文件;代码在版本控制之下,需要的话可以重新生成某个版本的构建物;可以从部署的构建物回溯到对应版本的代码;有些CI工具可以使运行过的测试可视化。
是否真正理解CI的三个问题:你是否每天签入代码到主线?你是否有一组测试来验证修改?当构建失败后,团队是否把修复CI当作第一优先级的事情来做?
2.把持续集成映射到微服务
微服务、CI构建、源代码三者的关系如何?分步骤开始:a.所有代码在一个代码库、只有一个CI构建,生成多个微服务。缺点:修改某服务的一行代码,所有服务代码都需要重新构建和验证,浪费时间和资源,更会导致不知道哪些构建物要重新部署。b.还是一个代码库,但是多个CI分别映射代码库的不同部分。优点:简化检出/检入流程。缺点:会觉得同时提交多个服务修改很简单,做出将多个服务耦合在一起的修改。c.每个微服务都有自己的代码库和CI,微服务相关的测试应该和其本身的代码放在一起。优点:修改某个微服务后可以快速验证,修改代码只需要做该微服务的构建和测试,代码库与团队的匹配度更高。
3.构建流水线和持续交付
流水线第一阶段运行快速测试,第二阶段运行耗时测试。在某个阶段失败,能够快速反馈,不用进行后面阶段的测试,节省时间。越接近后面阶段,越能在生产环境下工作。持续交付(CD)检查每次提交是否达到了部署到生产环境的要求,并持续反馈信息。一个示例流水线的几个阶段:编译及快速测试、耗时测试、用户验收测试、性能测试、生产环境。不要对CI工具扩展来做CD,会导致复杂化,要使用为CD设计的工具。
如果在团队初始阶段,没有很好识别出服务边界,可以将所有代码放在一个库中,所有服务放在一个构建,减轻跨服务修改所带来的代价。
4.平台特定的构建物
各语言有自己的构建物类型和构建工具,Ruby有gem,Java有JAR包或WAR包,Python有egg。对微服务部署来说,还需要一些其它工具。Ruby和Python需要运行在Apache或Nginx中的进程管理器。为了部署和启动,需要Puppet、Chef和Ansible这样的自动化配置管理工具,它们支持多种技术栈(编程语言)的构建物。
5.操作系统构建物
可以使用操作系统支持的构建物来取代不同技术栈的构建物避免部署的复杂性。Redhat或CentOS使用RPM,Ubuntu使用deb包,Windows使用MSI。优点:不用考虑底层技术,使用内置工具即可完成软件安装。有些OS包管理器可以完成Chef或Puppet的工作。缺点:刚开始写构建脚本会比较困难。Linux下的FPM包管理器功能比较完善,Windows的MSI原生打包系统差了不少,NuGet好一些,Chocolatey
NuGet提供的功能和Linux上的很接近了。另外,如果多种操作系统部署,代价也就高了。
6.定制化镜像
使用自动化配置管理工具,安装环境仍然耗时耗力。一种方法是创建虚拟机镜像,其中包含常用的依赖。优点:减少装环境的时间,缺点:构建镜像花时间长,镜像文件大,例如20GB。构建不同镜像工具链不同,AWS
AMI、Vagrant镜像、Rackspace镜像。Packer可以简化创建过程,可以支持多种镜像。
将镜像作为构建物。不仅将环境部署在镜像中,将服务也部署进去。
不可变服务器。部署完成后,如果有人登录上去对机器做一些修改,可能导致实际配置与源代码中的配置不一致,导致配置漂移。可以在镜像创建过程中禁止SSH,确保没人能登录修改。
7.环境。要重视测试环境和生产环境的差异性,要在测试环境尽可能接近生产环境和消耗的人力物力之间做出权衡。
8.服务配置。服务配置的工作量应该很小,仅仅局限于环境间的不同之处。如果配置修改了服务很多基本行为,或者环境之间配置差异很大,有可能在某个环境中出现特定问题。如何处理不同环境之间的配置差异:a.每个环境创建一个构建物,配置内建于构建物中。但这样对于测试环境生成的构建物是无法保证在生产环境能正常运行的。b.更好的方法是只创建一个构建物,将配置单独管理。可以用专用系统来提供配置。
9.服务与主机之间的映射
一个主机可以运行多少服务?这里的主机(host)是指运行独立操作系统的隔离单元。没有虚拟化的话,一个主机对应的是一台物理机,采用虚拟化的话,对应的就是一个虚拟机。
单主机多服务。优点:管理主机工作量小,硬件成本低,简化开发人员工作。缺点:监控服务占用资源困难,服务之间互相影响,单一服务负载过高可能影响其它服务正常运行;服务部署困难,每个服务可能依赖不同的环境;不利于团队自治,可能需要独立团队来管理主机配置,增加协调工作;限制部署构建物的选择;增加单个服务进行扩展的复杂性,每个服务对于主机的需求未必一致。
应用程序容器。可以利用IIS的.NET应用程序部署或基于servlet容器的Java应用程序部署,将不同的服务放在同一个容器中,再把容器放在单台主机。优点:简化了管理,对多实例提供集群支持、监控等;节省语言运行时开销,因为多个服务泡在一个JVM上了。缺点:限制了技术栈的使用;其在内存中共享会话状态的方式对于微服务来说应该避免,限制了服务伸缩性;容器启动时间很长;在类似于JVM的平台上,多个应用程序处在一个进程中,分析资源使用和生命周期管理都很困难。建议:对应技术栈的自包含的微服务构建物。如.NET中的Nancy。以及Jetty嵌入式容器中就包含了非常轻量级的HTTP服务器。这样能够保证伸缩性。
每个主机一个服务。优点:避免了单主机多服务的很多问题,简化了监控和错误恢复;减少潜在的单点故障;对某一服务扩展容易;支持不同部署方式如镜像部署或不可变服务器。缺点:管理服务器工作量增加。
平台即服务(PaaS)。PaaS的工作层次比单个主机高,往往依赖于特定技术的构建物,如Java WAR包或Ruby gem等。优点:还能帮助自动配置运行,有的能透明地进行系统伸缩管理。Heroku是一个很好的PaaS平台,能管理服务并以简单的方式提供数据库等服务。缺点:出问题时解决起来较困难。越想根据应用程序使用情况来自动收缩,越难以做好。平台尽量满足的是通用需求,对于应用的特定需求,往往难以满足。
10.自动化
软件部署、服务监控、进程查看和日志收集等工作在服务规模大的时候,应该靠自动化来解决。理想情况,开发人员使用的工具链要和部署生产环境时使用的完全一样,这样能及早发现问题。自动化能显著提高微服务的开发和部署效率。
11.从物理机到虚拟机
管理大量主机的关键之一是,找到一些方法把物理机划分成小块。
传统的虚拟化技术。标准的虚拟化技术包括AWS、VMWare、VSphere、Xen和KVM,其架构从底层到最上层是:机器、内核、主机操作系统、hypervisor、虚拟机(又包括内核、操作系统和应用)。其中的虚拟机可以安装不同的操作系统,可被认为完全封闭的机器。Hypervisor会占用CPU、I/O和内存等资源,开的虚拟机越多,hypervisor占用的资源就越多。因此,物理机切分的越来越小的时候,收益也越小。
Vagrant。是一个部署平台,通常在开发和测试环境使用,而非生产环境。能帮助在本地机器上轻松创建类生产环境,可以同时创建多个VM,通过关闭几个测试故障模式。缺点:开发机上会有更多的资源消耗。
Linux容器。Linux容器可以创建一个隔离的进程空间,进而在这个空间中运行其他的进程。最流行的是LXC。其架构从底层到最上层是:机器、内核、主机操作系统、容器(包含操作系统和应用)。容器运行的操作系统必须要和主机操作系统相同的内核。没有了hypervisor,启动速度更快。相同硬件上能比VM运行更多的数量的容器,资源利用更高效。容器也能很好地在虚拟机上工作。缺点:需要花费工作让外界看到一台主机上的不同容器,并讲外部请求路由到容器。容器的隔离性没有VM好,某些容器进程可能会跳出容器,与其它容器进程或底层系统发生干扰。
Docker。构建在容器上的平台,进行容器管理,可以创建和部署应用。可以在Vagrant中启动单个VM,其中运行多个Docker实例,每个实例包含一个服务。这样在单机上开发和测试更便捷和省资源。CoreOS是专为Docker设计的操作系统,占用资源更少。Google的开源工具Kubernetes和CoreOS集群能进行跨集群的Docker管理和调度。有个工具Deis(http://deis.io/),试图像Heroku那样在Docker上提供PaaS。
12.一个部署接口
参数化的命令行调用是触发任何部署的最合理的方式。需要包含微服务名字、版本和要部署的环境。Python库Fabric可以讲命令行调用映射到函数,也提供类似SSH的机制控制远程机器。Ruby可以用Capistrano,Windows可以用PowerShell。
环境定义。对微服务配置,完成微服务到计算、网络和存储资源之间的映射。可用YAML文件描述。例子中配置的资源包括:开发和生产环境下不同的节点名称、资源大小、凭证(credential),服务以及节点个数;微服务中定义了运行的Puppet文件名称、连接属性(tcp,端口号,允许范围)。构建定义系统工作量很大,Hashicorp有个工具Terraform可以帮助做这些事情。
13.小结。服务要能够独立于其它服务部署。每个服务放到单独的主机/容器中。自动化一切。推荐Jez Humble和David Farley的《持续交付》。