Serverless 风格微服务的持续交付(上):架构案例

GitChat 作者:顾宇
原文:Serverless 风格微服务的持续交付(上):架构案例
关注微信公众号:GitChat 技术杂谈 ,一本正经的讲技术

【不要错过文末彩蛋】

无服务器架构 (Serverless Architectures) 简介

Serverless 架构最早可以追溯到 Ken Fromm 发表的文章《Why The Future Of Software And Apps Is Serverless》。在这篇文章里, Ken Fromm 描述了未来云计算基础设施成熟的条件下应用程序是不需要服务器端的。在无武器场景下构建应用程序的时候。开发人员和运维人员无需担心服务器如何安装配置,如何设置网络和负载均衡,无需监控状态,甚至不再会出现服务器相关的工作内容。这样可以让原本建设机房的时间成本和货币成本从按年计算缩短至按秒计算。

在 Martin Fowler 的博客《Serverless Architectures》中,他将无服务器架构分为两种:

第一种无服务器架构被称为被称为 BaaS(Backend as a Service,后端应用即服务)。即应用的架构是由一大堆第三方 API 来组织的。一切状态和逻辑都由这些服务提供方来管理。随着移动应用和单页 Web 应用这样的富客户端(Rich Client)应用的普及,前后端的通信渐渐以 API 调用为主,而所需的服务不再由 服务端应用开发工程师和运维工程师来维护,只需要调用提供服务的第三方 API 就可以完成相应的功能。例如云上的数据库服务和用户认证服务。

另一种无服务器架构被称为 FaaS(Function as a Service,函数即服务)。这一架构的兴起源于 AWS Lambda 的发展。 AWS Lambda 是一种无状态的代码运行时服务,这项服务提供最小的代码运行资源。你可以使用 Java,Node.js,Python 和 C# 编写程序处理 AWS 各种服务的事件。无需初始化一台服务器,安装操作系统并配置程序运行环境。由于运行资源很少,完成的计算有限,使得这种应用无法保存状态,因此这类程序以函数的方式存在。

本文所介绍的 Serverless 架构主要是以 AWS Lambda 以及 Amazon API Gateway 架构的应用,它同时也具备 BaaS 的特征。

AWS Lambda 的编程模型

AWS Lambda 运行在一个假想的虚拟容器里,但你无法通过 API 配置这个容器。此外,这个虚拟的容器有一些资源限制,主要限制如下:

  1. 5 分钟(300 秒)的程序运行时间。

  2. 512 MB 的文件系统空间。(在 /tmp 目录下)

  3. 最大1536 MB 的内存。(最小 128 MB,以 64 MB 作为增量)

  4. 最多 1024 个文件描述符。

  5. 最大 1024 个内部线程。

AWS Lambda 的编程模型如下所示:

enter image description here
  1. 当请求通过域名访问到应用的时候,应用会将 HTTP 请求转发给 CDN (CloudFornt)。

  2. CloudFront 会根据转发规则把对应的 API 请求转发到 API Gateway 上。

  3. API Gateway 会根据请求的访问点和内容交给对应的 AWS Lambda 或者 EC2 服务处理,也可以发送给其它可访问的服务。

  4. 处理完成后将返回请求结果给客户端。在返回的时候,API Gateway 也可以通过 Lambda 对返回内容进行处理。

相较于传统的微服务架构,通过 API Gateway 和 Lambda 的这种集成方式可以得到更轻量级的微服务。团队只需要规划好 API 访问并完成函数的开发,就可以快速的构建出一个最简单的微服务,使得微服务基础设施的搭建时间从几周缩短为几个小时。此外,大大提升了微服务架构的开发效率和稳定性。

一次微服务架构的奇遇

2016年12月初,当时我正在以一名 DevOps 咨询师的身份参与悉尼某一移动电话运营商的 Digital (电子渠道)部门的 DevOps 转型项目。这个项目是提升该部门在 AWS (Amazon Web Services)云计算平台上的 DevOps 能力。

Digital 部门负责该电信运营商所有的互联网和移动设备应用开发。这些应用主要是用来为用户提供诸如 SIM 卡激活,话费查询,话费充值,优惠套餐订购等自助服务(Self service),从而降低营业厅和人工话务客服的成本。

