🔧 阿川の電商水電行
Shopify 顧問、維護與客製化
💡
小任務 / 單次支援方案
單次處理 Shopify 修正/微調
⭐️
維護方案
每月 Shopify 技術支援 + 小修改 + 諮詢
🚀
專案建置
Shopify 功能導入、培訓 + 分階段交付

介紹

透過結合 Terraform 與 AWS SAM(Serverless Application Model),可以明確地分離基礎架構與應用程式的責任,實現針對團隊的適當權限管理以及安全的基礎架構即程式碼(IaC)運用

此架構的核心優勢

  • 應用開發者: 透過 SAM 快速部署經常更新的 Lambda 函式
  • 基礎架構團隊: 嚴格管理網路與重要資源的 Terraform
  • 安全性: 根據最小權限原則實現的權限分離,減輕風險

本文將透過一個可運作的完整範例專案,解釋以下內容:

  • 基礎篇: Terraform 與 SAM 的責任分離及團隊權限管理
  • 實作篇: VPC、DynamoDB 及多環境支援的實作
  • 運營篇: 監控、故障排除、CI/CD
  • 驗證結果: 實際部署及運行確認

本文中學到的內容

✅ Terraform 與 SAM 的適當分配
✅ VPC 內 Lambda 的安全設計
✅ DynamoDB 單表設計的實作
✅ 環境別(dev/staging/prod)的管理方法
✅ 透過 CloudWatch 設置監控與警報
✅ 使用 GitHub Actions 建立 CI/CD
✅ 實際發生的錯誤及其解決方案

範例專案

完整的源代碼公開於以下連結:
https://github.com/higakikeita/test

架構圖(可編輯):
https://github.com/higakikeita/test/blob/main/docs/architecture.drawio

為什麼選擇 Terraform + SAM?

各自的優勢

工具 擅長的事情 不擅長的事情
Terraform 全面管理基礎架構,與其他服務整合 Lambda 的建置、本地測試
SAM Lambda 的開發與部署、本地測試 VPC 等通用的基礎架構管理

責任分離的原則

┌─────────────────────────────────────┐
│        Terraform (基礎架構層)       │
│      👷 由基礎架構團隊管理          │
├─────────────────────────────────────┤
│ • VPC / 子網路 / 安全群組         │
│ • DynamoDB 表格 / IAM 角色        │
│ • S3 存儲桶 / CloudWatch 配置     │
│                                   │
│ 變更頻率: 低 (週次至月次)          │
│ 影響範圍: 大 (安全與網路)         │
└─────────────────────────────────────┘
              ↓ outputs
┌─────────────────────────────────────┐
│       SAM (應用層)                 │
│      👨‍💻 由應用開發者管理         │
├─────────────────────────────────────┤
│ • Lambda 函式 / Lambda 層         │
│ • API Gateway / 事件來源           │
│ • 應用邏輯                       │
│                                   │
│ 變更頻率: 高 (日次至小時)          │
│ 影響範圍: 小 (應用內部)           │
└─────────────────────────────────────┘

透過團隊別的責任分離實現安全的 IaC 管理

該架構的最大優點是能夠實現依據團隊角色的適當權限分離

🏗️ 基礎架構團隊(Terraform)

管理範圍:

  • 網路結構(VPC、子網路、安全群組)
  • 數據存儲(DynamoDB、RDS 等)
  • IAM 角色與政策
  • 監控與日誌基礎設施(CloudWatch)

特徵:

  • 變更頻率低(週次至月次)
  • 設定直接影響安全性
  • 本番環境的影響範圍較大
  • 審核流程嚴格

權限:

# 僅能由基礎架構團隊執行
terraform apply -var-file=environments/prod.tfvars

👨‍💻 應用開發團隊(SAM)

管理範圍:

  • Lambda 函式的代碼
  • API Gateway 的設定
  • Lambda 層
  • 事件來源(DynamoDB Streams、EventBridge)

特徵:

  • 變更頻率高(日次至小時)
  • 應用邏輯的改進與錯誤修正
  • 對基礎架構的影響最小
  • 可快速部署

