ניסוי localstack: הקמת תור ופונקציה לטיפול בהודעות
לוקאלסטאק הוא כלי להרצת הרבה סרביסים של AWS בצורה מקומית. הוא מצוין לפיתוח או בדיקות ואולי גם לפרודקשן כשרוצים לחסוך בעלויות. בשביל להבין איך הוא עובד בניסוי היום אני בונה תור הודעות על SQS ופונקציית lambda, עם מיפוי ביניהם שגורם לפונקציה לרוץ אוטומטית כשנכנסת הודעה חדשה לתור.
1. קוד הפונקציה
בשביל המשחק הפונקציה לא צריכה לעשות הרבה. פונקציה שמופעלת בתגובה להודעה בתור מקבלת פרמטר event שמייצג את האירוע ובו יש שדה בשם Records שמכיל את ההודעות מאירוע זה (הפעלה אחת יכולה לכלול מנה של מספר הודעות). ההודעות יהיו אוביקטי JSON עם שדות num1
ו num2
והפונקציה תדפיס את מכפלת המספרים ללוג. זה הקוד בקובץ index.js
:
function handleRecord(body) {
const product = body.num1 * body.num2;
console.log(body);
const response = {
statusCode: 200,
body: "The product of " + body.num1 + " and " + body.num2 + " is " + product,
};
console.log(response);
return response;
}
exports.handler = async (event) => {
try {
for (const record of event.Records) {
console.log(`record = `);
console.log(record);
handleRecord(JSON.parse(record.body));
}
} catch (_err) {
console.log(`Error parsing body. Body was: ${event.body}`);
}
};
אחרי שיש לי את הקובץ אני שומר אותו לקובץ zip עם הפקודה:
$ zip function.zip index.js
ואנחנו מוכנים להמשיך.
2. יצירת הסטאק
אפשר ליצור אוביקטים על AWS אחד אחד או באמצעות סקריפט CloudFormation שיוצר את כל הסטאק הטכנולוגי. אני משתמש בסקריפט ולכן יוצר את הקובץ stack.yml עם התוכן הבא:
AWSTemplateFormatVersion: '2010-09-09'
Description: 'CloudFormation template for SQS queue with Lambda consumer'
Resources:
# SQS Queue
MyQueue:
Type: AWS::SQS::Queue
Properties:
VisibilityTimeout: 30
# CloudWatch Log Group
MyFunctionLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub '/aws/lambda/${AWS::StackName}-MyFunction'
RetentionInDays: 14
# Lambda Function
MyFunction:
Type: AWS::Lambda::Function
Properties:
Code:
ZipFile: function.zip
Handler: index.handler
Role: !GetAtt LambdaExecutionRole.Arn
Runtime: nodejs18.x
Timeout: 10
FunctionName: !Sub '${AWS::StackName}-MyFunction'
Environment:
Variables:
QUEUE_URL: !Ref MyQueue
# Lambda Execution Role
LambdaExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: SQSAccess
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- sqs:ReceiveMessage
- sqs:DeleteMessage
- sqs:GetQueueAttributes
Resource: !GetAtt MyQueue.Arn
# Event Source Mapping to connect SQS to Lambda
MyEventSourceMapping:
Type: AWS::Lambda::EventSourceMapping
Properties:
BatchSize: 1
Enabled: true
EventSourceArn: !GetAtt MyQueue.Arn
FunctionName: !Ref MyFunction
Outputs:
QueueURL:
Description: URL of the SQS Queue
Value: !Ref MyQueue
QueueARN:
Description: ARN of the SQS Queue
Value: !GetAtt MyQueue.Arn
FunctionARN:
Description: ARN of the Lambda Function
Value: !GetAtt MyFunction.Arn
הסטאק יוצר תור, פונקציה ו EventSourceMapping שזה החיבור ביניהם. גודל המנה שם הוא 1 ולכן במערך ההודעות של הפונקציה נראה תמיד רק רשומה אחת.
3. הפעלה ובדיקה
בשביל ליצור את הסטאק אני מריץ:
$ awslocal cloudformation create-stack \
--stack-name my-sqs-lambda-stack-2 \
--template-body file://stack.yml \
--capabilities CAPABILITY_IAM
לאחר מכן אני יכול להסתכל מה נוצר עם הפקודה:
$ awslocal cloudformation describe-stacks \
--stack-name my-sqs-lambda-stack-2
הפלט אצלי הוא:
{
"Stacks": [
{
"StackId": "arn:aws:cloudformation:us-east-1:000000000000:stack/my-sqs-lambda-stack-2/512113f2",
"StackName": "my-sqs-lambda-stack-2",
"Description": "CloudFormation template for SQS queue with Lambda consumer",
"CreationTime": "2025-02-07T13:12:29.585000+00:00",
"LastUpdatedTime": "2025-02-07T13:12:29.585000+00:00",
"RollbackConfiguration": {},
"StackStatus": "CREATE_COMPLETE",
"DisableRollback": false,
"NotificationARNs": [],
"Capabilities": [
"CAPABILITY_IAM"
],
"Outputs": [
{
"OutputKey": "QueueURL",
"OutputValue": "http://sqs.us-east-1.localhost.localstack.cloud:4566/000000000000/my-sqs-lambda-stack-2-MyQueue-0becc311",
"Description": "URL of the SQS Queue"
},
{
"OutputKey": "QueueARN",
"OutputValue": "arn:aws:sqs:us-east-1:000000000000:my-sqs-lambda-stack-2-MyQueue-0becc311",
"Description": "ARN of the SQS Queue"
},
{
"OutputKey": "FunctionARN",
"OutputValue": "arn:aws:lambda:us-east-1:000000000000:function:my-sqs-lambda-stack-2-MyFunction",
"Description": "ARN of the Lambda Function"
}
],
"Tags": [],
"EnableTerminationProtection": false,
"DriftInformation": {
"StackDriftStatus": "NOT_CHECKED"
}
}
]
}
וכמובן מה שהכי מעניין שם זה ה Outputs שם אני רואה את הפרטים של התור והפונקציה שנוצרו.
אני שולח הודעה לתור עם:
$ awslocal sqs send-message \
--queue-url "http://sqs.us-east-1.localhost.localstack.cloud:4566/000000000000/my-sqs-lambda-stack-2-MyQueue-0becc311" \
--message-body '{"num1": "10", "num2": "10"}'
ומסיים בחיפוש פלט הפונקציה בלוג. בשביל זה מפעילים:
$ awslocal logs describe-log-groups
{
"logGroups": [
{
"logGroupName": "/aws/lambda/my-sqs-lambda-stack-2-MyFunction",
"creationTime": 1738933949640,
"metricFilterCount": 0,
"arn": "arn:aws:logs:us-east-1:000000000000:log-group:/aws/lambda/my-sqs-lambda-stack-2-MyFunction:*",
"storedBytes": 12439
}
]
}
ממנו אנחנו לומדים שבלוג יש קבוצה אחת ורואים את השם שלה. אחרי זה עם שם הקבוצה אני מפעיל את הפקודה הבאה כדי לראות את כל הלוגים של היומיים האחרונים:
$ awslocal logs tail /aws/lambda/my-sqs-lambda-stack-2-MyFunction --since 2d