Update - Building Lambda Layers with AWS SAM
Last year, I posted an article exploring AWS Lambda support for Ruby and Lambda Layers. Since that time AWS has released support for Ruby 2.7 and AWS SAM introducd support for building layers.
In this post, I will revisit the build process used to build a Ruby layer, but this time using the build support available in SAM.
When including a layer in your SAM template file, you can now include a Metadata
resource that defined the build method for the layer. Two build methods are supported: (1) a Lambda runtime or (2) a Makefile. When including the Metadata
attribute on a AWS::Serverless::LambdaLayer
resource, the sam build
command will build the layer either as an independent object or as a dependency of a Lambda function. The layer will also work when running your functions locally using SAM CLI.
To define a Lambda Layer version in your SAM template:
Resources:
RubyLayer:
Type: AWS::Serverless::LayerVersion
Properties:
ContentUri: ./ruby_layer
CompatibleRuntimes:
- ruby2.7
Metadata:
BuildMethod: makefile
Unfortunately, I've found that the default ruby2.7
build process does not properly package the layer and have opened an issue on Github to fix. Using the work from my earlier post, I was able to easily use the Makefile approach to properly package a layer that contains both Gems and shared code.
To use the Custom Makefile build, we need to include a Makefile
in the layer ContentUri
with a target named build-{MyLayerResourceName}
. Following the example above, my target would be named build-RubyLayer
. Before we get to the contents of that target, a quick look at the contents of the layer directory:
ruby_layer/
|-- Gemfile
|-- Makefile
|-- lib/
|-- { common directory }/
|-- { common file }.rb
In the lib/
directory, include any shared Ruby code. This may include model classes or utility code used by Lambda functions that leverage this layer.
The Gemfile
is a standard Gemfile, including the various Gems to be bundled with the layer. Manage this Gemfile as you would any others.
The Makefile
contains a single target that is run by the sam build
command to build and package your layer. Again, the name of the target in this file will vary based on the name of the layer, but the remainder should be the same for the directory structure above.
build-RubyLayer:
bundle install --path=$(ARTIFACTS_DIR)
rm -rf $(ARTIFACTS_DIR)/ruby/2.7.0/cache && rm -rf $(ARTIFACTS_DIR)/ruby/2.7.0/bin
mkdir $(ARTIFACTS_DIR)/ruby/gems
mv $(ARTIFACTS_DIR)/ruby/2.7.0 $(ARTIFACTS_DIR)/ruby/gems
mkdir $(ARTIFACTS_DIR)/ruby/lib
cp -r lib/* $(ARTIFACTS_DIR)/ruby/lib
For further details on the package structure see my previous post and AWS documentation.
The resulting layer will contain bundled Gems in the path ruby/gems/2.7.0
(GEM_PATH
) and shared libraries/code in ruby/lib
(RUBYLIB
). Because the structure of Gems is slightly different than the desired GEM_PATH
, we need to flip the directory structure.
With the Layer defined, you can now reference it from Lambda functions in your SAM template file and use sam build
to build both the function and layer. This functionality is supported with the latest version of the SAM CLI.
MyRubyFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: my-ruby-function/
Layers:
- !Ref RubyLayer
...
For a working version of this approach, see the project associated with my previous post on Amazon API Gateway Lambda Authorizers.