一个完整的 AWS 无服务器架构教程

下面是一个完整的 AWS 无服务器架构教程,用于实现一个 OTA(Over-The-Air)固件更新服务。该服务提供两个 HTTP 接口:

  • GET /check:设备请求当前是否有可用更新(返回预签名 S3 URL)
  • POST /report:设备上报更新结果(成功/失败等)

组件说明:

  • API Gateway:暴露 REST API
  • Lambda:处理业务逻辑(鉴权、查询 DynamoDB、生成 S3 预签名 URL、写入回执)
  • S3 :存储固件文件(如 firmware-v1.2.3.bin
  • DynamoDB:存储设备信息、当前版本、目标版本、任务状态等

一、准备工作

1.1 AWS 账户与权限

确保你有权限创建以下资源:

  • API Gateway
  • Lambda
  • S3 Bucket
  • DynamoDB Table
  • IAM Roles/Policies

建议使用 AWS CLI + SAM(Serverless Application Model)Terraform/CDK ,但本教程以 控制台 + 手动配置 为主,便于理解。


二、创建 S3 存储桶(用于固件)

  1. 进入 S3 控制台
  2. 创建新存储桶,例如:my-ota-firmware-bucket-12345(全局唯一)
  3. 不要启用"阻止所有公共访问"以外的公开访问(固件通过预签名 URL 访问,无需公开)
  4. 记下存储桶名称

✅ 固件示例路径:s3://my-ota-firmware-bucket-12345/firmware/device-type-a/v1.2.3.bin


三、创建 DynamoDB 表

表名:OtaDevices

主键设计:

  • 分区键(Partition Key)deviceId(字符串,如 device-001

其他属性(可选,根据业务扩展):

字段名 类型 说明
currentVersion string 当前固件版本
targetVersion string 目标固件版本(若为空则无更新)
deviceType string 设备类型(用于选择固件)
lastCheck string 最后一次检查时间(ISO8601)
updateStatus string pending / downloaded / applied / failed
firmwareKey string S3 中的固件对象键(如 firmware/device-type-a/v1.2.3.bin

创建步骤:

  1. 进入 DynamoDB 控制台
  2. 点击"创建表"
  3. 表名:OtaDevices
  4. 分区键:deviceId(类型:字符串)
  5. 其他保持默认,点击"创建表"

📌 示例数据:

json 复制代码
{
  "deviceId": "device-001",
  "currentVersion": "v1.1.0",
  "targetVersion": "v1.2.3",
  "deviceType": "sensor-x1",
  "firmwareKey": "firmware/sensor-x1/v1.2.3.bin",
  "updateStatus": "pending"
}

四、创建 Lambda 函数

我们将创建一个 Lambda 函数,处理 /check/report 逻辑。

4.1 创建 Lambda

  1. 进入 Lambda 控制台
  2. 点击"创建函数" → "从头开始创作"
  3. 名称:OtaHandler
  4. 运行时:Python 3.12(或 Node.js 18.x,本教程用 Python)
  5. 权限:创建新角色(稍后手动添加权限)

4.2 编写代码(Python)

python 复制代码
import json
import boto3
import os
from datetime import datetime, timedelta
from botocore.exceptions import ClientError

# 初始化客户端
dynamodb = boto3.resource('dynamodb')
s3_client = boto3.client('s3')
table = dynamodb.Table(os.environ['DYNAMODB_TABLE'])
BUCKET_NAME = os.environ['S3_BUCKET']

def lambda_handler(event, context):
    print("Event:", json.dumps(event))
    
    # 获取 HTTP 方法和路径
    http_method = event['httpMethod']
    resource_path = event['resource']
    
    # 简单鉴权:检查 header 中的 token(实际应使用更安全机制,如 JWT、API Key)
    auth_token = event['headers'].get('Authorization') or event['headers'].get('authorization')
    if not auth_token or not auth_token.startswith("Bearer "):
        return {
            'statusCode': 401,
            'body': json.dumps({'error': 'Missing or invalid Authorization header'})
        }
    
    device_id = auth_token.split(" ")[1]  # 假设 token 是 "Bearer <deviceId>"
    
    try:
        if resource_path == '/check' and http_method == 'GET':
            return handle_check(device_id)
        elif resource_path == '/report' and http_method == 'POST':
            body = json.loads(event['body']) if event.get('body') else {}
            return handle_report(device_id, body)
        else:
            return {'statusCode': 404, 'body': json.dumps({'error': 'Not found'})}
    except Exception as e:
        print("Error:", str(e))
        return {'statusCode': 500, 'body': json.dumps({'error': 'Internal error'})}

def handle_check(device_id):
    # 1. 查询设备信息
    response = table.get_item(Key={'deviceId': device_id})
    if 'Item' not in response:
        return {'statusCode': 404, 'body': json.dumps({'error': 'Device not found'})}
    
    item = response['Item']
    
    # 2. 检查是否有待更新
    if not item.get('targetVersion') or item.get('currentVersion') == item.get('targetVersion'):
        return {
            'statusCode': 200,
            'body': json.dumps({'updateAvailable': False})
        }
    
    # 3. 生成预签名 URL(有效期 10 分钟)
    firmware_key = item['firmwareKey']
    try:
        presigned_url = s3_client.generate_presigned_url(
            'get_object',
            Params={'Bucket': BUCKET_NAME, 'Key': firmware_key},
            ExpiresIn=600  # 10 分钟
        )
    except ClientError as e:
        return {'statusCode': 500, 'body': json.dumps({'error': 'Failed to generate URL'})}
    
    # 4. 更新 lastCheck 时间
    table.update_item(
        Key={'deviceId': device_id},
        UpdateExpression="SET lastCheck = :t",
        ExpressionAttributeValues={':t': datetime.utcnow().isoformat()}
    )
    
    return {
        'statusCode': 200,
        'body': json.dumps({
            'updateAvailable': True,
            'version': item['targetVersion'],
            'downloadUrl': presigned_url,
            'firmwareKey': firmware_key
        })
    }

def handle_report(device_id, report_data):
    # 报告内容示例:{"status": "success", "message": "Applied v1.2.3"}
    status = report_data.get('status', 'unknown')
    message = report_data.get('message', '')
    
    # 更新设备状态
    table.update_item(
        Key={'deviceId': device_id},
        UpdateExpression="SET updateStatus = :s, currentVersion = :v, lastReport = :t",
        ExpressionAttributeValues={
            ':s': status,
            ':v': report_data.get('version', 'unknown'),
            ':t': datetime.utcnow().isoformat()
        }
    )
    
    return {
        'statusCode': 200,
        'body': json.dumps({'message': 'Report received'})
    }

4.3 配置环境变量

在 Lambda 函数的"配置" → "环境变量"中添加:

DYNAMODB_TABLE OtaDevices
S3_BUCKET my-ota-firmware-bucket-12345

4.4 添加 IAM 权限

编辑 Lambda 的执行角色(在"配置" → "权限"中点击角色名),附加以下策略:

json 复制代码
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "dynamodb:GetItem",
        "dynamodb:UpdateItem"
      ],
      "Resource": "arn:aws:dynamodb:YOUR_REGION:YOUR_ACCOUNT:table/OtaDevices"
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject"
      ],
      "Resource": "arn:aws:s3:::my-ota-firmware-bucket-12345/*"
    }
  ]
}

