After hard effort, using David Conde's idea, I managed to find some solution for it. What we want to achieve is uploading the lambda's zip (with all of it's dependencies) once and point all the lambdas to use this zip.
This process is seperated into couple of steps which I'll try to describe in much
details as I can. Some of them might not be relevant exactly for your case.
General idea
The general idea is to create a layer which contains the code we want to upload once. Then, for each lambda, specify it uses this layer. Each lambda will have it handler points to somewhere in the source directory. Hence, running code "from the layer". But! we must specify code for the lambda to run/attach a zip - even if it's not what going to run. To skip that, we are going to attach it an "empty" code.
Build folder
First, create a build folder where we are going to work, for example: mkdir -p .build/
In addition, define the following variables:
s3_bucket="aaaaaaaa"
s3_prefix="aaaaaaaa"
s3_lambda_zip_suffix="$s3_prefix/lambda.zip"
s3_lambda_zip="s3://$s3_bucket/$s3_lambda_zip_suffix"
Creating the source zip
When lambda is unzipped, it's content is written to the working directory. When a layer is unzipped, it is unzipped into /opt
as documented in AWS. Because our lambda needs to find our source code, which is "a depndencie" it needs to find it under /opt
. To achieve it, we need it to be unzipped into /opt/python
. We can do that by zipping python/...
into a zip file.
First, we create the folder we will install the dependencies into the python folder:
mkdir -p .build/lambda_zip/python
pip3 install -q --target .build/lambda_zip/python -r requirements.txt
Then we zip it:
pushd .build/lambda_zip/ > /dev/null
zip --quiet -r ./lambda.zip ./python
popd > /dev/null
Now, you probably want to add your src direcory:
zip --quiet -r .build/lambda_zip/lambda.zip src
#Uploading to S3
Now, we have to upload the zip into S3 for our lambdas to load it.
aws s3 cp ".build/lambda_zip/lambda.zip" $s3_lambda_zip_path
Adding layer to template.yaml
Now, we need to add the layer into our template.yaml
file, you can copy the following code after you read at AWS documentation:
Parameters:
LambdaCodeUriBucket:
Type: String
LambdaCodeUriKey:
Type: String
Resources:
OnUpHealthLayer:
Type: AWS::Lambda::LayerVersion
Properties:
CompatibleRuntimes:
- python3.8
Content:
S3Bucket: !Sub '${LambdaCodeUriBucket}'
S3Key: !Sub '${LambdaCodeUriKey}'
Create empty zip for the lambdas
Cloudformation must upload zip for lambdas, so we want it to create an empty zip. But it scans the dependencies from the requirements.txt
file in the directory the same as the one of template.yaml
. And we want it to upload something empty. Hence it must be in another folder.
To solve it, I copy the template.yaml
to an empty directory and add empty requirements.txt
file. After that, we can run sam build
and sam deploy
as usuall. Notice that we must pass it LambdaCodeUriBucket
and LambdaCodeUriKey
:
#create "empty" environment for the template to be built in
mkdir -p .build/empty_template
cp template.yaml .build/empty_template
pushd .build/empty_template > /dev/null
touch requirements.txt
sam build --template template.yaml
sam deploy \
--template-file .aws-sam/build/template.yaml \
--capabilities "CAPABILITY_IAM" \
--region $region \
--s3-bucket $s3_bucket \
--s3-prefix $s3_prefix \
--stack-name $stack_name \
--parameter-overrides LambdaCodeUriBucket=$s3_bucket LambdaCodeUriKey=$s3_lambda_zip_suffix
popd > /dev/null
Notice that although we copied the template.yaml and called sam build
on the new one, we already uploaded to s3 the zip file.
Important thing you must do is specify .
as the CodeUri
for your lambdas. Because they now use the "empty zip".
In the future, we will be able to do:
InlineCode: |
def handler(event, context):
pass
And not specify folder .
.
But, currently sam
doesn't support inline code for python3.8
hence we use .
. Anyway you will have to move it to seperate folder to remove it's dependencies.