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
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
Setup Lambda Function Debugging
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" ] } ] }
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.