替换 YOUR_REGIONYOUR_ACCOUNT(或直接使用控制台 ARN)


五、配置 API Gateway

5.1 创建 REST API

  1. 进入 API Gateway 控制台
  2. 点击"创建 API" → "REST API" → "构建"
  3. 选择"新建 API",名称:OtaApi,端点类型:Regional

5.2 创建资源和方法

创建 /check 资源
  • 在"资源"中点击"操作" → "创建资源"
    • 资源名称:check
    • 资源路径:check
  • 选中 /check,点击"操作" → "创建方法" → 选择 GET → 勾选"使用 Lambda 代理集成"
  • Lambda 函数:选择 OtaHandler
  • 点击"保存",确认权限
创建 /report 资源
  • 同样创建资源 report
  • 添加 POST 方法,同样指向 OtaHandler,启用 Lambda 代理集成

5.3 部署 API

  1. 点击"操作" → "部署 API"

  2. 新建阶段(如 prod

  3. 记下调用 URL,例如:

    复制代码
    https://abc123.execute-api.us-east-1.amazonaws.com/prod

六、测试流程

6.1 上传固件到 S3

bash 复制代码
aws s3 cp firmware/sensor-x1/v1.2.3.bin s3://my-ota-firmware-bucket-12345/firmware/sensor-x1/v1.2.3.bin

6.2 在 DynamoDB 插入测试设备

json 复制代码
{
  "deviceId": "device-001",
  "currentVersion": "v1.1.0",
  "targetVersion": "v1.2.3",
  "deviceType": "sensor-x1",
  "firmwareKey": "firmware/sensor-x1/v1.2.3.bin",
  "updateStatus": "pending"
}

6.3 调用 /check

bash 复制代码
curl -H "Authorization: Bearer device-001" \
  https://abc123.execute-api.us-east-1.amazonaws.com/prod/check

✅ 响应示例:

json 复制代码
{
  "updateAvailable": true,
  "version": "v1.2.3",
  "downloadUrl": "https://my-ota-firmware-bucket-12345.s3.amazonaws.com/...?X-Amz-Signature=..."
}

6.4 调用 /report

bash 复制代码
curl -X POST \
  -H "Authorization: Bearer device-001" \
  -H "Content-Type: application/json" \
  -d '{"status": "success", "version": "v1.2.3"}' \
  https://abc123.execute-api.us-east-1.amazonaws.com/prod/report

✅ 响应:{"message": "Report received"}


七、安全增强建议(生产环境)

  1. 鉴权升级

    • 使用 API Gateway 的 自定义授权方(Custom Authorizer)Cognito
    • 或使用 API Key + Usage Plan
  2. 输入验证

    • 在 Lambda 中校验 deviceId 格式
    • 防止路径遍历(如 firmwareKey 不应包含 ../
  3. 日志与监控

    • 启用 CloudWatch Logs
    • 设置告警(如大量 401 请求)
  4. S3 安全

    • 确保 S3 存储桶策略禁止公开读取
    • 启用服务器端加密(SSE-S3)
  5. Lambda 超时与内存

    • 设置合理超时(如 10 秒)
    • 内存 128MB 足够

八、清理(避免产生费用)

  • 删除 API Gateway
  • 删除 Lambda 函数
  • 清空并删除 S3 存储桶
  • 删除 DynamoDB 表

✅ 至此,你已成功搭建一个基于 AWS 的 OTA 固件分发服务!

相关推荐
文军的烹饪实验室2 小时前
CPU 架构(CPU Architecture)
架构·cpu
想不明白的过度思考者3 小时前
Rust——Tokio的多线程调度器架构:深入异步运行时的核心机制
开发语言·架构·rust·多线程
Elieal4 小时前
深入 Maven:从仓库配置到私服架构的进阶实践
java·架构·maven
喻师傅4 小时前
数据仓库——总线架构详解
数据仓库·架构
zhilin_tang4 小时前
构建一个完整的Linux物联网设备共享内存程序测试框架
linux·c语言·架构
数据智能老司机4 小时前
Apache Hudi权威指南——通过index提高效率
大数据·架构·数据分析
心灵宝贝5 小时前
申威ky10架构安装MongoDB 4.0.1(rpm包:mongodb-4.0.1-8.ky10.sw_64.rpm)详细步骤
数据库·mongodb·架构
小小王app小程序开发5 小时前
旧衣回收小程序的技术架构与商业落地:开发者视角的全链路解析
小程序·架构