Skip to main content

AWS Lambda Function with Docker Container: Run containerized Python Code, AWS CloudFormation Version

885 words·
AWS AWS CLI CloudFormation Lambda Elastic Container Registry Docker Python
Table of Contents

Python Docker Container
#

Project Folder
#

# Create project folder
TF_PROJECT_NAME=python-email-validator
mkdir $TF_PROJECT_NAME && cd $TF_PROJECT_NAME

The file and folder structure looks like this:

python-email-validator
├── app.py
├── Dockerfile

Dockerfile
#

  • Dockerfile
# Lambda base image for Docker
FROM public.ecr.aws/lambda/python:latest

# Copy the application code into the container
COPY app.py .

# Set the Lambda handler (should match function name in app.py)
CMD ["app.validate"]

Python App
#

  • app.py
import json
import re

def validate(event, context):
    event_body = json.loads(event['body'])
    email_regex = re.compile('^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$')
    matches = email_regex.match(event_body['email']) != None

    response = {
        'statusCode': 200,
        'body': json.dumps({ 'result': matches })
    }

    return response

Build Docker Image
#

# Build container image
docker build -t python-email-validator .



CloudFormation Configuration
#

Project Folder
#

# Create project folder
TF_PROJECT_NAME=aws-ecr-lambda-cloudformation
mkdir $TF_PROJECT_NAME && cd $TF_PROJECT_NAME

The file and folder structure looks like this:

aws-ecr-lambda-cloudformation
├── ecr-cloudformation.yaml
└── lambda-cloudformation.yaml

ECR Configuration
#

  • ecr-cloudformation.yaml
AWSTemplateFormatVersion: "2010-09-09"

Parameters:
  RepositoryName:
    Type: String
    Default: lambda-docker-example
    Description: "Name of the ECR repository"


Resources:
  ECRRepository:
    Type: AWS::ECR::Repository
    Properties:
      EmptyOnDelete: true
      EncryptionConfiguration:
        EncryptionType: AES256
      ImageScanningConfiguration:
        ScanOnPush: true
      ImageTagMutability: MUTABLE
      LifecyclePolicy:
        LifecyclePolicyText: |
          {
            "rules": [
              {
                "rulePriority": 1,
                "description": "Expire untagged images older than 14 days",
                "selection": {
                  "tagStatus": "untagged",
                  "countType": "sinceImagePushed",
                  "countUnit": "days",
                  "countNumber": 14
                },
                "action": {
                  "type": "expire"
                }
              }
            ]
          }          
      RepositoryName: !Ref RepositoryName


Outputs:
  ECRRepositoryUri:
    Value: !GetAtt ECRRepository.RepositoryUri
    Description: "ECR repository URI"

LambdaConfiguration
#

  • lambda-cloudformation.yaml
AWSTemplateFormatVersion: "2010-09-09"

Parameters:
  RepositoryName:
    Type: String
    Default: lambda-docker-example
    Description: "Name of the ECR repository"


Resources:
  LambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: jkw-lambda-example
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: 
                - lambda.amazonaws.com
            Action: 
              - sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

  LambdaFunction:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: lambda-email-validator
      PackageType: Image
      Code:
        ImageUri: 
          !Sub "${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${RepositoryName}:latest"
      Role: !GetAtt LambdaExecutionRole.Arn
      Timeout: 15
      MemorySize: 128  # Default size


Outputs:
  LambdaFunctionArn:
    Value: !GetAtt LambdaFunction.Arn
    Description: "Lambda function ARN"



ECR
#

Create Stack
#

# Create the ECR stack
aws cloudformation create-stack \
    --stack-name ecr-stack \
    --template-body file://ecr-cloudformation.yaml \
    --region eu-central-1

# Shell output:
{
    "StackId": "arn:aws:cloudformation:eu-central-1:073526172187:stack/ecr-stack/9954b650-e543-11ef-b3f0-023c814d0f7b"
}

Monitor the Deployment
#

Wait till “StackStatus” changes to “CREATE_COMPLETE”:

# Monitor the deployment
aws cloudformation describe-stacks \
  --stack-name ecr-stack \
  --region eu-central-1 \
  --no-cli-pager

# Shell output:
{
    "Stacks": [
        {
            "StackId": "arn:aws:cloudformation:eu-central-1:073526172187:stack/ecr-stack/9954b650-e543-11ef-b3f0-023c814d0f7b",
            "StackName": "ecr-stack",
            "Parameters": [
                {
                    "ParameterKey": "RepositoryName",
                    "ParameterValue": "lambda-docker-example"
                }
            ],
            "CreationTime": "2025-02-07T11:06:37.018000+00:00",
            "RollbackConfiguration": {},
            "StackStatus": "CREATE_COMPLETE",
            "DisableRollback": false,
            "NotificationARNs": [],
            "Outputs": [
                {
                    "OutputKey": "ECRRepositoryUri",
                    "OutputValue": "073526172187.dkr.ecr.eu-central-1.amazonaws.com/lambda-docker-example",
                    "Description": "URI of the created ECR repository"
                }
            ],
            "Tags": [],
            "EnableTerminationProtection": false,
            "DriftInformation": {
                "StackDriftStatus": "NOT_CHECKED"
            }
        }
    ]
}

Outputs
#

Verify the ECR URL:

# Verify the ECR URL:
aws cloudformation describe-stacks \
    --stack-name ecr-stack \
    --query "Stacks[0].Outputs" \
    --region eu-central-1