權限:

# 應用開發者可自由執行
sam deploy --stack-name my-app-dev

🔒 安全的 IaC 管理優勢

  1. 最小權限原則

    • 應用開發者無法更改 VPC 或 IAM
    • 基礎架構團隊不參與應用的頻繁部署
  2. 變更管理的分離

    • 基礎架構變更:嚴格的審查與批准過程
    • 應用變更:快速的 CI/CD 管道
  3. 減輕安全風險

    • 防止不必要的權限提升
    • 防止對網路設置的無意變更
    • 防止 IAM 角色的錯誤修改
  4. 提升開發速度

    • 應用開發者能專注於開發,而不必擔心基礎架構
    • Lambda 函式的更新可以即時部署
  5. 審核與合規性支援

    • 誰更改了什麼一目了然
    • 變更歷史可由 Git 追蹤
    • 環境別的權限管理變得容易

實作範例:權限分離

# GitHub Actions - 基礎架構部署(僅限 main 分支)
deploy-infrastructure:
  if: github.ref == 'refs/heads/main'
  environment: production
  # 僅能由基礎架構團隊批准

# GitHub Actions - 應用部署(feature 分支也可以)
deploy-application:
  if: github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/main'
  # 應用開發者可以自由部署

這樣的架構使得在維持安全性的同時,最大化開發速度

架構概覽

此次構建的系統架構如下:

系統架構圖

使用 Python 的 diagrams 資料庫自動生成,並使用 AWS 官方圖示的架構圖。

📋 簡單版

簡潔的圖示,一目瞭然基本資料流。
系統概覽

🏗️ 詳細版

詳細顯示所有組件及其關係的圖。
Terraform + SAM 架構

🔄 資料流詳細

分層顯示從請求到響應的資料流動。
資料流詳細

主要組件詳細

1. Lambda 函式

  • API 函式 (256MB, ARM64, 30s timeout)
    • REST API 端點
    • CRUD 操作,驗證
    • 錯誤處理
  • 處理器函式 (256MB, ARM64)
    • 處理 DynamoDB Streams 事件
    • 批次大小:10,窗口:5秒
    • 重試設定,DLQ 啟用
  • 定時函式 (256MB, ARM64, 60s timeout)
    • 定期執行任務(每日 UTC 00:00)
    • 維護,數據清理

2. DynamoDB 表格

  • 設計: 單表設計
  • : PK (字串), SK (字串)
  • GSI: EntityTypeIndex, GSI1
  • Streams: NEW_AND_OLD_IMAGES
  • 計費: 按請求計費
  • PITR: 僅在本番環境啟用

3. VPC 結構

  • CIDR: 環境別 (dev: 10.0.0.0/16)
  • 公有子網: 配置 NAT Gateway
  • 私有子網: 配置 Lambda
  • VPC 端點: S3, DynamoDB (免費)
  • NAT Gateway: dev=1個、prod=2個

4. 監控與警報

  • CloudWatch Logs: 保存 7-90 日
  • Metrics: Lambda, DynamoDB, API Gateway
  • 警報: 檢測錯誤、流量限制
  • 儀表板: 整合視圖

專案結構

terraform-sam-demo/
├── terraform/              # 基礎架構定義
│   ├── main.tf
│   ├── variables.tf
│   ├── outputs.tf
│   ├── iam.tf             # IAM 角色與政策
│   ├── vpc.tf             # VPC 設定
│   ├── dynamodb.tf        # DynamoDB 表格
│   ├── cloudwatch.tf      # 監控設定
│   └── environments/      # 環境別設定
│       ├── dev.tfvars
│       ├── staging.tfvars
│       └── prod.tfvars
├── sam/                   # SAM 應用程式
│   ├── template.yaml      # SAM 模板
│   ├── functions/         # Lambda 函式
│   │   ├── api/
│   │   │   ├── index.py
│   │   │   └── requirements.txt
│   │   └── processor/
│   │       ├── index.py
│   │       └── requirements.txt
│   ├── layers/            # Lambda 層
│   │   └── common/
│   └── events/            # 測試事件
├── scripts/               # 腳本
│   ├── deploy.sh          # 部署腳本
│   ├── validate.sh        # 驗證腳本
│   └── generate_diagrams.py  # 圖的自動生成
├── .github/workflows/     # CI/CD
│   └── deploy.yml
└── docs/                  # 文件
    ├── architecture.md
    ├── TROUBLESHOOTING.md
    ├── BEST_PRACTICES.md
    └── images/            # 架構圖
        ├── architecture.png
        ├── architecture_simple.png
        ├── dataflow.png
        └── README.md      # 圖的生成方法

