Skip to content

Latest commit

 

History

History
325 lines (253 loc) · 10.9 KB

SAMPLE05-Lambda-Container-ApiGateway-FlaskApp.md

File metadata and controls

325 lines (253 loc) · 10.9 KB

SAMPLE-05: Provisioning ECR, Lambda Function and API Gateway to run Flask App Container on Lambda

This sample shows:

  • how to create Flask-app-serverless image to run on Lambda,
  • how to create ECR and to push image to ECR,
  • how to create Lambda function, Lambda role, policy, policy-role attachment, Lambda API Gateway permission
  • how to create API Gateway resource and method definition, Lambda - API Gateway connection, deploying API Gateway

There are 3 main parts:

  • 0_ecr.tf: includes private ECR code.
  • 1_lambda.tf: includes lambda function, lambda role, policy, policy-role attachment, lambda api gateway permission code.
  • 2_api_gateway.tf: includes api-gateway resource and method definition, lambda - api gateway connection, deploying api gateway code.

image

Code: https://github.com/omerbsezer/Fast-Terraform/tree/main/samples/lambda-container-apigateway-flaskapp

Table of Contents

Prerequisite

Steps

Flask App Docker Image Creation

Creating ECR (Elastic Container Repository), Pushing Image into ECR

  • Create 0_ecr.tf:
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.16"
    }
  }
  required_version = ">= 1.2.0"
}

# Creating Elastic Container Repository for application
resource "aws_ecr_repository" "flask_app_serverless" {
  name = "flask-app-serverless"
}

Code: https://github.com/omerbsezer/Fast-Terraform/blob/main/samples/lambda-container-apigateway-flaskapp/ecr/0_ecr.tf

cd /ecr
terraform init
terraform plan
terraform apply
  • On AWS ECR:

    image

  • To see the pushing docker commands, click "View Push Commands"

aws ecr get-login-password --region eu-central-1 | docker login --username AWS --password-stdin <UserID>.dkr.ecr.eu-central-1.amazonaws.com
docker tag flask-app-serverless:latest <UserID>.ecr.eu-central-1.amazonaws.com/flask-app-serverless:latest
docker push <UserID>.dkr.ecr.eu-central-1.amazonaws.com/flask-app-serverless:latest
  • Image on AWS ECR:

    image

Creating Lambda

  • Difference between Lambda function code and Lambda container: Defining the image on the Lambda Function.
# Getting data existed ECR
data "aws_ecr_repository" "flask_app_serverless" {
  name = "flask-app-serverless" 
}

# Lambda Function, in terraform ${path.module} is the current directory.
resource "aws_lambda_function" "lambda_function" {
 function_name = "Lambda-Function"
 role          = aws_iam_role.lambda_role.arn
 # tag is required, "source image ... is not valid" error will pop up
 image_uri     = "${data.aws_ecr_repository.flask_app_serverless.repository_url}:latest"    # lambda image on ECR
 package_type  = "Image"
 depends_on    = [aws_iam_role_policy_attachment.attach_iam_policy_to_iam_role]
}

image

  • Create 1_lambda.tf:
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.16"
    }
  }
  required_version = ">= 1.2.0"
}

# Create IAM Role for lambda
resource "aws_iam_role" "lambda_role" {
 name   = "aws_lambda_role"
 assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF
}

# IAM policy for the lambda
resource "aws_iam_policy" "iam_policy_for_lambda" {

  name         = "aws_iam_policy_for_aws_lambda_role"
  path         = "/"
  description  = "AWS IAM Policy for managing aws lambda role"
  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": "arn:aws:logs:*:*:*",
      "Effect": "Allow"
    }
  ]
}
EOF
}

# Role - Policy Attachment
resource "aws_iam_role_policy_attachment" "attach_iam_policy_to_iam_role" {
  role        = aws_iam_role.lambda_role.name
  policy_arn  = aws_iam_policy.iam_policy_for_lambda.arn
}

# Getting data existed ECR
data "aws_ecr_repository" "flask_app_serverless" {
  name = "flask-app-serverless"
}

# Lambda Function, in terraform ${path.module} is the current directory.
resource "aws_lambda_function" "lambda_function" {
 function_name = "Lambda-Function"
 role          = aws_iam_role.lambda_role.arn
 # tag is required, "source image ... is not valid" error will pop up
 image_uri     = "${data.aws_ecr_repository.flask_app_serverless.repository_url}:latest"
 package_type  = "Image"
 depends_on    = [aws_iam_role_policy_attachment.attach_iam_policy_to_iam_role]
}