自助服务的应用系统基于 Ruby on Rails 框架开发,前端部分采用 AngularJS 1.0,但是没有采用前后端分离的设计,页面代码仍然是通过 ERB 组合而成。yi移动端则采用 Cordova 开发。为了降低开发难度和工作量, 移动端的应用内容实际上是把 AngularJS 所生成的 Web 页面通过响应式样式的方式嵌入到移动端。但因为经常超时,所以这款 APP 体验并不好。

整套 Rails 应用部署在 AWS 上,并且通过网关和内部业务 BOSS (Business Operating Support System) 系统隔离。BOSS 系统采用 SOAP 对外暴露服务,并由另外一个部门负责。因此,云上的应用所做的业务是给用户展现一个使用友好的界面,并通过数据的转化和内部 BOSS 系统进行交互。系统架构如下图所示:

enter image description here

在我们的架构里,实现新的需求就要变动老的应用。我们的想法是:

  1. 构建出新的业务页面,生成微服务契约。

  2. 根据 API 契约构建出新的微服务。

  3. 部署 Web 前端到 S3 上,采用 S3 的 Static Web Hosting (静态 Web 服务) 发布。

  4. 部署后端微服务上线,并采用临时的域名和 CDN 加载点进行测试。

  5. 通过更新 CDN 把原应用的流量导向新的微服务。

  6. 删除旧的服务代码。

我们原本要在原有的应用上增加一个 API 用来访问以前应用的逻辑。但想想这实际上也是一种挖坑。在评估了业务的复杂性之后。我们发现这个功能如果全新开发只需要 2人2周(一个人月)的时间,这仅仅占我们预估工作量的20%不到。因此我们放弃了对遗留代码动工的念头。最终通过微服务直接访问后台系统,而不需要通过原有的应用。

在我们拆微服务的部分十分简单。对于后端来说说只需要修改 CDN 覆盖原先的访问源(Origin)以及保存在 route.rb 里的原功能访问点,就可以完成微服务的集成。

构建出新的业务页面,生成微服务契约

结合上面的应用痛点和思路,在构建微服务的技术选型时我们确定了以下方向:

  1. 前端框架要具备很好的 Responsive 扩展。

  2. 采用 Swagger 来描述 API 需要具备的行为。

  3. 通过消费者驱动进行契约测试驱动微服务后端开发。

  4. 前端代码库和后端代码库分开。

  5. 前端代码框架要对持续交付友好。

因此我们选择了 React 作为前端技术栈并且用 yarn 管理依赖和任务。另外一个原因是我们能够通过 React-native 为未来构建新的应用做好准备。此外,我们引入了 AWS SDK 的 nodejs 版本。用编写一些常见的诸如构建、部署、配置等 AWS 相关的操作。并且通过 swagger 描述后端 API 的行为。这样,后端只需要满足这个 API 规范,就很容易做前后端集成。

部署前端部分到 S3 上

由于 AWS S3 服务自带 Static Web Hosting (静态页面服务) 功能,这就大大减少了我们构建基础环境所花费的时间。如果你还想着用 Nginx 和 Apache 作为静态内容的 Web 服务器,那么你还不够 CloudNative。

虽然 AWS S3 服务曾经发生过故障,但 SLA 也比我们自己构建的 EC2 实例处理静态内容要强得多。此外还有以下优点:

  1. 拥有独立的 URL,很容易做很多 301 和 302 的重定向和改写操作。

  2. 和 CDN (CloudFront)集成很好。

  3. 很容易和持续集成工具集成。

  4. 最大的优点:比 EC2 便宜

根据 API 契约构建出新的微服务

在构建微服务的最初,我们当时有两个选择:

  1. 采用 Sinatra (一个用来构建 API 的 Ruby gem) 构建一个微服务 ,这样可以复用原先 Rails 代码库的很多组件。换句话说,只需要 copy 一些代码,放到一个单独的代码库里,就可以完成功能。但也同样会面临之前 Ruby 技术栈带来的种种问题。

  2. 采用 Spring Boot 构建一个微服务,Java 作为成熟工程语言目前还是最好的选择,社区和实践都非常成熟。可以复用后台很多用来做 SOAP 处理的 JAR 包。另一方面是解决了 Ruby 技术栈带来的问题。

