AWS Lambda
开发人员指南
AWS 服务或AWS文档中描述的功能,可能因地区/位置而异。点 击 Getting Started with Amazon AWS to see specific differences applicable to the China (Beijing) Region.

使用 SAM Local 在本地测试您的无服务应用程序 (公开测试版)

此功能在公开测试版中提供,可能随时更改。

如前所述,AWS SAM 是用于部署无服务应用程序的快速简单的方式,您可以用它来编写简单的模板,描述您的函数及其事件源 (Amazon API Gateway、Amazon S3、Kinesis 等)。SAM Local 是以 AWS SAM 为基础的 AWS CLI 工具,在将无服务应用程序上传到 Lambda 运行时前,为您提供在本地开发、测试和分析它们的环境。无论您使用 Linux、Mac 或 Microsoft Windows 进行开发,均可使用 SAM Local 创建模拟 AWS 运行时环境的本地测试环境。这样可以帮助您解决性能之类的问题。使用 SAM Local 还可以更快地迭代开发您的 Lambda 函数代码,因为无需将您的应用程序包重新部署到 AWS Lambda 运行时。有关更多信息,请参阅 使用 SAM Local 构建一个简单的应用程序

SAM Local 与 AWS SAM 配合使用,可以直接调用或通过 API 网关 终端节点调用利用 SAM 模板定义的函数。您可以使用 SAM Local 功能在您自己的测试环境中分析无服务应用程序的性能并进行相应更新。以下示例利用操作代码样本介绍了使用 SAM Local 的其他好处。例如,您可以执行以下操作:

  • 生成示例函数负载 (例如 Amazon S3 事件)。

    Copy
    $ sam local generate-event s3 --bucket bucket-name --key key-name > event_file.json
  • 利用您的 Lambda 函数对示例函数负载进行本地测试。

    Copy
    $ sam local invoke function-name -e event_file.json
  • 引发本地 API 网关 以测试 HTTP 请求和响应功能。可以使用热重载功能测试和迭代您的函数,而无需重启或将它们重新加载到 AWS 运行时。

    Copy
    $ sam local start-api

    SAM Local 将在您的 SAM 模板内自动查找任何定义了 API 事件源的函数,并将其安装在定义的 HTTP 路径中。在以下示例中,Ratings 函数将在 /ratings 中为 GET 请求安装 ratings.py:handler()

    Copy
    Ratings: Type: AWS::Serverless::Function Properties: Handler: ratings.handler Runtime: python3.6 Events: Api: Type: Api Properties: Path: /ratings Method: get

    默认情况下,SAM Local 使用代理集成 并期望来自 Lambda 函数的响应包含以下各项中的一个或多个:statusCodeheaders 和/或 body。例如:

    Copy
    // Example of a Proxy Integration response exports.handler = (event, context, callback) => { callback(null, { statusCode: 200, headers: { "x-custom-header" : "my custom header value" }, body: "hello world" }); }

    如果您的 Lambda 函数不返回有效的代理集成响应,您将会在访问您的函数时收到 HTTP 500 (Internal Server Error) 响应。SAM Local 还会打印以下错误日志消息来帮助您诊断问题:

    Copy
    ERROR: Function ExampleFunction returned an invalid response (must include one of: body, headers or statusCode in the response object)
  • 验证是否遵循所有运行时约束,例如可使用的最大内存或 Lambda 函数调用的超时限制。

  • 检查 AWS Lambda 运行时日志,以及您在 Lambda 函数代码中指定的所有自定义日志记录输出 (例如 console.log)。SAM Local 会自动显示此输出。下面是一个示例。

    Copy
    START RequestId: 2137da9a-c79c-1d43-5716-406b4e6b5c0a Version: $LATEST 2017-05-18T13:18:57.852Z 2137da9a-c79c-1d43-5716-406b4e6b5c0a Error: any error information END RequestId: 2137da9a-c79c-1d43-5716-406b4e6b5c0a REPORT RequestId: 2137da9a-c79c-1d43-5716-406b4e6b5c0a Duration: 12.78 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 29 MB
  • 使用 AWS CLI 遵循您建立的安全凭证。这就意味着您的 Lambda 函数可以对组成无服务应用程序的 AWS 服务进行远程调用。如果您尚未安装 AWS CLI,请参阅安装 AWS 命令行界面

    SAM Local 与 AWS CLI 和开发工具包一样,会按照以下顺序查找凭证:

    • 环境变量 (AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY)

    • AWS 凭证文件,在 Linux、MacOS 或 Unix 上位于 ~/.aws/credentials,在 Windows 上位于 C:\Users\USERNAME \.aws\credentials

    • 实例配置文件凭证 (如果在分配了实例角色的 Amazon EC2 实例上运行)

