亚马逊AWS官方博客

Amazon CloudFront 部署小指南(四)- CloudFront Function 基础与诊断

内容简介

本文适用于希望使用 CloudFront Functions 提升 Amazon CloudFront 边缘计算能力的用户,旨在帮助您更好的进行 CloudFront Functions 的开发、调试、测试、部署等工作。

首先我们会对 CloudFront Function 做个简单的介绍,然后分为七个步骤为您讲述如何创建一个带有 CloudFront Function 处理能力的测试环境,并开展一些相关的调试工作。

步骤一:准备基于部署小指南(二)的整体架构

步骤二:创建一个 CloudFront Function 函数

步骤三:发布函数并关联 CloudFront 分配

步骤四:CloudFront 日志输出及查询

步骤五:使用 Response Header 来完成快速调试

步骤六:查看 CloudFront Functions 计算利用率

步骤七:CloudFront Extension 中的 CFF 应用

通过本指南,您将学会如何使用 CloudFront,快速构建一个内容分发网络,并展示分发效果。

CloudFront Function 和 Lambda@Edge 的简单的介绍

使用 Amazon CloudFront,您可以编写自己的代码来处理 HTTP 请求和响应。代码在您的用户附近运行以最大限度的减少延迟,并且您无需管理服务器或其他基础设施。您可编写代码来操纵流经 CloudFront 的请求和响应、执行基本身份验证和授权、在边缘生成 HTTP 响应等。您编写并附加到 CloudFront 分配的代码称为边缘函数。CloudFront 目前提供了两种编写和管理边缘函数的方法:

  • CloudFront Functions – 借助 CloudFront Functions,您可以使用 JavaScript 编写轻量级函数,以实现大规模、对延迟敏感的 CDN 自定义处理。 CloudFront Functions 运行时环境提供亚毫秒级启动时间,可立即扩展以每秒处理数百万个请求,并且高度安全。CloudFront Functions 是 CloudFront 的原生功能,这意味着您可以完全在 CloudFront 中构建、测试和部署代码。
  • Lambda@Edge – Lambda@Edge 是 AWS Lambda 的扩展,它为复杂功能和完整的应用程序逻辑提供强大而灵活的计算,更接近您的查看者,并且高度安全。Lambda@Edge 函数在 js 或 Python 运行时环境中运行。您将它们发布到单个 AWS 区域,但当您将函数与 CloudFront 分配相关联时,Lambda@Edge 会自动在全球范围内复制您的代码。

有关 CloudFront Functions 和 Lambda@Edge 的区别以及选择可以参阅 CloudFront 文档

本文会致力于介绍 CloudFront Functions,Lambda@Edge 相关的会在后续的 CloudFront 部署小指南系列里做深入讲解。

如何开始 CloudFront Functions 的开发

CloudFront Functions 目前仅支持 JavaScript 作为开发语言,运行时环境符合 ECMAScript(ES)版本 5.1,还支持 ES 版本 6 到 9 的一些功能,同时还提供一些不属于 ES 规范的非标准方法。具体细节可以参考 JavaScript runtime features for CloudFront Functions。鉴于此,建议您使用 AWS CloudFront Functions 控制台进行 CloudFront Function 的开发和测试。

您仅需用定义函数名称和说明即可快速创建一个 CloudFront Function。

创建完成后,将会得到一个带有示例代码的 CloudFront Function,您可以在此进行代码的开发、测试和发布。在构建页面,可以对代码进行编辑(开发),同时也能看到目前线上部署的最新代码(实时标签)。

CloudFront Function 以事件(Event)驱动,我们编写的 CloudFront Function 代码,可以关联在 CloudFront 的 viewer event 或者 response event 上。例如,当关联在 CloudFront viewer event 时,每一个送达 CloudFront 分发点,匹配关联 CFF 特定 behavior 的 Web 请求都会触发 Cloud Front Pop 节点的运行环境里的 CloudFront Function 代码,依照代码对请求做出修改。如修改访问的 uri 来根据不同的设备提供适配设备屏幕的网页。或者验证请求的合法性等轻量级的计算工作。把这些计算工作交给遍布全球的 PoP 点中成百上千的服务器。能够大大提高最终用户的网页访问速度。减小源站的计算压力。

在这里,每一个 Web 请求的事件都提供给 CloudFront Function 一个 Event 的数据结构(在调试中 Event 数据结构以 json 格式呈现)。CloudFront Function 对 Event 数据结构进行修改,并返回数据结构。即完成了 Web 请求定制修改的工作。

下面我们就着手搭建一个最小的 CloudFront Function 环境:

步骤一: 准备基于部署小指南(二)的整体架构来进行部署过程的演示

