[Terraform] 서버리스 애플리케이션 배포하기 - Lambda + API gateway

h222ji·2022년 4월 11일
1

참조 : Hashcorp Learn

1. 환경 구성하기

git clone https://github.com/hashicorp/learn-terraform-lambda-api-gateway.git

현재 디렉토리 위치에 learn-terraform-lambd-api-gateway 폴더 복제됨

main.tf

## terraform 공급자 정의

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.0.0"
    }
    random = {
      source  = "hashicorp/random"
      version = "~> 3.1.0"
    }
    archive = {
      source  = "hashicorp/archive"
      version = "~> 2.2.0"
    }
  }

  required_version = "~> 1.0"
}

provider "aws" {
  region = var.aws_region
}

resource "random_pet" "lambda_bucket_name" {
  prefix = "learn-terraform-functions"
  length = 4
}

## S3 버킷 정의

resource "aws_s3_bucket" "lambda_bucket" {
  bucket = random_pet.lambda_bucket_name.id

  acl           = "private"
  force_destroy = true
}
terraform init

구성 적용 중 error 발생

단순히 버전 업그레이드하면 되는 오류라서 -upgrade 옵션만 추가하여 재시도

terraform init -upgrade

terraform apply

또 에러 발생...
검색해보니 Terraform AWS Provider is upgraded to version 4.0.0 which is published on 10 February 2022.
provider가 업그레이드되면서 일부 서비스 지원이 안되는듯!

main.tf

terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0.0"

"~> 3.73.0"으로 수정

}
terraform init -upgrade
terraform apply

2. Lambda 함수 아카이브 생성 및 업로드

hello.js

module.exports.handler = async (event) => {
  console.log('Event: ', event);
  let responseMessage = 'Hello, World!';

  return {
    statusCode: 200,
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      message: responseMessage,
    }),
  }
}

lambda에서 event를 가져와 콘솔에 기록하고 API gateway가 http 응답을 생성할 객체를 반환

main.tf 코드 추가

data "archive_file" "lambda_hello_world" {
  type = "zip"

  source_dir  = "${path.module}/hello-world"
  output_path = "${path.module}/hello-world.zip"
}

resource "aws_s3_object" "lambda_hello_world" {
  bucket = aws_s3_bucket.lambda_bucket.id

  key    = "hello-world.zip"
  source = data.archive_file.lambda_hello_world.output_path

  etag = filemd5(data.archive_file.lambda_hello_world.output_path)
}
terraform apply

또 오류 ^0^
4.0버전을 기준으로 데이터 소스들의 이름이 설정되어있기때문에
다시 3.x버전의 소스명으로 바꿔준다.
(Terraform AWS Provider Version 4 Upgrade Guide에서 참조!)

암튼 그래서 line43의 aws_s3_objectaws_s3_bucket_object로 변경해준다.


S3안에 hello-world.zip 파일이 잘 들어가 있는 것을 확인할 수 있다.

3. Lambda 함수 생성

main.tf 코드 추가

resource "aws_lambda_function" "hello_world" {
  function_name = "HelloWorld"

  s3_bucket = aws_s3_bucket.lambda_bucket.id
  s3_key    = aws_s3_bucket_object.lambda_hello_world.key

  runtime = "nodejs12.x"
  handler = "hello.handler"

  source_code_hash = data.archive_file.lambda_hello_world.output_base64sha256

  role = aws_iam_role.lambda_exec.arn
}

resource "aws_cloudwatch_log_group" "hello_world" {
  name = "/aws/lambda/${aws_lambda_function.hello_world.function_name}"

  retention_in_days = 30
}

resource "aws_iam_role" "lambda_exec" {
  name = "serverless_lambda"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Action = "sts:AssumeRole"
      Effect = "Allow"
      Sid    = ""
      Principal = {
        Service = "lambda.amazonaws.com"
      }
      }
    ]
  })
}

resource "aws_iam_role_policy_attachment" "lambda_policy" {
  role       = aws_iam_role.lambda_exec.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}

이 코드에서도 aws_s3_objectaws_s3_bucket_object로 변경해줬다

output.tf 코드 추가

output "function_name" {
  description = "Name of the Lambda function."

  value = aws_lambda_function.hello_world.function_name
}

4. API Gateway로 HTTP API 생성

main.tf 코드 추가

resource "aws_apigatewayv2_api" "lambda" {
  name          = "serverless_lambda_gw"
  protocol_type = "HTTP"
}

resource "aws_apigatewayv2_stage" "lambda" {
  api_id = aws_apigatewayv2_api.lambda.id

  name        = "serverless_lambda_stage"
  auto_deploy = true

  access_log_settings {
    destination_arn = aws_cloudwatch_log_group.api_gw.arn

    format = jsonencode({
      requestId               = "$context.requestId"
      sourceIp                = "$context.identity.sourceIp"
      requestTime             = "$context.requestTime"
      protocol                = "$context.protocol"
      httpMethod              = "$context.httpMethod"
      resourcePath            = "$context.resourcePath"
      routeKey                = "$context.routeKey"
      status                  = "$context.status"
      responseLength          = "$context.responseLength"
      integrationErrorMessage = "$context.integrationErrorMessage"
      }
    )
  }
}

resource "aws_apigatewayv2_integration" "hello_world" {
  api_id = aws_apigatewayv2_api.lambda.id

  integration_uri    = aws_lambda_function.hello_world.invoke_arn
  integration_type   = "AWS_PROXY"
  integration_method = "POST"
}

resource "aws_apigatewayv2_route" "hello_world" {
  api_id = aws_apigatewayv2_api.lambda.id

  route_key = "GET /hello"
  target    = "integrations/${aws_apigatewayv2_integration.hello_world.id}"
}

resource "aws_cloudwatch_log_group" "api_gw" {
  name = "/aws/api_gw/${aws_apigatewayv2_api.lambda.name}"

  retention_in_days = 30
}

resource "aws_lambda_permission" "api_gw" {
  statement_id  = "AllowExecutionFromAPIGateway"
  action        = "lambda:InvokeFunction"
  function_name = aws_lambda_function.hello_world.function_name
  principal     = "apigateway.amazonaws.com"

  source_arn = "${aws_apigatewayv2_api.lambda.execution_arn}/*/*"
}

output.tf 코드 추가

output "base_url" {
  description = "Base URL for API Gateway stage."

  value = aws_apigatewayv2_stage.lambda.invoke_url
}

5. Lambda 함수 업데이트

hello.js 코드 추가

 module.exports.handler = async (event) => {
   console.log('Event: ', event)
   let responseMessage = 'Hello, World!';

+  if (event.queryStringParameters && event.queryStringParameters['Name']) {
+    responseMessage = 'Hello, ' + event.queryStringParameters['Name'] + '!';
+  }
+
   return {
     statusCode: 200,
     headers: {
       'Content-Type': 'application/json',
     },
     body: JSON.stringify({
       message: responseMessage,
     }),
   }
 }

이제 모두 아실것... 또 apply해줌미다.

terraform apply

6. 인프라 정리

terraform destroy

profile
희지야 블로그 좀 쓰쟈.. plz....

0개의 댓글