Working with layers for Ruby Lambda functions
A Lambda layer is a .zip file archive that contains supplementary code or data. Layers usually contain library dependencies, a custom runtime, or configuration files. Creating a layer involves three general steps:
-
Package your layer content. This means creating a .zip file archive that contains the dependencies you want to use in your functions.
-
Create the layer in Lambda.
-
Add the layer to your functions.
This topic contains steps and guidance on how to properly package and create a Ruby Lambda layer with external library dependencies.
Topics
Prerequisites
To follow the steps in this section, you must have the following:
-
Ruby 3.3
, which is distributed with the gem
package installer.
Throughout this topic, we reference the layer-ruby
In the layer-ruby
sample application, you package the tzinfo
layer/
directory contains the scripts to generate the layer. The
function/
directory contains a sample function to help test that the layer works. The majority
of this tutorial walks through how to create and package this layer.
Ruby layer compatibility with the Lambda runtime environment
When you package code in a Ruby layer, you specify the Lambda runtime environments that the code is compatible with. To assess code compatibility with a runtime, consider what versions of Ruby, what operating systems, and what instruction set architectures the code is designed for.
Lambda Ruby runtimes specify their Ruby version and operating system. In this document, you use the
Ruby 3.3 runtime, which is based on AL2023. For more information about runtime versions, see Supported runtimes. When you create a Lambda function, you specify the instruction set
architecture. In this document, you use the x86_64
architecture. For more information
about architectures in Lambda, see Selecting and configuring an instruction set architecture for your Lambda function.
When you use code provided in a package, each package maintainer independently defines their compatibility. Most gems are written completely in Ruby, and are compatible with any runtime using a compatible Ruby language version.
Sometimes, gems use a Ruby feature called extensions to compile code or include precompiled code as part
of their install process. If you depend on a gem with a native extension, you must assess operating system
and instruction set architecture compatibility. To assess the compatibility between your gems and your Ruby
runtime, you need to inspect your gems and their documentation. You can identify if your gem uses
extensions by checking whether extensions
is defined in its Gem specification. Ruby identifies
the platform it is running on through the RUBY_PLATFORM
global constant. The Lambda Ruby runtime
defines the platform as aarch64-linux
when running on the arm64
architecture, or
x86_64-linux
when running on the x86_64
architecture. There is no guaranteed way
to check if a gem is compatible with these platforms, but some gems declare their supported platforms
through the platform
Gem specification attribute.
Layer paths for Ruby runtimes
When you add a layer to a function, Lambda loads the layer content into the
/opt
directory of that execution environment. For each Lambda runtime,
the PATH
variable already includes specific folder paths within the
/opt
directory. To ensure that Lambda picks up your layer content,
your layer .zip file should have its dependencies
in the following folder paths:
-
ruby/gems/
, where x is the Ruby version on your runtime, such asx
3.3.0
. -
ruby/lib/
In this document, use the ruby/gems/
path. Lambda expects the
contents of this directory to correspond to the structure of a Bundler install directory. Store your gem
dependencies in the x
/gems
subdirectory of the layer path, alongside other metadata
subdirectories. For example, the resulting layer .zip file that you create in this tutorial has the
following directory structure:
layer_content.zip
└ ruby
└ gems
└ 3.3.0
└ gems
└ tzinfo-2.0.6
└ <other_dependencies> (i.e. dependencies of the tzinfo package)
└ ...
└ <metadata generated by bundle>
The tzinfo
library is correctly located in the ruby/gems/3.3.0/
directory. This ensure that Lambda can locate the library during function invocations.
Packaging the layer content
In this example, you package the Ruby tzinfo
library in a layer .zip
file. Complete the following steps to install and package the layer content.
To install and package your layer content
-
Clone the
aws-lambda-developer-guide
GitHub repo, which contains the sample code that you need in the sample-apps/layer-ruby
directory.git clone https://github.com/awsdocs/aws-lambda-developer-guide.git
-
Navigate to the
layer
directory of thelayer-ruby
sample app. This directory contains the scripts that you use to create and package the layer properly.cd aws-lambda-developer-guide/sample-apps/layer-ruby/layer
-
Examine the
Gemfile
. This file defines the dependencies that you want to include in the layer, namely the tzinfo
library. You can update this file to include any dependencies that you want to include in your own layer.Example Gemfile
source "https://rubygems.org" gem "tzinfo"
-
Ensure that you have permissions to run both scripts.
chmod 744 1-install.sh && chmod 744 2-package.sh
-
Run the
1-install.sh
script using the following command: ./1-install.sh
This script configures bundler to install dependencies in your project directory. It then installs all required dependencies in the
vendor/bundle/
directory.Example 1-install.sh
bundle config set --local path 'vendor/bundle' bundle install
-
Run the
2-package.sh
script using the following command: ./2-package.sh
This script copies the contents from the
vendor/bundle
directory into a new directory namedruby
. It then zips the contents of theruby
directory into a file namedlayer_content.zip
. This is the .zip file for your layer. You can unzip the file and verify that it contains the correct file structure, as shown in the Layer paths for Ruby runtimes section.Example 2-package.sh
mkdir -p ruby/gems/3.3.0 cp -r vendor/bundle/ruby/3.3.0/* ruby/gems/3.3.0/ zip -r layer_content.zip ruby
Creating the layer
In this section, you take the layer_content.zip
file that you
generated in the previous section and upload it as a Lambda layer. You can upload a layer using
the Amazon Web Services Management Console or the Lambda API via the Amazon Command Line Interface (Amazon CLI). When you upload your layer .zip
file, in the following PublishLayerVersion Amazon CLI command, specify
ruby3.3
as the compatible runtime and arm64
as the compatible
architecture.
aws lambda publish-layer-version --layer-name ruby-requests-layer \ --zip-file fileb://layer_content.zip \ --compatible-runtimes ruby3.3 \ --compatible-architectures "arm64"
From the response, note the LayerVersionArn
, which looks like
arn:aws:lambda:us-east-1:
.
You'll need this Amazon Resource Name (ARN) in the next step of this tutorial, when you add
the layer to your function.123456789012
:layer:ruby-requests-layer:1
Adding the layer to your function
In this section, you deploy a sample Lambda function that uses the tzinfo
library in its function code, then you attach the layer. To deploy the function, you need a
Defining Lambda function permissions with an execution role. If you don't have an existing execution role,
follow the steps in the collapsible section.
To create an execution role
-
Open the roles page
in the IAM console. -
Choose Create role.
-
Create a role with the following properties.
-
Trusted entity – Lambda.
-
Permissions – AWSLambdaBasicExecutionRole.
-
Role name –
lambda-role
.
The AWSLambdaBasicExecutionRole policy has the permissions that the function needs to write logs to CloudWatch Logs.
-
The Lambda function codetzinfo
library, then returns the status code and a localized date string.
require 'json' require 'tzinfo' def lambda_handler(event:, context:) tz = TZInfo::Timezone.get('America/New_York') { statusCode: 200, body: tz.to_local(Time.utc(2018, 2, 1, 12, 30, 0)) } end
To deploy the Lambda function
-
Navigate to the
function/
directory. If you're currently in thelayer/
directory, then run the following command:cd ../function
-
Create a .zip file deployment package using the following command:
zip my_deployment_package.zip lambda_function.rb
-
Deploy the function. In the following Amazon CLI command, replace the
--role
parameter with your execution role ARN:aws lambda create-function --function-name ruby_function_with_layer \ --runtime ruby3.3 \ --architectures "arm64" \ --handler lambda_function.lambda_handler \ --role
arn:aws:iam::
\ --zip-file fileb://my_deployment_package.zip123456789012
:role/lambda-role -
Next, attach the layer to your function. In the following Amazon CLI command, replace the
--layers
parameter with the layer version ARN that you noted earlier:aws lambda update-function-configuration --function-name ruby_function_with_layer \ --cli-binary-format raw-in-base64-out \ --layers "
arn:aws:lambda:us-east-1:
"123456789012
:layer:ruby-requests-layer:1 -
Finally, try to invoke your function using the following Amazon CLI command:
aws lambda invoke --function-name ruby_function_with_layer \ --cli-binary-format raw-in-base64-out \ --payload '{ "key": "value" }' response.json
You should see output that looks like this:
{ "StatusCode": 200, "ExecutedVersion": "$LATEST" }
The output
response.json
file contains details about the response.
You can now delete the resources that you created for this tutorial, unless you want to retain them. By deleting Amazon resources that you're no longer using, you prevent unnecessary charges to your Amazon Web Services account.
To delete the Lambda layer
-
Open the Layers page
of the Lambda console. -
Select the layer that you created.
-
Choose Delete, then choose Delete again.
To delete the Lambda function
-
Open the Functions page
of the Lambda console. -
Select the function that you created.
-
Choose Actions, Delete.
-
Type
confirm
in the text input field and choose Delete.