# Shell output:
[
    {
        "OutputKey": "ECRRepositoryUri",
        "OutputValue": "073526172187.dkr.ecr.eu-central-1.amazonaws.com/lambda-docker-example",
        "Description": "URI of the created ECR repository"
    }
]

Docker Image
#

Docker Login
#

Login to the ECR repository:

# Retrieve an authentication token and authenticate the Docker client
aws ecr get-login-password --region eu-central-1 | docker login --username AWS --password-stdin 073526172187.dkr.ecr.eu-central-1.amazonaws.com/lambda-docker-example

# Shell output:
WARNING! Your password will be stored unencrypted in /home/ubuntu/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credential-stores

Login Succeeded

Tag the Image for AWS ECR
#

Tag the previously created Docker image to associate it with the ECR repository:

# Tag the local Docker image
docker tag python-email-validator:latest 073526172187.dkr.ecr.eu-central-1.amazonaws.com/lambda-docker-example:latest

Verify the image:

# List Docker images
docker images

# Shell output:
REPOSITORY                                                              TAG       IMAGE ID       CREATED         SIZE
073526172187.dkr.ecr.eu-central-1.amazonaws.com/lambda-docker-example   latest    5dae1b122e72   6 minutes ago   515MB
python-email-validator                                                  latest    5dae1b122e72   6 minutes ago   515MB

Push the Image to ECR
#

# Push the image to ECR
docker push 073526172187.dkr.ecr.eu-central-1.amazonaws.com/lambda-docker-example:latest

# Shell output:
The push refers to repository [073526172187.dkr.ecr.eu-central-1.amazonaws.com/lambda-docker-example]
cc88962b2437: Pushed
d40d1b6fe31d: Pushed
2195ebbc1042: Pushed
0091b0f618e4: Pushed
4225886892d5: Pushed
393348208dca: Pushed
6281cd02ff74: Pushed
latest: digest: sha256:644a8c8a754632b76c141299dfd546c4db846c172f9b3cb849f3855a2b5651f9 size: 1785



Lambda Function
#

Create Stack
#

# Create the Lambda stack
aws cloudformation create-stack \
    --stack-name lambda-stack \
    --template-body file://lambda-cloudformation.yaml \
    --capabilities CAPABILITY_NAMED_IAM \
    --region eu-central-1

# Shell output:
{
    "StackId": "arn:aws:cloudformation:eu-central-1:073526172187:stack/lambda-stack/7f7f9820-e544-11ef-8a1a-0a91f6b8750b"
}

Monitor the Deployment
#

Wait till “StackStatus” changes to “CREATE_COMPLETE”:

# Monitor the deployment
aws cloudformation describe-stacks \
  --stack-name lambda-stack \
  --region eu-central-1 \
  --no-cli-pager

# Shell output:
{
    "Stacks": [
        {
            "StackId": "arn:aws:cloudformation:eu-central-1:073526172187:stack/lambda-stack/7f7f9820-e544-11ef-8a1a-0a91f6b8750b",
            "StackName": "lambda-stack",
            "Parameters": [
                {
                    "ParameterKey": "RepositoryName",
                    "ParameterValue": "lambda-docker-example"
                }
            ],
            "CreationTime": "2025-02-07T11:13:03.213000+00:00",
            "RollbackConfiguration": {},
            "StackStatus": "CREATE_COMPLETE",
            "DisableRollback": false,
            "NotificationARNs": [],
            "Capabilities": [
                "CAPABILITY_NAMED_IAM"
            ],
            "Outputs": [
                {
                    "OutputKey": "LambdaFunctionArn",
                    "OutputValue": "arn:aws:lambda:eu-central-1:073526172187:function:lambda-email-validator",
                    "Description": "Lambda function ARN"
                }
            ],
            "Tags": [],
            "EnableTerminationProtection": false,
            "DriftInformation": {
                "StackDriftStatus": "NOT_CHECKED"
            }
        }
    ]
}

Test Lambda Function
#

Example Payload
#

# Install jq for encoding
Install jq
# Create example payload files
echo '{"body": "{\"email\": \"test@example.com\"}"}' > payload1.json
echo '{"body": "{\"email\": \"test.example.com\"}"}' > payload2.json

# Encode payload files
cat payload1.json | base64 | tr -d '\n' > payload1_base64.json
cat payload2.json | base64 | tr -d '\n' > payload2_base64.json

Invoke Lambda Function
#

# Invoke the Lambda function
aws lambda invoke --function-name lambda-email-validator \
  --region eu-central-1 \
  --payload file://payload1_base64.json\
  response1.json

# Invoke the Lambda function
aws lambda invoke --function-name lambda-email-validator \
  --region eu-central-1 \
  --payload file://payload2_base64.json \
  response2.json


# Shell output:
{
    "StatusCode": 200,
    "ExecutedVersion": "$LATEST"
}

Verify the output:

# Cat the response files:
cat response1.json
cat response2.json

# Shell output:
{"statusCode": 200, "body": "{\"result\": true}"}
{"statusCode": 200, "body": "{\"result\": false}"}



Cleanup
#

# Delete the Lambda stack
aws cloudformation delete-stack \
  --stack-name lambda-stack \
  --region eu-central-1
# Delete the ECR stack
aws cloudformation delete-stack \
  --stack-name ecr-stack \
  --region eu-central-1