然而,这两个方案的都有一个共同的问题:需要通过 ruby 语言编写的基础设施工具构建一套运行微服务的基础设施。而这个基础设施的搭建,前前后后估计得需要至少 1个月,这还是在运维团队有人帮助的情况下的乐观估计。

所以,要找到一种降低环境构建和运维团队阻塞的方式避开传统的 EC2 搭建应用的方式。

这,只有 Lambda 可以做到!

基于上面的种种考量,我们选择了 Amazon API Gateway + Lambda 的组合。而 Amazon API Gateway + Lambda 还有额外好处:

  1. 支持用 Swagger 规范配置 API Gateway。也就是说,你只要导入前端的 Swagger 规范,就可以生成 API Gateway。

  2. 可以用数据构建 Mock API,这样就可以很大程度上实现消费者驱动契约开发。

  3. 通过 Amazon API Gateway 的 Stage 功能,我们无需构建 QA 环境,UAT 环境和 Staging 环境。只需要指定不同的 Stage,就可以完成对应的切换。

  4. Lambda 的发布生效时间很短,反馈很快。原先用 CloudFormation 构建的 API 基础设施需要至少 15 分钟,而 Lambda 的生效只需要短短几秒钟。

  5. Lambda 的编写很方便,可以采用在线的方式。虽然在线 IDE 并不很好用,但是真的也写不了几行代码。

  6. Lambda 自动根据请求自扩展,无需考虑负载均衡。

虽然有这么多优点,但不能忽略了关键性的问题:AWS Lambda 不一定适合你的应用场景!

根据上文对 AWS Lambda 的介绍,支持 AWS Lambda 运行的资源和时间很有限。因此很多对同步和强一致性的业务需求是无法满足的。所以,AWS Lambda 更适合能够异步处理的业务场景。此外,AWS Lambda 对消耗存储空间和 CPU 很多的场景支持不是很好,例如 AI 和 大数据。(PS: AWS 已经有专门的 AI 和大数据服务了,所以不需要和自己过不去)

对于我们的应用场景而言,上文中的 Ruby On Rails 应用中的主要功能(至少60% 以上)实际上只是一个数据转换适配器:把前端输入的数据进行加工,转换成对应的 SOAP 调用。

因此,对于这样一个简单的场景而言,Amazon API Gateway + Lambda 完全满足需求!

部署后端微服务

选择了Amazon API Gateway + Lambda 后,后端的微服务部署看起来很简单:

  1. 更新 Lambda 函数。

  2. 更新 API 规范,并要求 API 绑定对应 Lambda 函数处理请求。

但是,这却不是很容易的一件事。我们将在《Serverless 风格微服务的持续交付(中):持续交付的挑战》中对这方面踩过的坑详细介绍。

把原应用的请求导向新的微服务

这时候在 CDN 上给新的微服务配置 API Gateway 作为一个新的源(Origin),覆盖原先写在 route.rb 和 nginx.conf 里的 API 访问规则就可以了。CDN 会拦截访问请求,使得请求在 nginx 处理之前就会把对应的请求转发到 API Gateway。

当然,如果你想做灰度发布的话,就不能按上面这种方式搞了。CloudFront 和 ELB 负载均衡 并不具备带权转发功能。因此你需要通过 nginx 配置,按访问权重把 API Gateway 作为一个 upstream 里的一个 Server 就可以。

删除旧的服务代码

不要留着无用的遗留代码!

不要留着无用的遗留代码!

不要留着无用的遗留代码!

重要且最容易被忽略的事情要说三遍。斩草要除根,虽然我们可以保持代码不动。但是清理不再使用的遗留代码和自动化测试可以为其它团队减少很多不必要的工作量。

最终的架构

经过6个人两个月的开发(原计划8个人3个月),我们的 Serverless 微服务最终落地了。当然这中间有 60% 的时间是在探索全新的技术栈。如果熟练的话,估计 4 个人一个月就可以完成工作。

最后的架构如下图所示:

这里写图片描述

文章知识点与官方知识档案匹配,可进一步学习相关知识云原生入门技能树首页概览8746 人正在系统学习中

来源:软件供应链

声明:本站部分文章及图片转载于互联网,内容版权归原作者所有,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2017年8月19日
下一篇 2017年8月19日

相关推荐