自動生成架構圖

本專案使用 Python 的 diagrams 資料庫自動生成使用 AWS 官方圖示的架構圖。

生成方法

# 安裝所需工具
brew install graphviz
pip3 install diagrams

# 生成圖
python3 scripts/generate_diagrams.py

執行後,docs/images/ 將會生成以下三個 PNG 圖片:

  • architecture_simple.png - 簡單的概覽圖
  • architecture.png - 詳細的完整構成圖
  • dataflow.png - 資料流詳細圖

圖的特點

布局的巧妙:

graph_attr = {
    "splines": "ortho",    # 直角的美麗線條
    "nodesep": "0.8",      # 節點之間的間距
    "ranksep": "1.0",      # 階層間的空白
}

色彩區分的視覺化:

# 主流
users >> Edge(color="darkblue", style="bold", label="HTTPS") >> apigw
apigw >> Edge(color="darkgreen", style="bold", label="Invoke") >> lambda_api

# Stream 處理
dynamodb >> Edge(color="orange", style="bold", label="Streams") >> lambda_processor

# 日誌
lambda_api >> Edge(color="gray", style="dotted") >> cloudwatch

優勢:

  • 由於以程式碼管理,能追蹤變更歷史
  • 在構成變更時,自動重新生成
  • 使用 AWS 官方圖示,專業的完成度
  • 易於版本管理

自定義

透過編輯 scripts/generate_diagrams.py 可以輕鬆自定義:

# 新增 AWS 服務的範例
from diagrams.aws.network import CloudFront
from diagrams.aws.security import WAF

# 添加至圖中
cloudfront = CloudFront("CloudFront")
waf = WAF("WAF")

詳情請參閱 diagrams 官方文檔

實作:使用 Terraform 構建基礎架構

1. VPC 設定(vpc.tf)

將 Lambda 放置在 VPC 內以實現安全環境。

# VPC
resource "aws_vpc" "main" {
  cidr_block           = var.vpc_cidr
  enable_dns_hostnames = true
  enable_dns_support   = true

  tags = {
    Name = "${local.resource_prefix}-vpc"
  }
}

# 私有子網(用於 Lambda 的配置)
resource "aws_subnet" "private" {
  count = length(var.private_subnet_cidrs)

  vpc_id            = aws_vpc.main.id
  cidr_block        = var.private_subnet_cidrs[count.index]
  availability_zone = var.availability_zones[count.index]

  tags = {
    Name = "${local.resource_prefix}-private-subnet-${count.index + 1}"
  }
}

# NAT Gateway(用於 Lambda 連接外部 API)
resource "aws_nat_gateway" "main" {
  count = var.enable_nat_gateway ? (var.single_nat_gateway ? 1 : length(var.public_subnet_cidrs)) : 0

  allocation_id = aws_eip.nat[count.index].id
  subnet_id     = aws_subnet.public[count.index].id

  tags = {
    Name = "${local.resource_prefix}-nat-${count.index + 1}"
  }
}

# VPC 端點(降低成本)
resource "aws_vpc_endpoint" "s3" {
  vpc_id       = aws_vpc.main.id
  service_name = "com.amazonaws.${var.aws_region}.s3"

  route_table_ids = concat(
    [aws_route_table.public.id],
    aws_route_table.private[*].id
  )

  tags = {
    Name = "${local.resource_prefix}-s3-endpoint"
  }
}

