Skip to content

Latest commit

 

History

History
852 lines (653 loc) · 30.7 KB

File metadata and controls

852 lines (653 loc) · 30.7 KB

十四、作为代码的基础设施

典型的基于 Lambda 的应用由事件触发的多个函数组成,例如 S3 bucket 中的新对象、传入的 HTTP 请求或新的 SQS 消息。这些函数可以独立运行,也可以利用其他资源,例如 DynamoDB 表、amazons3 存储桶和其他 Lambda 函数。到目前为止,我们已经了解了如何从 AWS 管理控制台或使用 AWS CLI 创建这些资源。在真实场景中,您希望花费更少的时间来调配所需的资源,并将更多精力放在应用逻辑上。最后,这就是无服务器方法。

最后一章将介绍基础架构作为代码的概念,以帮助您以自动化的方式设计和部署 N 层无服务器应用,从而避免人为错误和可重复的任务。

技术要求

本书假设您基本熟悉 AWS 无服务器应用模型。如果您对 SAM 本身不熟悉,请参考第 1 章无服务器第 10 章测试您的无服务器应用。您将获得一份关于如何开始 SAM 的分步指南。本章的代码包位于 GitHub 上的https://github.com/PacktPublishing/Hands-On-serverless-Applications-with-Go

使用 Terraform 部署 AWS Lambda

Terraform是 HashiCorp 构建的开源自动化工具。它用于通过声明性配置文件创建、管理和更新基础结构资源。它支持以下提供程序:

  • 云提供商:AWS、Azure、Oracle 云、GCP
  • 基础设施软件
    • 领事:是一个分布式、高可用的服务发现和配置系统。
    • Docker:这是一种工具,旨在通过使用容器更轻松地创建、部署和运行应用。
    • Nomad:是一款易于使用的企业级集群调度器。
    • 金库:是一种提供安全可靠的方式存储和分发机密的工具。
  • 其他SaaSPaaS

Terraform 不是配置管理工具(如 Ansible、Chef 和 Puppet&Salt)。创建它是为了生成和销毁基础设施,而配置管理工具用于在现有基础设施之上安装东西。然而,Terraform 可以做一些资源调配(https://www.terraform.io/docs/provisioners/index.html )。

本指南将向您展示如何使用 Terraform 部署 AWS Lambda,因此您需要安装 Terraform。您可以找到适合您系统的软件包并下载它(https://www.terraform.io/downloads.html )。下载后,确保PATH变量上有terraform二进制文件。配置您的凭据,以便 Terraform 能够代表您行事。以下是提供身份验证凭据的四种方法:

  • 直接通过供应商提供 AWSaccess_keysecret_key
  • AWS 环境变量。
  • 共享凭据文件。
  • EC2 IAM 角色

如果您遵循第 2 章开始使用 AWS Lambda,您应该已经安装并配置了 AWS CLI。因此,您无需采取任何行动。

创建 Lambda 函数

要开始创建 Lambda 函数,请执行给定的步骤:

  1. 创建具有以下结构的新项目:

  1. 我们将使用最简单的 Hello world 示例。function文件夹包含一个基于 Go 的 Lambda 函数,该函数显示一条简单消息:
package main

import "github.com/aws/aws-lambda-go/lambda"

func handler() (string, error) {
  return "First Lambda function with Terraform", nil
}
func main() {
  lambda.Start(handler)
}
  1. 您可以使用以下命令构建基于 Linux 的二进制文件并生成deployment包:
GOOS=linux go build -o main main.go
zip deployment.zip main
  1. 现在函数代码已经定义,让我们使用 Terraform 创建第一个 Lambda 函数。将以下内容复制到main.tf文件中:
provider "aws" {
  region = "us-east-1"
}

resource "aws_iam_role" "role" {
  name = "PushCloudWatchLogsRole"
  assume_role_policy = "${file("assume-role-policy.json")}"
}

resource "aws_iam_policy" "policy" {
  name = "PushCloudWatchLogsPolicy"
  policy = "${file("policy.json")}"
}

resource "aws_iam_policy_attachment" "profile" {
  name = "cloudwatch-lambda-attachment"
  roles = ["${aws_iam_role.role.name}"]
  policy_arn = "${aws_iam_policy.policy.arn}"
}

resource "aws_lambda_function" "demo" {
  filename = "function/deployment.zip"
  function_name = "HelloWorld"
  role = "${aws_iam_role.role.arn}"
  handler = "main"
  runtime = "go1.x"
}
  1. 这告诉 Terraform,我们将使用 AWS 提供商,并默认使用us-east-1区域来创建我们的资源:
  • IAM 角色是执行期间由 Lambda 函数承担的执行角色。它定义了 Lambda 函数可以访问的资源:
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
  • IAM 策略是授予 Lambda 函数将其日志流式传输到 CloudWatch 的权限列表。以下策略将附加到 IAM 角色:
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "1",
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogStream",
        "logs:CreateLogGroup",
        "logs:PutLogEvents"
      ],
      "Resource": "*"
    }
  ]
}
  • Lambda 函数是基于 Go 的 Lambda 函数。部署包可以直接指定为本地文件(使用filename属性),也可以通过 Amazon S3 bucket 指定。有关如何将 Lambda 功能部署到 AWS 的详细信息,请参阅第 6 章部署无服务器应用
  1. 在终端上运行terraform init命令下载安装 AWS 提供程序,如下图:

  1. 使用terraform plan命令创建执行计划(干运行)。它向您展示了将提前创建的内容,这有助于调试并确保您没有做错任何事情,如下一个屏幕截图所示:

  1. 在将 Terraform 部署到 AWS 之前,您将能够检查 Terraform 的执行计划。准备好后,继续并通过发出以下命令应用更改:
