更新于2024年1月,创建的函数可使用Nodejs 16/18运行
一、背景
AWS的CDN服务CloudFront在Edge端(边缘侧)具有一定的算力,可运行简单程序满足应用交互需要,这些场景包括:
- 身份验证
- 无须连接源站(ALB/EC2/S3)的情况下进行业务逻辑运算给源站卸载(Offload)压力
- 按访问者的环境(如浏览器)、或访问设备等信息定制输出结果
- 为静态源站(S3)增加处理能力
- 防爬虫等应用层防护(可与ALB/WAF搭配防护)
实现这些场景,可通过Edge的边缘侧算力实现。具体实现又有两个产品:Lambda@Edge和CloudFront Function。
这两个产品推出时间有先后,二者简单对比如下表。
项目 | Lambda@Edge | CloudFront Function |
---|---|---|
使用场景 | 复杂、长持续时间功能 | 简单、低延迟、高并发功能 |
运行位置 | 13个Regional节点 | 410+边缘节点 |
支持代码 | Node.JS/Python | JavaScript (ECMAScript 5.1 compliant) |
触发器 | Viewer request/response, Origin request/response | Viewer request/response |
内存上限 | 128MB (Viewer triggers), 10GB (Origin triggers | 2MB |
最大包体积 | 1MB (viewer triggers), 50MB (origin triggers) | 10KB |
获取Request body | YES | NO |
文件系统和网络 | 是 | 否 |
成本 | 中-低 | 低 |
以上对比描述了在不同的需求中可采用的技术手段。
本实验只演示一个简单场景,在一个通过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的加速点和区域性边缘缓存分布位置
创建Lambda@Edge函数
测试和调试 Lambda@Edge 函数
查看 CloudFront 和边缘函数指标
英文博客:Four Steps for Debugging your Content Delivery on AWS
删除 Lambda@Edge 函数和副本