AWS Lambda + CloudWatch Events 實現每月 EC2 快照備份與驗證

🌏 Read the English version


在雲端架構中,備份是確保資料安全的關鍵環節。透過 AWS 提供的 EC2 快照功能,我們可以輕鬆地為 EC2 實例建立資料備份。但手動建立快照可能費時費力,因此使用 AWS Lambda 搭配 CloudWatch Events 自動化備份成為一個理想的解決方案。

本文將一步步介紹如何:

  • 建立 Lambda 函數來執行快照操作
  • 配置 CloudWatch Events 排程每月執行備份
  • 驗證快照建立是否成功

為什麼需要自動化 EC2 備份?

使用場景:

  • 災難復原:當 EC2 實例發生故障時,快速從快照還原
  • 合規要求:許多產業規範要求定期備份資料
  • 資料遷移:將 EC2 實例遷移到不同區域或帳戶
  • 版本管理:保留不同時間點的系統狀態

為什麼選擇 Lambda + CloudWatch Events?

  • 無伺服器架構:無需管理執行備份的伺服器
  • 成本效益:只在執行時計費,閒置時無成本
  • 高可靠性:AWS 託管服務保證高可用性
  • 靈活排程:可設定每月、每週、每日等不同頻率

架構流程

系統元件:

  • Lambda 函數:執行快照操作的核心邏輯
  • CloudWatch Events (EventBridge):定義排程(如每月執行一次)
  • IAM 角色:提供 Lambda 必要的 EC2 權限
  • CloudWatch Logs:記錄執行日誌與錯誤訊息
  • EC2 Snapshots:儲存備份資料

工作流程:

  1. CloudWatch Events 根據排程觸發 Lambda 函數
  2. Lambda 函數透過 IAM 角色取得 EC2 權限
  3. Lambda 讀取指定 EC2 實例的所有磁碟區(Volumes)
  4. 為每個磁碟區建立快照(Snapshot)
  5. 驗證快照狀態直到完成
  6. 記錄執行結果到 CloudWatch Logs

步驟 1:建立 Lambda 函數

  1. 登入 AWS Lambda 控制台
  2. 點擊 Create function
  3. 配置以下參數:
    • Function nameMonthlyEC2Backup
    • RuntimePython 3.12(或最新版本)
    • Permissions:選擇 Create a new role with basic Lambda permissions
  4. 點擊 Create function

步驟 2:編寫 Lambda 程式碼

Function code 區域,新增以下程式碼:

import boto3
import datetime
import time

def lambda_handler(event, context):
    ec2 = boto3.client('ec2')

    # 設定需要備份的 EC2 實例 ID
    instances = ['<INSTANCE_ID>']  # 替換為你的 EC2 實例 ID

    for instance_id in instances:
        # 建立快照描述
        description = f"Backup of {instance_id} - {datetime.datetime.now().strftime('%Y-%m-%d')}"

        # 獲取實例相關的所有卷
        volumes = ec2.describe_volumes(Filters=[
            {'Name': 'attachment.instance-id', 'Values': [instance_id]}
        ])

        # 為每個卷創建快照並檢查狀態
        for volume in volumes['Volumes']:
            volume_id = volume['VolumeId']

            print(f"Creating snapshot for volume: {volume_id}")

            # 建立快照
            response = ec2.create_snapshot(
                VolumeId=volume_id,
                Description=description
            )

            snapshot_id = response['SnapshotId']
            print(f"Snapshot {snapshot_id} created for volume {volume_id}")

            # 驗證快照狀態
            print(f"Verifying snapshot {snapshot_id} status...")
            while True:
                snapshot_status = ec2.describe_snapshots(
                    SnapshotIds=[snapshot_id]
                )['Snapshots'][0]['State']

                if snapshot_status == 'completed':
                    print(f"Snapshot {snapshot_id} for volume {volume_id} completed successfully!")
                    break
                elif snapshot_status == 'error':
                    print(f"Snapshot {snapshot_id} for volume {volume_id} failed!")
                    break
                else:
                    print(f"Snapshot {snapshot_id} is in progress...")
                    time.sleep(5)

    return {
        'statusCode': 200,
        'body': 'EC2 backup completed successfully'
    }

程式碼說明:

  • boto3.client('ec2'):建立 EC2 客戶端
  • describe_volumes():取得 EC2 實例的所有磁碟區
  • create_snapshot():建立快照
  • describe_snapshots():檢查快照狀態
  • time.sleep(5):每 5 秒檢查一次狀態

重要:<INSTANCE_ID> 替換為實際的實例 ID(如 i-0abcd1234efgh5678),然後點擊 Deploy

步驟 3:配置 IAM 權限

  1. 進入 Lambda 函數的 ConfigurationPermissions
  2. 點擊執行角色(Execution role)連結
  3. 在 IAM 控制台中,點擊 Add permissionsCreate inline policy
  4. 使用以下 JSON 策略:
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ec2:DescribeVolumes",
                "ec2:CreateSnapshot",
                "ec2:DescribeSnapshots",
                "ec2:CreateTags"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "arn:aws:logs:*:*:*"
        }
    ]
}

權限說明:

  • ec2:DescribeVolumes:讀取磁碟區資訊
  • ec2:CreateSnapshot:建立快照
  • ec2:DescribeSnapshots:檢查快照狀態
  • ec2:CreateTags:為快照新增標籤(選用)
  • logs:*:寫入 CloudWatch Logs

