• בלוג
  • עמוד 4
  • ניסוי localstack: הקמת תור ופונקציה לטיפול בהודעות

ניסוי localstack: הקמת תור ופונקציה לטיפול בהודעות

09/02/2025

לוקאלסטאק הוא כלי להרצת הרבה סרביסים של 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