支持的运行时

SAM Local 支持以下 AWS 运行时:

  • node.js 4.3

  • node.js 6.10

  • python 2.7

  • python 3.6

  • java8

使用 SAM Local 的要求

要使用 SAM Local,您需要安装 Docker 和 SAM Local。

安装 Docker

Docker 是开源软件容器平台,允许您构建、管理和测试在 Linux、Mac 或 Windows 上运行的应用程序。有关更多信息和下载说明,请参阅 Docker

安装 Docker 后,SAM Local 会自动提供自定义 Docker 镜像,名为 docker-lambda。此镜像由 AWS 合作伙伴专门设计,用于模拟真正的 AWS Lambda 执行环境。此环境中包括已安装的软件、库、安全权限、环境变量以及Lambda 执行环境和可用库中介绍的其他功能。

您可以使用 docker-lambda 在本地调用您的 Lambda 函数。在此环境中,您的无服务应用程序就像在 AWS Lambda 运行时中那样执行和表现,而您不必重新部署运行时。它们在此环境中的执行和性能反应了超时和内存使用等情况。

重要

由于这是模拟环境,不能保证本地测试结果与实际 AWS 运行时完全相同。

有关更多信息,请参阅 GitHub 上的 Docker Lambda。(如果您没有 Github 账户,则可以免费创建一个,然后访问 Docker Lambda。)

安装 SAM Local

您可以在 Linux、Mac 和 Windows 环境中运行 SAM Local。SAM Local 最简单的安装方法是使用 NPM

Copy
npm install -g aws-sam-local

然后验证安装是否成功。

Copy
sam --version

如果 NPM 不适用,您可以下载最新二进制文件,立即开始使用 SAM Local。您可以在 SAM CLI GitHub 存储库中的“Releases”部分找到二进制文件。

SAM Local 使用入门