首先,让我们基于 CloudFront 部署小指南(二),搭建一个加速动静结合网站的 CloudFront 环境。在本文中,我们主要会用这个环境里动态网站的部分来完成调试。

在调试过程中,我们会主要使用/api 这个关闭缓存的 Behavior path 进行调试。利用 Echo-Sever 观察 CloudFront Function 带来的 Web 请求的改变。

步骤二:创建一个 CloudFront Function 函数

我们以将跨源资源共享(CORS)标头添加到响应为需求通过 CloudFront Functions 的方式实现。

下面是一段添加 CORS Header 以支持跨域访问的代码案例,如果 Access-Control-Allow-Origin CORS Header 不存在,则添加该值。

function handler(event)  {
    var response  = event.response;
    var headers  = response.headers;

    // If Access-Control-Allow-Origin CORS header is missing, add it.
    // Since JavaScript doesn't allow for hyphens in variable names, we use the dict["key"] notation.
    if (!headers['access-control-allow-origin']) {
        headers['access-control-allow-origin'] = {value: "*"};
        console.log("Access-Control-Allow-Origin was missing, adding it now.");
    }

    return response;
}

将上面的代码片段复制到控制台编辑窗口后保存更改。

在开发过程中,可以通过测试页面对代码进行调试,该代码会在 viewer response 阶段影响响应标头,事件类型选择 viewer response,测试阶段选择 Development,然后执行测试,测试结果如下:

测试函数支持模拟请求和响应的 headers、cookies、query string。我们在响应 – 响应标头下添加标头信息进行测试:

获得的测试效果结果如下,可以看到如果响应中已经包含 access-control-allow-origin 响应头,则不会重复添加该值。

至此,我们完成了示例代码的全覆盖测试。

步骤三:发布函数并关联 CloudFront 分配

