The first thing we need to have is access to AWS. You will have to sign up and provide your credit card details for account creation. AWS Lambda is essentially a language-specific environment that runs in an execution environment called runtime. This runtime passes events, context information and responses between other lambdas as well.
Install Python & PIP
Python should be most probably installed on your computer. You can test by running
python --version
or python3 --version
. Or, you can install Python from here.
Try running pip --version
if it's installed, if not then you can install it from here.
Installing Docker
I installed Docker on my iMac using the GUI package as it would handle setting all the configurations and paths correctly. If the GUI installer doesn't work then try the CLI installer. Download Docker here.
Installing AWS CLI
We would need the AWS CLI to push the docker container to the AWS ECR which is just a registry or repository for storing the images. Download the GUI installer from here. This completes our setup.
Building the App
Create a Dockerfile
# source the base image
FROM public.ecr.aws/lambda/python:3.9
# copy function code
COPY app.py ${LAMBDA_TASK_ROOT}
# each required library is listed as a separate line
COPY requirements.txt .
RUN pip3 install -r requirements.txt
# set the cmd to the handler
CMD ["app.handler"]
Create a Lambda Function
We will create a simple function that returns the parameter it receives. This way we can know the information our lambda function receives when it is invoked. Also, we create a requirements.txt and add jsonpickle
as a dependency.
- Event is a dict that contains the parameters which are sent when the function is invoked
- Context param is the context in which the function is called
# We are referring to the filename and function
# name in Dockerfile's CMD statement ["<filename>.<function-name>"]
import jsonpickle
def handler(event, context):
return {
"EVENT": jsonpickle.encode(event),
"CONTEXT": jsonpickle.encode(context)
}
jsonpickle==1.3
Running Lambda Container Locally
Build
docker build -t <container-name> .
e.g. if we name our container raring-rusty then the cmd will look like this:
docker build -t raring-rusty .
After the build is complete, we can quickly verify by hitting the following command.
docker images
It should output something like this:
➜ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
raring-rusty latest a0ea76e99b97 1 minute ago 595MB
Before we can run the docker, we have to install a Runtime Interface Client which defines an HTTP interface for runtimes to receive invocation events from Lambda and respond.
Testing Lambda Invocation
Install RIC
We need this package to allow your code to interact with the Lambda service.
pip install awslambdaric
Now, let's run the docker image locally to verify if it is working ok.
# docker run -p 9000:8080 <container-name>:latest
docker run -p 9000:8080 raring-rusty:latest
Using Postman
You can install Postman(a dev tool for making HTTP requests) from here and test the lambda function before deploying it to AWS ECR.
URL
http://localhost:9000/2015-03-31/functions/function/invocations
Method
POST
Body -
- Select
raw
as the body type andjson
as the format
{ "is_cat": "true" }
Authorization
No Auth
Using CURL
curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"is_cat":"true"}'
Result
At this point, you should get a response back from your lambda function. Mine looked like the below:
{
"EVENT": "{\"is_cat\": \"true\"}",
"CONTEXT": "{\"_epoch_deadline_time_in_ms\": 1657690206053, \"aws_request_id\": \"048c7493-7487-4af8-9b28-6a774ccfad8c\", \"client_context\": null, \"function_name\": \"test_function\", \"function_version\": \"$LATEST\", \"identity\": {\"cognito_identity_id\": null, \"cognito_identity_pool_id\": null, \"py/object\": \"awslambdaric.lambda_context.CognitoIdentity\"}, \"invoked_function_arn\": \"arn:aws:lambda:us-east-1:012345678912:function:test_function\", \"log_group_name\": \"/aws/lambda/Functions\", \"log_stream_name\": \"$LATEST\", \"memory_limit_in_mb\": \"3008\", \"py/object\": \"awslambdaric.lambda_context.LambdaContext\"}"
}
Pushing to ECR
Pushing to ECR takes a few steps, I usually have static parts of the commands in my zsh_aliases
file to speed up the dev. Because some of the docker commands are quite lengthy.
Build the Docker Image
docker build -t raring-rusty .
Authenticate ECR
Replace the aws_account_id with your account ID and region. A simpler way is to go to the AWS ECR page and create a new repository, then copy the URL from the URI column in the table.
aws ecr get-login-password --region region | docker login --username AWS --password-stdin <aws_account_id>.dkr.ecr.<region>.amazonaws.com
If this runs well, you should see a response of Login Succeeded
Tag the Docker Image
In your ECR repository, the image tag is a good way to identify what change you have probably pushed or you can use the tag name in all sorts of creative ways too. When I am collaborating with someone on the same repository, I tag the image with my initials to know which ones contain my changes. Or you can use tag names to individually identify different components you are pushing, e.g. you have multiple lambdas etc.
# fetch the docker image id by running docker images e.g. e9ae3c220b23 or raring-rusty:latest
# Copy the URL of the repo from the ECR page or create your own, but make sure the repository exists.
# add the tag of your choice
docker tag <docker-image-id> <aws_account_id>.dkr.ecr.<region>.amazonaws.com/<your-repository-name>:<tag>
After running this, when you run docker image
you should see another image showing up the URL as the image name with the tag you just created.
Final Push
We now push the docker image using the docker push
command. Some basic fails on this command arise from the repository not being created or having incorrect AWS credentials in your ~/.aws/credentials file. And lastly, you can verify the upload by going to the AWS ECR portal.
docker push <aws_account_id>.dkr.ecr.<region>.amazonaws.com/<your-repository-name>:<tag>
#outputs
The push refers to a repository [12341234.dkr.ecr.ap-south-1.amazonaws.com/my-repo]
095805977547: Layer already exists
3aaa42118c17: Layer already exists
d36909758ca1: Layer already exists
12b130f8cdef: Layer already exists
2140a70c0132: Layer already exists
11b26d093gcd: Layer already exists
42c27e6des9c: Layer already exists
60beda9b7a8d: Layer already exists
baef07789503: Layer already exists
my-tag: digest: sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxx09ec3 size: 2205