Debugging AWS Lambda with LocalStack

Debugging AWS Lambda with LocalStack

What is LocalStack?

LocalStack is an open-source software development tool that helps you deploy and run applications in a simulated cloud environment on your personal computer or server. It provides a replica of many popular cloud services, such as Amazon S3, AWS Lambda, DynamoDB, and many others. This allows you to develop, test, and debug your applications without having to connect to a real cloud environment.

Installation

Install LocalStack

Follow the instructions at the following link to install LocalStack:

https://docs.localstack.cloud/getting-started/installation/

Install Docker

Follow the instructions at the following link to install Docker:

https://docs.docker.com/engine/install/

Install AWS CLI

Follow the instructions at the following link to install AWS CLI and configure a profile for the local environment:

https://docs.localstack.cloud/user-guide/integrations/aws-cli/#aws-cli

Setup LocalStack

Create docker-compose.yml file

We will start LocalStack with a Docker-Compose setup. Create a new file named docker-compose.yml and add the following configuration:

version: "3.8"

services:
  localstack:
    container_name: "${LOCALSTACK_DOCKER_NAME-localstack_main}"  # Name of the container to be used
    image: localstack/localstack:latest  # Docker image for LocalStack, latest version
    ports:
      - "127.0.0.1:4566:4566"            # Port for accessing LocalStack Gateway from the local machine
      - "127.0.0.1:4510-4559:4510-4559"  # Port range for external services
    environment:
      - DEBUG=1  # Enable Debug mode in LocalStack
      - LAMBDA_REMOTE_DOCKER=0  # Turn off remote Docker usage for Lambda
      - LAMBDA_DOCKER_FLAGS=-e NODE_OPTIONS=--inspect-brk=0.0.0.0:9229 -p 9229:9229  # Exposes port 9229 for debugging the Lambda handler code.
      - LAMBDA_EXECUTOR=${LAMBDA_EXECUTOR-}  # Lambda execution type (if defined)
      - DOCKER_HOST=unix:///var/run/docker.sock  # Link to Docker via Docker socket
    volumes:
      - "${LOCALSTACK_VOLUME_DIR:-./volume}:/var/lib/localstack"  # Mount point for LocalStack data directory
      - "/var/run/docker.sock:/var/run/docker.sock"  # Mount Docker socket to access Docker from the LocalStack container
In addition, to see other configurations of LocalStack, you can refer to the following link:

Start up LocalStack with Docker

We will run the following command to start LocalStack:

docker-compose up -d

After running the above command, we see that the Docker Container is running

And successfully started LocalStack

Create launch.json file

We create a .vscode/launch.json file and add the following configuration:

{
  "version": "0.2.0",
  "configurations": [
    {
      "address": "127.0.0.1",
      "localRoot": "${workspaceFolder}",
      "name": "Attach to Remote Node.js",
      "port": 9229,
      "remoteRoot": "/var/task/",
      "request": "attach",
      "type": "node",
      "preLaunchTask": "Wait Remote Debugger Server"
    },
  ]
}

Create tasks.json file

We create a .vscode/tasks.json file and add the following configuration:

{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "Wait Remote Debugger Server",
      "type": "shell",
      "command": "while [[ -z $(docker ps | grep :9229) ]]; do sleep 1; done; sleep 1;"
    }
  ]
}

Note: The above code only executes on Linux and macOS. If you are using Windows, do the following:

Create a .vscode/tasks.json file

{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "Wait Remote Debugger Server",
      "type": "shell",
      "command": "powershell",
      "args": [
        ".\\.vscode\\wait-debugger.ps1"
      ]
    }
  ]
}
Create a .vscode/wait-debugger.ps1 file
while (-not (docker ps | Select-String ":9229")) {
  Start-Sleep -Seconds 1
}
Start-Sleep -Seconds 1

Create function.ts file

In this example, we will create a function.ts file containing Lambda Function code using Node.js and TypeScript for debug testing:

export const  handler = async (event: any) =>  {
  const response = {
    statusCode: 200,
    body: "hello word",
  };
  return response;
};

Create Lambda Function on LocalStack

To create a Lambda Function on LocalStack we use the following command:

aws --endpoint-url=http://localhost:4566 lambda create-function \
--function-name localstack-lambda-function \
--code S3Bucket="hot-reload",S3Key="$(pwd)/" \
--handler function.handler \
--runtime nodejs18.x \
--timeout 120 \
--role arn:aws:iam::000000000000:role/lambda-role

After running the above command, the Lambda Function has been successfully created on LocalStack

Implement Debugging Lambda Function

In VS Code you can debug by placing breakpoints where the code needs to be debugged and pressing F5 to run

Then execute the Lambda Function with the following command:

aws --endpoint-url=http://localhost:4566 lambda invoke \
--function-name localstack-lambda-function lambda.log \
--cli-binary-format raw-in-base64-out \
--payload '{"hello":"world"}'

After running the above command, you have successfully debugged

Implement Debugging Lambda Function with Amazon S3 to trigger AWS Lambda when uploading files

Create a bucket on Amazon S3

To create a bucket on Amazon S3, we use the following command:

aws s3api create-bucket --bucket local-stack-bucket --create-bucket-configuration LocationConstraint=ap-northeast-1 --endpoint-url=http://localhost:4566

In there:

+ local-stack-bucket:  is the name of the bucket

Configure Amazon S3 trigger to Lambda Function

To trigger the Lambda Function when uploading a file to Amazon S3, we use the following command:

aws --endpoint-url=http://localhost:4566 s3api put-bucket-notification-configuration \
    --bucket local-stack-bucket \
    --notification-configuration '{
        "LambdaFunctionConfigurations": [
          {
            "Id": "1", 
            "LambdaFunctionArn": "arn:aws:lambda:ap-northeast-1:000000000000:function:localstack-lambda-function",
            "Events": ["s3:ObjectCreated:*"]
          }
        ]
    }'

In there:

+ local-stack-bucket:  is the name of the bucket to be configured

+ LambdaFunctionArn:  is the Arn of the Lambda Function that needs to trigger

+ Events: is an event on Amazon S3 that triggers a Lambda Function

Proceed to debug the Lambda Function

In VS Code you can debug by placing breakpoints where the code needs to be debugged and pressing F5 to run

Then proceed to upload the file to the Amazon S3 bucket on the LocalStack cloud

 

After uploading the file, you have successfully debugged

The Drawbacks of LocalStack

Limited Support For AWS Services:  LocalStack provides emulation for several important services such as S3, DynamoDB, Lambda, and SQS. However, not all AWS services are supported, and some services may be unavailable or implemented with only some basic features. This can be a hassle if your application depends on unsupported services.

Missing Features: Although LocalStack helps simulate services, not all of their features are implemented. Configuration options can be limited, reducing compatibility between the local development environment and the actual environment on AWS.

Limited Scalability: LocalStack is not designed to scale as aggressively as the AWS environment. If you are working on large projects at scale, testing and development in a local environment may not reflect reality on AWS.

Delay: Due to emulation and attempts to replicate AWS services, LocalStack may experience delays compared to the actual use of services. This can reduce performance and slow down development and testing.

Slow Update: LocalStack may not update quickly with new versions of AWS services. This can lead to asynchrony when new features are needed or when there are changes in the AWS API.

Conclusion

Using LocalStack helps us develop and debug Lambda Functions, as well as other AWS services, effectively in a local environment. It helps ensure proper integration and functionality before deploying to actual AWS, saving time, and cost, and reducing impact on your real AWS account during development.

References

https://docs.localstack.cloud/