AWS Lambda 函式

Vibe Prompt

「幫我用 Python 寫一個 AWS Lambda 函式:接收 API Gateway 的 POST 請求,解析 JSON body,存入 DynamoDB,回傳 201 Created。」

Lambda 函式

import json
import boto3
from datetime import datetime

dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('Visitors')

def lambda_handler(event, context):
    body = json.loads(event['body'])
    
    item = {
        'id': str(int(datetime.now().timestamp())),
        'name': body.get('name', 'anonymous'),
        'message': body.get('message', ''),
        'created_at': datetime.now().isoformat()
    }
    
    table.put_item(Item=item)
    
    return {
        'statusCode': 201,
        'headers': {'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*'},
        'body': json.dumps({'id': item['id'], 'message': 'Created successfully'})
    }

CDK 部署

import * as lambda_ from 'aws-cdk-lib/aws-lambda';
import * as apigw from 'aws-cdk-lib/aws-apigateway';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';

const table = new dynamodb.Table(this, 'Visitors', {
  partitionKey: { name: 'id', type: dynamodb.AttributeType.STRING },
  billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
});

const fn = new lambda_.Function(this, 'VisitorFunc', {
  runtime: lambda_.Runtime.PYTHON_3_11,
  handler: 'index.lambda_handler',
  code: lambda_.Code.fromAsset('lambda'),
  environment: { TABLE_NAME: table.tableName },
});

table.grantWriteData(fn);

new apigw.LambdaRestApi(this, 'VisitorApi', { handler: fn });

關鍵要點

| 概念 | 說明 | |------|------| | Lambda | 事件驅動的無伺服器函式,按使用量計費 | | 觸發器 | API Gateway、S3、DynamoDB Streams、SQS、EventBridge | | Timeout | 最大 15 分鐘(同步呼叫) | | 記憶體 | 128 MB - 10,240 MB(CPU 與記憶體成正比) | | 冷啟動 | 閒置後首次調用需載入執行環境(延遲 100-1000ms) | | 並發 | 每個帳戶 1000 並發執行(可提高配額) |

冷啟動優化

# 1. 減少部署包大小
# 2. 使用 Provisioned Concurrency(預先啟動容器)
# 3. 使用 SnapStart (Java/Python) 快照加速
# 4. 避免大型依賴套件(如整包 Pandas、TensorFlow)
# 5. 使用 Lambda Layer 分離程式碼與依賴

Lambda 最佳實踐

  • ✅ 將設定寫入環境變數,不要寫死在程式碼
  • ✅ 使用 Lambda Layer 管理依賴套件
  • ✅ 資料庫連線寫在 Handler 外部(全域變數),重複使用
  • ✅ 日誌使用結構化 JSON 格式,方便 Loki/CloudWatch 解析
  • ✅ 設定 DLQ(Dead Letter Queue)處理失敗的事件

實戰經驗:Lambda 常見陷阱與解法

| 陷阱 | 症狀 | 解法 | |:----|:----|:----| | Handler 外層初始化過重 | 每次冷啟動都超時 | 只 import 需要的套件,懶載入(Lazy Import) | | 環境變數硬編碼 | 測試/正式環境切換困難 | 一律使用 os.environ + Lambda Console 設定 | | 日誌印太多 | CloudWatch 費用暴增 | 使用結構化 JSON 日誌 + Debug Level 動態控制 | | VPC 內 Lambda 沒設 NAT | 無法連線外部 API | 使用 VPC Endpoint 或 NAT Gateway | | Timeout 設太短 | 資料量大時總是失敗 | 根據實際測試設定(預設 3 秒,建議先設 30 秒) |

# 實戰級別 Lambda Handler 範本
import json
import os
import logging

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

# Lazy Import:按需載入,加速冷啟動
def _get_dynamodb():
    import boto3
    return boto3.resource('dynamodb')

def lambda_handler(event, context):
    logger.info(json.dumps({'event': event, 'remaining_ms': context.get_remaining_time_in_millis()}))
    
    try:
        table = _get_dynamodb().Table(os.environ['TABLE_NAME'])
        body = json.loads(event.get('body', '{}'))
        
        # 主要邏輯
        result = table.put_item(Item=body)
        
        return {
            'statusCode': 201,
            'body': json.dumps({'success': True, 'id': body.get('id')})
        }
    except Exception as e:
        logger.exception('Handler failed')
        return {'statusCode': 500, 'body': json.dumps({'error': str(e)})}

為什麼要學 Lambda?

AWS Lambda 是 Serverless 應用的核心積木。你不需要管理任何伺服器,只要寫程式、上傳、設定觸發器,剩下的擴展、可用性、安全修補都由 AWS 處理。對於小型團隊或獨立開發者來說,這代表你可以在一個週末內建立一個能撐住百萬使用者的 API。

如何學好 Lambda?

重點不是語法——Python/Node.js 寫 Lambda 跟寫一般函式幾乎一樣。關鍵在於:

  1. 理解執行模型:冷啟動、並發限制、Timeout 如何影響你的架構設計
  2. 學會除錯:CloudWatch Logs、X-Ray 分散式追蹤、結構化日誌
  3. 掌握 IAM 權限:Least Privilege 原則——給 Lambda 剛剛好的權限
  4. 部署自動化:用 CDK/Terraform 管理基礎設施,不要手動上傳 ZIP

接下來學什麼

你已經學會了 Lambda 的基礎——寫程式、設觸發器、部署上線。下一章 DynamoDB 與資料模型 將教你在 Serverless 世界裡如何設計資料庫。這和傳統的關聯式資料庫思維完全不同——你會學到為什麼「一個表格放所有資料」反而是最佳做法,以及如何設計 Partition Key 讓查詢永遠在毫秒級完成。

會員專屬免費教學

本章節為註冊會員專屬的免費開放內容!請先登入或註冊會員,即可立即解鎖閱讀。

立即登入 / 註冊