重點:

  • 開發環境中配置單一 NAT Gateway,本番環境中則在各 AZ 配置
  • S3/DynamoDB 透過 VPC 端點存取(免費 & 高速)
  • 安全群組僅允許出口流量

2. DynamoDB 表格(dynamodb.tf)

通過單表設計高效管理多個實體。

resource "aws_dynamodb_table" "main" {
  name           = local.dynamodb_table_name
  billing_mode   = var.dynamodb_billing_mode
  hash_key       = "PK"
  range_key      = "SK"

  attribute {
    name = "PK"
    type = "S"
  }

  attribute {
    name = "SK"
    type = "S"
  }

  attribute {
    name = "EntityType"
    type = "S"
  }

  attribute {
    name = "CreatedAt"
    type = "N"
  }

  # GSI: 用於依據實體類型查詢
  global_secondary_index {
    name            = "EntityTypeIndex"
    hash_key        = "EntityType"
    range_key       = "CreatedAt"
    projection_type = "ALL"
  }

  # DynamoDB Streams(用於處理器 Lambda)
  stream_enabled   = var.enable_dynamodb_streams
  stream_view_type = "NEW_AND_OLD_IMAGES"

  # 逐點恢復(僅在本番環境)
  point_in_time_recovery {
    enabled = var.enable_dynamodb_point_in_time_recovery
  }

  # TTL 設定
  ttl {
    enabled        = true
    attribute_name = "ExpiresAt"
  }
}

單表設計範例:

# 使用者實體
PK: USER#123, SK: METADATA
EntityType: User
Name: "John Doe"

# 使用者的訂單
PK: USER#123, SK: ORDER#456
EntityType: Order
Amount: 1000

3. IAM 角色(iam.tf)

基於最小權限原則創建 Lambda 的 IAM 角色。

# Lambda API 函式用角色
resource "aws_iam_role" "lambda_api" {
  name               = "${local.resource_prefix}-lambda-api-role"
  assume_role_policy = data.aws_iam_policy_document.lambda_assume_role.json
}

# DynamoDB 存取政策
data "aws_iam_policy_document" "lambda_dynamodb_access" {
  statement {
    effect = "Allow"
    actions = [
      "dynamodb:GetItem",
      "dynamodb:Query",
      "dynamodb:PutItem",
      "dynamodb:UpdateItem",
      "dynamodb:DeleteItem"
    ]
    resources = [
      aws_dynamodb_table.main.arn,
      "${aws_dynamodb_table.main.arn}/index/*"
    ]
  }
}

resource "aws_iam_role_policy" "lambda_api_dynamodb" {
  role   = aws_iam_role.lambda_api.id
  policy = data.aws_iam_policy_document.lambda_dynamodb_access.json
}