# With Lambda permission, API Gateway can invoke Lambda 
resource "aws_lambda_permission" "apigw" {
 statement_id  = "AllowAPIGatewayInvoke"
 action        = "lambda:InvokeFunction"
 function_name = aws_lambda_function.lambda_function.function_name
 principal     = "apigateway.amazonaws.com"
 # The "/*/*" portion grants access from any method on any resource within the API Gateway REST API.
 source_arn = "${aws_api_gateway_rest_api.example.execution_arn}/*/*"
}

Code: https://github.com/omerbsezer/Fast-Terraform/blob/main/samples/lambda-container-apigateway-flaskapp/1_lambda.tf

image

Creating API Gateway

  • Create 2_api_gateway.tf:
# Create API Gateway with Rest API type
resource "aws_api_gateway_rest_api" "example" {
  name        = "Serverless"
  description = "Serverless Application using Terraform"
}

resource "aws_api_gateway_resource" "proxy" {
   rest_api_id = aws_api_gateway_rest_api.example.id
   parent_id   = aws_api_gateway_rest_api.example.root_resource_id
   path_part   = "{proxy+}"     # with proxy, this resource will match any request path
}

resource "aws_api_gateway_method" "proxy" {
   rest_api_id   = aws_api_gateway_rest_api.example.id
   resource_id   = aws_api_gateway_resource.proxy.id
   http_method   = "ANY"       # with ANY, it allows any request method to be used, all incoming requests will match this resource
   authorization = "NONE"
}

# API Gateway - Lambda Connection
resource "aws_api_gateway_integration" "lambda" {
   rest_api_id = aws_api_gateway_rest_api.example.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"  # With AWS_PROXY, it causes API gateway to call into the API of another AWS service
   uri                     = aws_lambda_function.lambda_function.invoke_arn
}

# The proxy resource cannot match an empty path at the root of the API. 
# To handle that, a similar configuration must be applied to the root resource that is built in to the REST API object
resource "aws_api_gateway_method" "proxy_root" {
   rest_api_id   = aws_api_gateway_rest_api.example.id
   resource_id   = aws_api_gateway_rest_api.example.root_resource_id
   http_method   = "ANY"
   authorization = "NONE"
}

resource "aws_api_gateway_integration" "lambda_root" {
   rest_api_id = aws_api_gateway_rest_api.example.id
   resource_id = aws_api_gateway_method.proxy_root.resource_id
   http_method = aws_api_gateway_method.proxy_root.http_method
   integration_http_method = "POST"
   type                    = "AWS_PROXY"  # With AWS_PROXY, it causes API gateway to call into the API of another AWS service
   uri                     = aws_lambda_function.lambda_function.invoke_arn
}

# Deploy API Gateway
resource "aws_api_gateway_deployment" "example" {
   depends_on = [
     aws_api_gateway_integration.lambda,
     aws_api_gateway_integration.lambda_root,
   ]
   rest_api_id = aws_api_gateway_rest_api.example.id
   stage_name  = "test"
}

# Output to the URL 
output "base_url" {
  value = aws_api_gateway_deployment.example.invoke_url
}

Code: https://github.com/omerbsezer/Fast-Terraform/blob/main/samples/lambda-container-apigateway-flaskapp/2_api_gateway.tf

image

Demo: Terraform Run

  • Run:
terraform init
terraform validate
terraform plan
terraform apply

image

  • On AWS Lambda, new Lambda function is created:

    image

  • This time, Container is running on the Lambda:

    image

  • On AWS API-Gateway, new API is created:

    image

  • Flask App is running in container, container is running on Lambda:

    image

  • Edit posts with "Edit" button, new post with "New Post", Delete post with "Delete Post".

  • Copied DB file into the "/tmp/" directory to get write permission. Lambda allows only the files under "/tmp/" to have write permission.

    image

    image

  • CloudWatch automatically logs the Lambda. If there is a debug issue, view the log groups under the Cloud Watch.

    image

  • Destroy the infra:

terraform destroy

image

  • Delete the ECR Repo:
cd ecr
terraform destroy

image