terraform apply
  1. 通过键入yes确认配置。将显示以下输出(为简洁起见裁剪了某些零件):

确保用于执行这些命令的 IAM 用户具有执行 IAM 和 Lambda 操作的权限

  1. 如果您返回 AWS Lambda 控制台,则应创建一个新的 Lambda 函数。如果您尝试调用它,它将返回预期的消息,如下一个屏幕截图所示:

  1. 到目前为止,我们在模板文件中定义了 AWS 区域和函数名。然而,我们使用基础设施作为代码工具的原因之一是可用性和自动化。因此,应始终使用变量并避免硬编码值。幸运的是,Terraform 允许您定义自己的变量。为此,创建一个variables.tf文件,如下所示:
variable "aws_region" {
  default = "us-east-1"
  description = "AWS region"
}

variable "lambda_function_name" {
  default = "DemoFunction"
  description = "Lambda function's name"
}
  1. 更新main.tf以使用变量而不是硬编码值。注意${var.variable_name}关键字的用法:
provider "aws" {
  region = "${var.aws_region}"
}

resource "aws_lambda_function" "demo" {
  filename = "function/deployment.zip"
  function_name = "${var.lambda_function_name}"
  role = "${aws_iam_role.role.arn}"
  handler = "main"
  runtime = "go1.x"
}
  1. 在函数按预期工作的情况下,创建到目前为止我们使用 Terraform 构建的无服务器 API。
  2. 在新目录中,创建一个名为main.tf的文件,该文件包含以下配置:
resource "aws_iam_role" "role" {
 name = "FindAllMoviesRole"
 assume_role_policy = "${file("assume-role-policy.json")}"
}

resource "aws_iam_policy" "cloudwatch_policy" {
 name = "PushCloudWatchLogsPolicy"
 policy = "${file("cloudwatch-policy.json")}"
}

resource "aws_iam_policy" "dynamodb_policy" {
 name = "ScanDynamoDBPolicy"
 policy = "${file("dynamodb-policy.json")}"
}

resource "aws_iam_policy_attachment" "cloudwatch-attachment" {
 name = "cloudwatch-lambda-attchment"
 roles = ["${aws_iam_role.role.name}"]
 policy_arn = "${aws_iam_policy.cloudwatch_policy.arn}"
}

resource "aws_iam_policy_attachment" "dynamodb-attachment" {
 name = "dynamodb-lambda-attchment"
 roles = ["${aws_iam_role.role.name}"]
 policy_arn = "${aws_iam_policy.dynamodb_policy.arn}"
}
  1. 前面的代码片段创建了一个 IAM 角色,该角色具有扫描 DynamoDB 表的权限,并将日志条目写入 CloudWatch。将 DynamoDB 表名作为环境变量配置基于 Go 的 Lambda 函数:
