已经准备好向容器迁移了吗?如果你正考虑从现有非容器化的系统上将服务迁移到基于容器的环境中,那么你一定想知道该如何实现它。有什么正确的方法?有没有最好的方法?或者说,有没有某种直接迁移过程(lift-and-shift process)可以适用于所有的应用程序?

通常来讲,上述问题都有肯定的答案。虽然因各公司的具体情况不同,迁移到容器和微服务的具体细节会各有差异,但是对于实现应用程序从传统基础设施到容器环境的无缝迁移来说,还是有一些普适性原则和最佳实践经验值得大家遵循。

这篇博文将简要介绍成功将传统应用程序迁移到容器的指导原则。

微服务——它们是什么?

在处于项目迁移的最初规划阶段,关于容器化,最需要注意的是容器系统架构是基于(或应该基于)微服务的。那么,微服务是什么?

实际上,界定什么是微服务、什么不是微服务,这其间的标准是有些模糊的。这种情况其实是一种必然,因为不同的设计容器化应用程序的方法(我们稍后会提到),正是基于不同的定义微服务的方法。

从总体上来说,微服务可以描述为一个基本的、功能分离的服务,它由应用程序的其他部分调用。我们可以把从数据库中检索数据的服务看作是一个微服务,既可以将数据发送到存储设备,也可以处理用户输入。

例如,在线上商店中,访问库存数据库是由一个微服务处理,客户的购物车由一个微服务更新,交易再由一个微服务完成,并且还有一个微服务处理信用卡的授权,而这些微服务都是独立的。

而对于同一家店,可以由多个微服务工作,一个微服务处理所有的数据库访问(库存、客户和销售),一个微服务处理购物车和交易,而第三个微服务用做运输物流。这种特定的微服务结构,在很大程度上是一种设计选择,它取决于系统整体架构。

你当前的架构是什么?

不过,在开始构建容器化应用程序之前,还需要着留意一下你当前的架构。宏观上,非容器化的应用程序可以大体分为两类:单体架构和SOA架构(服务导向架构)。当前架构不同,进行容器化重构的方法就不同,因此我们先将它们区分开来,再讨论设计选择以及整体的重构策略。

单体架构

大多数传统设计的应用程序都是单体架构。它们可能由单个包含了支持的库、服务以及配置文件的程序,或者少量带支持资源的程序组成。然而,在这两种情况下,大部分甚至全部的核心功能都包含在一个或几个二进制文件中,其中服务就包含于这些在二进制文件定义的应用程序边界内进行操作和通信的功能中。桌面级的应用程序传统上讲是单体的,大多数基于网络的应用程序也是如此。

在桌面或局域网环境中,单体架构通常是十分有效的。它的安装和更新相当容易,并且单体的设计能够轻松地监控组件,而且桌面/LAN的使用一般不会对应用程序的资源造成太大的压力。然而,在云或互联网的环境中,单体应用程序可能相对过于庞大、笨拙、缓慢,它们不仅难以更新,且不能够处理大量的流量。另外,它们也不适用持续交付以及大多数构成DevOps的实践。

如果想重构容器的单体架构应用,那可能需要在概念层面上进行全盘的重新设计。即便应用程序的架构已经相当清晰地定义了其内部服务,并且保证了它的离散性,实际拆分成微服务时可能还需要对这些服务的边界以及它们彼此通信的方式进行大规模的更改。对于大多数的单体应用程序,许多服务都需要重新定义,甚至需要从头开始定义。

SOA架构

有一些大型的非容器应用程序可能已经具有了基于服务的架构。例如,销售点的应用程序可能包含了独立的程序用来处理销售、库存、客户记录、订单、运输、差异、税收、应收账款以及应付账款。每一个模块都是一个单独的程序,具有明确的应用程序边界,而且这种跨边界的模块间通信和基于容器的应用程序十分相似。

如果有进行细分的需要,这种架构(假设它是令人满意的)可以作为进一步分解成微服务的基础。进一步分解可以是基于现有的模块分解成更小的服务,也可以是基于抽象的广义服务(比如访问单个数据库或打印收据的微服务),或者两者都是。另一方面,如果现有模块已经是小容量、功能分散且组织良好的,那么只需要进行极少的修改就可以将其迁移到容器中。

图片描述

对于单体应用你要怎么做?

在这一点上,应该弄清楚的是,将单体应用程序分解成微服务通常是一个复杂而极具挑战的工作。就像之前说的那样,首先是根据微服务重新定义架构。这里的重新定义以及实际细分成微服务本身的过程通常称为分解(decomposition)。关于分解有一些基本的模式。在很多情况下,了解这些模式的基础和基本性质比选择哪种模式更重要,而在实际生产中,根据系统的功能要求,将单体应用程序分解成微服务时常常涉及到多种模式。

根据用例分解

用例是指用户在执行任务时通常会采取的一组操作。用户既可以是实际的人,可以是应用程序的另一部分,也可以是外部的应用程序。用例的关键要素是它是与任务相关联的一组可定义的动作。在线上商店的例子中,一组客户操作(如选择和购买商品)就可以是一个用例,单纯的内部操作也可以是用例,例如在销售之后更新库存、客户信息以及交易数据库。

根据功能分解

根据功能分解是分解的另一种常见模式。例如,销售交易可以是一个由功能定义的单元,而信用授权可以作为由功能定义的另一个服务。你可以根据诸如差异跟踪、运输和自动补货这些功能来定义功能域。功能分解和用例分解非常相似,不过它的定义更多的是由需要执行的行为决定而非执行的对象。

根据资源分解

在多数情况下,根据资源定义特定的微服务无疑是最好的方案。例如,你可以定义一个单一的微服务来处理所有与具体某个数据库或一组数据库的交互,或者处理所有与持久存储器的交互。在许多方面,设备驱动程序是基于资源的微服务。如果通过某种类似于设备驱动程序的通用服务来和资源进行交互是有意义的,那么将该服务定义为微服务也可能有实际价值。

分解途径

确定了分解单体应用的整体模式后,就可以开始将其分解成微服务。你的最终目标应该是将整个应用程序缩成一组微服务层级的容器,它们和原始的单体应用一样,提供相同的一组服务(能够根据需要添加或改进功能),可以根据需要进行管理和部署,而且相比之下有更出色的速度、流量以及灵活性。

比较好的一点是,你并不需要一下子把它拆散。可以将前面说的大型SOA架构作为中间阶段。你可以在开始的时候将单体应用程序拆成大型的基于服务的块,然后再将其细分成越来越小的服务,直到最终达到期望目标级别的微服务。而这里还有另一种方案,你可以从定义明确的服务入手,先将这些服务拆分成基于容器的微服务,然后再以类似先前的方法,分几次拆分应用程序的其余部分。

无论采取哪种方法,一定要牢记一点,那就是要明确定义微服务,而且这些定义在应用程序的整体功能和架构上都应该是有实际意义的。只要你遵循了这些原则,相信你的迁移工作就会成功。


Rancher
1.2k 声望2.5k 粉丝

Rancher是一个开源的企业级Kubernetes管理平台,实现了Kubernetes集群在混合云+本地数据中心的集中部署与管理。Rancher一向因操作体验的直观、极简备受用户青睐,被Forrester评为“2020年多云容器开发平台领导厂商...