13、使用代码部署无服务器应用:Terraform与CloudFormation实践

使用代码部署无服务器应用:Terraform与CloudFormation实践

基础设施即代码简介

基于Lambda的典型应用由多个事件触发的函数组成,这些事件包括S3存储桶中的新对象、传入的HTTP请求或新的SQS消息。这些函数可以独立运行,也可以利用其他资源,如DynamoDB表、Amazon S3存储桶和其他Lambda函数。在实际场景中,我们希望减少配置所需资源的时间,将更多精力放在应用逻辑上,这正是无服务器方法的核心。基础设施即代码(Infrastructure as Code)的概念可以帮助我们以自动化的方式设计和部署N层无服务器应用,避免人为错误和重复性任务。

技术要求

在开始之前,需要对AWS无服务器应用模型有一定的了解。代码包可在GitHub上获取:https://github.com/PacktPublishing/Hands-On-serverless-Applications-with-Go 。

使用Terraform部署AWS Lambda

Terraform是HashiCorp构建的开源自动化工具,通过声明式配置文件创建、管理和更新基础设施资源。它支持多种云提供商和基础设施软件,如AWS、Azure、Consul、Docker等。Terraform与配置管理工具(如Ansible、Chef等)不同,它主要用于创建和销毁基础设施。

安装和配置Terraform

要使用Terraform部署AWS Lambda,需要先安装Terraform。可从https://www.terraform.io/downloads.html 下载适合系统的包,并确保terraform二进制文件在PATH变量中可用。同时,需要配置凭证,有四种方式提供认证凭证:
1. 通过提供者直接提供AWS访问密钥和秘密密钥。
2. 使用AWS环境变量。
3. 使用共享凭证文件。
4. 使用EC2 IAM角色。

如果之前已经安装并配置了AWS CLI,则无需额外操作。

创建Lambda函数

创建Lambda函数的步骤如下:
1. 创建一个新的项目结构。
2. 使用简单的Hello world示例,函数文件夹包含一个基于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的二进制文件并生成部署包:
GOOS=linux go build -o main main.go
zip deployment.zip main
  1. 创建 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"
}

其中,IAM角色定义了Lambda函数在执行期间可访问的资源,IAM策略授予Lambda函数将日志流式传输到CloudWatch的权限。
5. 在终端运行 terraform init 命令下载并安装AWS提供者。
6. 使用 terraform plan 命令创建执行计划,查看将创建的资源,有助于调试和确保操作正确。
7. 确认执行计划后,运行 terraform apply 命令应用更改,并在提示时输入 yes 确认配置。确保执行这些命令的IAM用户具有执行IAM和Lambda操作的权限。
8. 若一切顺利,在AWS Lambda控制台将创建一个新的Lambda函数,调用该函数应返回预期的消息。

为了提高模板的可用性和自动化程度,建议使用变量避免硬编码值。创建 variables.tf 文件:

variable "aws_region" {
  default = "us-east-1"
  description = "AWS region"
}
variable "lambda_function_name" {
  default = "DemoFunction"
  description = "Lambda function's name"
}

