• Classes
  • 07 - Lambda Functions
  • Part 3

Lambda and Docker

Introduction

We saw in previous classes why Docker is important for ML. In this class, we will see how to create Docker images and deploy Lambda functions from them.

Create Source Code

Consider the following lambda function code:

import sys

def hello_from_docker(event, context):
    return {
        "created_by": "your name",
        "message": "Hello World!",
        "version": sys.version
    }

Our goal is to deploy this function with Docker + Lambda.

Important!

Create a folder for today's class and store the suggested files in the root of this folder

Question 1

Save the source code above in lambda_function.py file.

Question 2

Create a requirements.txt with some dummy dependencies like textblob or pandas.

For now, we will not use these dependencies, they will only serve as an example for the tasks at the end of the class.

Create Image

Question 3

Create a file Dockerfile with the content:

Notice:

  • The image used
  • The dependencies
  • The file with the handler

See more images Here

FROM public.ecr.aws/lambda/python:3.10

# Copy requirements.txt
COPY requirements.txt ${LAMBDA_TASK_ROOT}

# Copy function code
COPY lambda_function.py ${LAMBDA_TASK_ROOT}

# Install the specified packages
RUN pip install -r requirements.txt

# Set the CMD to your handler (could also be done as a parameter override outside of the Dockerfile)
CMD [ "lambda_function.hello_from_docker" ]

Let's name the image lambda-ex-image and give it the test tag:

$ docker build --platform linux/amd64 -t lambda-ex-image:test .

Test locally

Before deploying, let's test the function locally.

Start the Docker image with the docker run command:

$ docker run -p 9500:8080 lambda-ex-image:test


Let's make a request with:

$ curl "http://localhost:9500/2015-03-31/functions/function/invocations" -d '{}'

Question 4

Check if you can get the expected return, as defined in the handler of the lambda function.

If that doesn't work, try with double quotes:

$ curl "http://localhost:9500/2015-03-31/functions/function/invocations" -d "{}"

Tip! 1

If you prefer, make the request from Python with the requests library!

Click to see
import requests
import json

url = "http://localhost:9500/2015-03-31/functions/function/invocations"
data = {}

response = requests.post(url, json=data, timeout=2)

response_json = response.json()

print(f"Status code: {response.status_code}")
print("Response:")
print(json.dumps(response_json, indent=4))

Container Registry

The Amazon Elastic Container Registry (ECR) is a fully managed service that allows you to store, manage, and deploy container images. With ECR, you can securely store and manage your Docker container images.

Let's upload our image to ECR. But first, we need to create a container repository:

Atention!

Change the repository_name variable.

Provide a name in the pattern test1-mlops-<INSPER_USERNAME>

import boto3
import os
from dotenv import load_dotenv

load_dotenv()

# Provide a name like test1-mlops-<INSPER_USERNAME>
repository_name = "test1-mlops-xxxxx"

# Create a Boto3 client for ECR
ecr_client = boto3.client(
    "ecr",
    aws_access_key_id=os.getenv("AWS_ACCESS_KEY_ID"),
    aws_secret_access_key=os.getenv("AWS_SECRET_ACCESS_KEY"),
    region_name=os.getenv("AWS_REGION"),
)

response = ecr_client.create_repository(
    repositoryName=repository_name,
    imageScanningConfiguration={"scanOnPush": True},
    imageTagMutability="MUTABLE",
)

print(response)

print(f"\nrepositoryArn: {response['repository']['repositoryArn']}")
print(f"repositoryUri: {response['repository']['repositoryUri']}")

If you want to use AWS CLI, remember to Activate MLOps Profile

$ repository_name="test1-mlops-xxxxxx"

$ aws ecr create-repository \
    --repository-name "$repository_name" \
    --image-scanning-configuration scanOnPush=true \
    --image-tag-mutability MUTABLE \
    --query 'repository.{repositoryArn:repositoryArn, repositoryUri:repositoryUri}' \
    --output text

Important!

Write down the repositoryUri

Upload Image to ECR

Before uploading our Docker image to ECR, we need to configure the AWS Command Line Interface (AWS CLI). If you didn't, check this out in our previous class.

Let's authenticate and login to ECR using the Docker CLI:

Atention!

Change the <AWS_ACCOUNT_ID> for the <AWS_ACCOUNT_ID> used during classes, for example 123456789012

Check if the AWS profile name, set with aws configure, really is mlops. Change if necessary.

$ aws ecr get-login-password --region us-east-2 --profile mlops | docker login --username AWS --password-stdin AWS_ACCOUNT_ID.dkr.ecr.us-east-2.amazonaws.com


Then, we will run the docker tag command to tag our local Docker image into your Amazon ECR repository as the latest version by doing:

Atention!

Provide the <REPOSITORY_URI> from before.

Example of <REPOSITORY_URI> (notice the AWS_ACCOUNT_ID, AWS_REGION and REPOSITORY_NAME):

AWS_ACCOUNT_ID.dkr.ecr.AWS_REGION.amazonaws.com/REPOSITORY_NAME

$ docker tag lambda-ex-image:test REPOSITORY_URI:latest


And push image to ECR with:

Atention!

Provide the <REPOSITORY_URI> from before.

Example of <REPOSITORY_URI> (notice the AWS_ACCOUNT_ID, AWS_REGION and REPOSITORY_NAME):

AWS_ACCOUNT_ID.dkr.ecr.AWS_REGION.amazonaws.com/REPOSITORY_NAME

$ docker push REPOSITORY_URI:latest

Create Function

Now we can create the lambda function from the image already stored in the ECR.

To do this, run:

Atention!

Change the function_name and image_uri.

The image_uri will follow the pattern <repositoryUri>:<imageTag>, for example: 123456789012.dkr.ecr.us-east-2.amazonaws.com/test1-mlops-joaoxr:latest

Tip! 2

You can copy the image_uri from the last docker push command!

import boto3
import os
from dotenv import load_dotenv

load_dotenv()

# Provide function name: "ex_docker_<INSPER_USERNAME>"
function_name = ""

# Provide Image URI from before
image_uri = ""

lambda_role_arn = os.getenv("AWS_LAMBDA_ROLE_ARN")

# Create a Boto3 client for AWS Lambda
lambda_client = boto3.client(
    "lambda",
    aws_access_key_id=os.getenv("AWS_ACCESS_KEY_ID"),
    aws_secret_access_key=os.getenv("AWS_SECRET_ACCESS_KEY"),
    region_name=os.getenv("AWS_REGION"),
)

response = lambda_client.create_function(
    FunctionName=function_name,
    PackageType="Image",
    Code={"ImageUri": image_uri},
    Role=lambda_role_arn,
    Timeout=30,  # Optional: function timeout in seconds
    MemorySize=128,  # Optional: function memory size in megabytes
)

print("Lambda function created successfully:")
print(f"Function Name: {response['FunctionName']}")
print(f"Function ARN: {response['FunctionArn']}")

Question 5

Use the codes from the previous class as a reference and test the function.

Call the lambda function directly, without creating an API in API Gateway.

Proceed to the APS!

References