发布时间:2022-09-07 08:30
从现在开始,我们将从更高的维度讨论微服务,涵盖了组织敏捷性、设计和依赖的思考、领域驱动设计以及Promise理论。当我们深入使用之前介绍的三个流行的微服务框架:Spring Boot、Dropwizard和WildFly Swarm,我们能够使用它们开箱即用的能力去构建一个暴露或者消费REST服务的应用,能够使用外部环境对应用进行配置,可以打包成一个可执行的jar,同时提供Metrics信息,但这些都是围绕着一个微服务实例。当我们需要管理微服务之间的依赖、集群的启动和关闭、健康检查以及负载均衡的时候,我们使用微服务架构会面临什么问题呢?本章,我们将讨论这些高阶话题用来理解部署微服务时面对的挑战。
当我们开始将我们的应用和服务按照微服务的思路进行拆分后,我们将面临这样的场景:我们有了更多的服务、更多的二进制内容、更多的配置,更多的交互点等等。传统方式是将这些构建成一个二进制单元,比如:WARs或者EARs,然后将其打包后等待运维人员将它部署到我们指定的应用服务器上。如果对于高可用有要求,也会将应用服务器进行分布式部署,形成集群,依靠负载均衡、共享磁盘(数据库)等方式提升可用性。传统运维体系下也开发了一些自动化部署的工具,比如:Chef
和Ansible
,工具虽然简化了部署,但是开发人员还是需要面对部署时容易出现的问题,比如:配置、环境等不可预知的问题。
chef
Chef是由Ruby与Erlang写成的配置管理软件,它以一种纯Ruby的领域专用语言(DSL)保存系统配置“烹饪法(recipes)”或“食谱(cookbooks)”。Chef由Opscode公司开发,并在Apache协议版本2.0下开源发布。ansible
使用python构建,中文化资料比较多,Ansible的简洁界面和可用性非常迎合系统管理员的想法
在传统方式下尝试微服务的部署,将会是个糟糕的结果。如何解决应用服务器在开发、测试以及生产环境的不同配置?如果没有,如何能够捕获到这些配置的变更?而这些变更如何确认已经运行在应用服务器中了?运行时软件环境,比如:操作系统、JVM以及相关的组件在生产和开发环境下的不同问题如何解决?如果我们的应用已经针对特定的JVM做了调优,这些调优参数会不会影响到他人?如果部署微服务,你会选择使用进程隔离的方式将它们部署在一台机器上吗?如果其中一个微服务实例消耗了系统100%的资源,该如何是好?如果过度的占用了I/O或者共享存储怎么办?如果部署了多个微服务实例的宿主机崩溃了怎么办?我们的应用为此做过应对方案吗?将应用分拆为微服务是小,但面对的问题显然会更多。
不可变的递交(Immutable delivery)原则可以帮助我们应对上述的部分问题,在这个体系下,我们将使用镜像技术来尝试减少开发到生产的步骤。例如:构建系统能够输出一个包含了操作系统、JVM、配置、相关组件的镜像,我们可以将它部署到一个环境中,测试它,如果通过测试,最终可以将它部署到生产环境中而不用担心开发流程使交付的软件缺少了什么。如果你想变更应用,那么可以回到刚才这个流程的最开始,应用你的修改,重新构建镜像,最终完成部署,如果出乎你的意料,程序有问题,你可以直接选择回滚到上一个正确的镜像而不用担心遗漏了什么。
这听起来很好,但是我们怎么做到呢?将应用打包成一个jar还不足以足够做到这些。JVM是底层实现,我们如何将它也打包进去,而JVM又使用了操作系统级别组件,这些内容我们都要打包,除此之外,我们还需要配置、环境变量、权限等等,这些都需要打包,而这些内容无法被打包到一个可执行jar中去。更加重要的是,不止java一种微服务,如果程序使用NodeJS、Golang编写,我们还要针对不同的语言环境做不同的打包。你可能想使用自动化手段完成这些软件的安装,将基础设施作为服务(IaaS),使用它们的API完成环境的搭建。事实上Netflix已经使用了自动化构建工具来完成VM的构建,并利用这项技术实现了不可变的递交,但是VM很难管理、更新和变更,而每个VM都有自己完备的虚拟化环境,对资源有些浪费。
那么有什么更加轻量化的打包和镜像化方式让我们使用吗?
Docker是近几年出现用于解决不可变递交的优雅解决方案,它允许我们将应用以及应用的所有依赖(包括了:OS,JVM以及其他组件)打包成为一个轻量的、分层的镜像格式。然后Docker使用这些镜像,运行它们,产生实例,而这些实例都运行在Linux containers中,在Linux containers中,会带来CPU、内存、网络以及磁盘的隔离。在这种模式下,这些容器实例就是一种应用虚拟化的方式,它运行一个进程去执行,你甚至可以在实例中运行ps
查看你的进程,而且这个容器实例具备访问CPU、内存、磁盘和网络的能力,但是它只能使用指定好的配额。例如:能够启动一个Docker容器,只为它分配一部分的CPU、内存以及I/O的访问限制。如果在Linux containers外部去看,在主机上,这个容器就是一个进程,不需要设备驱动的虚拟化、操作系统、网络栈以及特殊的中间层,它仅仅是一个进程。这意味着,我们可以在一台机器上部署尽可能多的容器,提供了比虚拟机更高的部署密度。
在这些激动人心的特性下,其实没有革命性的技术。Docker使用到的技术有:cgroups
、namespaces
以及chroot
,这些都已经在Linux内核中运行了相当长的时间,而这些技术被Docker用来构造应用虚拟化技术。Linux containers已经推出了十几年,而进程虚拟化技术在Solaris
和FreeBSD
上出现的时间更早。以往使用这些技术的lxc
会比较复杂,而Docker通过简单的API以及优秀的用户体验使得Linux containers的运用变得火热起来,Docker通过一个客户端命令工具能够与Linux containers进行交互,去部署Docker镜像,而Docker镜像的出现改变了我们打包和交付软件的方式。
一旦你拥有了镜像,可以迅速的转化为Linux containers,镜像是按照层进行构建的,一般会在一个基础的层(例如:RHEL、 Debian等)上进行构建,然后包含应用所需的内容,构建应用其实也就是在基础层上进行一层一层的镜像构建。镜像的出现,是的发布到各种环境变得容易,不会在面对一堆零散的内容,如果发现基础镜像中有问题,可以进行重新构建,其他镜像进行重新选择构建即可,这使得从开发环境到测试,再到生产环境减少了人工干预发布内容的环节,如果我们要新发布一版本,只需要重新构建一个镜像即可,而改动只是去修改了镜像中对应的层。
构建了镜像,但是我们怎样启动一个应用?怎样停止它?怎样做健康检查?怎样收集应用的日志、Metrics等信息,使用标准的API可以使我们自己构建工具来完成这些工作。出色的集群机制,例如服务发现、负载均衡、失败容错以及配置使得开发人员很容易获得这些特性。
Docker相关的技术可以关注 The Docker Book
外界都知晓Google使用Linux containers技术来支撑其扩展性,事实上Google的所有应用都运行在Linux containers上,并且被他们的管理系统Brog进行着管理。前Google工程师Joe Beda
说,公司每周要启动超过20亿次的容器,Google甚至投入资源涉及到linux底层技术来支持其容器在生产环境的运用。在2006年,Google开始了一个名叫 进程容器 的项目,最终演变成为了cgroups
,而它在2008被合并到了Linux核心,同年正式发布。Google在拥有极强的运维容器的背景下,其对构建容器平台的影响力就不言而喻了,事实上,一些流行的容器管理项目都受到了Google的影响。
Derek Collison
和Vadim Spivak
都在Google工作过,并且使用Borg系统很多年Ben Hindman
在Google实习过,与Google的诸多工程师有过容器技术的交流(围绕容器集群、调度和管理等技术)