resource "aws_lambda_function" "findall" {
  function_name = "FindAllMovies"
  handler = "main"
  filename = "function/deployment.zip"
  runtime = "go1.x"
  role = "${aws_iam_role.role.arn}"

  environment {
    variables {
      TABLE_NAME = "movies"
    }
  }
}

设置 DynamoDB 表

接下来,我们必须设置 DynamoDB 表。执行以下步骤:

  1. 创建一个以 ID 作为表分区键的 DynamoDB 表:
resource "aws_dynamodb_table" "movies" {
  name = "movies"
  read_capacity = 5
  write_capacity = 5
  hash_key = "ID"

  attribute {
      name = "ID"
      type = "S"
  }
}
  1. 使用新项初始化movies表:
resource "aws_dynamodb_table_item" "items" {
  table_name = "${aws_dynamodb_table.movies.name}"
  hash_key = "${aws_dynamodb_table.movies.hash_key}"
  item = "${file("movie.json")}"
}
  1. 项目属性在movie.json文件中定义:
{
  "ID": {"S": "1"},
  "Name": {"S": "Ant-Man and the Wasp"},
  "Description": {"S": "A Marvel's movie"},
  "Cover": {"S": http://COVER_URL.jpg"}
}

配置 API 网关

最后,我们需要使用 API 网关触发函数:

  1. 在 RESTAPI 上创建一个movies资源,并在其上公开一个GET方法。如果传入请求与定义的资源匹配,它将调用前面定义的 Lambda 函数:
resource "aws_api_gateway_rest_api" "api" {
  name = "MoviesAPI"
}

resource "aws_api_gateway_resource" "proxy" {
  rest_api_id = "${aws_api_gateway_rest_api.api.id}"
  parent_id = "${aws_api_gateway_rest_api.api.root_resource_id}"
  path_part = "movies"
}

resource "aws_api_gateway_method" "proxy" {
  rest_api_id = "${aws_api_gateway_rest_api.api.id}"
  resource_id = "${aws_api_gateway_resource.proxy.id}"
  http_method = "GET"
  authorization = "NONE"
}

resource "aws_api_gateway_integration" "lambda" {
  rest_api_id = "${aws_api_gateway_rest_api.api.id}"
  resource_id = "${aws_api_gateway_method.proxy.resource_id}"
  http_method = "${aws_api_gateway_method.proxy.http_method}"

  integration_http_method = "POST"
  type = "AWS_PROXY"
  uri = "${aws_lambda_function.findall.invoke_arn}"
}
  1. 发出以下命令以安装 AWS 插件、生成执行计划并应用更改:
terraform init
terraform plan
terraform apply
  1. 创建整个基础设施需要几秒钟的时间。创建步骤完成后,应创建并正确配置 Lambda 函数,如以下屏幕截图所示:

  1. API 网关也是如此,需要在/movies资源上用GET方法定义一个新的 REST API,如下图:

  1. 在 DynamoDB 控制台中,应使用电影项创建一个新表,如下一个屏幕截图所示:

  1. 为了调用我们的 API 网关,我们需要部署它。创建一个部署阶段,我们称之为staging
resource "aws_api_gateway_deployment" "staging" {
  depends_on = ["aws_api_gateway_integration.lambda"]

  rest_api_id = "${aws_api_gateway_rest_api.api.id}"
  stage_name = "staging"
}
  1. 我们将使用 Terraform 的输出特性公开 API URL;创建一个包含以下内容的outputs.tf文件:
output "API Invocation URL" {
  value = "${aws_api_gateway_deployment.staging.invoke_url}"
}
  1. 再次运行terraform apply创建这些新对象,它会检测到这些变化,并要求您确认它应该执行这些操作,如下所示:

  1. API 网关 URL 将显示在输出部分;将其复制到剪贴板:

  1. 如果将常用浏览器指向 API 调用 URL,则应显示错误消息,如下一屏幕截图所示:

  1. 我们将通过向 API 网关授予调用 Lambda 函数的执行权限来解决这个问题。更新main.tf文件以创建aws_lambda_permission资源:
resource "aws_lambda_permission" "apigw" {
  statement_id = "AllowAPIGatewayInvoke"
  action = "lambda:InvokeFunction"
  function_name = "${aws_lambda_function.findall.arn}"
  principal = "apigateway.amazonaws.com"

  source_arn = "${aws_api_gateway_deployment.staging.execution_arn}/*/*"
}
  1. 使用terraform apply命令应用最新的更改。在 Lambda 控制台上,应显示 API 网关触发器,如下所示:

  1. 在您喜爱的 web 浏览器中加载跑步输出中给定的 URL。如果一切正常,您将看到以 JSON 格式存储在 DynamoDB 表中的电影,如下一个屏幕截图所示:

Terraform 将基础设施的状态存储在状态文件(.tfstate中)。状态包含资源 ID 和所有资源属性。如果您使用 Terraform 创建 RDS 实例,则数据库凭据将以明文形式存在于状态文件中。因此,您应该将文件保存在远程后端,例如 S3 bucket。

清理

最后,要删除所有资源(Lambda 函数、IAM 角色、IAM 策略、DynamoDB 表和 API 网关),可以发出terraform destroy命令,如下所示:

如果您想删除特定的资源,可以使用如下的--target选项:terraform destroy --target=RESOURCE_NAME。操作将仅限于资源及其依赖项。

到目前为止,我们已经使用模板文件定义了 AWS Lambda 函数及其依赖项。因此,我们可以像其他代码一样对其进行版本设置。我们使用和配置的整个无服务器基础设施被视为源代码,允许我们在团队成员之间共享,在其他 AWS 区域复制,并在出现故障时回滚。

使用 CloudFormation 部署 AWS Lambda

AWS CloudFormation是一种基础设施,作为以声明方式指定资源的代码工具。您可以在蓝图文档(模板)中对希望 AWS 加速的所有资源进行建模,AWS 将为您创建定义的资源。因此,您花更少的时间管理这些资源,而更多的时间关注在 AWS 中运行的应用。

Terraform 涵盖了 AWS 提供的几乎所有服务和功能,并支持第三方提供商(平台无关),而 CloudFormation 是特定于 AWS 的(供应商锁定)。

您可以使用 AWS CloudFormation 来指定、部署和配置无服务器应用。您可以创建一个模板来描述无服务器应用依赖关系(Lambda 函数、DynamoDB 表、API 网关、IAM 角色等),而 AWS CloudFormation 负责为您提供和配置这些资源。您不需要单独创建和配置 AWS 资源,也不需要弄清楚什么取决于什么。

在深入了解 CloudFormation 之前,我们需要了解模板结构:

  • AWSTemplateFormatVersion:云信息模板版本。
  • 说明:模板的简要说明。
  • 映射:可用于指定条件参数值的键和关联值的映射。
  • 参数:运行时传递给模板的值。
  • 资源:AWS 资源及其属性(Lambda、DynamoDB、S3 等)。
  • 输出:描述查看堆栈属性时返回的值。

了解 AWS CloudFormation 模板的不同部分后,您可以将它们放在一起,并在template.yml文件中定义一个最小模板,如下所示:

AWSTemplateFormatVersion: "2010-09-09"
Description: "Simple Lambda Function"
Parameters:
  FunctionName:
    Description: "Function name"
    Type: "String"
    Default: "HelloWorld"
  BucketName:
    Description: "S3 Bucket name"
    Type: "String"
Resources:
  ExecutionRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              Service:
                - "lambda.amazonaws.com"
            Action:
              - "sts:AssumeRole"
      Policies:
        - PolicyName: "PushCloudWatchLogsPolicy"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
              - Action:
                - logs:CreateLogGroup
                - logs:CreateLogStream
                - logs:PutLogEvents
              - Resource: "*"
  HelloWorldFunction:
    Type: "AWS::Lambda::Function"
    Properties:
      Code:
        S3Bucket: !Ref BucketName
        S3Key: deployment.zip
      FunctionName: !Ref FunctionName
      Handler: "main"
      Runtime: "go1.x"
      Role: !GetAtt ExecutionRole.Arn

前面的文件定义了两个资源:

  • ExecutionRole:分配给 Lambda 函数的 IAM 角色,它定义 Lambda 运行时调用的代码具有哪些权限。
  • HelloWorldFunction:在 AWS Lambda 定义中,我们将运行时属性设置为使用 Go,并将函数代码设置为存储在 S3 上的 ZIP 文件中。该函数使用 CloudFormation 的内置GetAtt函数引用 IAM 角色;它还使用Ref关键字引用参数部分中定义的变量。

也可以使用 JSON 格式;可以在 GitHub 存储库(上找到 JSON 版本 https://github.com/PacktPublishing/Hands-On-serverless-Applications-with-Go )。

执行以下步骤开始:

  1. 使用以下命令构建部署包后,创建一个 S3 bucket,在其上存储部署包:
aws s3 mb s3://hands-on-serverless-go-packt/
GOOS=linux go build -o main main.go
zip deployment.zip main
aws s3 cp deployment.zip s3://hands-on-serverless-go-packt/
  1. 导航到 AWS CloudFormation 控制台,并选择创建堆栈,如下一个屏幕截图所示:

  1. 在选择模板页面,选择模板文件,将其上传到 Amazon S3 bucket,如下图:

  1. 单击“下一步”,定义堆栈名称,并根据需要覆盖默认参数,如下一个屏幕截图所示:

  1. 单击 Next(下一步),保留默认选项,然后单击 Create(创建),如下一个屏幕截图所示:

  1. 堆栈将开始创建模板文件中定义的所有资源。一旦创建,堆栈状态将从创建中更改为创建完成(如果出现错误,将自动执行回滚),如下所示:

  1. 因此,应创建 Lambda 函数,如以下屏幕截图所示:

  1. 您可以随时更新 CloudFormation 模板文件。例如,让我们创建一个新的 DynamoDB 表:
AWSTemplateFormatVersion: "2010-09-09"
Description: "Simple Lambda Function"
Parameters:
  FunctionName:
    Description: "Function name"
    Type: "String"
    Default: "HelloWorld"
  BucketName:
    Description: "S3 Bucket name"
    Type: "String"
  TableName:
    Description: "DynamoDB Table Name"
    Type: "String"
    Default: "movies"
Resources:
  ExecutionRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - 
            Effect: "Allow"
            Principal:
              Service:
                - "lambda.amazonaws.com"
            Action:
              - "sts:AssumeRole"
      Policies:
        - 
          PolicyName: "PushCloudWatchLogsPolicy"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                - logs:CreateLogGroup
                - logs:CreateLogStream
                - logs:PutLogEvents
                Resource: "*"
        - 
          PolicyName: "ScanDynamoDBTablePolicy"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                - dynamodb:Scan
                Resource: "*"
  HelloWorldFunction:
    Type: "AWS::Lambda::Function"
    Properties:
      Code:
        S3Bucket: !Ref BucketName
        S3Key: deployment.zip
      FunctionName: !Ref FunctionName
      Handler: "main"
      Runtime: "go1.x"
      Role: !GetAtt ExecutionRole.Arn
      Environment:
        Variables:
          TABLE_NAME: !Ref TableName
  DynamoDBTable:
    Type: "AWS::DynamoDB::Table"
    Properties:
      TableName: !Ref TableName
      AttributeDefinitions:
        -
          AttributeName: "ID"
          AttributeType: "S"
      KeySchema:
        -
          AttributeName: "ID"
          KeyType: "HASH"
      ProvisionedThroughput:
        ReadCapacityUnits: 5
        WriteCapacityUnits: 5
  1. 在 CloudFormation 控制台上,选择我们之前创建的堆栈,然后从菜单中单击 Update stack,如下所示:

  1. 上传更新后的模板文件,如下图:

  1. 与 Terraform 类似,AWS CloudFormation 将检测更改并显示将提前更改的资源,如下所示:

  1. 单击更新按钮以应用更改。堆栈状态将更改为“正在更新”,如下一屏幕截图所示:

  1. 应用更改后,将创建一个新的 DynamoDB 表,并将 DynamoDB 权限授予 Lambda 函数,如下所示:

当 CloudFormation 必须定义 IAM 角色、策略或相关资源时,--capabilities CAPABILITY_IAM选项是必需的。

  1. AWS CLI 还可用于使用以下命令创建 CloudFormation 堆栈:
aws cloudformation create-stack --stack-name=SimpleLambdaFunction \
 --template-body=file://template.yml \
 --capabilities CAPABILITY_IAM \
 --parameters ParameterKey=BucketName,ParameterValue=hands-on-serverless-go-packt 
 ParameterKey=FunctionName,ParameterValue=HelloWorld \
 ParameterKey=TableName,ParameterValue=movies

云层设计师

除了从头开始编写自己的模板外,还可以使用 CloudFormation 设计模板功能轻松创建堆栈。下面的屏幕截图显示了如何查看到目前为止我们创建的堆栈的设计:

如果一切顺利,您应该看到以下组件:

现在可以通过从左侧菜单拖放组件来创建复杂的 CloudFormation 模板。

使用 SAM 部署 AWS Lambda

AWS 无服务器应用模型AWS SAM是定义无服务器应用的模型。AWS SAM 本机受 AWS CloudFormation 支持,并定义了用于表示无服务器资源的简化语法。您只需在模板文件中定义作为应用一部分所需的资源,并使用 SAM deploy 命令创建 CloudFormation 堆栈。

之前,我们了解了如何使用 AWS SAM 在本地测试 Lambda 函数。此外,SAM 还可用于设计并将功能部署到 AWS Lambda。您可以使用以下命令初始化基于 quick Go 的无服务器项目(样板):

sam init --name api --runtime go1.x

前面的命令将创建具有以下结构的文件夹:

sam init命令提供了创建无服务器应用的快速方法。它在 Go 中生成一个简单的 Lambda 函数,并进行相关的单元测试。此外,将生成一个 Makefile,其中包含构建和生成部署包的步骤列表。最后,将创建一个称为 SAM 文件的模板文件,该文件描述了将功能部署到 AWS Lambda 所需的所有 AWS 资源。

现在我们知道了如何使用 SAM 生成样板,让我们从头开始编写自己的模板。创建一个名为findall的文件夹,在其中创建一个包含FindAllMovies函数代码内容的main.go文件:

// Movie entity
type Movie struct {
  ID string `json:"id"`
  Name string `json:"name"`
  Cover string `json:"cover"`
  Description string `json:"description"`
}

func findAll() (events.APIGatewayProxyResponse, error) {
  ...
  svc := dynamodb.New(cfg)
  req := svc.ScanRequest(&dynamodb.ScanInput{
    TableName: aws.String(os.Getenv("TABLE_NAME")),
  })
  res, err := req.Send()
  if err != nil {
    return events.APIGatewayProxyResponse{
      StatusCode: http.StatusInternalServerError,
      Body: "Error while scanning DynamoDB",
    }, nil
  }

  movies := make([]Movie, 0)
  for _, item := range res.Items {
    movies = append(movies, Movie{
      ID: *item["ID"].S,
      Name: *item["Name"].S,
      Cover: *item["Cover"].S,
      Description: *item["Description"].S,
    })
  }
  ...
  return events.APIGatewayProxyResponse{
    StatusCode: 200,
    Headers: map[string]string{
      "Content-Type": "application/json",
      "Access-Control-Allow-Origin": "*",
    },
    Body: string(response),
  }, nil
}

func main() {
  lambda.Start(findAll)
}

接下来,在template.yaml文件中创建一个无服务器应用定义。下面的示例说明了如何使用 DynamoDB 表创建 Lambda 函数:

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::serverless-2016-10-31
Resources:
  FindAllFunction:
    Type: AWS::serverless::Function
    Properties:
      Handler: main
      Runtime: go1.x
      Policies: AmazonDynamoDBFullAccess 
      Environment:
        Variables: 
          TABLE_NAME: !Ref MoviesTable
  MoviesTable: 
     Type: AWS::serverless::SimpleTable
     Properties:
       PrimaryKey:
         Name: ID
         Type: String
       ProvisionedThroughput:
         ReadCapacityUnits: 5
         WriteCapacityUnits: 5

该模板类似于我们前面编写的 CloudFormation 模板。SAM 扩展了 CloudFormation 并简化了表示无服务器资源的语法。

使用package命令将部署包上传到CloudFormation节中创建的 S3 bucket:

sam package --template-file template.yaml --output-template-file serverless.yaml \
    --s3-bucket hands-on-serverless-go-packt

前面的命令将把部署页面上传到 S3 bucket,如下面的屏幕截图所示:

此外,将根据您提供的定义文件生成名为serverless.yaml的 SAM 模板文件。它应该包含指向您指定的 Amazon S3 bucket 中的deploymentZIP 的CodeUri属性:

AWSTemplateFormatVersion: '2010-09-09'
Resources:
  FindAllFunction:
    Properties:
      CodeUri: s3://hands-on-serverless-go-packt/764cf76832f79ca7f29c6397fe7ccd91
      Environment:
        Variables:
          TABLE_NAME:
            Ref: MoviesTable
      Handler: main
      Policies: AmazonDynamoDBFullAccess
      Runtime: go1.x
    Type: AWS::serverless::Function
  MoviesTable:
    Properties:
      PrimaryKey:
        Name: ID
        Type: String
      ProvisionedThroughput:
        ReadCapacityUnits: 5
        WriteCapacityUnits: 5
    Type: AWS::serverless::SimpleTable
Transform: AWS::serverless-2016-10-31

最后,使用以下命令将函数部署到 AWS Lambda:

sam deploy --template-file serverless.yaml --stack-name APIStack \
 --capabilities CAPABILITY_IAM

CAPABILITY_IAM用于明确确认允许 AWS CloudFormation 代表您为 Lambda 函数创建 IAM 角色。

当您运行sam deploy命令时,它会创建一个名为 APIStack 的 AWS CloudFormation 堆栈,如下一个屏幕截图所示:

创建资源后,应将该函数部署到 AWS Lambda,如下所示:

SAM 范围仅限于无服务器资源(支持的 AWS 服务列表位于:https://docs.aws.amazon.com/serverlessrepo/latest/devguide/using-aws-sam.html )。

导出无服务器应用

AWS Lambda 允许您导出现有函数的 SAM 模板文件。选择目标函数并从操作菜单中单击导出函数,如下所示:

单击下载 AWS SAM 文件以下载模板文件,如下所示:

模板将包含函数的定义、必要的权限和触发器:

AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::serverless-2016-10-31'
Description: An AWS serverless Specification template describing your function.
Resources:
  FindAllMovies:
    Type: 'AWS::serverless::Function'
    Properties:
      Handler: main
      Runtime: go1.x
      CodeUri: .
      Description: ''
      MemorySize: 128
      Timeout: 3
      Role: 'arn:aws:iam::ACCOUNT_ID:role/FindAllMoviesRole'
      Events:
        Api1:
          Type: Api
          Properties:
            Path: /MyResource
            Method: ANY
        Api2:
          Type: Api
          Properties:
            Path: /movies
            Method: GET
      Environment:
        Variables:
          TABLE_NAME: movies
      Tracing: Active
      ReservedConcurrentExecutions: 10

您现在可以使用sam packagesam deploy命令将函数导入不同的 AWS 区域或 AWS 帐户。

总结

管理无服务器应用资源可以非常手动,也可以自动化工作流。但是,如果您有一个复杂的基础设施,那么自动化过程可能会很棘手。这就是 AWS CloudFormation、SAM 和 Terraform 等工具的用武之地。

在本章中,我们学习了如何将基础架构作为代码工具来自动创建 AWS 中的无服务器应用资源和依赖项。我们看到了特定于云的工具,以及在多个平台上工作的松散耦合的工具。然后,我们了解了如何使用这些工具将基于 Lambda 的应用部署到 AWS。

现在,您可以编写一次无服务器基础结构代码,然后多次使用它。与任何其他代码一样,定义基础结构的代码可以进行版本控制、分叉、回滚(时光倒流),并用于审核基础结构更改。此外,可以通过编程的方式发现和解决它。换言之,如果已手动修改基础结构,则会销毁该基础结构,并为不可变的基础结构重新生成一个干净的副本。

问题

  1. 编写一个地形模板来创建InsertMovieLambda 函数资源。
  2. 更新 CloudFormation 模板,以使用 API 网关触发定义的 Lambda 函数,以响应传入的 HTTP 请求。
  3. 编写一个 SAM 文件,对构建本书中构建的无服务器 API 所需的所有资源进行建模和定义。
  4. 将 Terraform 配置为将生成的状态文件存储在远程 S3 后端。
  5. 为我们在本书中构建的无服务器 API 创建一个 CloudFormation 模板。
  6. 为我们在本书中构建的无服务器 API 创建一个 Terraform 模板。