AWS Lambda
What is AWS Lambda?#
- Compute service - run code without the server
- Server admin, operating system maintenance, capacity provisioninig, auto-scaling, monitoring and logging is done for you.
- All you do is supply your code in a lanaguage supported
- Code is organised into lambda functions
- Pay only while the code is running
Triggering lambda function:
- using the Lambda API
- in response to events from other AWS services
- data processing triggers from AWS s3 (simple storage service) or dynamodb
- streaming data from kinesis
When should I use Lambda?#
When you don’t want or need to fine tune and manage the runtime environment and resources.
Lambda Features#
- Concurrency and scaling controls
- Functions defined as container images - use container image tooling to build, test and deploy lambda functions
- Code signing - proof that approved developers write the code
- Lambda extensions - integrate with monitoring, observability, security and governance
- function blueprints - sample code
- database access
- filesystem access
Lambda Pricing#
- There is no additional charge for creating Lambda functions.
- There are charges for running a function and for data transfer between Lambda and other AWS services.
- Some optional Lambda features (such as provisioned concurrency) also incur charges.
Tools#
AWS Cli#
You can access lmabda info from the cli:
aws lambda list-functions
AWS SAM#
AWS Serverless Application Model is an extension of the CloudFormation template language - lets you define serverless applications at a higher level.
The AWS SAM CLI is a seperate commandline tool to manage and test AWS SAM applications.
Code authoring tools#
- Node.js
- Java - AWS toolkit for eclipse and intellij
- C# - AWS toolkit for visual studio
- Python - Lambda console and Pycharm with AWS Toolkit for JetBrains
- Ruby
- Go
- Powershell
Getting Started#
- You can author functions in the Lambda console, or with an IDE toolkit, command line tools, or the AWS SDKs
- The Lambda console provides a code editor for non-compiled languages that lets you modify and test code quickly
Deploying code:
- A
.zip
file archive that contains your function code and its dependencies - A container image that is compatible with the Open Container Initiative (OCI) specification.
- Go to Lambda home
- Create a function
- Give it a name and select the runtime (programming language)
- An execution role will be created automatically
Go the the Test
tab and run the function
There is a summary reported in the log output:
START RequestId: 4ee13858-8904-45af-9465-e911f6fd054b Version: $LATEST
END RequestId: 4ee13858-8904-45af-9465-e911f6fd054b
REPORT RequestId: 4ee13858-8904-45af-9465-e911f6fd054b Duration: 1.27 ms Billed Duration: 2 ms Memory Size: 128 MB Max Memory Used: 36 MB Init Duration: 107.95 ms
Not the best logs…
Go the the Monitor
tab and click Metrics
you can then see the graph of metrics sent to cloudwatch.
To clean up:
- Delete the Function. Functions page: Actions -> Delete
- Delete the log group. Log groups page:
/aws/lambda/my-function
Actions -> Delete - Delete the execution role. IAM console:
my-function-role-31exxmpl
Delete role
AWS Lambda foundations#
- Function - resource to run your code, it processes events you send it
- Trigger - resource that starts a lambda function. They can be aws services, manually triggered (api) or with an event source mapping.
- Event - json formatted document containing data to process converted to a
dict
if you are using python - Event Source Mapping - Amazon Simple Queue Service (Amazon SQS) queue, an Amazon Kinesis stream, or an Amazon DynamoDB stream, and sends the items to your function in batches
- Execution environment - isolated runtime for your function
- Instruction-set architecture (cpu architecture) - type of processor running your functions
arm64
orx86_64
- Deployment package -
.zip
or container - Runtime - language-specific environment that runs in an execution environment
- Layer - a zip file that can contain: libraries, a custom runtime, data, or configuration files. Way to package libraries and other dependencies for sharing.
- Extension - augment with mointoring, observability, security and governance
- Concurrency - number of requests your function is serving at a time
- Qualifier - an alias or pointer to a specific version to split traffic between 2 versions
- Destination - an AWS resource where Lambda can send events from an asynchronous invocation
Instruction set architectures#
Advantages of ARM64:
- Better price
- Better performance
VPC (Virtual Private Cloud) Private Network#
- A Lambda function always runs inside a VPC owned by the Lambda service
- By default, Lambda runs your functions in a secure VPC. Lambda owns this VPC, which isn’t connected to your account’s default VPC.
- Your lambda VPC can access otehr VPCs with a VPC-to-VPC NAT (V2N) - one way.
- Multiple Lambda functions can share a network interface, if the functions share the same subnet and security group.
- To connect to another AWS service, you can use VPC endpoints
- a NAT gateway to route outbound traffic to another AWS service
- To give your function access to the internet, route outbound traffic to a NAT gateway in a public subnet. The NAT gateway has a public IP address and can connect to the internet through the VPC’s internet gateway.
- A ENI (Elastic Network Interface) is used
Lambda scaling#
- If multi events arrive before the prior one has finished processing - another instance of the lambda function is added.
- The default regional concurrency quota starts at 1,000 instances
- Note that the burst concurrency quota is not per-function; it applies to all your functions in the Region - initial burst of traffic
- When requests come in faster than your function can scale, or when your function is at maximum concurrency, additional requests fail with a throttling error (429 status code).
Lambda with AWS CLI#
Create the execution role:
aws iam create-role --role-name lambda-ex \
--assume-role-policy-document '{"Version": "2012-10-17","Statement": [{ "Effect": "Allow", "Principal": {"Service": "lambda.amazonaws.com"}, "Action": "sts:AssumeRole"}]}'
You can also specify a json file:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
aws iam create-role --role-name lambda-ex --assume-role-policy-document file://trust-policy.json
To add policies to the role:
aws iam attach-role-policy --role-name lambda-ex --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
- The
AWSLambdaBasicExecutionRole
policy has the permissions that the function needs to write logs to CloudWatch Logs.
Commands for Lambda Functions#
List functions:
aws lambda list-functions --max-items 10
Get a lambda function:
aws lambda get-function --function-name my-function
Delete a function:
aws lambda delete-function --function-name my-function
Lambda Permissions#
- Every Lambda function has an IAM role called an execution role.
- In this role, you can attach a policy that defines the permissions that your function needs to access other AWS services and resources.
- At a minimum, your function needs access to Amazon CloudWatch Logs for log streaming.
- If your function calls other service APIs with the AWS SDK, you must include the necessary permissions in the execution role’s policy.
- To give other accounts and AWS services permission to use your Lambda resources, use a resource-based policy.
When a user tries to access a Lambda resource, Lambda considers both the user’s identity-based policies and the resource’s resource-based policy. When an AWS service such as Amazon Simple Storage Service (Amazon S3) calls your Lambda function, Lambda considers only the resource-based policy.
Lambda execution role#
- Select Function
- Choose
Configuration
tab - Under resource summary - review the resources the function can access
To debug an error you can configure a function to have the same permissions as another function
Creating a Lambda Execution Role#
- Open the Roles page
- in the IAM console.
- Choose Create role.
- Under Common use cases, choose Lambda.
- Choose Next: Permissions.
- Under Attach permissions policies, choose the AWS managed policies
AWSLambdaBasicExecutionRole
andAWSXRayDaemonWriteAccess
. - Choose Next: Tags.
- Choose Next: Review.
- For Role name, enter lambda-role.
- Choose Create role.
When it goes into production remember to apply least privilege
Use IAM Access Analyzer to help identify the required permissions for the IAM execution role policy.
AWS Managed Policies#
AWSLambdaBasicExecutionRole
- upload to cloudwatchAWSLambdaDynamoDBExecutionRole
- read DynamoDB recordsAWSLambdaKinesisExecutionRole
- read events on kinesisAWSLambdaMSKExecutionRole
- read and access to records on Apache Kafka (MSK)AWSLambdaSQSQueueExecutionRole
- Read from SQSAWSLambdaVPCAccessExecutionRole
- manage ENIs within an Amazon VPCAWSXRayDaemonWriteAccess
- upload trace data to X-Ray.CloudWatchLambdaInsightsExecutionRolePolicy
- write runtime metrics to CloudWatch Lambda InsightsAmazonS3ObjectLambdaExecutionRolePolicy
- interact with s3
Services lambda reads from:
- Amazon DynamoDB
- Amazon Kinesis
- Amazon MQ
- Amazon Managed Streaming for Apache Kafka (Amazon MSK)
- Self-managed Apache Kafka
- Amazon Simple Queue Service (Amazon SQS)
Identity-based IAM policies for Lambda#
- You can use identity-based policies in AWS Identity and Access Management (IAM) to grant users in your account access to Lambda
Levels:
AWSLambda_FullAccess
- Grants full access to Lambda actions and other AWS servicesAWSLambda_ReadOnlyAccess
- Grants read-only access to Lambda resources.AWSLambdaRole
- Grants permissions to invoke Lambda functions
Working with Python#
The minimum runtime is python3.7 and the max at time of writing is python3.9. Important to note that AWS Lambda does deprecate old runtimes so you then have to rewrite your code sometimes.
There is a sample application to get you up and running with dev and deployment as well as practices regarding logging and tracking:
Python Sample Lambdas Application
- Your Lambda function comes with a CloudWatch Logs log group.
- The function runtime sends details about each invocation to CloudWatch Logs.
- It relays any logs that your function outputs during invocation.
- If your function returns an error, Lambda formats the error and returns it to the invoker.
Handler#
The Lambda function handler is the method in your function code that processes events.
def handler_name(event, context):
...
return some_value
On lambda you set the hander as the path to the function: eg. mymodule.myfunction
Two arguments are passed:
- Event object - json formatted data (usually arrives as a dict, but can be str, int, float or None type)
-
Context object - methods and properties around invocation and the environment
print(type(event))
When you invoke a function, you determine the structure and contents of the event. When an AWS service invokes your function, the service defines the event structure.
Returning a value#
Returning a value is optional.
- If a
RequestResponse
invocation type is used the response is returned - If the response can’t be serialised by
json.dumps
a runtime error is raised - If the function returns none - the truntime returns
null
Deploy Python Lambda functions with .zip file archives#
Zip must contain:
- function code and dependencies
- if it is larger than 50MB it is recommended to upload to S3
- If your package contains native libraries you can use AWS SAM CLI
sam build --use-container
to build them for a container at runtime - The package must be built for the instruction set of the function / cpu architecture arm64 or x86_64
- Lambda uses POSIX file permissions
A python package may contain initialization code in the
__init__.py
file. Prior to Python 3.9, Lambda did not run the__init__.py
code for packages in the function handler’s directory or parent directories.
Runtime Dependencies#
A deployment package is required to update a lambda A dependency can be any package, module or other assembly dependency that is not included with the Lambda runtime environment for your function’s code.
The boto3
and standard library are included with all runtimes after python3.8
Deployment without dependencies#
Copy function into a folder: my-math-function
, then run:
zip my-deployment-package.zip lambda_function.py
Deployment package with dependencies#
Create the folder:
mkdir my-sourcecode-function
cd my-sourcecode-function
Create the module function: lambda_function.py
:
import requests
def lambda_handler(event, context):
response = requests.get("https://www.example.com/")
print(response.text)
return response.text
Install the requests
dependency to a new packages
directory:
pip install --target ./package requests
Create a deployment package with the installed library at the root:
cd package
zip -r ../my-deployment-package.zip .
This generates a
my-deployment-package.zip
file in your project directory.
Add the lambda function file to the root of the zip:
cd ..
zip -g my-deployment-package.zip lambda_function.py
Using a virtual environment#
~/my-function$ source myvenv/bin/activate
(myvenv) ~/my-function$ pip install requests
(myvenv) ~/my-function$ deactivate
cd myvenv/lib/python3.8/site-packages
zip -r ../../../../my-deployment-package.zip .
You can always pip show <packagename>
to find where a package lives:
$ pip show requests
Name: requests
Version: 2.28.1
Summary: Python HTTP for Humans.
Home-page: https://requests.readthedocs.io
Author: Kenneth Reitz
Author-email: me@kennethreitz.org
License: Apache 2.0
Location: ~/.virtualenvs/api-testing/lib/python3.7/site-packages
Requires: certifi, charset-normalizer, idna, urllib3
Required-by: requests-aws4auth
Add the function to the root of the library:
zip -g my-deployment-package.zip lambda_function.py
Deploying#
You can use the frontend to upload your deployment package or you can use the cli:
aws lambda update-function-code --function-name MyLambdaFunction --zip-file fileb://my-deployment-package.zip
Deploy Python Lambda functions with container images#
Use:
- AWS base images for Lambda
- Using a private or community base image you need the runtime interface client
- Open-source runtime interface emulator (RIE) - Lambda provides a runtime interface emulator for you to test your function locally
The workflow for a function defined as a container image includes these steps:
- Build your container image
- Upload to ECR - Elastic Container Registry
- Create the lambda function or update function code
AWS Base Images#
Creating an Image from the Base#
-
Creae a directory and add the function ‘my_function.py`:
import sys def handler(event, context): return ‘Hello from AWS Lambda using Python’ + sys.version + ‘!’
-
Add a
requirements.txt
-
Create a
DockerFile
:
FROM public.ecr.aws/lambda/python:3.8
# Copy function code
COPY app.py ${LAMBDA_TASK_ROOT}
# Install the function's dependencies using file requirements.txt
# from your project folder.
COPY requirements.txt .
RUN pip3 install -r requirements.txt --target "${LAMBDA_TASK_ROOT}"
# Set the CMD to your handler (could also be done as a parameter override outside of the Dockerfile)
CMD [ "app.handler" ]
-
Build
docker build -t hello-world .
-
Run
docker run -p 9000:8080 hello-world
-
Test, once happy upload:
Auth with ECR#
aws ecr get-login-password –region us-east-1 | docker login –username AWS –password-stdin 123456789012.dkr.ecr.us-east-1.amazonaws.com
Create repo#
aws ecr create-repository –repository-name hello-world –image-scanning-configuration scanOnPush=true –image-tag-mutability MUTABLE
tag and push#
docker tag hello-world:latest 123456789012.dkr.ecr.us-east-1.amazonaws.com/hello-world:latest docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/hello-world:latest
Python runtime interface clients#
When not using a lambda base image you must install the runtime interface clients
pip install awslambdaric
AWS Lambda context object in Python#
print(type(context))
<class 'awslambdaric.lambda_context.LambdaContext'>
When Lambda runs your function, it passes a context object to the handler.
Available properties of context
:
function_name
– The name of the Lambda function.function_version
– The version of the function.invoked_function_arn
– The Amazon Resource Name (ARN) that’s used to invoke the function. Indicates if the invoker specified a version number or alias.memory_limit_in_mb
– The amount of memory that’s allocated for the function.aws_request_id
– The identifier of the invocation request.log_group_name
– The log group for the function.log_stream_name
– The log stream for the function instance.identity
– (mobile apps) Information about the Amazon Cognito identity that authorized the request.cognito_identity_id
– The authenticated Amazon Cognito identity.cognito_identity_pool_id
– The Amazon Cognito identity pool that authorized the invocation.
client_context
– (mobile apps) Client context that’s provided to Lambda by the client application.client.installation_id
client.app_title
client.app_version_name
client.app_version_code
client.app_package_name
custom
– A dict of custom values set by the mobile client application.env
– A dict of environment information provided by the AWS SDK.
Logging#
To output logs from your function code, you can use the print
method or any logging library that writes to stdout
or stderr
.
Eg:
def lambda_handler(event, context):
print(event)
print(context)
Output:
START RequestId: a5719ed3-2754-4880-b810-0223dfb9497f Version: $LATEST
{'key1': 'value1', 'key2': 'value2', 'key3': 'value3'}
LambdaContext([aws_request_id=a5719ed3-2754-4880-b810-0223dfb9497f,log_group_name=/aws/lambda/stephen-test-function,log_stream_name=2022/08/03/[$LATEST]e16c2353ced84a249c9909dd774997be,function_name=stephen-test-function,memory_limit_in_mb=128,function_version=$LATEST,invoked_function_arn=arn:aws:lambda:eu-west-1:449369602245:function:stephen-test-function,client_context=None,identity=CognitoIdentity([cognito_identity_id=None,cognito_identity_pool_id=None])])
END RequestId: a5719ed3-2754-4880-b810-0223dfb9497f
REPORT RequestId: a5719ed3-2754-4880-b810-0223dfb9497f Duration: 1.27 ms Billed Duration: 2 ms Memory Size: 128 MB Max Memory Used: 36 MB Init
The Report log:
RequestId
– The unique request ID for the invocation.Duration
– The amount of time that your function’s handler method spent processing the event.Billed Duration
– The amount of time billed for the invocation.Memory Size
– The amount of memory allocated to the function.Max Memory Used
– The amount of memory used by the function.Init Duration
– For the first request served, the amount of time it took the runtime to load the function and run code outside of the handler method.XRAY TraceId
– For traced requests, the AWS X-Ray trace ID.SegmentId
– For traced requests, the X-Ray segment ID.Sampled
– For traced requests, the sampling result.
Using the CloudWatch console#
- Open Log Groups on CloudWatch console
- Choose the log group for your function:
/aws/lambda/your-function-name
- Choose a log stream
To find logs for a specific invocation, we recommend instrumenting your function with AWS X-Ray. X-Ray records details about the request and the log stream in the trace.
Using the AWS#
aws lambda invoke --function-name my-function out --log-type Tail
{
"StatusCode": 200,
"LogResult": "U1RBUlQgUmVxdWVzdElkOiA4N2QwNDRiOC1mMTU0LTExZTgtOGNkYS0yOTc0YzVlNGZiMjEgVmVyc2lvb...",
"ExecutedVersion": "$LATEST"
}
Decode the logs:
aws lambda invoke --function-name my-function out --log-type Tail --query 'LogResult' --output text | base64 -d
Deleting Logs#
- Log groups aren’t deleted automatically when you delete a function.
- To avoid storing logs indefinitely, delete the log group, or configure a retention period after which logs are deleted automatically.
Logging Library#
Make sure to set the loglevel - by default only warning and above will log:
import json
import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
def lambda_handler(event, context):
message = f'{context.function_name} {context.function_version} {context.client_context}'
logger.debug(event)
logger.debug(context)
logger.debug(message)
key1_name = event.get('key1')
return {
'statusCode': 200,
'body': f'Hello {key1_name}, from Lambda!',
'context': message
}
The format:
START RequestId: eef2f4b3-5150-4c89-a3fc-62802d3cb297 Version: $LATEST
[DEBUG] 2022-08-03T10:21:36.705Z eef2f4b3-5150-4c89-a3fc-62802d3cb297 {'key1': 'value1', 'key2': 'value2', 'key3': 'value3'}
[DEBUG] 2022-08-03T10:21:36.706Z eef2f4b3-5150-4c89-a3fc-62802d3cb297 LambdaContext([aws_request_id=eef2f4b3-5150-4c89-a3fc-62802d3cb297,log_group_name=/aws/lambda/stephen-test-function,log_stream_name=2022/08/03/[$LATEST]4fd4ab7fefa54c869f54ad55ec6fbb1c,function_name=stephen-test-function,memory_limit_in_mb=128,function_version=$LATEST,invoked_function_arn=arn:aws:lambda:eu-west-1:449369602245:function:stephen-test-function,client_context=None,identity=CognitoIdentity([cognito_identity_id=None,cognito_identity_pool_id=None])])
[DEBUG] 2022-08-03T10:21:36.706Z eef2f4b3-5150-4c89-a3fc-62802d3cb297 stephen-test-function $LATEST None
Raise an execption - which will include a stacktrace:
logger.exception('whoops what now?')
...
[ERROR] 2022-08-03T10:24:46.155Z ba9581f5-470c-4f9c-892f-f807eb6cc6c5 whoops what now?
NoneType: None[ERROR] ValueError: Whoops some problem
Traceback (most recent call last):
File "/var/task/lambda_function.py", line 16, in lambda_handler
raise ValueError('Whoops some problem')END RequestId: ba9581f5-470c-4f9c-892f-f807eb6cc6c5
If an error is raised:
raise ValueError('Whoops some problem')
...
[DEBUG] 2022-08-03T10:26:21.984Z bbcf1882-2631-4008-9c55-7d61caad0a73 stephen-test-function $LATEST None
[ERROR] ValueError: Whoops some problem
Traceback (most recent call last):
File "/var/task/lambda_function.py", line 14, in lambda_handler
raise ValueError('Whoops some problem')END RequestId: bbcf1882-2631-4008-9c55-7d61caad0a73
REPORT RequestId: bbcf1882-2631-4008-9c55-7d61caad0a73 Duration: 7.97 ms Billed Duration: 8 ms Memory Size: 128 MB Max Memory Used: 36 MB Init Duration: 134.31 ms
Errors#
When your code raises an error, Lambda generates a JSON representation of the error.
Invoke and run your lambda:
aws lambda invoke \
--function-name my-function \
--cli-binary-format raw-in-base64-out \
--payload '{"key1": "value1", "key2": "value2", "key3": "value3"}' output.txt
Examples#
Runtime exception (Import error):
{
"errorMessage": "Unable to import module 'lambda_function': Cannot import name '_imaging' from 'PIL' (/var/task/PIL/__init__.py)",
"errorType": "Runtime.ImportModuleError"
}
This happens if you have uploaded a deployment package that contains a C or C++ library: Pillow, Numpy or Pandas
In that case the recommendation is to use thesam build
command with the --use-container
option to create your deployment package - that creates a Docker container with a Lambda-like environment that is compatible with Lambda
JSON Serialization error:
{
"errorMessage": "Unable to marshal response: Object of type AttributeError is not JSON serializable",
"errorType": "Runtime.MarshalError"
}
This can be caused by base64 encoding:
import base64
encrypted_data = base64.b64encode(payload_enc).decode("utf-8")
or not specifying your zip file as a binary file:
aws lambda create-function --function-name my-function --zip-file fileb://my-deployment-package.zip --handler lambda_function.lambda_handler --runtime python3.8 --role arn:aws:iam::your-account-id:role/lambda-ex
Tracing#
AWS clearly want you to use AWS X-Ray - as it is mentioned throughout the docs.
Using AWS Distro for OpenTelemetry or the X-RAY SDK for Python
ADOT (Open Telemetry) is the preferred method for instrumenting your Lambda functions.
More on Python tracing in the docs
Adding a trigger#
When you add a trigger the IAM role of the lambda executor must have access to the tragger otherwise you get an error when creating the trigger:
An error occurred when creating the trigger: Cannot access stream arn:aws:dynamodb:eu-west-1:449369602245:table/xxx-qa/stream/2021-03-23T15:21:51.322. Please ensure the role can perform the GetRecords, GetShardIterator, DescribeStream, and ListStreams Actions on your stream in IAM.
In that case you should get the executor role and then in IAM
create an inline policy for dynamo DB to add:
Allow: dynamodb:DescribeStream
Allow: dynamodb:GetRecords
Allow: dynamodb:GetShardIterator
Allow: dynamodb:ListStreams
Allow: dynamodb:ListShards
Monitoring#
Check the Monitoring Lambda Docs
Security#
Check the Security Lambda Docs
Troubleshooting#
Check the Troubleshooting Lambda Docs
Profiling Lambdas with CodeGuru#
Check the Profiling Lambdas with CodeGuru Docs