SAM Local 包含以下 CLI 操作:

  • start-api:创建本地 HTTP 服务器,用于托管您的所有 Lambda 函数。如果使用浏览器或 CLI 访问,此操作会在本地启动 Docker 容器来调用您的函数。它会读取 AWS::Serverless::Function 资源的 CodeUri 属性,在您的文件系统中找到包含 Lambda 函数代码的路径。对于 Node.js 或 Python 等解释性语言,此路径可以是项目的根目录;此路径还可以是存储编译构件的构建目录;对于 Java 可以是 .jar 文件。

    如果使用解释性语言,本地更改在同一 Docker 容器中可用。这种方法意味着无需重新部署即可重新调用您的 Lambda 函数。对于编译语言或需要复杂包装支持的项目,建议运行您自己的构建解决方案,并使 AWS SAM 指向包含所需的构建依赖关系文件的目录。

  • invoke:调用一次本地 Lambda 函数,调用完成后即终止。

    Copy
    # Invoking function with event file $ sam local invoke "Ratings" -e event.json # Invoking function with event via stdin $ echo '{"message": "Hey, are you there?" }' | sam local invoke "Ratings" # For more options $ sam local invoke --help
  • generate-event:生成模拟无服务事件。您可以使用这些操作在本地开发并测试响应异步事件的函数,例如 Amazon S3、Kinesis 和 DynamoDB 中的函数。以下为 generate-event 操作可用的命令选项。

    Copy
    sam local generate-event NAME: sam local generate-event - Generates Lambda events (e.g. for S3/Kinesis etc) that can be piped to 'sam local invoke' USAGE: sam local generate-event command [command options] [arguments...] COMMANDS: s3 Generates a sample Amazon S3 event sns Generates a sample Amazon SNS event kinesis Generates a sample Amazon Kinesis event dynamodb Generates a sample Amazon DynamoDB event api Generates a sample Amazon API Gateway event schedule Generates a sample scheduled event OPTIONS: --help, -h show help
  • validate:根据官方 AWS 无服务应用程序模型规范验证您的模板。以下是示例。

    Copy
    $ sam validate ERROR: Resource "HelloWorld", property "Runtime": Invalid value node. Valid values are "nodejs4.3", "nodejs6.10", "java8", "python2.7", "python3.6"(line: 11; col: 6) # Let's fix that error... $ sed -i 's/node/nodejs6.10/g' template.yaml $ sam validate Valid!
  • packagedeploysam packagesam deploy 隐式调用 AWS CloudFormation 的 packagedeploy 命令。有关 SAM 应用程序包装和部署的更多信息,请参阅打包和部署

    以下内容演示了如何在 SAM Local 中使用 packagedeploy 命令。

    Copy
    # Package SAM template $ sam package --template-file sam.yaml --s3-bucket mybucket --output-template-file packaged.yaml # Deploy packaged SAM template $ sam deploy --template-file ./packaged.yaml --stack-name mystack --capabilities CAPABILITY_IAM

使用 SAM Local 构建一个简单的应用程序

假设您希望构建一个简单的 RESTful API 操作,来创建、读取、更新和删除一组产品。开始需要创建以下目录结构:

dir/products.js

dir/template.yaml

template.yaml 文件是 AWS SAM 模板,描述可以处理所有 API 请求的单个 Lambda 函数。

注意

默认情况下,start-apiinvoke 命令会在您的工作目录中搜索 template.yaml 文件。如果您引用其他目录中的 template.yaml 文件,请在这些操作中添加 -t--template 参数,并传递该文件的绝对或相对路径。

复制并在 template.yaml 文件中粘贴以下内容。

Copy
AWSTemplateFormatVersion : '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: My first serverless application. Resources: Products: Type: AWS::Serverless::Function Properties: Handler: products.handler Runtime: nodejs6.10 Events: ListProducts: Type: Api Properties: Path: /products Method: get CreateProduct: Type: Api Properties: Path: /products Method: post Product: Type: Api Properties: Path: /products/{product} Method: any

上述示例配置以下 RESTful API 终端节点:

  • 向 /products 发送 PUT 请求,创建新产品。

  • 向 /products 发送 GET 请求,列出所有产品。

  • 向 /products/{product} 发送 GETPUTDELETE 请求,读取、更新或删除一个产品。

接下来,将以下代码复制并粘贴到 products.js 文件中。

