透過 AWS Lambda、API Gateway 和 DynamoDB,可以建構高效能且可擴充套件的無伺服器架構,實作貓圖上傳 API。API Gateway 作為前端入口,接收 HTTP 請求並觸發 Lambda 函式執行,Lambda 函式則負責處理上傳邏輯,並將貓圖資料儲存至 DynamoDB。此架構不僅簡化了後端管理,也提升了應用程式的彈性與可靠度。文章將逐步說明如何使用 AWS SAM 定義和佈署此架構,並提供 Rust 程式碼範例,演示如何處理 HTTP 請求、與 DynamoDB 互動,以及進行測試與佈署。
6.5 完整架構解析
在前一節中,我們建立了一個簡單的Lambda函式,但它還無法直接處理HTTP請求。要實作這一點,我們需要在其前端佈署一個API Gateway REST API。完整的架構如圖6-5所示。
簡單REST API架構圖示
@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam rectangleBackgroundColor #F5F5F5
skinparam rectangleBorderColor #333333
skinparam arrowColor #333333
title 簡單REST API架構圖示
rectangle "HTTP請求" as node1
rectangle "觸發Lambda" as node2
rectangle "存取資料函式庫" as node3
rectangle "靜態檔案" as node4
rectangle "自訂網域名稱" as node5
node1 --> node2
node2 --> node3
node3 --> node4
node4 --> node5
@enduml此圖示展示了使用者端如何透過API Gateway與Lambda函式互動,並存取DynamoDB資料函式庫。同時,靜態檔案可以儲存在S3儲存桶中,並可選用CloudFront CDN進行內容分發。
架構解析
REST API透過API Gateway提供服務。API Gateway負責處理HTTP連線並為每個請求觸發對應的Lambda函式。如果需要服務兩個API(例如GET /cats和POST /cat),可以為每個API組態一個獨立的Lambda函式。資料函式庫方面,我們將使用AWS DynamoDB。DynamoDB是一種高效能的NoSQL資料函式庫,可以直接透過AWS SDK for Rust從Lambda函式中存取。
此外,我們還有一些前端檔案:HTML、CSS和JavaScript。這些檔案可以儲存在S3儲存桶中並對外提供服務。S3儲存桶是一種物件儲存服務,不僅可以儲存檔案,還可以像靜態網站伺服器一樣透過HTTP提供檔案服務。
API Gateway和S3靜態檔案託管所產生的URL是由AWS自動生成的,因此無法直接自訂。不過,我們可以透過Route53(一個受管理的DNS服務)新增CloudFront CDN並設定自訂網域名稱,從而完全掌控API和靜態檔案所使用的網域名稱。但這部分內容超出了本文的範圍,且與Rust無直接關聯,因此在此不作探討。有興趣的讀者可以參考AWS官方檔案進行設定。
6.6 使用AWS Serverless Application Model (AWS SAM)
透過Web控制檯組態所有這些資源並非易事。很難追蹤實際佈署在生產環境中的內容。如果不小心銷毀了整個堆積疊,也很難從頭開始重建。基礎設施即程式碼(Infrastructure-as-code,簡稱IaC)是一種解決這個問題的方法。你可以透過程式碼定義基礎設施和組態,然後使用你選擇的IaC工具按照你的程式碼組態一切。如果你對定義進行了任何更改,可以透過快速佈署來反映這些更改。因此,你可以像管理程式碼一樣對基礎設施進行版本控制,修復或重建整個堆積疊只需簡單的佈署即可完成。
在本章中,你將使用AWS Serverless Application Model(簡稱AWS SAM)來定義你的基礎設施。AWS SAM不僅管理基礎設施(底層使用AWS CloudFormation的擴充套件),還能協助管理應用的整個生命週期,從測試、封裝Lambda程式碼到日誌記錄等。
設定AWS SAM CLI憑證
SAM CLI需要設定AWS憑證,以便代表你管理AWS資源。你可以按照逐步指示進行設定:https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/prerequisites.html
簡而言之,你需要執行以下步驟:
- 建立一個具有程式化存取權的新IAM使用者,並附加AdministratorAccess策略。
- 複製新建立的使用者的存取金鑰和秘密存取金鑰。
- 安裝AWS CLI V2(如果尚未安裝)以組態憑證。
- 使用
aws configure命令設定存取金鑰和秘密存取金鑰。
設定指令範例
$ aws configure
AWS Access Key ID [None]: 你的存取金鑰
AWS Secret Access Key [None]: 你的秘密存取金鑰
Default region name [None]: 你預設的區網域名稱
Default output format [None]: 你預設的輸出格式
6.7 建立Catdex無伺服器專案
要建立一個新的專案,可以執行與之前相同的Cargo Lambda命令,但需指定新的專案名稱:catdex-serverless。這次你應該選擇建立一個HTTP函式,並指定將建立一個Amazon API Gateway REST API。
建立專案指令範例
$ cargo lambda new catdex-serverless
> Is this function an HTTP function? y
> Which service is this function receiving events from? Amazon Api Gateway REST Api
建立專案後的目錄結構
.
+-- template.yaml
+-- Cargo.toml
+-- src
| +-- main.rs
現在,你應該有一個包含範例HTTP Lambda函式處理器的專案。Cargo Lambda非常適合用於建立包含Lambda函式的基本專案,但它並不直接設定我們剛才描述的支援架構。
建立基礎設施的第一步是建立template.yaml檔案,即AWS SAM範本,這將是定義無伺服器架構各個組成部分的地方。AWS SAM範本是CloudFormation的擴充套件,CloudFormation是一種AWS服務,允許你透過將基礎設施視為程式碼來建模、提供和管理AWS及第三方資源。
template.yaml範例內容
# template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
catdex-serverless
Sample SAM Template for catdex-serverless
內容解密:
AWSTemplateFormatVersion: 指定範本的版本。Transform: 指定使用AWS SAM的轉換器。Description: 提供對範本的描述。
這個AWS SAM範本檔案使用AWS SAM規範定義了基礎設施。該範本是AWS CloudFormation範本的擴充套件,具有一些額外的元件,能夠在較低層級的CloudFormation元件上提供更高層級的抽象。
使用 AWS SAM 佈署 Serverless 應用程式
在現代雲端運算中,Serverless 架構因其靈活性、可擴充套件性和成本效益而越來越受到歡迎。AWS SAM(Serverless Application Model)是一種開源框架,簡化了在 AWS 上構建 Serverless 應用程式的過程。本篇文章將探討如何使用 AWS SAM 佈署一個簡單的 Serverless 應用程式,該程式使用 AWS Lambda、Amazon API Gateway 和 Amazon DynamoDB。
範本檔案結構
AWS SAM 使用 YAML 或 JSON 格式的範本檔案來定義 Serverless 應用程式的資源。在本例中,我們使用 template.yaml 檔案來定義我們的應用程式。
Globals:
Function:
Timeout: 3
Resources:
CatTable:
Type: AWS::Serverless::SimpleTable
Properties:
PrimaryKey:
Name: cat
Type: String
PostCatFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: target/lambda/catdex-serverless/
Handler: bootstrap
Runtime: provided.al2
Architectures: ["arm64"]
Events:
HelloWorld:
Type: Api
Properties:
Path: /cat
Method: post
Environment:
Variables:
TABLE_NAME: !Ref CatTable
Policies:
- DynamoDBWritePolicy:
TableName: !Ref CatTable
Outputs:
PostApi:
Description: "API Gateway endpoint URL for Prod stage for Post function"
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.${AWS::URLSuffix}/Prod/"
PostCatFunction:
Description: "Post Lambda Function ARN"
Value: !GetAtt PostCatFunction.Arn
CatTable:
Description: "DynamoDB table name"
Value: !GetAtt CatTable.Arn
內容解密:
Globals部分:定義了所有 Lambda 函式的預設超時時間為 3 秒。Resources部分:CatTable:定義了一個 Amazon DynamoDB 表,使用AWS::Serverless::SimpleTable型別,指定了主鍵cat為字串型別。PostCatFunction:定義了一個 AWS Lambda 函式,使用AWS::Serverless::Function型別。該函式的程式碼位於target/lambda/catdex-serverless/,使用自定義執行環境provided.al2,並且支援arm64架構。Events部分:定義了一個 API Gateway 事件,當收到 POST 請求到/cat路徑時觸發該 Lambda 函式。Environment部分:將CatTable的名稱作為環境變數TABLE_NAME傳遞給 Lambda 函式。Policies部分:授予 Lambda 函式寫入CatTable的許可權。
Outputs部分:定義了佈署完成後輸出的資訊,包括 API Gateway 的 URL、Lambda 函式的 ARN 和 DynamoDB 表的 ARN。
使用 AWS SAM CLI 佈署應用程式
- 初始化佈署:執行
sam deploy --guided命令,按照提示填寫相關資訊,完成初始佈署組態。 - 生成組態檔案:完成初始佈署後,會生成一個
samconfig.toml檔案,用於儲存佈署組態,方便後續直接執行sam deploy命令進行佈署更新。
samconfig.toml 示例
version = 0.1
[default]
[default.deploy]
[default.deploy.parameters]
stack_name = "sam-app"
s3_bucket = "<your auto-generated S3 bucket name>"
s3_prefix = "sam-app"
region = "us-east-2"
capabilities = "CAPABILITY_IAM"
image_repositories = []
測試佈署的 Lambda 函式
使用 AWS CLI 測試佈署的 Lambda 函式,首先需要取得 Lambda 函式的 ARN,然後執行以下命令:
aws lambda invoke \
--cli-binary-format raw-in-base64-out \
--payload '{"httpMethod": "POST"}' \
--function-name arn:aws:lambda:us-east-2:003164948199:function:sam-app-PostCatFunction-SpBjt2nLOlpa \
output.json
內容解密:
aws lambda invoke命令:用於呼叫指定的 Lambda 函式。--cli-binary-format raw-in-base64-out引數:指定 CLI 的二進位制格式處理方式。--payload '{"httpMethod": "POST"}'引數:指定傳遞給 Lambda 函式的事件資料,模擬一個 HTTP POST 請求。--function-name引數:指定要呼叫的 Lambda 函式的 ARN。
使用AWS Lambda和DynamoDB建立無伺服器貓圖上傳API
概述
本章節將介紹如何使用AWS Lambda和DynamoDB建立一個無伺服器的貓圖上傳API。首先,我們會設定基本的基礎設施組態,並測試佈署是否成功。接著,我們將自定義Lambda函式,以允許上傳貓圖到DynamoDB。
基礎設施組態與測試
首先,CLI會呼叫Lambda函式,並傳送一個空的HTTP POST請求。由於我們的Hello World程式碼並未對請求進行任何處理,因此應該會成功並傳回一個回應。回應會被儲存到output.json檔案中。如果一切設定正確,您應該會看到類別似以下的回應:
{
"statusCode": 200,
"headers": {
"content-type": "text/html"
},
"multiValueHeaders": {
"content-type": ["text/html"]
},
"body": "Hello AWS Lambda HTTP request",
"isBase64Encoded": false
}
此時,您已經完成了基本的基礎設施組態,並測試了佈署是否成功。現在可以探討Lambda函式的具體細節。
建立上傳API
接下來,我們將自定義Lambda函式,以允許上傳貓圖到DynamoDB。首先,將src/main.rs移到src/bin/lambda/post-cat.rs,以便支援多個Lambda函式。然後,在src資料夾中建立一個lib.rs檔案。
更新專案結構
更新後的專案結構如下:
.
+-- template.yaml
+-- Cargo.toml
+-- src
|-- bin
| |-- lambda
| |-- post-cat.rs
|-- lib.rs
同時,需要更新Cargo.toml以新增二進位制檔案到專案中:
[[bin]]
name = "post-cat"
path = "src/bin/lambda/post-cat.rs"
新增依賴項
執行以下命令以新增AWS SDK for DynamoDB和serde crate:
cargo add aws_sdk_dynamodb serde
確保使用的版本是0.24.0。同時,也需要新增aws-config crate:
cargo add aws-config
確保版本是0.54.1。
更新template.yaml
將template.yaml中的PostCatFunction的CodeUri從target/lambda/catdex-serverless更新為target/lambda/post-cat。
Lambda函式實作
在post-cat.rs中,我們使用以下程式碼:
use aws_sdk_dynamodb as dynamodb;
use aws_sdk_dynamodb::model::AttributeValue;
use lambda_http::{http::StatusCode, run, service_fn, Body, Error, Request, RequestExt, Response};
use serde::Deserialize;
#[derive(Deserialize)]
struct RequestBody {
name: String,
}
async fn function_handler(
request: Request,
client: &dynamodb::Client,
table_name: &str,
) -> Result<Response<Body>, Error> {
#### 內容解密:
此函式處理Lambda函式的事件,包含以下步驟:
1. 從請求中提取貓的名字。
2. 建立一個PutItemRequest,將新的貓新增到資料函式庫中。
3. 呼叫client.put_item()將專案新增到DynamoDB。
let body: RequestBody = match request.payload() {
Ok(Some(body)) => body,
_ => {
return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST)
.body("Invalid payload".into())
.expect("Failed to render response"))
}
};
let dynamo_request = client
.put_item()
.table_name(table_name)
.item("cat", AttributeValue::S(body.name.clone()));
dynamo_request.send().await?;
let resp = Response::builder()
.status(StatusCode::OK)
.header("content-type", "text/html")
.body(format!("Added cat {}", body.name).into())
.map_err(Box::new)?;
Ok(resp)
}
#[tokio::main]
async fn main() -> Result<(), Error> {
#### 內容解密:
此函式是Lambda函式的入口點,包含以下步驟:
1. 初始化tracing_subscriber。
2. 從環境變數中載入AWS組態。
3. 建立一個DynamoDB客戶端。
4. 從環境變數中取得TABLE_NAME。
tracing_subscriber::fmt()
.with_max_level(tracing::Level::INFO)
.with_target(false)
.without_time()
.init();
let config = aws_config::load_from_env().await;
let client = dynamodb::Client::new(&config);
let table_name = std::env::var("TABLE_NAME")?.to_string();
run(service_fn(|request| {
function_handler(request, &client, &table_name)
}))
.await
}
程式碼說明
此Lambda函式處理HTTP請求,並將貓的名字新增到DynamoDB中。首先,從請求中提取貓的名字,然後建立一個PutItemRequest,將新的貓新增到資料函式庫中。最後,傳回一個成功的HTTP回應。