步驟 4:建立 CloudWatch Events 排程

  1. 進入 Amazon EventBridge 控制台Rules
  2. 點擊 Create rule
  3. 配置規則:
    • NameMonthlyEC2BackupSchedule
    • Rule typeSchedule
  4. 設定排程模式:
    • 選擇 Cron-based schedule
    • 輸入排程表達式:cron(0 0 1 * ? *)
  5. 選擇目標:
    • TargetLambda function
    • FunctionMonthlyEC2Backup
  6. 點擊 Create rule

Cron 表達式說明:

cron(0 0 1 * ? *)
     │ │ │ │ │ │
     │ │ │ │ │ └─ 年份(* 表示任何年份)
     │ │ │ │ └─── 星期(? 表示不指定)
     │ │ │ └───── 月份(* 表示每月)
     │ │ └─────── 日期(1 表示每月 1 號)
     │ └───────── 小時(0 表示凌晨 0 點)
     └─────────── 分鐘(0 表示整點)

其他常用排程範例:

  • 每週日凌晨 2 點:cron(0 2 ? * SUN *)
  • 每日凌晨 3 點:cron(0 3 * * ? *)
  • 每月 15 號凌晨 1 點:cron(0 1 15 * ? *)

步驟 5:測試與驗證

測試 Lambda 函數

  1. 在 Lambda 控制台中,點擊 Test
  2. 建立測試事件(可使用空的 JSON:{}
  3. 執行測試並檢查輸出

檢查 CloudWatch Logs

  1. 進入 CloudWatch 控制台Log groups
  2. 找到 /aws/lambda/MonthlyEC2Backup
  3. 檢查執行日誌,確認是否有錯誤訊息

驗證快照

  1. 進入 EC2 控制台Snapshots
  2. 確認快照狀態為 Completed
  3. 檢查快照描述與建立時間

常見問題與解決方案

問題 1:Lambda 執行逾時

原因:

  • 預設 Lambda 執行時間限制為 3 秒
  • 建立大型快照需要較長時間

解決方案:

  • 在 Lambda ConfigurationGeneral configuration 中調整 Timeout 為 5-10 分鐘
  • 或移除快照狀態驗證的迴圈,改為非同步處理

問題 2:權限不足錯誤

錯誤訊息:

An error occurred (UnauthorizedOperation) when calling the CreateSnapshot operation

解決方案:

  • 確認 IAM 角色包含 ec2:CreateSnapshot 權限
  • 檢查是否有 SCP(Service Control Policy)限制

問題 3:如何備份多個 EC2 實例?

解決方案:

instances 擴展為列表:

instances = [
    'i-0abcd1234efgh5678',
    'i-0ijkl5678mnop1234',
    'i-0qrst9012uvwx3456'
]

問題 4:如何自動清理舊快照?

解決方案:

在 Lambda 函數中新增清理邏輯:

# 刪除 30 天前的快照
retention_days = 30
cutoff_date = datetime.datetime.now() - datetime.timedelta(days=retention_days)

snapshots = ec2.describe_snapshots(OwnerIds=['self'])['Snapshots']
for snapshot in snapshots:
    if snapshot['StartTime'].replace(tzinfo=None) < cutoff_date:
        print(f"Deleting old snapshot: {snapshot['SnapshotId']}")
        ec2.delete_snapshot(SnapshotId=snapshot['SnapshotId'])

成本考量

費用組成:

  • Lambda 執行:免費額度每月 100 萬次請求 + 40 萬 GB-秒運算時間
  • EBS 快照儲存:每 GB 每月約 $0.05 美元(依區域而異)
  • CloudWatch Logs:每 GB 約 $0.50 美元

成本估算範例:

假設備份 1 個 100GB 的 EC2 實例,每月執行一次:

  • Lambda 執行費用:幾乎為 0(在免費額度內)
  • 快照儲存費用:100GB × $0.05 = $5.00/月
  • CloudWatch Logs:可忽略(< $0.10)

成本優化建議:

  • 定期清理舊快照(如保留最近 3 個月)
  • 使用 EBS 快照生命週期管理(Data Lifecycle Manager)
  • 評估是否需要跨區域複製快照(會增加傳輸費用)

進階優化

1. 新增 SNS 通知

在快照完成後發送通知:

sns = boto3.client('sns')
sns.publish(
    TopicArn='arn:aws:sns:us-east-1:123456789012:EC2BackupNotification',
    Subject='EC2 Backup Completed',
    Message=f'Snapshot {snapshot_id} created successfully'
)

2. 為快照新增標籤

ec2.create_tags(
    Resources=[snapshot_id],
    Tags=[
        {'Key': 'Name', 'Value': f'Backup-{instance_id}'},
        {'Key': 'CreatedBy', 'Value': 'Lambda'},
        {'Key': 'BackupDate', 'Value': datetime.datetime.now().strftime('%Y-%m-%d')}
    ]
)

3. 使用環境變數

將實例 ID 設定為環境變數,避免硬編碼:

import os
instances = os.environ['INSTANCE_IDS'].split(',')

結論

透過本文的實作,您已經學會:

  • 建立自動化 EC2 備份系統:使用 Lambda + CloudWatch Events
  • 配置適當的 IAM 權限:確保安全性與最小權限原則
  • 驗證快照狀態:確保備份成功完成
  • 處理常見問題:解決權限、逾時等問題
  • 優化成本:透過自動清理舊快照降低費用

後續建議:

  • 實作跨區域快照複製以提升災難復原能力
  • 整合 AWS Backup 服務進行集中管理
  • 設定 CloudWatch 告警監控備份失敗
  • 建立快照還原測試流程驗證備份可用性

Related Articles

Leave a Comment