简介

在本系列文章的第一部分讨论了微服务的主要优势,并且接触到一些在使用微服务时需要考虑的问题。

在第二部分我们将看一下容器是如何加入微服务故事的。对于一些开发人员和架构师来说,微服务和容器仍然是新的领域,所以对这两个术语仍然有一些疑惑,在有的时候会将他们混淆。微服务和容器实际上是不同的两个东西。微服务是一种架构形式;容器是一种工具,经常用来帮助基于微服务的应用。

本文的目的是让大家深入的了解容器是如何帮助微服务的,并且提供了一些使用编排工具运行容器化微服务的一些高级概念。

容器与微服务

这篇博客的目的,是让你能够考虑容器作为封装的、独立部署的组件,利用操作系统级别的虚拟化,以隔离的实例运行在相同的内核之上,因此能够非常快速(秒级)的启动。

隔离与密度

容器的隔离级别介于虚拟机(VM)和进程之间。对于微服务来说,运行在VM当中会获得更好的隔离性,但是它也无法进行快速的扩展,因为通常VM的启动需要花费一些时间。容器能够在几秒钟之内快速启动,能够立即对负载或者流量的增加做出反应。

进程的启动也很快,能够对资源,比如内存,进行动态的分配,并且能够对它们进行高效的共享。从密度的角度看,每个进程或者进程组运行多个服务实例也是很好的。在每个进程中运行一个或者多个微服务实例不好的地方是,它们不能很好的与其他的环境进行隔离,会受到其相邻环境产生的噪音的影响,如果代码写的不好,有可能影响到整个虚拟机环境。对服务代码进行容器化,它的运行环境、依赖关系、系统库等,都被打包在一起,运行在一个容器当中,通过它完整的、私有访问的操作系统结构视图,解决在进行运行时所遇到的一些隔离问题。

图1 展示了不同的隔离级别

微服务架构 占用资源 微服务架构 容器_miscroservice

DevOps的好处

良好的DevOps实践是微服务应用成功的关键,并且容器对它也是非常有益的。设想一个基于微服务的应用是由多种语言编写的不同服务组成的。在这个例子当中,DevOps团队需要专门的知识来部署每一个服务,这极大的增加了运维的复杂性。如果你将微服务打包为容器的一部分,容器镜像就成为一个部署单元,DevOps团队只需要知道如何部署容器就可以了,不需要去考虑里面运行的是什么应用。通过这种方式,你也能够避免由于缺少依赖库,或者环境版本(比如你的服务所需要的框架、依赖)不匹配所造成的服务失败,服务所需要的所有的东西都被打包在一起,成为一个不可变的环境,这就是典型的持续集成的一部分。从运维的角度,通常在一个容器当中只运行一个服务,容器能够让你将系统元素(CPU,内存等)收集到服务本身。容器还可以将开发人员和运维人员对主机和操作系统的一些特殊技术细节进行屏蔽。比如,如果基础架构团队决定更换主机上的Linux操作系统,因为它上面运行都是容器,所以不会对应用造成影响。

这些都是使用容器所带来的一些好处。从微服务的角度看,它可以被看做是开发和运维之间的桥梁,让你在微服务环境运维变得更加简单。

让我们看一个例子。图2展示了一个运行在容器中的微服务应用。这个应用包括三个微服务:一个Order服务、一个Profile服务和一个Catalog服务。每一个服务,他们的依赖环境和运行环境都被打包到一个容器当中。由于容器的封装和隔离级别,即使你的服务有不同的版本(比如Order v1和Order v2),不同的运行环境和不同的依赖组件(Profile服务依赖于LibraryC v1,Catalog服务依赖于LibraryC v2),你可以让他们运行在相同的主机之上。如果有一个服务的运行出现问题,耗尽了所有的资源,比如CPU或内存,它也不会影响到其他的服务,因为它只能够使用分配给它所在容器的资源。

图2:一个运行在容器中的微服务应用

微服务架构 占用资源 微服务架构 容器_容器_02

微服务的编排