更新 main.tf 文件以使用变量:

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"
}
设置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. 在REST API上创建一个movies资源,并公开一个GET方法:

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函数、API网关和DynamoDB表应已创建并正确配置。
  2. 创建一个部署阶段(如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. 创建 outputs.tf 文件以暴露API URL:
output "API Invocation URL" {
  value = "${aws_api_gateway_deployment.staging.invoke_url}"
}
  1. 再次运行 terraform apply 命令创建新对象,确认更改后,API Gateway URL将显示在输出部分。
  2. 若在浏览器中访问API调用URL显示错误消息,需要授予API网关调用Lambda函数的执行权限。更新 main.tf 文件:
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 命令应用最新更改,再次访问API调用URL,应能看到DynamoDB表中存储的电影信息以JSON格式显示。

Terraform将基础设施的状态存储在状态文件(.tfstate)中,为了安全起见,建议将文件保存在远程后端,如S3存储桶。如果要删除所有资源,可使用 terraform destroy 命令;若要删除特定资源,可使用 --target 选项。

以下是使用Terraform部署的流程图:

graph LR
    A[安装和配置Terraform] --> B[创建Lambda函数]
    B --> C[设置DynamoDB表]
    C --> D[配置API网关]
    D --> E[部署和测试]
    E --> F[清理资源]

通过以上步骤,我们可以使用Terraform自动化部署和管理AWS Lambda函数、DynamoDB表和API网关等资源,提高开发效率和减少人为错误。

使用代码部署无服务器应用:Terraform与CloudFormation实践

使用CloudFormation部署AWS Lambda

AWS CloudFormation是一种基础设施即代码工具,可通过声明式方式指定资源。用户在蓝图文档(模板)中对想要AWS创建的所有资源进行建模,AWS会为用户创建这些定义好的资源,这样用户就能将更多时间花在运行于AWS的应用程序上,而减少管理资源的时间。

Terraform几乎涵盖了AWS的所有服务和功能,并且支持第三方提供商(平台无关),而CloudFormation则是特定于AWS的(存在供应商锁定)。用户可以使用AWS CloudFormation来指定、部署和配置无服务器应用程序,只需创建一个描述无服务器应用程序依赖项(如Lambda函数、DynamoDB表、API网关、IAM角色等)的模板,AWS CloudFormation就会负责为用户配置和供应这些资源,无需用户单独创建和配置AWS资源并理清它们之间的依赖关系。

在深入了解CloudFormation之前,需要先了解模板的结构:
- AWSTemplateFormatVersion :CloudFormation模板的版本。
- Description :模板的简要描述。
- Mappings :键和关联值的映射,可用于指定条件参数值。
- Parameters :在运行时传递给模板的值。
- Resources :AWS资源及其属性(如Lambda、DynamoDB、S3等)。
- Outputs :描述查看堆栈属性时返回的值。

理解了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格式也可以使用,JSON版本可在GitHub存储库(https://github.com/PacktPublishing/Hands-On-serverless-Applications-with-Go )中找到。

以下是使用CloudFormation部署的步骤:
1. 创建一个S3存储桶,用于存储部署包:

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控制台,选择“创建堆栈”。
  2. 在“选择模板”页面,选择模板文件,该文件将被上传到Amazon S3存储桶。
  3. 点击“下一步”,定义堆栈名称,并根据需要覆盖默认参数。
  4. 再次点击“下一步”,将选项保留为默认值,然后点击“创建”。
  5. 堆栈将开始创建模板文件中定义的所有资源。创建完成后,堆栈状态将从 CREATE_IN_PROGRESS 变为 CREATE_COMPLETE (如果出现问题,将自动执行回滚)。
  6. 最终,Lambda函数应该会被创建。

如果需要更新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控制台中,选择之前创建的堆栈,然后从菜单中点击“更新堆栈”。
  2. 上传更新后的模板文件。
  3. 与Terraform类似,AWS CloudFormation会检测到更改并提前显示将更改的资源。
  4. 点击“更新”按钮应用更改,堆栈状态将变为 UPDATE_IN_PROGRESS
  5. 更改应用后,将创建一个新的DynamoDB表,并向Lambda函数授予DynamoDB权限。

当CloudFormation需要定义IAM角色、策略或相关资源时,需要使用 --capabilities CAPABILITY_IAM 选项。也可以使用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部署的流程图:

graph LR
    A[了解模板结构] --> B[创建模板文件]
    B --> C[创建S3存储桶并上传部署包]
    C --> D[创建堆栈]
    D --> E[更新模板(可选)]
    E --> F[更新堆栈]
总结

通过Terraform和CloudFormation这两种基础设施即代码的工具,我们可以实现无服务器应用的自动化部署和管理。Terraform具有平台无关性,支持多种云提供商和基础设施软件;而CloudFormation则是专门为AWS设计的,能很好地与AWS生态系统集成。

在实际应用中,可以根据项目的需求和团队的技术栈来选择合适的工具。使用这两种工具都能提高开发效率,减少人为错误,并且可以像管理代码一样对基础设施进行版本控制,方便团队成员之间的共享、在其他AWS区域进行复制以及在出现故障时进行回滚操作。

以下是Terraform和CloudFormation的对比表格:
| 特性 | Terraform | CloudFormation |
| ---- | ---- | ---- |
| 平台支持 | 支持多种云提供商和基础设施软件 | 特定于AWS |
| 模板格式 | HCL | YAML或JSON |
| 状态管理 | 存储在状态文件(.tfstate) | 由AWS CloudFormation管理 |
| 社区支持 | 活跃的开源社区 | AWS官方支持 |

无论是使用Terraform还是CloudFormation,都可以帮助开发者更高效地构建和管理无服务器应用,让开发者将更多的精力放在应用逻辑的开发上。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值