### **内容简介**
本文适用于希望使用 [Amazon CloudFront](https://aws.amazon.com/cn/cloudfront/?trk=cndc-detail) Lambda\@Edge 提升 [Amazon CloudFront](https://aws.amazon.com/cn/cloudfront/?trk=cndc-detail) 边缘计算能力的用户,旨在帮助您更好的进行 CloudFront Lambda\@Edge 的开发、调试、测试、部署等工作。
首先我们会对 CloudFront Lambda\@Edge 做个简单的介绍,然后分七个步骤为您讲述如何创建一个带有 CloudFront Lambda\@Edge 处理能力的测试环境,并开展一些相关的调试工作。
* 步骤一:准备基于[部署小指南(二)](https://dev.amazoncloud.cn/column/article/64b513651da59862086bbddd)的整体架构
* 步骤二:创建一个 Lambda 函数
* 步骤三:添加 CloudFront 触发器来运行函数
* 步骤四:CloudFront Lambda\@Edge 日志输出及查询
* 步骤五:使用 Response Header 提升调试效率
* 步骤六:查看 Lambda\@Edge 资源利用率
* 步骤七:CloudFront Extension 中的 CFF 应用
通过本指南,您将学会如何使用 CloudFront,快速构建一个内容分发网络,并展示分发效果。
### **CloudFront Function 和 Lambda\\@Edge 的简单介绍**
使用 [Amazon CloudFront](https://aws.amazon.com/cn/cloudfront/?trk=cndc-detail),您可以编写自己的代码来处理 HTTP 请求和响应。代码在您的用户附近运行以最大限度的减少延迟,并且您无需管理服务器或其他基础设施。您可编写代码来操纵流经 CloudFront 的请求和响应、执行基本身份验证和授权、在边缘生成 HTTP 响应等。您编写并附加到 CloudFront 分配的代码称为边缘函数。CloudFront 目前提供了两种编写和管理边缘函数的方法:
* CloudFront Functions – 借助 CloudFront Functions,您可以使用 JavaScript 编写轻量级函数,以实现大规模、对延迟敏感的 CDN 自定义处理。CloudFront Functions 运行时环境提供亚毫秒级启动时间,可立即扩展以每秒处理数百万个请求,并且高度安全。CloudFront Functions 是 CloudFront 的原生功能,这意味着您可以完全在 CloudFront 中构建、测试和部署代码。
* Lambda\@Edge – Lambda\@Edge 是 Amazon Lambda 的扩展,它为复杂功能和完整的应用程序逻辑提供强大而灵活的计算,更接近您的查看者,并且高度安全。Lambda\@Edge 函数在 js 或 Python 运行时环境中运行。您将它们发布到单个 Amazon 区域,但当您将函数与 CloudFront 分配相关联时,Lambda\@Edge 会自动在全球范围内复制您的代码。
有关 CloudFront Functions 和 Lambda\@Edge 的区别以及选择可以参阅 CloudFront 文档。
本文会致力于介绍 CloudFront Lambda\@Edge,有关
CloudFront Functions 的详细介绍可以参考[ Amazon CloudFront 部署小指南(四)- CloudFront Function 基础与诊断](https://dev.amazoncloud.cn/column/article/64d074085306fa4a7fa4becf)。
如下图所示,Lambda\@Edge 既可以放在 Viewer 阶段,又可以放在 Origin 阶段。Lambda\@Edge 非常适合以下场景:
* 需要几毫秒或更长时间才能完成的函数。
* 需要可调节 CPU 或内存的函数。
* 依赖于第三方库(包括 Amazon 开发工具包,用于与其他 Amazon 服务集成)的函数。
* 需要网络访问才能使用外部服务进行处理的函数。
* 需要文件系统访问或访问 HTTP 请求正文的函数。
![image.png](https://dev-media.amazoncloud.cn/a8ccabf98be04f4d89b457bbb475a5f7_image.png "image.png")
### **如何开始 CloudFront Lambda\\@Edge 的开发**
CloudFront Lambda\@Edge 是 Lambda 的扩展,实际使用时也是先完成 Lambda 的开发、测试、部署,然后将其与 CloudFront 分配相关联,CloudFront 则会将 Lambda 分发到边缘站点,在边缘站点中截获请求和响应进行处理,可以显著的减少延迟并改善用户体验。
与 Lambda 不同的是,Lambda\@Edge 只支持 Node.js 和 Python 作为开发语言,且必须创建在 US East(N. Virginia)区域,它会自动分发到各个边缘站点去执行。更多对 Lambda\@Edge 的限制可以参考开发人员指南。
**下面我们就着手搭建一个最小的 CloudFront Lambda\@Edge 环境。**
### **步骤一: 准备基于部署小指南(二)的整体架构,来进行部署过程的演示**
首先,让我们基于 [CloudFront 部署小指南(二)](https://dev.amazoncloud.cn/column/article/64b513651da59862086bbddd),搭建一个加速动静结合网站的 CloudFront 环境。在本文中,我们主要会用这个环境里动态网站的部分来完成调试。
在调试过程中,我们会主要使用/api 这个关闭缓存的 Behavior path 进行调试。利用 Echo-Sever 观察 CloudFront Lambda\@Edge 带来的 Web 请求的改变。
### **步骤二:创建一个 Lambda 函数**
您可以参考[官方文档第三步](https://docs.aws.amazon.com/zh_cn/AmazonCloudFront/latest/DeveloperGuide/lambda-edge-how-it-works-tutorial.html#lambda-edge-how-it-works-tutorial-create-function?trk=cndc-detail),通过使用蓝图来快速创建一个修改响应头的示例函数。使用以下代码进行测试,以下代码中增加了 AWS_REGION 环境变量的输出,并和自定义的 function-version 一起放在 header 中返回给客户端。
如果想了解更多环境变量,可以参考官方文档检索环境变量。
*注意,修改响应头的函数需要关联 CloudFront 的 Viewer Response 或者源响应(Origin Response)事件,并且修改的是 CloudFront 的 response event 数据结构。该数据结构在 Viewer Request 和 Origin Request 事件里是无法获取的。
```js
'use strict';
exports.handler = (event, context, callback) => {
//Get contents of response
const response = event.Records[0].cf.response;
const headers = response.headers;
let region = process.env.AWS_REGION;
console.log("region: ", region);
//Set new headers
headers['strict-transport-security'] = [{key: 'Strict-Transport-Security', value: 'max-age= 63072000; includeSubdomains; preload'}];
headers['content-security-policy'] = [{key: 'Content-Security-Policy', value: "default-src 'none'; img-src 'self'; script-src 'self'; style-src 'self'; object-src 'none'"}];
headers['x-content-type-options'] = [{key: 'X-Content-Type-Options', value: 'nosniff'}];
headers['x-frame-options'] = [{key: 'X-Frame-Options', value: 'DENY'}];
headers['x-xss-protection'] = [{key: 'X-XSS-Protection', value: '1; mode=block'}];
headers['referrer-policy'] = [{key: 'Referrer-Policy', value: 'same-origin'}];
headers['function-version'] = [{key: 'Function-Version', value: 'V01'}];
headers['function-region'] = [{key: 'Function-Region', value: region}];
//Return modified response
callback(null, response);
};
```
### **步骤三:添加 CloudFront 触发器来运行函数**
您可以参考[官方文档第四步](https://docs.aws.amazon.com/zh_cn/AmazonCloudFront/latest/DeveloperGuide/lambda-edge-how-it-works-tutorial.html#lambda-edge-how-it-works-tutorial-add-trigger?trk=cndc-detail),为 Lambda 函数配置 CloudFront 触发器以运行您的函数。这儿您可以选择步骤一中提前创建好的 CloudFront 分配,缓存行为选择 /api/\*,这一次,CloudFront 事件类型我们选择**源响应(Origin Response)**,勾选确认部署到 Lambda\@Edge,点击部署。
![image.png](https://dev-media.amazoncloud.cn/df315c2066b64c44bd2cd15fbe98e030_image.png "image.png")
部署完成后,可以通过 curl 命令进行测试,我们看到响应头中已经包含了在 Lambda 中添加的标头信息。
注意:Lambda@Edge 的部署需要一些时间,可以进入 CloudFront 分配中的常规页面查看上次修改时间,确认部署进度。
```js
curl -i http://xxxx.cloudfront.net/api/test
```
![image.png](https://dev-media.amazoncloud.cn/1f0f18e919e342aa90ae49ab80643d85_image.png "image.png")
### **更新 Lambda\\@Edge 函数**
**方式一**:在 Lambda 控制台重新发布
对于已经部署到 Lambda\@Edge 的函数,如果在 Lambda 中修改了代码,需要先点击**部署(Deploy)**按钮,保存修改后的代码。然后重复步骤三,将修改后的函数部署到 Lambda\@Edge。
**方式二**:发布新版本后,更新 CloudFront
在**部署(Deploy)**之后,切换到**版本**标签页,点击**发布新版本**进行发布,发布后会得到一个新的版本号,然后到 CloudFront 控制台选择需要部署的目标分配,在**行为(Behavior)**中选择对应的行为,点击**编辑**按钮,在最下面的**函数关联**中更新**函数 ARN/名称**为刚刚生成的版本号,点击**保存更改**。
对于需要同时更新**源请求**和**源响应**的场景,可以采取方式二,同时变更**源请求**和**源响应**的版本,这样 CloudFront 在部署的时候则可以同时更新两个函数。
![image.png](https://dev-media.amazoncloud.cn/f68971e3976e4d7b86a15587d3ace245_image.png "image.png")
### **步骤四:CloudFront Lambda\\@Edge 日志输出及查询**
使用 Console.log 进行日志输出,Lambda\@Edge 会自动将日志发送到 CloudWatch 日志组,在函数运行的 Amazon 区域中创建日志流,日志组名称的格式为 `/aws/lambda/us-east-1.function-name` ,其中 `function-name` 是您在创建函数时为函数指定的名称, `us-east-1` 是运行函数的 Amazon 区域的区域代码。
Lambda\@Edge 的日志存放在函数实际执行的区域,而非函数部署的区域( `us-east-1` ),所以在调试的时候,需要先确定函数执行的区域,在 CloudWatch 控制台中切换到对应的区域,搜索 `function-name` 进入日志组方能查看最新日志。在不确定执行区域的情况下,如果在各个区域之间不停的切换,查找日志信息,极其耽误时间。而且由于日志存在延迟,还会出现明明找对了区域,但是因为延迟而错过日志,然后不停的在错误区域中寻找,耽误了宝贵的时间。
官方文档中提到的通过在 CloudFront 控制台上查看对应函数的指标图表的方式,由于是统计信息,在请求非常少的时候或许适用,如果多个区域都存在一定的请求数量,就比较难判断。
通过 Header 中的 X-Amz-Cf-Pop 值,可以获得当前请求所对应的边缘位置编号,通过 [Amazon CloudFront](https://aws.amazon.com/cn/cloudfront/?trk=cndc-detail) CDN Edge Locations 网站查找可以指导其对应的国家和城市,可以在其邻近的区域中查找日志。
**这里介绍一种可以快速准确的获得函数每一次执行所在区域的方法,就是获取 AWS_REGION 环境变量,并在 Response Header 中返回**。这里的 `AWS_REGION` 指的是执行 Lambda 函数的 Amazon 区域。
**实现方式:**
如果您已经创建了用于**源响应(Origin Response)**的 Lambda 代码,则在代码中返回修改后的 response 之前添加下面这行代码:
```js
event.Records[0].cf.response.headers['execution-region'] = [{key: 'Execution-Region', value: process.env.AWS_REGION}];
```
如果您只创建了用于**源请求(Origin Request**)的 Lambda 代码,则建议您使用以下代码创建一个用于**源响应(Origin Response**)的 Lambda 代码,并部署到 Lambda\@Edge。
```js
'use strict';
exports.handler = (event, context, callback) => {
event.Records[0].cf.response.headers['execution-region'] = [{key: 'Execution-Region', value: process.env.AWS_REGION}];
callback(null, response);
};
```
下面是响应结果示例,只要能获得相应,就可以通过 response header 获悉执行函数的区域,接下来就可以在 CloudWatch 控制台中切换到该区域等待日志的出现。
![image.png](https://dev-media.amazoncloud.cn/3ed60d5b8e8f4b31b1997b3622f370d6_image.png "image.png")
注意:在发布到生产环境时,建议删除不必要的日志输出,以节约成本。如有需要,还可以禁用 Lambda 对 CloudWatch 日志输出的权限杜绝任何日志,将**执行角色**中的 Allow:logs:PutLogEvents 移除。
### **步骤五:使用 Response Header 提升调试效率**
遇到在线调试的场景,可以通过在 Response Header 中添加版本标头的方式,来确定当前请求使用的是哪个版本的代码。在每次更新代码的时候需使用新的版本号,可以参考上面代码中的 Function-Version。
调试的时候,如果想打印查看某个变量的值,可以将该变量转换为 string 类型放到 Response Header 中输出。这样可以在得到响应结果后即可查看该值,无需再到控制台中查看,大大提高效率。
该方法在[ Amazon CloudFront 部署小指南(四)- CloudFront Function 基础与诊断](https://dev.amazoncloud.cn/column/article/64d074085306fa4a7fa4becf)中有详细介绍,思想和方式上都非常相似,可以参考。
使用 response header 输出信息进行调试的时候,需要遵循 response header 的规范,value 必须是 string 类型,且只有 CloudFront Function 执行正常将 response 完整返回给客户端时才能看到相关信息。如果代码执行异常,中断的情况,则无法获得正常响应,此时无法通过 response header 查看调试信息,这种情况下 console.log()可以将异常中断前的日志都打印出来。我们在实际开发测试时可以结合使用。
### **步骤六:查看 Lambda\\@Edge 资源利用率**
我们可以通过 CloudWatch Logs 查看函数执行时长以及使用的内存信息。建议在发布到生产环境之前,使用 Lambda Power Tuning 进行测试,以获得最佳的内存配置。
![image.png](https://dev-media.amazoncloud.cn/2cc4540630a949f0820c2d808f4b3a29_image.png "image.png")
### **步骤七:CloudFront Extension 中的 Lambda\\@Edge 应用**
上面以修改 HTTP Response 为案例介绍了 CloudFront Lambda\@Edge 的开发及调试全流程,在亚马逊解决方案团队(CSDC)维护的 CloudFront Extension 项目 中还有很多代码案例,可以供大家参考学习。CloudFront Extenstion 的 Github 链接在这里:aws-cloudfront-extensions。
### **总结**
通过本篇文章,您应该通过动手操作对 CloudFront Lambda\@Edge 有了一个初步的了解。您现在已经有了一个小巧实用的 CloudFront Lambda\@Edge 的调试环境,能够在这个环境中利用 CloudFront Lambda\@Edge 代码按照自己的需要修改 Web 请求,并且通过 Echo-Server、CloudWatch 指标和日志观察它们的运行情况,更可以通过在 Response Header 里写入的日志头即时了解函数执行所在的区域以及程序的运行状态。
**亚马逊云科技 CloudFront 部署小指南系列文章**
点击标题,即可查看往期文章:
[Amazon CloudFront 部署小指南(一)- 快速构建 CDN 内容分发](https://dev.amazoncloud.cn/column/article/64ac21d60f1a002f2aff1c2c)
[Amazon CloudFront 部署小指南 (二)- 进阶部署](https://dev.amazoncloud.cn/column/article/64b513651da59862086bbddd)
[Amazon CloudFront 部署小指南 (三)- 持续部署](https://dev.amazoncloud.cn/column/article/64bf7d47d6513d1ed1129be5)
[Amazon CloudFront 部署小指南(四)- CloudFront Function 基础与诊断](https://dev.amazoncloud.cn/column/article/64d074085306fa4a7fa4becf)
[Amazon CloudFront 部署小指南(五)- 使用 Amazon 边缘技术优化游戏内资源更新发布](https://dev.amazoncloud.cn/column/article/64da0f0a6938af044b149a89)
![开发者尾巴.gif](https://dev-media.amazoncloud.cn/2ceeca58cee44d948c2b8a44df1451b4_%E5%BC%80%E5%8F%91%E8%80%85%E5%B0%BE%E5%B7%B4.gif "开发者尾巴.gif")