直截了当一点,如果你的应用在一个主机之上运行,但是一个主机不是真正的生产环境;比如,为了实现HA,你需要至少两个主机。因为微服务应用本质上是分布式应用,通常他们都运行在一个集群之上。集群就是一组耦合的计算机(通常叫做节点),可以看做是一个单独的系统,能够通过网络进行连接。在集群中调度新的服务看似简单。然而,你也需要一些措施来保证服务在失败的时候仍处于活动状态;在节点失败时能够将服务移动到其他节点;或者服务正在被调用,并且你可以进行滚动升级,你的服务可以“永远在线”。此外,它还需要了解依赖关系、位置、资源约束、优化、不同类型的资源和对资源的需求,以便列出对这些挑战的看法。好消息是,当你的应用运行在容器当中,你不需要担心这些事情,容器编排,有时也被称为调度器,能够帮你解决这些问题。你能够自己安装的最流行的容器编排工具包括Kubernetes,DC/OS和Docker Swarm。你也可以使用受管的容器服务,比如Oracle Container Cloud Service或者其他的受管容器服务。使用完全受管服务的好处是你完全不需要担心底层架构的问题。在核心功能上,所有的容器编排解决方案,受管的或非受管的,都提供了相似的功能。

从微服务的角度看,你所面对的是一个非常动态的环境。你通常不会知道你的服务(容器)在集群当中的什么位置,因为你依赖于编排器,基于资源的有效性与/或者位置约束来查找服务的最佳位置。服务注册和服务发现时解决这个问题的方案,他们能够对服务进行注册,并且能够很容易的在系统中被别的服务发现。集群中的服务发现有时也被称为东西路由;通常,额外的服务元数据会连同服务的端点信息一起存储,并且/或者服务的健康检查和服务实例的健康状态一起进行维护。Etcd、Consul和Zookeeper是非常流行的注册和发现解决方案。

图3展示了一个集群的高阶视图,集群中带有微服务实例,并且他们的端点存储在服务注册当中,Order服务的一个实例正在对Catalog服务进行调用。Order服务调用节点上的一个端点,本地代理服务处理对这个服务的查找。请注意,有多种解决方案可以实现这些功能,一些受管的容器服务或者编排解决方案都已经内建服务注册和服务发现。这个例子当中提供了一个简单的视图,来说明服务发现解决方案如何工作。

图3:服务发现解决方案——高阶视图

微服务架构 占用资源 微服务架构 容器_微服务架构 占用资源_03

你所需要考虑的是如何将集群之外的客户端调用流量路由给集群内的服务。有时这被称为南北路由。

一种常用的模式是使用应用网关,也称为API网管。在微服务架构当中,API网关用于流量聚合,并且把客户端请求路由给需要的服务。此外,API网关也用于身份认证卸载、SSL卸载、服务质量控制和监控视图。Nginx和HA-Proxy是目前最流行的两个应用网关。典型的网关部署模式包括对等网关路由,就是在集群中的每一个节点都运行网关。这种模式通常用于小规模的集群。另一种模式是专用的或者独立的网关节点。专用是指通过放置约束将网关服务放置在特定的集群节点之上。独立的网关节点的工作模式类似,不同的地方是节点服务的所在的节点不是集群调度的一部分。图4展示了一个集群架构,有一个网关节点将用户请求转发给Order服务。负载均衡器将流量定向到其中一个网关实例。网关使用路径来指向一个从服务注册返回的Order服务实例,将请求路由给它。

图4:用户请求到Order服务

微服务架构 占用资源 微服务架构 容器_docker_04

总结

除了服务注册、服务发现和服务网关之外,开发人员和DevOps人员还不得不考虑避免潜在的端口冲突、为每个容器分配服务一个IP等等。这就使开发人员不得不去学习更多的基础架构和网络知识。好消息是容器编排进化的非常快,几乎每一个容器编排工具现在都提供了一些功能来帮助开发人员简化基础架构级别的工作。这也说明,仍然有很多事情开发人员需要在创建基于微服务的应用时考虑。后边的博客中将有更详细的微服务模式的介绍,并且如何对容器化的微服务进行打包和DevOps。