故事背景
老板:今天的会议就到这里了,小白,这个 API 你来实现一下吧。上线初期的流量比较低,你选一个既省钱又容易扩容的方案。
小白:好的老板……
( 回到工位后…… )
小白:扩容不是大问题,但是还有比只用一台服务器更省钱的方案吗?
大神:比一台服务器更省钱的方案,当然是零台服务器了。
小白:零台服务器?还有这种技术?!
大神:难道你不知道 AWS 的无服务器架构吗?
小白:但求赐教!
( 大神开启教学模式 )
有服务器架构
传统的有服务器 API 架构是由负载均衡和 WEB 应用程序所组成的。客户端将请求发送到负载均衡,负载均衡再转发给 WEB 应用程序。WEB 应用程序生成响应结果再通过负载均衡返回给客户端。
无服务器架构
无服务器架构和传统的有服务器架构并没有本质的区别,只不过是用 AWS 提供的托管服务作为负载均衡和 WEB 应用程序。
具体来说是使用托管的 API Gateway 作为负载均衡,使用 Lambda 作为 WEB 应用程序。
不同之处在于 Lambda 是按需启动的。当 API Gateway 接收到请求时会创建 Lambda 实例来处理请求;如果 API Gateway 在一段时间内没有收到请求,那么 Lambda 实例就会被自动释放。释放后停止计费,达到降低成本的目的。
扩容也非常简单。当 API Gateway 收到的请求量超出 Lambda 实例的处理能力时,更多的 Lambda 实例就会被自动创建,实现自动扩容。
创建一个简单的无服务器 Hello World API 只需 5 步。
步骤 1:安装 AWS SAM CLI
AWS Serverless Application Model ( AWS SAM ) 是一个开源框架,你可以使用这个框架在 AWS 上构建无服务器应用。
根据你使用的操作系统,依照这篇 文档 中的步骤安装 AWS SAM CLI。如果你想在本地测试你的 API,你还需要根据文档中的指示安装 Docker。
步骤 2:创建示例工程
执行以下命令创建一个示例工程。
sam init
如果你根据提示选择了 Python 项目,你将会看到如下输出。
----------------------- Generating application: ----------------------- Name: sam-app Runtime: python3.7 Dependency Manager: pip Application Template: hello-world Output Directory: . Next steps can be found in the README file at ./sam-app/README.md
至此,一个示例工程就创建完毕了,目录结构如下。
sam-app/ ├── README.md ├── events/ │ └── event.json ├── hello_world/ │ ├── __init__.py │ ├── app.py #Contains your AWS Lambda handler logic. │ └── requirements.txt #Contains any Python dependencies the application requires, used for sam build ├── template.yaml #Contains the AWS SAM template defining your application's AWS resources. └── tests/ └── unit/ ├── __init__.py └── test_handler.py
有三个文件比较重要:
- template.yaml: 定义 AWS 资源的文件,例如 API Gateway 和 Lambda。
- hello_world/app.py: Lambda 的处理函数。
- hello_world/requirements.txt: 声明 Python 的依赖项。
步骤 3:构建你的 API
进入到工程所在目录,然后执行以下命令。
sam build
你会看到如下输出。
Build Succeeded Built Artifacts : .aws-sam/build Built Template : .aws-sam/build/template.yaml Commands you can use next ========================= [*] Invoke Function: sam local invoke [*] Deploy: sam deploy --guided
这个命令会把应用程序需要的所有依赖项以及应用程序本身的代码复制到 .aws-sam/build
目录下。
.aws-sam/ └── build/ ├── HelloWorldFunction/ └── template.yaml
步骤 4:部署到 AWS
执行以下命令把程序部署到 AWS 云端。
sam deploy --guided
根据提示输入相应信息,直接输入回车就是遵循默认设置。
你可能会被问到 HelloWorldFunction may not have authorization defined, Is this okay? [y/N],这是 AWS SAM 在告诉你,你的 API 没有身份验证,部署后任何人都可以调用。 在这里输入 y 就可以,我们先不管身份验证的事情。
之后你会得到如下输出。
Deploying with following values =============================== Stack name : sam-app Region : us-east-1 Confirm changeset : False Deployment s3 bucket : sam-bucket Capabilities : ["CAPABILITY_IAM"] Parameter overrides : {} Initiating deployment ===================== Waiting for changeset to be created.. CloudFormation stack changeset --------------------------------------------------------------------------------------------------------------------------------------------------- Operation LogicalResourceId ResourceType --------------------------------------------------------------------------------------------------------------------------------------------------- + Add HelloWorldFunctionHelloWorldPermissionProd AWS:: Lambda:: Permission + Add ServerlessRestApiDeployment47fc2d5f9d AWS:: ApiGateway:: Deployment + Add ServerlessRestApiProdStage AWS:: ApiGateway:: Stage + Add ServerlessRestApi AWS:: ApiGateway:: RestApi - Modify HelloWorldFunctionRole AWS:: IAM:: Role - Modify HelloWorldFunction AWS:: Lambda:: Function --------------------------------------------------------------------------------------------------------------------------------------------------- 2019-11-21 14:33:24 - Waiting for stack create/update to complete CloudFormation events from changeset ------------------------------------------------------------------------------------------------------------------------------------------------- ResourceStatus ResourceType LogicalResourceId ResourceStatusReason ------------------------------------------------------------------------------------------------------------------------------------------------- UPDATE_IN_PROGRESS AWS:: IAM:: Role HelloWorldFunctionRole - UPDATE_COMPLETE AWS:: IAM:: Role HelloWorldFunctionRole - UPDATE_IN_PROGRESS AWS:: Lambda:: Function HelloWorldFunction - UPDATE_COMPLETE AWS:: Lambda:: Function HelloWorldFunction - CREATE_IN_PROGRESS AWS:: ApiGateway:: RestApi ServerlessRestApi - CREATE_COMPLETE AWS:: ApiGateway:: RestApi ServerlessRestApi - CREATE_IN_PROGRESS AWS:: ApiGateway:: RestApi ServerlessRestApi Resource creation Initiated CREATE_IN_PROGRESS AWS:: ApiGateway:: Deployment ServerlessRestApiDeployment47fc2d5 Resource creation Initiated f9d CREATE_IN_PROGRESS AWS:: Lambda:: Permission HelloWorldFunctionHelloWorldPermis Resource creation Initiated sionProd CREATE_IN_PROGRESS AWS:: Lambda:: Permission HelloWorldFunctionHelloWorldPermis - sionProd CREATE_IN_PROGRESS AWS:: ApiGateway:: Deployment ServerlessRestApiDeployment47fc2d5 - f9d CREATE_COMPLETE AWS:: ApiGateway:: Deployment ServerlessRestApiDeployment47fc2d5 - f9d CREATE_IN_PROGRESS AWS:: ApiGateway:: Stage ServerlessRestApiProdStage - CREATE_IN_PROGRESS AWS:: ApiGateway:: Stage ServerlessRestApiProdStage Resource creation Initiated CREATE_COMPLETE AWS:: ApiGateway:: Stage ServerlessRestApiProdStage - CREATE_COMPLETE AWS:: Lambda:: Permission HelloWorldFunctionHelloWorldPermis - sionProd UPDATE_COMPLETE_CLEANUP_IN_PROGRES AWS:: CloudFormation:: Stack sam-app - S UPDATE_COMPLETE AWS:: CloudFormation:: Stack sam-app - ------------------------------------------------------------------------------------------------------------------------------------------------- Stack sam-app outputs: --------------------------------------------------------------------------------------------------------------------------------------------------- OutputKey-Description OutputValue --------------------------------------------------------------------------------------------------------------------------------------------------- HelloWorldFunctionIamRole - Implicit IAM Role created for Hello World arn: aws: iam::123456789012: role/sam-app- function HelloWorldFunctionRole-104VTJ0TST7M0 HelloWorldApi - API Gateway endpoint URL for Prod stage for Hello World https://0ks2zue0zh.execute-api.us-east-1.amazonaws.com/Prod/hello/ function HelloWorldFunction - Hello World Lambda Function ARN arn: aws: lambda: us-east-1:123456789012: function: sam-app- HelloWorldFunction-1TY92MJX0BXU5 --------------------------------------------------------------------------------------------------------------------------------------------------- Successfully created/updated stack - sam-app in us-east-1
至此,你的 API 就被部署到 AWS 云端了。具体来说 sam deploy
命令创建了一个 S3 bucket,并且把 sam build
命令产生的文件上传至这个 S3 bucket。最后再用 Cloud Formation 部署应用程序。sam deploy
会输出 URL 供你测试,你可以用 curl
工具向 API 发送请求。
curl https://<restapiid>.execute-api.us-east-1.amazonaws.com/Prod/hello/
如果你的 API 部署成功的话,你应该能看到如下响应。
{"message": "hello world"}
步骤 5: ( 可选 ) 在本地测试你的 API
当你在开发的时候,你通常希望在本地就能测试你的 API。AWS SAM CLI 提供了 sam local
命令,该命令基于 Docker 来模拟 Lambda 环境。具体来说有两种在本地测试的方式:
- 在本地部署你的 API
- 直接调用 Lambda 函数
下面分别展示这两种方式。
在本地部署 API
sam local start-api
你会得到如下输出。
2019-07-12 15:27:58 Mounting HelloWorldFunction at http://127.0.0.1:3000/hello [GET] 2019-07-12 15:27:58 You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your functions, changes will be reflected instantly/automatically. You only need to restart SAM CLI if you update your AWS SAM template 2019-07-12 15:27:58 * Running on http://127.0.0.1:3000/ ( Press CTRL+C to quit ) Fetching lambci/lambda: python3.7 Docker container image...................................................................................................................................................................................... 2019-07-12 15:28:56 Mounting /<working-development-path>/sam-app/.aws-sam/build/HelloWorldFunction as /var/task: ro,delegated inside runtime container START RequestId: 52fdfc07-2182-154f-163f-5f0f9a621d72 Version: $LATEST END RequestId: 52fdfc07-2182-154f-163f-5f0f9a621d72 REPORT RequestId: 52fdfc07-2182-154f-163f-5f0f9a621d72 Duration: 4.42 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 22 MB 2019-07-12 15:28:58 No Content-Type given. Defaulting to 'application/json'. 2019-07-12 15:28:58 127.0.0.1 - - [12/Jul/2019 15:28:58] "GET /hello HTTP/1.1" 200 -
Docker 会花一点时间加载镜像。加载完毕之后,你就可以用 curl
向本地部署的 API 发送请求了。
curl http://127.0.0.1:3000/hello
你会得到如下输出。
2019-07-12 15:29:57 Invoking app.lambda_handler ( python3.7 ) 2019-07-12 15:29:57 Found credentials in shared credentials file: ~/.aws/credentials Fetching lambci/lambda: python3.7 Docker container image...... 2019-07-12 15:29:58 Mounting /<working-development-path>/sam-app/.aws-sam/build/HelloWorldFunction as /var/task: ro,delegated inside runtime container START RequestId: 52fdfc07-2182-154f-163f-5f0f9a621d72 Version: $LATEST END RequestId: 52fdfc07-2182-154f-163f-5f0f9a621d72 REPORT RequestId: 52fdfc07-2182-154f-163f-5f0f9a621d72 Duration: 7.92 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 22 MB {"statusCode":200,"body":"{\"message\": \"hello world\"}"}
start-api
这个命令会在本地创建一个 Docker 容器以执行 Lambda 函数,看起来就像在云端执行一样。
直接调用 Lambda 函数
执行以下命令就可以直接调用 Lambda 函数,而不用通过 HTTP 终结点。
sam local invoke "HelloWorldFunction" -e events/event.json
你会得到如下输出。
2019-07-01 14:08:42 Found credentials in shared credentials file: ~/.aws/credentials 2019-07-01 14:08:42 Invoking app.lambda_handler ( python3.7 ) Fetching lambci/lambda: python3.7 Docker container image............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................... 2019-07-01 14:09:39 Mounting /<working-development-path>/sam-app/.aws-sam/build/HelloWorldFunction as /var/task: ro,delegated inside runtime container START RequestId: 52fdfc07-2182-154f-163f-5f0f9a621d72 Version: $LATEST END RequestId: 52fdfc07-2182-154f-163f-5f0f9a621d72 REPORT RequestId: 52fdfc07-2182-154f-163f-5f0f9a621d72 Duration: 3.51 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 22 MB {"statusCode":200,"body":"{\"message\": \"hello world\"}"}
invoke
命令直接调用 Lambda 函数,并且能用你在 event.json
文件里提供的数据作为输入。
清理资源
由于 AWS SAM 基于 Cloud Formation 创建资源,所以如果你不再需要这篇文章里创建的 API,你可以很容易地清理掉这些资源,以免产生额外费用。
- 打开 Cloud Formation 的管理页面:https://console.aws.amazon.com/cloudformation。
- 在左侧的面板选择 Stacks。
- 在 Stack 列表里选择 sam-app ( 或者你创建工程时自己填的名字 ) 。
- 选择 Delete。
总结
小白:原来使用 AWS SAM 构建无服务器 API 这么简单,可以让老板满意了。
大神:是的,无服务器架构的特点是在资费和性能上均有极大的弹性。当产品初期流量较低时,可以通过减少 Lambda 实例的数量降低成本,甚至在闲时释放所有的实例降到 0 资费。当流量增多时会自动创建更多 Lambda 实例,无需额外工作即可实现扩容。无服务器架构适合产品从 0 到 1,以及从 1 到 100 的整个生命周期。