如同 iPhone 当年颠覆了诺基亚,Serverless 的出现也带来了一种全新的、颠覆式的云开发架构模式。在 Serverless 出现前,开发者们根本无法想象几分钟就能快速部署一个 Web 应用上线。近日,亚马逊云科技 Tech Talk 特别邀请了资深无服务器技术专家孙华带来分享《 如何高效、极简构造无服务器 Web 应用》。孙华以 Amazon Lambda 的视角介绍了无服务器 Web 应用的构造方式,并讲述了如何利用最新发布的 Lambda Function URLs 和 Lambda Adapter 进一步简化无服务器 Web 应用的开发和调试并且实现 Web 应用在 Lambda,Fargate 和 EC2 等计算平台之间平滑迁移。
Serverless 时代的应用开发
2012 年,Iron 公司首次提出 Serverless 的概念。2014 年亚马逊云科技发布 Amazon Lambda 第一次让 Serverless 从概念走向落地,也标志着 Serverless 时代的开启。孙华认为,Amazon Lambda 作为无服务器计算服务,主要有以下四个特点。
Amazon Lambda 的出现为应用开发和部署提供了极简的模型。目前,Amazon Lambda 已有数十万用户,用户的调用峰值通常超过每秒超过百万级。
可口可乐公司就是 Amazon Lambda 的用户之一。在疫情期间,可口可乐通过 Amazon Lambda 仅用一周时间就快速把 mobile pour 应用的原型上线,实现手机与饮料机毫秒级的通讯,从创意到交付的时间缩短了 50%。
Babble 是一家线上教育服务商。疫情期间,线上学习访问量激增 2-3 倍。Babble 通过将服务迁移到 Amazon Lambda 服务器,P95 性能提高了 250ms (降低近 50%),且通过提高利用率,成本降低了四分之一。
无服务器 Web 应用使用场景
Serverless 架构常见的应用场景有数据处理、IT 运维自动化等,不少用户还会将 Serverless 应用到前端的 Web 应用上,包括 Flask、Django、Express.js、Rails 等应用。Serverless 可以渲染相应的 Web 页面到客户端,同时也支持提供 json 的 REST API,进行前后端的分离,静态的 React、Angular、Vue 前端通过 HTTP 请求, 来调用后端的 API,获取数据,从而展示给客户。此外,手机后端应用也经常会使用 Serverless 开发小程序的后端。
无服务器 Web 应用有哪些特点呢?首先,最常见的 Web 应用开发方式,服务器里面运行的是 Web 应用进程,此时,Web 应用会监听到一个端口。并且,在 Amazon Lambda 里面每个请求都是由一个单独的计算实例来处理的,不管 CPU 还是内存,都专门为这一个请求服务。这种方式的优势在于,可以将每个请求全部隔离开,不会出现某一个请求崩溃,影响其他用户的并发需求。
其次,Web 应用在 Amazon Lambda 里面运行,需要是无状态的应用。因为 Amazon Lambda 请求会随机分发到多个计算实例上,每个计算实例在本地缓存数据,下一个请求进入,可能不会分配到同一个实例上。所以,如果需要共享状态,可以存储在 Redis 或者数据库中。
第三,在 Amazon Lambda 的计算环境上,每次调用的请求处理完毕后,计算环境会被冻结到下一次请求来之前。在这段时间里是没有 CPU 资源的,后台任务不能运行,所以不能在 Amazon Lambda 里面运行后台的进程。
最常见的基于前后端分离的 Web 应用架构,通常会把前端的 SPEA 应用放在 Amazon S3 上做 HTTP、Django 等,前端用 CDN 加速。用户下载了前端后,前端会通过 API Gateway,来调用后端动态的数据。API Gateway 提供相应的 HTTP 的入口,触发 Lambda 函数,从而运行 Web 应用。Web 应用可以访问数据库或者任何后端。
新功能:Lambda Function URLs
亚马逊云科技经常会收到客户的反馈。客户希望在 Amazon Lambda 上学习运行 Web 应用或者将单独的应用运行在 Lambda Function 里面。在 Lambda Function 里面处理所有 URL 相应的业务逻辑,只需要暴露一个 HTTP 入口就可以了,几乎不需要任何额外的学习成本。上个月,亚马逊云科技推出了 Amazon Lambda 内置的 Function URLs 端口,在 Lambda 函数里配置一个 Function URL,就可以提供 HTTPS Endpoint。目前该功能仅在 Amazon Global 提供,在中国区会于近期上线。Function URLs 指向 Amazon Lambda 的最新版本,或者是 Amazon Lambda 别名。Amazon Lambda 别名可以指向 1~2 个 Amazon Lambda 的版本,因此可以通过指向 alias 的方式进行灰度发布或者蓝绿部署。
Lambda Function URLs 与 API Gateway 之间有什么区别?
首先,Function URLs 是非常简单地内置提供 HTTP 入口的方式。它是暴露在公网上的 HTTP 入口,其入口方式是 HTTPS 的,认证方式可以采用 IAM 的权限控制或者在公网上实现。Function URLs 不提供 Custom domain 支持,但是提供 CORS 支持,因此可以跨站请求,比如,浏览器发来 Option,Function URLs 会主动根据配置,返回相应的结果,不会触犯 Lambda 函数。但是,Function URLs 只具备一些简单的功能,对于一些高级的功能没办法实现,比如,使用 Usage Plans,基于 WAF 的集成和在内部提供 Private Enedpoint,目前 Function URLs 是无法实现的。因此,如果想使用 WAF、Usage Private、Private Enedpoint 等功能,请使用 API Gateway 的 Rest API。
其次,Lambda Function URLs 的超时时间是与函数的超时时间是一样的,比如,函数最长要运行 15 分钟,Lambda Function URLs 请求就可以运行 15 分钟,而 Rest API 和 HTTP API 的超时时间仅为 29-30s。在 Payload Size 上,Function URLs 跟 Amazon Lambda 是一样的,均为 6MB。
第三,采用 Lambda Function URLs 不需要支付额外的费用,但是采用 Rest API 与 HTTP API 都需要支付相应的费用。
很多用户需要 Web 应用有相应的自定义域名访问网站。这时,可以在前面通过 CDN 的方式,加上自定义域名,比如,在 CloudFront 上加上自己的域名,同时结合 Amazon WAF 进行相应集成应用的防护。因为 Function URLs 只提供 HTTPS 的入口,在 CDN 回源的时候,是通过 HTTPS 回到源站的,不能把 Host Header 传回源站,不能获得用户真正访问的自定义域名。如果域名是固定的,可以在应用里通过配置了解,但如果域名不是固定的,那么就需要根据二级域名确定是哪个租户在做相应的处理。这种情况下,就可以利用 CloudFront Function 的功能。CloudFront Function 可以在用户请求端获取域名,加到另外的 Header 里面,再把 Header 传回源站,在源站的应用里面就可以通过这个 Header 获得用户访问的真实的域名。
如果基于 Lambda Function URLs 部署单函数的的 Web 应用,前面通过 CloudFront 做动态请求的代理,静态资源放在 Amazon S3,就会统一为如下架构,也就不需要 API Gateway 了。
新工具:Lambda Adapter
Lambda Function URLs 为大家提供了一个非常简单就可以获得 HTTP 入口的方式,但是 Amazon Lambda 的开发方式是基于事件驱动的方法来进行开发的,会将事件源过来的事件转换为 json 对象。对于熟悉 Web 应用开发的开发者,会有额外的学习成本,一般采用的做法是在应用里面写一个 Handler 函数,进行一层转换,把 json 转成 Web 应用需要的格式,但是这种方式可以进一步简化。
亚马逊云科技推出的新工具 Lambda Adapter 作用就是帮助用户完成转化。采用 Lambda Adapter 无需修改代码即可在 Amazon Lambda 上运行 Web 应用。Lambda Adapter 从 Amazon Lambda 获得相应的 json 对象,把它转换成 HTTP 请求,发送到 Web 应用上,Web 应用处理之后通过 HTTP Response 回到 Lambda Adapter,Lambda Adapter 会将它转换成 Lambda 需要的 json Response 的格式。这样不管用什么语言,什么样的 Web 框架,开发 Web 应用或者 HTTP API,都可以通过这样的方式,不需要修改代码,也不需要添加任何的依赖,就可以在 Amazon Lambda 上运行,且不需要用特殊的工具,就可以用熟悉的方法在本地开发调试 API。
此外,Lambda Adapter 支持 API Gateway 的 Rest API、HTTP API,作为 HTTP 的事件源。不管是哪种格式,Lambda Adapter 都可以做相应的解析并转换。同时因为 Lambda Function URLs 的事件格式和 API Gateway HTTP API V2 事件格式完全一致,所以 Lambda Adapter 不用做任何的修改,就可以接入 Lambda Function URLs。用户结合 Lambda Function URLs 和 Lambda Adapter 就可以把 Web 应用或者 HTTP API 运行在 Lambda 上面。同时,因为 Web 应用有可能返回图片等 Binary 数据,因为 json 里面是不能直接存 Binary 数据的,所以 Binary 数据要做 base64 encoding,Lambda Adapter 会检测返回的数据是不是 Binary 数据,并且自动完成 base64 转码工作。
同时,Adapter 支持用容器镜像的方式打包函数,也支持用 zip 的方式来打包函数。当用户使用容器镜像的方式打包函数,得到的容器镜像既可以在 Amazon Lambda 上运行,也可以在容器环境里面运行,甚至在本地的电脑上运行。所以 Web 应用可以非常方便地运行在不同的计算平台上,不需要做修改,就可以在不同的平台之间平滑地迁移。
构造无服务器 Web 应用实践
通过 Lambda Function URLs 和 Lambda Adapter 可以很方便的通过熟悉的 API 的方式,构建了 Web 应用。但是如果用 Lambda Adapter,是否可以把现有的 Web 应用运行在 Amazon Lambda 上呢?
亚马逊云科技用 WooCommerce 进行了尝试。WooCommerce 是一个电商的场景,为什么要尝试将 WooCommerce 运行在 Amazon Lambda 上呢?首先,在 Buildwith 网站上,访问量最大的一百个网站中,WooCommerce 所占份额第一;其次,Wordpress 是基于 PHP 开发的,PHP 在 Web 应用网站建设上,大概有 70-80% 的网站是基于 PHP 开发的,使用量非常广;第三,PHP 语言,每个请求进入,都需要重新进行初始化,同 Amazon Lambda 无状态的计算环境非常契合。
亚马逊云科技团队采用了如下无服务器 WooCommerce 架构,在 Amazon Lambda 上运行 WordPress。
该架构前面部分采用 Route53、CloudFront 做 CDN,用 Amazon Lambda 运行应用,前端用 Lambda Function URLs 代替 API Gateway 作为入口,并将静态资源通过 Amazon S3 存储。然后,在 Lambda 函数里面通过 Docker 镜像的方式,把 PHP Runtime、NGINX Server 和 PHP FPM 运行的进程管理服务加载进来,把 Wordpress 与 WooCommerce 代码打包进去。后端用相应的数据库,比如 Redis、最新的 V2 数据库做数据的缓存,用 EFS 共享的系统来保存用户上传的文件。通过该架构,亚马逊云科技实现了在 Serverless 基础上运行 WordPress。
在 Serverless 上运行网站实际性能如何呢?如果在 WordPress 的网站想要做到比较好的性能,可以把 WordPress 静态化,变成静态内容放在前端的 CDN 网站上。如果运行的是 WooCommerce 电商网站,用户浏览商品、将商品加入购物车、提交订单、支付,是动态请求,需要回到后端的 PHP 应用上。对于静态应用,性能方面可以做到 3 分钟内从无请求到最高峰值,每分可处理 2 万多笔订单,持续 1 小时。成本方面,每处理一笔订单需要调用 10 次 API,也就是处理 100 万笔订单需要调用 1000 万次 Lambda 函数。据统计,1000 万次 Lambda 函数调用的费用也仅为 300 美元。不仅如此,在测试过程中网站的 API 响应时间也非常平稳,P99 延迟仅为在 1-2s,P50 延迟只有 500ms 左右。