在無伺服器應用程式開發中,建構本地開發環境至關重要。LocalStack 提供了在本地模擬 AWS 服務的解決方案,讓開發者得以離線測試程式碼,減少佈署成本和風險。本文將引導您設定 LocalStack,並整合 DynamoDB 進行本地開發,同時探討 LocalStack 的限制與優勢。透過本文,您將學習如何利用 LocalStack 建立更流暢的無伺服器應用程式開發流程,並提升開發效率。文章也涵蓋了 cdklocal 的安裝與組態,以及如何設定 DynamoDB 與 LocalStack 的連線,讓您可以更輕鬆地在本地環境中測試和除錯程式碼。最後,我們也將探討 ISAA (無敵的無伺服器應用架構) 的概念,以及如何應用它來構建更具彈性、可擴充套件性和可維護性的應用程式。
使用 LocalStack 模擬 AWS 服務
在開發無伺服器應用程式時,缺乏適當的本地開發環境是一大痛點。為瞭解決這個問題,我們可以使用 LocalStack 在本地執行程式碼,而不必每次都將其佈署到 AWS。LocalStack 允許我們在本機上模擬 AWS 服務(如 DynamoDB 和 S3)的功能。這樣,我們就可以離線測試和開發雲端和無伺服器應用程式。
安裝 LocalStack
要使用 LocalStack,首先需要安裝其命令列介面(CLI)。以下是詳細的安裝步驟:
要安裝 CLI,您需要在電腦上安裝以下內容:
- Python(3.7 至 3.10)
- Pip(Python 套件管理器)
- Docker
安裝完成後,在終端機中執行以下命令:
$ python3 -m pip install localstack如果一切順利,您應該會在命令執行完成後看到以下提示:
Successfully installed localstack-1.3.1要確認 LocalStack 已安裝,請在終端機中執行以下命令:
$ localstack --help如果您看到以下提示,則表示安裝成功。
注意
在某些 Linux 環境中,預設情況下,PATH 不會識別 Pip 安裝套件的位置。因此,您需要手動將其新增至 PATH,方法是將 export PATH=$HOME/.local/bin:$PATH 新增至您的 .bashrc 或 .zshrc 檔案中。
啟動 LocalStack
安裝完成後,我們可以透過執行以下命令來啟動 LocalStack:
$ localstack start
啟動完成後,您應該會看到以 Ready. 結尾的提示。
驗證 LocalStack 是否正確啟動
您可以透過在另一個終端機視窗中執行 docker container ls 命令來檢查 LocalStack 是否正確啟動。您應該會看到 LocalStack 容器被列出。
注意
要了解其他安裝 LocalStack 的方法,請檢視其檔案網站:https://docs.localstack.cloud/getting-started/installation/。
安裝 cdklocal
下一步是安裝 cdklocal。這是一個 CLI 包裝器,需要使用其 API 將 CDK 程式碼佈署到 LocalStack。您可以透過將其安裝為 npm 函式庫來完成此操作,方法是執行以下命令:
$ npm install -g aws-cdk-local
要確認安裝是否正確,可以執行以下命令:
$ cdklocal --version
之後,您應該會看到類別似以下的提示。
組態 Chapter8Stack
現在,LocalStack 和 cdklocal 都已安裝並執行,請導航至專案的基礎設施資料夾。我們將在 lib/chapter-8-stack.ts 檔案中新增一些邏輯,以便在使用 LocalStack 時僅佈署 DynamoDB。
您的程式碼應如下所示:
export class Chapter8Stack extends Stack {
public readonly acm: ACM;
public readonly route53: Route53;
public readonly s3: S3;
public readonly vpc: Vpc;
public readonly dynamo: DynamoDB;
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
const isCDKLocal = process.env.NODE_ENV === 'CDKLocal';
this.dynamo = new DynamoDB(this, `Dynamo-${process.env.NODE_ENV || ''}`);
if (isCDKLocal) return;
this.route53 = new Route53(this, `Route53-${process.env.NODE_ENV || ''}`);
this.acm = new ACM(this, `ACM-${process.env.NODE_ENV || ''}`, {
hosted_zone: this.route53.hosted_zone,
});
this.s3 = new S3(this, `S3-${process.env.NODE_ENV || ''}`, {
acm: this.acm,
route53: this.route53,
});
new ApiGateway(this, `Api-Gateway-${process.env.NODE_ENV || ''}`, {
route53: this.route53,
acm: this.acm,
dynamoTable: this.dynamo.table,
});
}
}
#### 內容解密:
此段程式碼定義了一個名為 Chapter8Stack 的類別,繼承自 Stack。它包含了多個 AWS 資源的屬性,並在建構函式中根據環境變數 NODE_ENV 的值決定是否佈署特定的資源。當 NODE_ENV 等於 'CDKLocal' 時,表示正在使用 LocalStack,此時僅佈署 DynamoDB。
佈署至 LocalStack
在終端機中,進入基礎設施資料夾,並執行以下命令:
$ yarn cdklocal bootstrap
$ yarn cdklocal deploy
第一個命令將在本機雲端環境中引導,第二個命令則會將堆積疊佈署到相同的本機雲端環境中。第二個命令的輸出應類別似於以下內容:
恭喜!您已成功將開發堆積疊佈署到本機雲端基礎設施中。
注意
請注意,LocalStack 是臨時的,這意味著如果您執行 localstack stop 命令,先前佈署的任何堆積疊都將被銷毀。
使用LocalStack最佳化無伺服器開發流程
在無伺服器架構的開發過程中,如何有效率地進行本地測試與除錯是一個重要的課題。AWS提供了一系列的工具與服務來支援無伺服器應用的開發,但實際上,開發者仍需要一個模擬AWS服務的本地環境,以加速開發流程並減少佈署至雲端的成本與風險。LocalStack正是為瞭解決這個問題而誕生的工具。
設定DynamoDB與LocalStack協同工作
為了使DynamoDB能夠正確地與LocalStack協同工作,我們需要對其進行一些設定。預設情況下,LocalStack的端點位於本機的4566埠。以下是設定的步驟:
- 首先,開啟
infrastructure/lib/constructs/Lambda/post/lambda/index.ts檔案,並找到以下程式碼:
const tableName = process.env.TABLE_NAME!
const awsRegion = process.env.REGION || 'us-east-1';
const dynamoDB = new DynamoDB.DocumentClient({
region: awsRegion,
endpoint: process.env.DYNAMODB_ENDPOINT || `https://dynamodb.${awsRegion}.amazonaws.com`,
});
這裡,我們透過.env檔案傳遞自定義的端點給DocumentClient() DynamoDB API。請注意,這個值只在執行本地伺服器時存在於.env檔案中。當將堆積疊佈署到AWS時,預設的URL將被傳遞給DocumentClient() DynamoDB API。
內容解密:
process.env.TABLE_NAME:從環境變數中取得表格名稱。process.env.REGION || 'us-east-1':從環境變數中取得AWS區域,若無則預設為us-east-1。DynamoDB.DocumentClient:建立一個DynamoDB的檔案客戶端。endpoint:根據環境變數DYNAMODB_ENDPOINT決定是否使用LocalStack的端點或是AWS的預設端點。
接下來,導航至
/infrastructure/lib/constructs/Lambda/get/lambda/index.ts,並對DocumentClient()進行相同的修改。最後,在
server/資料夾下建立一個.env檔案,並加入以下變數與值:
PORT=3000
REGION=us-east-1
TABLE_NAME=todolist-cdklocal
DYNAMODB_ENDPOINT=http://localhost:4566
內容解密:
PORT=3000:設定本地伺服器的埠號。REGION=us-east-1:設定AWS區域。TABLE_NAME=todolist-cdklocal:設定DynamoDB的表格名稱。DYNAMODB_ENDPOINT=http://localhost:4566:指定DynamoDB的端點為LocalStack。
- 在終端機中,進入
server/資料夾,並執行以下指令:
$ export AWS_PROFILE=cdk
$ yarn dev
內容解密:
export AWS_PROFILE=cdk:匯出AWS設定檔。yarn dev:啟動本地開發伺服器。
- 當指令執行完成後,本地開發伺服器將會啟動,並顯示類別似以下的訊息,表示伺服器正在執行:
Figure 8.6 – Message indicating the local server is operational on a specified port
測試本地伺服器
你可以透過向localhost:3000傳送請求來測試伺服器,例如,向/healthcheck路徑傳送請求以檢查伺服器是否正常運作:
Figure 8.7 – Health check path response
另外,你也可以透過傳送POST請求到根路徑(/)來建立表格專案:
Figure 8.8 – POST root path response
或者,透過傳送GET請求到根路徑(/)來檢索所有表格專案:
Figure 8.9 – GET root path response
LocalStack的限制
雖然LocalStack為本地無伺服器開發提供了極大的便利,但它也有一些限制需要注意:
- LocalStack可能無法完全複製某些AWS服務的行為,例如S3服務在LocalStack中的行為可能與實際的AWS S3服務不同。
- 不是所有的AWS服務都受到LocalStack的支援,因此某些CDK構件可能無法在本地進行完整的測試。
無敵的無伺服器應用架構(ISAA)
在前面的章節中,我們已經探討了AWS CDK的實務應用。對於Westpoint的團隊來說,CDK以及其他類別似的工具,如Pulumi、CDKTF和cdk8s(我們將在下一章中詳細介紹),具有重要的意義。這是我們第一次看到雲端基礎設施自動化達到圖靈完備(Turing complete),並且藉助Node.js、TypeScript和CDK標準函式庫,我們現在擁有先進的工具來指導這台圖靈完備的機器。
本章學習重點
• 什麼是ISAA? • ISAA的原則 • 可以使用ISAA的示例場景
技術需求
什麼是ISAA?
新的工具創造了新的應用程式架構方式。React應用程式的組態方式與Backbone.js應用程式的開發、組織和架構方式不同。如果沒有Kubernetes作為服務的協調器,很難達到微服務相同的彈性水平。
與微服務架構類別似,AWS CDK允許新一代的應用程式具有更高的彈性、可擴充套件性和可維護性。這是我們在Westpoint使用的方法。事實上,我們維護的應用程式是兩年前開發的,需要很少的支援,僅限於修復錯誤和清理資料。只要AWS作為一個實體存在,這些應用程式將繼續正常執行,無需額外的干預。我們將這種雲端軟體架構稱為無敵的無伺服器應用架構(ISAA)。
在進一步討論之前,請考慮這裡討論的每個主題都可能是一本文的主題。我們的目標是向您展示我們的團隊如何開始思考構建強大的Web應用程式的方法。我們很確定還有進一步的討論點和所謂的優點的缺點,但對我們來說,目前的結果說明瞭一切。
軟體熵的概念
一個佈署的軟體系統如果任其自行發展,將會變得越來越不穩定。原因可能有很多。以下是一些示例場景:
- 您佈署的Web應用程式所在的伺服器因為應用程式日誌積累而耗盡磁碟空間,從而導致伺服器故障。
- 您的應用程式使用的函式庫模組存在記憶體洩漏,這將需要越來越多的系統記憶體,最終導致當機。
- 您的網頁應用程式因為您的網域TLS憑證過期而無法透過TLS存取。
- 您擁有一個分析應用程式,每日ETL服務因為前一天的資料處理時間超過24小時而卡住。
軟體熵的概念與熱力學中的熵概念密切相關。用簡單的話來說,就是“每個系統如果任其自行發展,總是會從有序變為無序”。ISAA拒絕讓系統(或軟體)自行發展。我們將利用AWS無伺服器強大的功能來塑造我們的應用程式,使其與支援它的AWS服務一樣可靠。
ISAA的解決方案
讓我們回顧一下那些會增加軟體佈署不穩定性的示例,並看看ISAA如何處理它們:
- 不要在伺服器上累積日誌。將它們傳送到日誌服務(如CloudWatch)。設定日誌到期日期為一個月或您希望回顧日誌的頻率,這樣就不會無限期地為儲存日誌付費。
- 將應用程式建立在AWS Lambda上。每個應用程式的功能都將呼叫一個Lambda函式,該函式將服務請求並立即銷毀。下一個Lambda函式將使用新鮮的資源啟動。基本上,每個呼叫都變得孤立。
- 使用AWS Certificate Manager服務頒發憑證,並在您的CDK程式碼中設定續期警示或組態自動憑證續期。
- 使用AWS Glue,它允許您的ETL作業幾乎無限擴充套件。快速完成作業。或者更好的是,將分析建立在Kinesis Data Analytics上,這是一個流式無伺服器map/reduce平台,提供即時結果。
ISAA只有在AWS CDK的基礎上才有可能實作。以第二個示例為例,解決方案可能需要組態100個Lambda函式。如果我們必須以Terraform或CloudFormation的方式定義每個函式,將導致無法維護的大量組態檔案。其他提到的案例也將對組態造成很大負擔,並且由於缺乏像HCL、YAML或JSON這樣的可遵循邏輯而難以遵循。
那麼,ISAA與無伺服器架構有何不同?它其實是無伺服器架構的一個子集,但加入了一些額外的原則,我們將在接下來進行介紹。
ISAA與Serverless架構的區別
ISAA是在Serverless架構的基礎上,加入了更多的設計原則和最佳實踐,以達到更高的可靠性和可維護性。簡單來說,ISAA是一種更加嚴謹和完善的Serverless架構實踐。
使用Plantuml圖表示ISAA的核心概念
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle
title LocalStack模擬AWS服務最佳化開發流程
package "AWS 雲端架構" {
package "網路層" {
component [VPC] as vpc
component [Subnet] as subnet
component [Security Group] as sg
component [Route Table] as rt
}
package "運算層" {
component [EC2] as ec2
component [Lambda] as lambda
component [ECS/EKS] as container
}
package "儲存層" {
database [RDS] as rds
database [DynamoDB] as dynamo
storage [S3] as s3
}
package "服務層" {
component [API Gateway] as apigw
component [ALB/NLB] as lb
queue [SQS] as sqs
}
}
apigw --> lambda
apigw --> lb
lb --> ec2
lb --> container
lambda --> dynamo
lambda --> s3
ec2 --> rds
container --> rds
vpc --> subnet
subnet --> sg
sg --> rt
@enduml
圖表翻譯: 此圖示闡述了ISAA與Serverless架構之間的關係,以及ISAA的核心組成部分,包括自動化、監控和高用性,並展示了相關的AWS服務。
ISAA的核心原則
- 自動化:透過使用AWS CDK等工具,實作基礎設施即程式碼(IaC),從而自動化佈署和管理流程。
- 監控和日誌管理:利用CloudWatch等服務進行日誌收集、監控和警示設定,確保及時發現和處理問題。
- 高用性和容錯性:透過使用Lambda、Glue等無伺服器服務,提高應用程式的可擴充套件性和容錯能力。
程式碼範例:使用AWS CDK定義Lambda函式
import * as cdk from 'aws-cdk-lib';
import * as lambda from 'aws-cdk-lib/aws-lambda';
export class IsaStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// 定義一個Lambda函式
const helloLambda = new lambda.Function(this, 'HelloLambda', {
functionName: 'HelloLambda',
runtime: lambda.Runtime.NODEJS_14_X,
handler: 'index.handler',
code: lambda.Code.fromAsset('lambda'),
});
}
}
內容解密:
此段程式碼展示瞭如何使用AWS CDK定義一個簡單的Lambda函式。首先,我們匯入必要的模組,然後定義一個名為IsaStack的類別。在建構函式中,我們建立了一個新的Lambda函式,指定了其名稱、執行時環境、處理函式和程式碼來源。這樣的定義使得我們可以輕鬆地管理和佈署Lambda函式,實作自動化和可重複性的基礎設施管理。