本文所介绍的 Serverless 架构主要是以 AWS Lambda 以及 Amazon API Gateway 架构的应用,它同时也具备 BaaS 的特征。
作者以一名 DevOps 咨询师的身份参与悉尼某一移动电话运营商的 Digital (电子渠道)部门的 DevOps 转型项目。这个项目是提升该部门在 AWS (Amazon Web Services)云计算平台上的 DevOps 能力。Digital 部门负责该电信运营商所有的互联网和移动设备应用开发。这些应用主要是用来为用户提供诸如 SIM 卡激活,话费查询,话费充值,优惠套餐订购等自助服务(Self service),从而降低营业厅和人工话务客服的成本
01
AWS Lambda的编程模式
AWS Lambda 运行在一个假想的虚拟容器里,但你无法通过 API 配置这个容器。此外,这个虚拟的容器有一些资源限制,主要限制如下:
AWS Lambda 的编程模型如下所示:
Lambda 的执行流程:
当事件请求大批量发生的时候。Lambda 会为每一个事件单独执行一次 。这意味着每一个请求之间的执行期间,内容是不能共享的(经本人亲测,内存中存储的是可以共享的,但内容保留的有效时间和状态无法保证)
1
02
Amazon API Gateway + AWS Lambda 的微服务架构
根据 Martin Fowler 对微服务的描述性定义,我们可以认为微服务从技术层面包含以下特征:
在 AWS 现有的服务情况下,AWS Lambda 满足了上面的第 1、3、5 点,这只是一个微服务的处理单元,非管理单元。而 2 和 4 则需要另外的服务作为管理单元共同构成微服务,这个任务一般交由 API 网关实现。
Amazon API Gateway 是一种完全托管的 API 网关服务,可以帮助开发者轻松创建、发布、维护、监控和保护任意规模的 API。它集成了很多 API 网关的功能,诸如缓存、用户认证等功能。并且支持通过 HAML 和 Swagger 配置,这样就可以用代码管理系统配置 API 了。
Amazon API Gateway 可以根据不同的 Restful API 访问点将请求的数据传递给不同的资源进行处理。一般的 AWS API 架构如下所示:
相较于传统的微服务架构,通过 API Gateway 和 Lambda 的这种集成方式可以得到更轻量级的微服务。团队只需要规划好 API 访问并完成函数的开发,就可以快速的构建出一个最简单的微服务,使得微服务基础设施的搭建时间从几周缩短为几个小时。此外,大大提升了微服务架构的开发效率和稳定性。
1
03
构建微服务的架构的策略
要做到前后端分离。使得比较小的开发团队可以并行开发,只要协商好了 接口之间的契约(Contract),未来开发完成之后会很好集成。
这让我想起了 Chris Richardson 提出了三种微服务架构策略,分别是:停止挖坑,前后端分离和提取微服务。
停止挖坑的意思是说:如果发现自己掉坑里,马上停止。
原先的单体应用对我们来说就是一个焦油坑,因此我们要停止在原来的代码库上继续工作。并且为新应用单独创建一个代码库。所以,我们拆分策略模式如下所示:
在我们的架构里,实现新的需求就要变动老的应用。我们的想法是:
原本要在原有的应用上增加一个 API 用来访问以前应用的逻辑。但想想这实际上也是一种挖坑。在评估了业务的复杂性之后。我们发现这个功能如果全新开发只需要 2人2周(一个人月)的时间,这仅仅占我们预估工作量的20%不到。因此我们放弃了对遗留代码动工的念头。最终通过微服务直接访问后台系统,而不需要通过原有的应用。
在我们拆微服务的部分十分简单。对于后端来说说只需要修改 CDN 覆盖原先的访问源(Origin)以及保存在 route.rb 里的原功能访问点,就可以完成微服务的集成。
1
04
构建出新的业务页面,生成微服务契约
结合上面的应用痛点和思路,在构建微服务的技术选型时我们确定了以下方向:
因此我们选择了 React 作为前端技术栈并且用 yarn 管理依赖和任务。另外一个原因是我们能够通过 React-native 为未来构建新的应用做好准备。此外,我们引入了 AWS SDK 的 nodejs 版本。用编写一些常见的诸如构建、部署、配置等 AWS 相关的操作。并且通过 swagger 描述后端 API 的行为。这样,后端只需要满足这个 API 规范,就很容易做前后端集成。
1
05
部署前端部分到 S3 上
由于 AWS S3 服务自带 Static Web Hosting (静态页面服务) 功能,这就大大减少了我们构建基础环境所花费的时间。如果你还想着用 Nginx 和 Apache 作为静态内容的 Web 服务器,那么你还不够 CloudNative。
虽然 AWS S3 服务曾经发生过故障,但 SLA 也比我们自己构建的 EC2 实例处理静态内容要强得多。此外还有以下优点:
1
06
根据 API 契约构建出新的微服务
在构建微服务的最初,我们当时有两个选择:
然而,这两个方案的都有一个共同的问题:需要通过 ruby 语言编写的基础设施工具构建一套运行微服务的基础设施。而这个基础设施的搭建,前前后后估计得需要至少 1个月,这还是在运维团队有人帮助的情况下的乐观估计。
所以,要找到一种降低环境构建和运维团队阻塞的方式避开传统的 EC2 搭建应用的方式。
基于上面的种种考量,我们选择了 Amazon API Gateway + Lambda 的组合。而 Amazon API Gateway + 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 完全满足需求!
1
07
部署后端微服务
选择了Amazon API Gateway + Lambda 后,后端的微服务部署看起来很简单:
但是,这却不是很容易的一件事。
1
08
把原应用的请求导向新的微服务
这时候在 CDN 上给新的微服务配置 API Gateway 作为一个新的源(Origin),覆盖原先写在 route.rb 和 nginx.conf 里的 API 访问规则就可以了。CDN 会拦截访问请求,使得请求在 nginx 处理之前就会把对应的请求转发到 API Gateway。
当然,如果你想做灰度发布的话,就不能按上面这种方式搞了。CloudFront 和 ELB 负载均衡 并不具备带权转发功能。因此你需要通过 nginx 配置,按访问权重把 API Gateway 作为一个 upstream 里的一个 Server 就可以。
不要留着无用的遗留代码!
不要留着无用的遗留代码!
不要留着无用的遗留代码!
重要且最容易被忽略的事情要说三遍。斩草要除根,虽然我们可以保持代码不动。但是清理不再使用的遗留代码和自动化测试可以为其它团队减少很多不必要的工作量。
1
09
最终架构图
经过6个人两个月的开发(原计划8个人3个月),我们的 Serverless 微服务最终落地了。当然这中间有 60% 的时间是在探索全新的技术栈。如果熟练的话,估计 4 个人一个月就可以完成工作。
最后的架构如下图所示:
在上图中,请求仍然是先到 CDN (CloudFront),然后:
1
10
Serverless 风格微服务架构的优点
由于没有 EC2 设施初始化的时间,我们减少了至少一个月的工作量,分别是:
如果要把 API Gateway 算作是基础设施初始化的时间来看。第一次初始化 API Gateway 用了一天,以后 API Gateway 结合持续交付流程每次修改仅仅需要几分钟。
无论怎么说,Serverless 大大降低了基础设施配置和运维门槛。 此外,对于团队来说,Amazon API Gateway + Lambda 的微服务还带来其它好处: