Skip to main content

AWS Lambda & API gateway

使用說明

透過 terraform 檔案來建立 AWS 無伺服器服務 Lambda,並建立 API gateway 來作為終端用戶呼叫 Lambda 函數的方式。在建立 Lambda 服務之前,需要先有欲執行的執行檔,並進一步編譯與包含所需的函式庫。之後執行 terraform 指令來建立 Lambda 與 API gateway 服務。

建置架構

labmda.png

目錄結構

labmda
├── lamda.tf
├── gateway.tf
├── output.tf
├── main.go
├── Makefile
└── bin
└── main

程式碼與組態檔內容

main.go
  package main

import (
"encoding/json"
"fmt"
"net/http"
"time"

"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
)

// Response of APItype Response struct {
Message string `json:"message"`
At string `json:"at"`
}

func handleRequest(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
resp := Response{
Message: "Hello World!",
At: time.Now().Format(time.RFC3339),
}
name, ok := req.QueryStringParameters["name"]
if ok {
resp.Message = fmt.Sprintf("Hello, %s!\n", name)
}

body, _ := json.Marshal(resp)
res := events.APIGatewayProxyResponse{
StatusCode: http.StatusOK,
Headers: map[string]string{"Content-Type": "text/json; charset=utf-8"},
Body: string(body),
}
return res, nil
}

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

Makefile
  build:
GOOS=linux go build -ldflags="-s -w" -o bin/main main.go

lambda.tf
  terraform {
required_providers {
aws = {
source = "hashicorp/aws"
}
}
}

provider "aws" {
profile = "default"
region = "ap-northeast-1"
}

provider "archive" {}

data "archive_file" "zip" {
type = "zip"
source_file = "bin/main"
output_path = "hello_lambda.zip"
}

data "aws_iam_policy_document" "policy" {
statement {
sid = ""
effect = "Allow"

principals {
identifiers = ["lambda.amazonaws.com"]
type = "Service"
}

actions = ["sts:AssumeRole"]
}
}

resource "aws_iam_role" "lambda_exec" {
name = "lambda_exec"
assume_role_policy = data.aws_iam_policy_document.policy.json
}

resource "aws_lambda_function" "hello" {
function_name = "hello_lambda"
filename = data.archive_file.zip.output_path
source_code_hash = data.archive_file.zip.output_base64sha256

role = aws_iam_role.lambda_exec.arn
handler = "main"
runtime = "go1.x"
}

gateway.tf
  resource "aws_api_gateway_rest_api" "hello" {
name = "hello"
description = "Serverless hello world"
}

resource "aws_api_gateway_resource" "hello" {
path_part = "hello"
parent_id = aws_api_gateway_rest_api.hello.root_resource_id
rest_api_id = aws_api_gateway_rest_api.hello.id
}

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

resource "aws_api_gateway_integration" "hello" {
rest_api_id = aws_api_gateway_rest_api.hello.id
resource_id = aws_api_gateway_resource.hello.id
http_method = aws_api_gateway_method.hello.http_method
integration_http_method = "POST"
type = "AWS_PROXY"
uri = aws_lambda_function.hello.invoke_arn
}

resource "aws_api_gateway_deployment" "hello_v1" {
depends_on = [
aws_api_gateway_integration.hello
]
rest_api_id = aws_api_gateway_rest_api.hello.id
stage_name = "v1"
}

resource "aws_lambda_permission" "hello" {
statement_id = "AllowAPIGatewayInvoke"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.hello.arn
principal = "apigateway.amazonaws.com"
}

output.tf
  output "url" {
value = "${aws_api_gateway_deployment.hello_v1.invoke_url}${aws_api_gateway_resource.hello.path}"
}

開始建置

  1. 先建立資料夾 labmda 並依據程式碼與組態檔內容創建五個文件

  2. 準備編譯 main.go

    1. 確認電腦有安裝 goAWS SDK
    2. 終端機到 lambda 資料夾執行指令 make build 編譯執行檔
    3. 執行檔會被存到 labmda/bin/main
    4. main.go 程式有使用到 AWS 兩個函式庫
      1. github.com/aws/aws-lambda-go/lambda
      2. github.com/aws/aws-lambda-go/events
  3. 執行 terraform init 初始化 Terraform

  4. 執行 terraform apply 建立 Lambda 與 API Gateway

    labmda.tf
    1. 使用 archive_file 打包編譯好的執行檔
    2. 使用 aws_iam_policy_document 跟 aws_iam_role 建立 IAM 角色
    3. 使用 aws_lambda_function 建立 Lambda 函數
    gateway.tf
    1. 使用 aws_api_gateway_rest_api 建立 API 閘道物件
    2. 使用 aws_api_gateway_resource 建立 API 閘道資源,設定路徑為 hello
    3. 使用 aws_api_gateway_method 建立 API 閘道資源的方法,設定方法為 GET
    4. 使用 aws_api_gateway_integration 整合 API 閘道跟 Lambda 函數
      1. integration_http_method 設定成 POST
      2. type 設定成 AWS_PROXY
      3. uri 填入 Lambda 函數的 invoke arn
      4. 使用 aws_api_gateway_deployment 部署 API 閘道,要設定 depends_on 確保 aws_api_gateway_integration 完成之後才能部署 API
      5. 使用 aws_lambda_permission 允許 API 閘道使用 Lambda 函數
    output.tf
    1. API 的完整網址被輸出到此文件
    2. (example) Outputs:測試 url = https://k09e1wi32e.execute-api.ap-northeast-1.amazonaws.com/v1/hello
  5. 測試

    測試 Lambda
    aws lambda invoke --region=ap-northeast-1 --function-name=hello_lambda  output.json

    {
    "StatusCode": 200,
    "ExecutedVersion": "$LATEST"
    }
    檢查輸出資料
      cat output.json
    {"statusCode":200,"headers":{"Content-Type":"text/json; charset=utf-8"},"multiValueHeaders":null,"body":"{\"message\":\"Hello World!\",\"at\":\"2020-09-26T09:35:12Z\"}"}%
    測試 API Gateway ( 使用前面 output 的 url )
    curl 'https://k09e1wi32e.execute-api.ap-northeast-1.amazonaws.com/v1/hello'
    {"message":"Hello World!","at":"2024-01-02T09:44:37Z"}%
  6. terraform destroy 刪除所有基礎架構