How to deploy your AWS Lambda based in containers with ECR
In previous working experiences, I was used to building and deploying AWS Lambda functions with zip files stored in AWS S3. I hadn't had the opportunity to use containers instead, but there is a chance to try new ways of building solutions if you work in cloud development.
So, this post is about deploying an AWS Lambda function using container images stored in the AWS Elastic Container Registry. Let's jump on it.
Prerequisites
- AWS Command Line Interface, aka AWS CLI.
- Docker
- Node.js
💡 To set up your AWS CLI, you can use Granted, as I explain in this blog post. 🙂
Creating a AWS Lambda Function
Create a directory called aws-lambda-nodejs-container. Bear with me; I like to have a well-structured name on my folders, even if this is just an example. 😉
mkdir aws-lambda-nodejs-container
Inside that folder, let's create a simple Node.js project with npm. You can accept the default options for this example.
cd aws-lambda-nodejs-container && npm init
Create a new lambda function in a new file index.mjs
export const lambdaHandler = async (event, context) => {
const response = {
statusCode: 200,
body: JSON.stringify({ message: 'This is a successful response from AWS Lambda', })
};
return response;
};
Now, we need to create a simple Dockerfile with the configuration to create an image from the AWS EC Registry, COPY our lambda function code, RUN an install command, and finally, set the handler.
FROM public.ecr.aws/lambda/nodejs:20-arm64
COPY index.mjs ${LAMBDA_TASK_ROOT}
CMD ["index.lambdaHandler"]
Let's build our image; we are using an arm64 image. You can find a specific picture in the Amazon ECR Public Gallery → https://gallery.ecr.aws/lambda/nodejs
docker build --platform linux/arm64 -t docker-image:test .
If you want to test the image locally, docker is here to help you. Run your image like this:
docker run --platform linux/arm64 -p 9000:8080 docker-image:test
Then, invoke your lambda function:
curl "<http://localhost:9000/2015-03-31/functions/function/invocations>" -d '{}'
You will get something like this:
Deploying the image to AWS ECR and your lambda function to AWS
To accomplish this, we need to create an AWS ECR Repository; we can do this through the AWS Console, AWS CLI, or the AWS CDK. Now, I'm going to use the AWS CLI; you must have experience using the CLI for your AWS tasks.
First, we must authenticate the Docker CLI to your Amazon ECR Registry.
In this example, I'm setting the us-east-1 as the AWS Region where I want to create the Amazon ECR repository.
Replace the <aws-account-id> string ****with your AWS Account ID.
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin <aws-account-id>.dkr.ecr.us-east-1.amazonaws.com
Now it is time to create your AWS ECR Repository
aws ecr create-repository --repository-name dev-repository --region us-east-1
Grab the value from the repositoryUri attribute for the next step.
{
"repository": {
"repositoryArn": "arn:aws:ecr:us-east-1:123456789012:repository/dev-repository",
"registryId": "705941374709",
"repositoryName": "dev-repository",
"repositoryUri": "123456789012.dkr.ecr.us-east-1.amazonaws.com/dev-repository",
"createdAt": "2024-02-17T16:02:31.898000-05:00",
"imageTagMutability": "MUTABLE",
"imageScanningConfiguration": {
"scanOnPush": false
},
"encryptionConfiguration": {
"encryptionType": "AES256"
}
}
}
We need to tag our local image. Also, you need to replace the <ECRrepositoryUri> with the value of the previous step.
docker tag docker-image:test <ECRrepositoryUri>:latest
Then, we push our image to the AWS ECR repository.
docker push <ECRrepositoryUri>:latest
Create an execution role for the AWS Lambda function: https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-awscli.html#with-userapp-walkthrough-custom-events-create-iam-role
Let's create an AWS Lambda function.
aws lambda create-function \\
--function-name nodejs-hello-world \\
--package-type Image \\
--code ImageUri=123456789012.dkr.ecr.us-east-1.amazonaws.com/dev-repository:latest \\
--role arn:aws:iam::123456789012:role/service-role/myNewFunction-role-67bgsgdp
You will get something like this.
{
"FunctionName": "nodejs-hello-world",
"FunctionArn": "arn:aws:lambda:us-east-1:705941374709:function:nodejs-hello-world",
"Role": "arn:aws:iam::705941374709:role/service-role/myNewFunction-role-67bgsgdp",
"CodeSize": 0,
"Description": "",
"Timeout": 3,
"MemorySize": 128,
"LastModified": "2024-02-18T01:51:12.111+0000",
"CodeSha256": "69b8fae1b0affd9d67c93341d0d3c07fbd85b702cf8cb97afaa1c32ffea4e331",
"Version": "$LATEST",
"TracingConfig": {
"Mode": "PassThrough"
},
"RevisionId": "60613992-6bb8-4258-9e60-5ed3efefec6f",
"State": "Pending",
"StateReason": "The function is being created.",
"StateReasonCode": "Creating",
"PackageType": "Image",
"Architectures": [
"x86_64"
],
"EphemeralStorage": {
"Size": 512
},
"SnapStart": {
"ApplyOn": "None",
"OptimizationStatus": "Off"
}
}
You can invoke the function,
aws lambda invoke --function-name nodejs-hello-world response.json
And get a response like this:
{
"StatusCode": 200,
"FunctionError": "Unhandled",
"ExecutedVersion": "$LATEST"
}
Remember, if you want to update the function code, you need to build the image again, upload it to the Amazon ECR Repository, and then use the update-function-code command to deploy the image to the Lambda function
That's it. I hope that you find this helpful.
See you in the next blog post!