在发布中点击添加关联,选择需要关联的 CloudFront 分配,事件类型选择 Viewer Response,缓存行为选择 /api/*,点击添加关联。关联添加完成后即可点击上面的发布函数按钮,将代码分发到关联的 CloudFront 分配上。

发布完成后,可以通过 curl 命令进行测试,我们看到响应头中已经包含了 CORS 标头信息。

curl -i http://xxxx.cloudfront.net/api/test

步骤四:CloudFront 日志输出及查询

上面的代码通过 Console.log 进行日志输出,生产环境下这些日志会自动发送到 CloudWatch Logs。需要注意的是,无论在哪个边缘站点运行该函数,CloudFront Functions 始终在美国东部(弗吉尼亚北部)区域(us-east-1)中创建日志流。日志组名称的格式为 /aws/cloudfront/function/FunctionName,其中 FunctionName 是您在创建函数时为函数指定的名称。

进入 CloudWatch 控制台,将 Region 切换到美国东部(弗吉尼亚北部)区域(us-east-1),搜索 FunctionName 找到对应的日志组,选择对应时间的日志流即可查看日志信息。

注意:在发布到生产环境时,建议删除不必要的日志输出,以节约成本。

步骤五:使用 Response Header 来完成快速调试

有些场景我们需要将 CloudFront Function 发布后和源一起调试,点击发布函数并不会立刻应用到所有的边缘节点,需要一段时间来部署完成。我们可以通过在 Response Header 中添加版本标头的方式,来确定当前请求使用的是哪个版本的代码。每次更新代码的时候,我们只需要变更版本号即可。

在 CloudFront 中进行调试的时候,我们需要频繁的去到 CloudWatch 查看日志,但日志发送到 CloudWatch 中会有一定的延迟,需要在 CloudWatch 中不断刷新以查看日志是否更新。此时我们可以通过在 Response Header 中输出信息来协助调试,对于复杂的代码逻辑,我们可以在 console.log()的同时将关键信息放到 Response Header 中。

比如下面的代码,我们想查看 request header 中的 Host 标头,以及查看源端返回的 cache-control 设置,我们可以将其通过 console.log()进行打印,同时将其放到 Response Header 中输出。

function handler(event)  {
    var response  = event.response;
    var headers  = response.headers;

    // check host value in request
    var host = event.request.headers.host;
    if (host) {
        console.log("print host value in request header: " + host.value)
        headers['log-host'] = { value: host.value }
    }
    
    // Print the cache-control header
    var originalCacheControl = headers['cache-control']
    if (originalCacheControl) {
        console.log("print original cache contorl value: " + originalCacheControl.value)
        headers['log-old-cache-control'] = { value: originalCacheControl.value }
    } else {
        headers['log-old-cache-control'] = { value: "undefined" }
    }
    // Set the cache-control header
    headers['cache-control'] = {value: 'public, max-age=63072000'};

    // If Access-Control-Allow-Origin CORS header is missing, add it.
    // Since JavaScript doesn't allow for hyphens in variable names, we use the dict["key"] notation.
    if (!headers['access-control-allow-origin']) {
        headers['access-control-allow-origin'] = {value: "*"};
        console.log("Access-Control-Allow-Origin was missing, adding it now.");
    }

    headers['function-version'] = { value: 'V04' }
    return response;
}

使用 curl 进行访问结果如下,在 response header 中,我们可以通过 function-version 标头确定当前执行代码的版本,通过 req-host 获取打印的日志信息,避免了到 CloudWatch 不断刷新等到日志的流程,大大提高效率。调试完成后切记注释或删除日志相关的代码。

使用 response header 输出信息进行调试的时候,需要遵循 response header 的规范,value 必须是 string 类型,且只有 CloudFront Function 执行正常将 response 完整返回给客户端时才能看到相关信息。如果代码执行异常,中断的情况,则无法获得正常响应,此时无法通过 response header 查看调试信息,这种情况下 console.log()可以将异常中断前的日志都打印出来。我们在实际开发测试时可以结合使用。

步骤六:查看 CloudFront Functions 计算利用率

Compute utilization(计算利用率)是函数运行所花费的时间占最大允许时间的百分比。例如,值为 35 表示函数在最大允许时间的 35% 内完成。如果函数持续超过最大允许时间,CloudFront 将限制该函数。

我们可以在测试中查看计算利用率,也可以使用 CloudWatch 查看发布后的函数的执行情况。依次进入 CloudWatch – Metrics – All metrics,确认 region 已经切换到 N. Virginia 区域,搜索 CloudFront,选择 Per-Function Metrics,选中对应 function 的 FunctionComputeUtilization 指标,进行查看。

步骤七:CloudFront Extension 中的 CFF 应用

上面以 CORS 为案例介绍了 CloudFront Functions 的开发及调试全流程,在亚马逊解决方案团队(CSDC)维护的 CloudFront Extension 项目中还有很多代码案例,可以供大家参考学习。CloudFront Extenstion 的 Github 链接在这里:aws-cloudfront-extensions

总结

通过本篇文章,您应该通过动手操作对 CloudFront Function 有了一个初步的了解。您现在已经有了一个小巧实用的的 CloudFront Function 的调试环境。能够在这个环境中利用 CloudFront Function 代码按照自己的需要修改 Web 请求。并且通过 Echo-Server,CloudWatch 指标和日志观察它们的运行情况。更可以通过在 Response Header 里写入的日志即时了解程序的运行状态。

亚马逊云科技 CloudFront 部署小指南系列文章

Amazon CloudFront 部署小指南(一)- 快速构建 CDN 内容分发:https://aws.amazon.com/cn/blogs/china/amazon-cloudfront-deployment-handbook-part-one/

Amazon CloudFront 部署小指南 (二)- 进阶部署:https://aws.amazon.com/cn/blogs/china/amazon-cloudfront-deployment-handbook-part-two/

Amazon CloudFront 部署小指南 (三)- 持续部署:https://aws.amazon.com/cn/blogs/china/amazon-cloudfront-deployment-handbook-part-three/

Amazon CloudFront 部署小指南(五)- 使用 AWS 边缘技术优化游戏内资源更新发布:https://aws.amazon.com/cn/blogs/china/amazon-cloudfront-deployment-handbook-part-five/

Amazon CloudFront 部署小指南(六)- Lambda@Edge 基础与诊断:https://aws.amazon.com/cn/blogs/china/amazon-cloudfront-deployment-handbook-part-six/

Amazon CloudFront 部署小指南(七)- 使用 CloudFront Function 模板加 EchoServer 进行快速调试:https://aws.amazon.com/cn/blogs/china/quick-debugging-using-cloudfront-function-templates-and-echoserver/

Amazon CloudFront 部署小指南(八)- 使用中国区 CloudFront 及 CloudFront SSL 插件部署免费证书:https://aws.amazon.com/cn/blogs/china/divert-website-access-traffic-from-ec2-to-amazon-cloudfront/

本篇作者

朱劲松

亚马逊云科技解决方案架构师,负责基于 AWS 云平台的解决方案咨询和设计,拥有 10 多年的 IT 行业工作经验,历任系统架构师、大数据总监、CTO 等职,在边缘计算、Serverless、大数据方面有丰富的实践经验。

崔俊杰

亚马逊云科技高级产品解决方案架构师,负责亚马逊云科技云边缘安全相关的服务产品。为亚马逊云用户提供 DDoS 防御/网站前端安全防御/域名安全相关的产品咨询。对 Cloudfront,Shield,WAF,Route53,Global Accelerator 等云边缘安全相关产品有深入了解。在计算机安全、数据中心和网络领域有多年的工作经验。