部署你的第一个边缘函数 Lambda@Edge并查看运行日志

更新于2024年1月,创建的函数可使用Nodejs 16/18运行

一、背景

AWS的CDN服务CloudFront在Edge端(边缘侧)具有一定的算力,可运行简单程序满足应用交互需要,这些场景包括:

  • 身份验证
  • 无须连接源站(ALB/EC2/S3)的情况下进行业务逻辑运算给源站卸载(Offload)压力
  • 按访问者的环境(如浏览器)、或访问设备等信息定制输出结果
  • 为静态源站(S3)增加处理能力
  • 防爬虫等应用层防护(可与ALB/WAF搭配防护)

实现这些场景,可通过Edge的边缘侧算力实现。具体实现又有两个产品:Lambda@Edge和CloudFront Function。

这两个产品推出时间有先后,二者简单对比如下表。

项目Lambda@EdgeCloudFront Function
使用场景复杂、长持续时间功能简单、低延迟、高并发功能
运行位置13个Regional节点410+边缘节点
支持代码Node.JS/PythonJavaScript (ECMAScript 5.1 compliant)
触发器Viewer request/response, Origin request/responseViewer request/response
内存上限128MB (Viewer triggers), 10GB (Origin triggers2MB
最大包体积1MB (viewer triggers), 50MB (origin triggers)10KB
获取Request bodyYESNO
文件系统和网络
成本中-低

以上对比描述了在不同的需求中可采用的技术手段。

本实验只演示一个简单场景,在一个通过CloudFront的CDN请求上,为源站返回的结果加上几个定制的Header,并反馈给浏览者客户端。此场景只需要使用Lambda@Edge即可实现。

二、配置Lambda@Edge

1、使用蓝图从模版创建一个用于CloudFront的默认的Lambda

首先进入Lambda服务,选择Region为北弗吉尼亚,代号是us-east-1,即N.Virginia

请注意必须选择这个Region。因为CloudFront是全球级别的服务,其配置和部署都通过这个Region下发。因此与之搭配的Lambda@Edge服务也要从这里下发。

确认Region后,点击新建函数。如下截图。

在创建向导界面中,选择类型是Use a blueprint。在下方选择Blueprint name位置,搜索关键字CloudFront,选中Modify HTTP response header。在Function name位置输入函数名称edge-demo,然后向下滚动页面。如下截图。

Execution role位置,选择Create a new role from AWS policy templates,在Role name位置,输入edge-demo-role,在Policy templates - optional位置已经可以看到自动加载了默认的Template,因此无需修改,继续向下滚动页面。如下截图。

在Lambda代码部分,无须修改,因为当前的Lambda是从Blueprint蓝图加载的,因此是默认的代码不允许修改,创建完毕后即可修改。点击右下角的Create Function按钮完成Lambda函数创建。如下截图。

在创建函数向导的最后一步,在Deploy to Lambda@Edge配置界面,在Select an option的选项位置,选择Configure new CLoudFront trigger表示部署一个新的CDN触发器;在Distribution位置,从下拉框中选择要使用的CDN发布点;在Cache behavior位置选择*,在CloudFront event位置,选择Origin response,即本函数会修改源站返回的信息;在Confirm deploy to Lambda@Edge位置选中。最后点击Deploy按钮部署。如下截图。

部署成功,此时页面会提示本Lambda将同步到运行Lambda@Edge的所有CloudFront Regional节点,因此需要一段时间同步。在以后修改了代码后,都需要一段时间进行同步。如下截图。

2、更新Lambda@Edge的代码

在上一步创建完毕的代码位置,点击标签页Code,点击右上角的修改代码。

在编辑代码位置,用如下代码完全替换之前的Blueprint模版的代码。新的代码如下:

'use strict';
export const handler = (event, context, callback) => {

    //Get contents of response
    const response = event.Records[0].cf.response;
    const headers = response.headers;

    //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'}];

    //Return modified response
    callback(null, response);
};

替换完毕后,点击代码上方的Deploy按钮。如下截图。

部署提示成功。如下截图。

3、生成测试事件并在Lambda控制台测试

点击页面上Test按钮,在Test event action中选择Create new event,在Event name输入modify-query-string,在Event sharing settings位置选择Private,从Template - optional下拉框选择CloudFront Modify Query String,然后按右下角的Save按钮。

生成测试后,再按下Test按钮,即可测试通过。

4、将更新代码后的Lambda重新部署到Edge

点击页面右上角的Add trigger按钮。如下截图。

在选择Trigger configuration界面,从下拉框中选择CloudFront,然后点击页面中间的Deploy to Lambda@Edge按钮。注意这个界面必须点击这个按钮,如果点击右下角的Add按钮,也会提示Lambda与CloudFront绑定时候必须通过Lambda@Edge发布。如下截图。

Deploy to Lambda@Edge界面,选择Use existing CloudFront trigger on this function,即可在下拉框中选择到上一步创建的默认函数(V1)的关联点。然后点击右下角Deploy按钮。如下截图。

页面提示新版本(V2)部署成功。如下截图。

4、确认CloudFront的发布点更新成功

进入CloudFront发布点,找到刚才Lambda@Edge绑定的发布点。点击进入详情。点击第三个标签页Behaviors,选中默认的发布行为,点击编辑按钮。如下截图。

将页面滚动最下方。查看现有发布点绑定的Lambda@Edge。本文实验第一步,选择Lambda的Blueprint时候,创建的是一个修改源站返回结果即Origin response的函数。因此这里可以看到正确的匹配关系,并且右侧能看到正确的Lambda名称和版本,即表示配置成功。

Lambda@Edge新部署和代码更新都会影响CloudFront的发布点的状态。因此第一次创建和每次修改代码后,要等到发布点显示可用后,才可以测试代码。如下截图中显示可用表示更新完成。

三、测试Lambda@Edge运行

1、验证Lambda@Edge运行

前文配置的Lambda代码的作用是在源站返回的信息中,添加新的Header。这里验证Lambda@Edge的正常运行,只需要检查特定Header是否被添加成功,即可确认运行正常。

(1) 浏览器访问

使用浏览器打开CDN发布点内的文件,然后按F12键进入浏览器的调试模式,从中查找特殊Header。可看到Lambda@Edge配置的Header可被客户端显示出来。如下截图。

(2) CURL模拟应用接口的访问

CURL命令可以通过添加-I(大些字母i)的参数表示打印访问请求的Header。执行如下命令后,可以看到CURL也获取了Lambda@Edge执行过程添加到源站返回结果上的特殊Header。如下截图。

2、查看Lambda@Edge运行日志

在通过本文第一章节的对比表格可以看到,Lambda@Edge的运行位置是在CloudFront的Regional区域性边缘缓存节点。因此为了查看Lambda@Edge日志,首先需要确认最终用户访问的POP点,然后定义到相邻的、最近的区域性边缘缓存节点的,即可在其上通过CloudWatch日志获取Lambda@Edge的运行日志。

(1)通过查看Header信息确认当前使用的POP点

以上一章节执行CURL返回的结果为例,信息如下:

HTTP/2 200
content-type: image/png
content-length: 2677139
date: Fri, 28 Jul 2023 06:29:13 GMT
last-modified: Fri, 28 Jul 2023 04:02:52 GMT
etag: "0b5f05cfd9c57254810f16c31718e543"
x-amz-server-side-encryption: AES256
x-amz-version-id: 1Fx6lv0PtZgZOjtgid4KfCF9mvtOTyle
accept-ranges: bytes
server: AmazonS3
strict-transport-security: max-age= 63072000; includeSubdomains; preload
content-security-policy: default-src 'none'; img-src 'self'; script-src 'self'; style-src 'self'; object-src 'none'
x-content-type-options: nosniff
x-frame-options: DENY
x-xss-protection: 1; mode=block
referrer-policy: same-origin
x-cache: Miss from cloudfront
via: 1.1 2d08c1a759237434f5ff684561073cb8.cloudfront.net (CloudFront)
x-amz-cf-pop: HKG60-C1
alt-svc: h3=":443"; ma=86400
x-amz-cf-id: mL-dOk-2UIXNjvkxdSxYJhIMLsRXJuDdfjaSxyivotp1bKmW3-uzvA==

这段信息中,可看到x-amz-cf-pop: HKG60-C1表示CloudFront返回的POP点代号。而HKG是国际航空运输协会机场代码(IATA)的城市代码,表示香港区域,由此即可得知,当前访问是香港的POP点响应了这个访问。

与香港最近的Regional区域性缓存节点是新加坡,因此访问AWS新加坡Region的CloudWatch服务,即可查看日志。

(2)通过CloudWatch查看日志

首先进入CloudWatch服务界面,点击左侧的遥测Telemetry菜单,点击右侧的Lambda@Edge标签页,选中当前的的函数,点击右上角的View metrics按钮。如下截图。

在弹出的监控数据查看页面,点击View function logs按钮,可看到弹出的下拉框就是CloudFront的13个Regional区域缓存节点的清单。我们从中选择上一步定位的新加坡Region,即可跳转。如下截图。

打开对应区域的CloudWatch服务后,可以在Log groups日志组下边,看到名为/aws/lambda/us-east-1.edge-demo-01的日志信息。其中的us-east-1就是部署CDN服务的美东1(即全球区域),而edge-demo-01就是Lambda的函数的名字。点击日志,如下截图。

可看到的CloudWatch日志如下:

需要注意的是,Lambda@Edge执行的日志是保存在距离用户距离最近的Regional区域缓存节点的CloudWatch上的。本例中访问到香港的POP点,因此Lambda@Edge的日志落到了新加坡区域。如果有用户从北美访问同一个CloudFront发布点的相同的网页,但又因为客户访问的是北美的POP,那么Lambda@Edge的日志就会落到距离客户访问最近的北美的某个Region上。具体落到哪一个Region,以最终访问者每次访问为准。

3、删除Lambda@Edge函数

Lambda@Edge一旦创建并分发到CloudFront,是不能直接被删除的。首先需要在CloudFront发布点内取消函数的关联。

在Lambda取消关联成功后,还需要等待数小时后,才可以彻底删除。如果立刻删除,会提示如下错误。如下截图。

这种情况只需等待几个小时再进行删除即可。有关说明,请参考本文末尾的参考文档。

四、参考文档

CloudFront的加速点和区域性边缘缓存分布位置

https://aws.amazon.com/cn/cloudfront/features/?nc=sn&loc=2&whats-new-cloudfront.sort-by=item.additionalFields.postDateTime&whats-new-cloudfront.sort-order=desc

创建Lambda@Edge函数

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

测试和调试 Lambda@Edge 函数

https://docs.aws.amazon.com/zh_cn/AmazonCloudFront/latest/DeveloperGuide/lambda-edge-testing-debugging.html

查看 CloudFront 和边缘函数指标

https://docs.aws.amazon.com/zh_cn/AmazonCloudFront/latest/DeveloperGuide/viewing-cloudfront-metrics.html#monitoring-console.lambda-at-edge

英文博客:Four Steps for Debugging your Content Delivery on AWS

https://aws.amazon.com/cn/blogs/networking-and-content-delivery/four-steps-for-debugging-your-content-delivery-on-aws/

删除 Lambda@Edge 函数和副本

https://docs.aws.amazon.com/zh_cn/AmazonCloudFront/latest/DeveloperGuide/lambda-edge-delete-replicas.html