安全重點:

  • 明確指定資源 ARN(不使用 *
  • 只允許必要的操作
  • 可以使用 Condition 進行進一步限制

4. CloudWatch 設定(cloudwatch.tf)

設定監控和警報。

# 日誌組
resource "aws_cloudwatch_log_group" "lambda_api" {
  name              = "/aws/lambda/${local.lambda_function_prefix}-api"
  retention_in_days = var.log_retention_days
}

# Lambda 錯誤警報
resource "aws_cloudwatch_metric_alarm" "lambda_api_errors" {
  alarm_name          = "${local.resource_prefix}-lambda-api-errors"
  comparison_operator  = "GreaterThanThreshold"
  evaluation_periods   = 2
  metric_name         = "Errors"
  namespace           = "AWS/Lambda"
  period              = 300
  statistic           = "Sum"
  threshold           = 5

  dimensions = {
    FunctionName = "${local.lambda_function_prefix}-api"
  }
}

# CloudWatch 儀表板
resource "aws_cloudwatch_dashboard" "main" {
  dashboard_name = "${local.resource_prefix}-dashboard"

  dashboard_body = jsonencode({
    widgets = [
      {
        type = "metric"
        properties = {
          metrics = [
            ["AWS/Lambda", "Invocations"],
            [".", "Errors"],
            [".", "Duration"]
          ]
          period = 300
          stat   = "Average"
          region = var.aws_region
          title  = "Lambda Metrics"
        }
      }
    ]
  })
}

5. 輸出(outputs.tf)

輸出供 SAM 使用的值。

output "vpc_id" {
  value = aws_vpc.main.id
}

output "private_subnet_ids" {
  value = aws_subnet.private[*].id
}

output "lambda_security_group_id" {
  value = aws_security_group.lambda.id
}

output "lambda_api_role_arn" {
  value = aws_iam_role.lambda_api.arn
}

output "dynamodb_table_name" {
  value = aws_dynamodb_table.main.name
}

output "sam_artifacts_bucket" {
  value = aws_s3_bucket.sam_artifacts.id
}

# 生成 SAM 用部署命令
output "sam_deploy_command" {
  value = <<-EOT
    sam deploy \
      --stack-name ${local.resource_prefix}-app \
      --s3-bucket ${aws_s3_bucket.sam_artifacts.id} \
      --parameter-overrides \
        VpcId=${aws_vpc.main.id} \
        SubnetIds=${join(",", aws_subnet.private[*].id)}
  EOT
}

實作:使用 SAM 構建應用程式

1. SAM 模板(template.yaml)

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

# 由 Terraform 傳遞參數
Parameters:
  Environment:
    Type: String
  VpcId:
    Type: String
  SubnetIds:
    Type: CommaDelimitedList
  SecurityGroupId:
    Type: String
  LambdaApiRoleArn:
    Type: String
  DynamoDBTableName:
    Type: String

# 全域設定
Globals:
  Function:
    Runtime: python3.11
    Timeout: 30
    MemorySize: 256
    Architectures:
      - arm64  # Graviton2(降低 20% 成本)
    Environment:
      Variables:
        ENVIRONMENT: !Ref Environment
        DYNAMODB_TABLE: !Ref DynamoDBTableName
        LOG_LEVEL: INFO
    VpcConfig:
      SecurityGroupIds:
        - !Ref SecurityGroupId
      SubnetIds: !Ref SubnetIds
    Tracing: Active  # 啟用 X-Ray

Resources:
  # Lambda 層(共通函式庫)
  CommonLayer:
    Type: AWS::Serverless::LayerVersion
    Properties:
      LayerName: !Sub ${Environment}-common-layer
      ContentUri: layers/common/
      CompatibleRuntimes:
        - python3.11

  # API Lambda 函式
  ApiFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: !Sub terraform-sam-demo-${Environment}-api
      CodeUri: functions/api/
      Handler: index.lambda_handler
      Role: !Ref LambdaApiRoleArn
      Layers:
        - !Ref CommonLayer
      Events:
        GetItems:
          Type: Api
          Properties:
            Path: /items
            Method: GET
        CreateItem:
          Type: Api
          Properties:
            Path: /items
            Method: POST
        GetItem:
          Type: Api
          Properties:
            Path: /items/{id}
            Method: GET

  # 處理器 Lambda 函式 (DynamoDB Streams)
  ProcessorFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: !Sub terraform-sam-demo-${Environment}-processor
      CodeUri: functions/processor/
      Handler: index.lambda_handler
      Role: !Ref LambdaProcessorRoleArn
      Events:
        DynamoDBStream:
          Type: DynamoDB
          Properties:
            Stream: !Ref DynamoDBStreamArn
            StartingPosition: LATEST
            BatchSize: 10

Outputs:
  ApiEndpoint:
    Value: !Sub https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/${Environment}

2. API Lambda 函式(functions/api/index.py)

import json
import os
import boto3
from boto3.dynamodb.conditions import Key
import logging

logger = logging.getLogger()
logger.setLevel(os.environ.get('LOG_LEVEL', 'INFO'))

dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table(os.environ['DYNAMODB_TABLE'])

def create_response(status_code, body):
    """創建 API Gateway 響應"""
    return {
        'statusCode': status_code,
        'headers': {
            'Content-Type': 'application/json',
            'Access-Control-Allow-Origin': '*'
        },
        'body': json.dumps(body, default=str)
    }

def get_items(event):
    """GET /items - 獲取項目列表"""
    try:
        response = table.query(
            IndexName='EntityTypeIndex',
            KeyConditionExpression=Key('EntityType').eq('Item'),
            Limit=20
        )

        items = response.get('Items', [])
        logger.info(f"Retrieved {len(items)} items")

        return create_response(200, {
            'items': items,
            'count': len(items)
        })
    except Exception as e:
        logger.error(f"Error: {str(e)}")
        return create_response(500, {'error': str(e)})

def create_item(event):
    """POST /items - 創建項目"""
    try:
        body = json.loads(event['body'])

        import uuid
        item_id = str(uuid.uuid4())

        item = {
            'PK': f'ITEM#{item_id}',
            'SK': 'METADATA',
            'EntityType': 'Item',
            'ItemId': item_id,
            'Name': body['name'],
            'CreatedAt': int(time.time())
        }

        table.put_item(Item=item)
        logger.info(f"Created item: {item_id}")

        return create_response(201, {
            'message': 'Item created',
            'item': item
        })
    except Exception as e:
        logger.error(f"Error: {str(e)}")
        return create_response(500, {'error': str(e)})

def lambda_handler(event, context):
    """Lambda 入口點"""
    logger.info(f"Event: {json.dumps(event)}")

    method = event['httpMethod']
    path = event['path']

    if path == '/items' and method == 'GET':
        return get_items(event)
    elif path == '/items' and method == 'POST':
        return create_item(event)
    else:
        return create_response(404, {'error': 'Not found'})

3. 處理器 Lambda 函式(functions/processor/index.py)

import json
import logging

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def process_insert(new_image):
    """INSERT 事件處理"""
    logger.info(f"New item created: {new_image.get('ItemId')}")
    # 發送通知,集計處理等

def process_modify(old_image, new_image):
    """MODIFY 事件處理"""
    logger.info(f"Item modified: {new_image.get('ItemId')}")
    # 變更內容分析,通知等

def lambda_handler(event, context):
    """DynamoDB Streams 事件處理"""
    logger.info(f"Processing {len(event['Records'])} records")

    for record in event['Records']:
        event_name = record['eventName']

        if event_name == 'INSERT':
            new_image = record['dynamodb']['NewImage']
            process_insert(new_image)
        elif event_name == 'MODIFY':
            old_image = record['dynamodb']['OldImage']
            new_image = record['dynamodb']['NewImage']
            process_modify(old_image, new_image)

    return {'statusCode': 200}

部署步驟

1. 前置條件

# 確認安裝必要的工具
terraform --version  # >= 1.5.0
sam --version        # >= 1.100.0
aws --version        # >= 2.0

# 設定 AWS 認證資訊
aws configure

2. 使用 Terraform 構建基礎架構

cd terraform

# 初始化
terraform init

# 查看計畫
terraform plan -var-file=environments/dev.tfvars

# 應用
terraform apply -var-file=environments/dev.tfvars

# 儲存輸出值(供 SAM 使用)
terraform output -json > ../sam/terraform-outputs.json

執行結果:

Apply complete! Resources: 45 added, 0 changed, 0 destroyed.

Outputs:

vpc_id = "vpc-0123456789abcdef0"
private_subnet_ids = [
  "subnet-0123456789abcdef0",
  "subnet-0123456789abcdef1",
]
dynamodb_table_name = "terraform-sam-demo-dev-data"
sam_artifacts_bucket = "terraform-sam-demo-dev-sam-artifacts-123456789012"

3. 使用 SAM 部署應用程式

cd ../sam

# 建置
sam build

# 本地測試(可選)
sam local invoke ApiFunction -e events/event.json

# 部署
sam deploy \
  --stack-name terraform-sam-demo-dev-app \
  --s3-bucket $(cat terraform-outputs.json | jq -r '.sam_artifacts_bucket.value') \
  --capabilities CAPABILITY_IAM \
  --parameter-overrides \
    Environment=dev \
    VpcId=$(cat terraform-outputs.json | jq -r '.vpc_id.value') \
    SubnetIds=$(cat terraform-outputs.json | jq -r '.private_subnet_ids.value | join(",")') \
    # ...其他參數

執行結果:

Successfully created/updated stack - terraform-sam-demo-dev-app

Outputs:
Key                 ApiEndpoint
Description         API Gateway 端點 URL
Value               https://abc123def.execute-api.ap-northeast-1.amazonaws.com/dev

4. 使用部署腳本(推薦)

# 一次性部署
./scripts/deploy.sh dev

# 僅部屬 Terraform
./scripts/deploy.sh dev --tf-only

# 僅部屬 SAM
./scripts/deploy.sh dev --sam-only

運作確認

測試 API 端點

# 健康檢查
curl https://abc123def.execute-api.ap-northeast-1.amazonaws.com/dev/health

# 獲取項目列表
curl https://abc123def.execute-api.ap-northeast-1.amazonaws.com/dev/items

# 創建項目
curl -X POST https://abc123def.execute-api.ap-northeast-1.amazonaws.com/dev/items \
  -H "Content-Type: application/json" \
  -d '{"name": "Test Item"}'

響應範例:

{
  "message": "Item created",
  "item": {
    "PK": "ITEM#123e4567-e89b-12d3-a456-426614174000",
    "SK": "METADATA",
    "EntityType": "Item",
    "ItemId": "123e4567-e89b-12d3-a456-426614174000",
    "Name": "Test Item",
    "CreatedAt": 1704067200
  }
}

驗證 CloudWatch 日誌

# 實時查看日誌
aws logs tail /aws/lambda/terraform-sam-demo-dev-api --follow

# 僅過濾錯誤日誌
aws logs tail /aws/lambda/terraform-sam-demo-dev-api --filter-pattern "ERROR"

檢查 CloudWatch 指標

# Lambda 執行次數
aws cloudwatch get-metric-statistics \
  --namespace AWS/Lambda \
  --metric-name Invocations \
  --dimensions Name=FunctionName,Value=terraform-sam-demo-dev-api \
  --start-time 2024-01-01T00:00:00Z \
  --end-time 2024-01-01T23:59:59Z \
  --period 3600 \
  --statistics Sum

環境管理

開發環境(dev)

# terraform/environments/dev.tfvars
environment = "dev"

# 降低成本配置
enable_nat_gateway = true
single_nat_gateway = true  # 單一 NAT Gateway

dynamodb_billing_mode = "PAY_PER_REQUEST"
log_retention_days = 7
enable_lambda_insights = false

本番環境(prod)

# terraform/environments/prod.tfvars
environment = "prod"

# 高可用性配置
enable_nat_gateway = true
single_nat_gateway = false  # 每個 AZ 配置 NAT Gateway

dynamodb_billing_mode = "PAY_PER_REQUEST"
enable_dynamodb_point_in_time_recovery = true

log_retention_days = 90
enable_lambda_insights = true

切換環境

# dev 環境
./scripts/deploy.sh dev

# staging 環境
./scripts/deploy.sh staging

# prod 環境
./scripts/deploy.sh prod

CI/CD: GitHub Actions

工作流程設定(.github/workflows/deploy.yml)


name: Deploy to AWS

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main, develop]

jobs:
  validate:
    runs-on

---

原文出處:https://qiita.com/keitah/items/d41b0888cf7b8b01616d

精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝26   💬5   ❤️7
800
🥈
我愛JS
📝2   💬7   ❤️3
118
🥉
酷豪
1
#5
1
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次
🔧 阿川の電商水電行
Shopify 顧問、維護與客製化
💡
小任務 / 單次支援方案
單次處理 Shopify 修正/微調
⭐️
維護方案
每月 Shopify 技術支援 + 小修改 + 諮詢
🚀
專案建置
Shopify 功能導入、培訓 + 分階段交付