Copy
'use strict'; exports.handler = (event, context, callback) => { let id = event.pathParameters.product || false; switch(event.httpMethod){ case "GET": if(id) { callback(null, {body: "This is a READ operation on product ID " + id}); return; } callback(null, {body: "This is a LIST operation, return all products"}); break; case "POST": callback(null, {body: "This is a CREATE operation"}); break; case "PUT": callback(null, {body: "This is an UPDATE operation on product ID " + id}); break; case "DELETE": callback(null, {body:"This is a DELETE operation on product ID " + id}); break; default: // Send HTTP 501: Not Implemented console.log("Error: unsupported HTTP method (" + event.httpMethod + ")"); callback(null, { statusCode: 501 }) } }

调用 start-api 命令,启动您的 API 操作的本地复本。

Copy
$ sam local start-api 2017/05/18 14:03:01 Successfully parsed template.yaml (AWS::Serverless-2016-10-31) 2017/05/18 14:03:01 Found 1 AWS::Serverless::Function 2017/05/18 14:03:01 Mounting products.handler (nodejs6.10) at /products [POST] 2017/05/18 14:03:01 Mounting products.handler (nodejs6.10) at /products/{product} [OPTIONS GET HEAD POST PUT DELETE TRACE CONNECT] 2017/05/18 14:03:01 Mounting products.handler (nodejs6.10) at /products [GET] 2017/05/18 14:03:01 Listening on http://localhost:3000 You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload while working on your functions, changes will be reflected instantly/automatically. You only need to restart if you update your AWS SAM template.

然后您可以使用浏览器或 CLI,在本地测试 API 终端节点。

Copy
$ curl http://localhost:3000/products "This is a LIST operation, return all products" $ curl -XDELETE http://localhost:3000/products/1 "This is a DELETE operation on product ID 1"

要查看更多示例,请参阅 aws sam local/samples

本地日志

您可以使用 invokestart-api 命令,将日志从 Lambda 函数的调用传递到文件中。如果要针对 SAM Local 运行自动化测试,并希望捕获日志以进行分析,这种方法就很有用。以下是示例。

Copy
$ sam local invoke --log-file ./output.log

使用环境变量文件

如果您的 Lambda 函数使用 环境变量,SAM Local 可为 invokestart-api 命令提供 --env-vars 参数。有了这个参数,您可以使用 JSON 文件,其中包含函数中定义的环境变量的值。JSON 文件的结构应与以下内容类似。

Copy
{ "MyFunction1": { "TABLE_NAME": "localtable", "BUCKET_NAME": "testBucket" }, "MyFunction2": { "TABLE_NAME": "localtable", "STAGE": "dev" }, }

然后您可以使用以下命令访问 JSON 文件:

Copy
$ sam local start-api --env-vars env.json

使用 Shell 环境

如果将您的 Shell 环境中定义的变量映射到您的 Lambda 函数中的变量,则会传递到 Docker 容器。函数可全局访问 Shell 变量。例如,假设您有两个函数,MyFunction1MyFunction2,有一个变量名为 TABLE_NAME。在这种情况下,通过 Shell 环境提供的 TABLE_NAME 的值对于两个函数而言均可用。

以下命令针对两个函数将 TABLE_NAME 的值设为 myTable

Copy
$ TABLE_NAME=mytable sam local start-api

注意

您可以组合使用 Shell 变量和具备环境变量的外部 JSON 文件,从而获得更高的灵活性。如果两处均定义了某一变量,外部文件的变量会覆盖 Shell 版本。以下是优先级从最高到最低的顺序:

  • 环境变量文件

  • Shell 环境

  • SAM 模板中包含的硬编码值

使用 SAM Local 进行调试

sam local invokesam local start-api 均支持函数的本地调试。要启用 SAM Local 的调试支持,请通过命令行指定 --debug-port-d

Copy
# Invoke a function locally in debug mode on port 5858 $ sam local invoke -d 5858 function logical id # Start local API Gateway in debug mode on port 5858 $ sam local start-api -d 5858

注意

如果使用 sam local start-api,本地 API 网关会公开您的所有 Lambda 函数。但是,因为只能指定一个调试端口,所以每次只能调试一个函数。

调试 Python 编写的函数

Python 与 Node.js 或 Java 不同,需要在您的 Lambda 函数代码中启用远程调试。如果您针对使用某种 Python 运行时 (2.7 或 3.6) 的函数启用调试 (使用上述 --debug-port-d 选项),SAM Local 会从您的主机通过该端口映射到 Lambda 容器。要启用远程调试,请使用 Python 程序包,例如 remote-pdb

重要

配置主机时,调试程序会侦听您的代码,请确保使用 0.0.0.0,而不是 127.0.0.1