<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Vittorio Nardone</title>
    <description>The latest articles on DEV Community by Vittorio Nardone (@vittorionardone).</description>
    <link>https://dev.to/vittorionardone</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F484462%2F54d79c10-71d7-44b4-8fc0-232f07be2389.png</url>
      <title>DEV Community: Vittorio Nardone</title>
      <link>https://dev.to/vittorionardone</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vittorionardone"/>
    <language>en</language>
    <item>
      <title>Properly use of AWS Lambda layers</title>
      <dc:creator>Vittorio Nardone</dc:creator>
      <pubDate>Wed, 07 Oct 2020 16:46:49 +0000</pubDate>
      <link>https://dev.to/vittorionardone/properly-use-of-aws-lambda-layers-10k4</link>
      <guid>https://dev.to/vittorionardone/properly-use-of-aws-lambda-layers-10k4</guid>
      <description>&lt;p&gt;What are &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html"&gt;AWS Lambda layers&lt;/a&gt;? As we know, AWS Lambda functions allow to execute code in the cloud according to the serverless paradigm. Each serverless cloud application is normally characterized by multiple independent Lambda functions capable of responding to specific events (like rest API, scheduled, triggers). Each Lambda function is defined by its own deployment package which contains its source code and any requirements, such as additional libraries, dependencies and middleware.&lt;/p&gt;

&lt;p&gt;In this type of architecture, the AWS Lambda layers allow to introduce the concept of code/dependency reusability, in order to share modules among different functions: the layers are simple packages that can be reused in Lambda and they actually extend the base runtime. Let's see how to use layers.&lt;/p&gt;

&lt;h2&gt;
  
  
  AWS Lambda layers 101
&lt;/h2&gt;

&lt;p&gt;How do you prepare an AWS Lambda layer? To show it let's consider this example: a Lambda built in Python that requires to run a binary application not included in the standard AWS runtime. In our example, the application is a simple bash script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash &lt;/span&gt;

&lt;span class="c"&gt;# This is version.sh script&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Hello from layer!"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To create the Layer we need to prepare a ZIP archive with the following structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;layer.zip 
└ bin/version.sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;From the AWS Lambda console we just need to create the Layer providing the source ZIP archive and compatible runtimes names.&lt;br&gt;
Now suppose to create a Lambda function that uses that layer. The code might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;json&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;os&lt;/span&gt;

    &lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;popen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'version.sh'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;'statusCode'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;'body'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Message from script: {}'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Its output will be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"statusCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Message from script: Hello from layer!&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;n&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Our AWS Lambda function correctly executes the bash script included in the layer. This happens because the contents of the Layer are extracted into the &lt;strong&gt;/opt&lt;/strong&gt; folder. Since we used the structure provided by AWS to build the deployment ZIP archive, our bash script is already included in the default PATH (/opt/bin). Great!&lt;/p&gt;

&lt;p&gt;Considering a more complete example, a project in Python I mentioned in another post: &lt;a href="https://www.vittorionardone.it/en/2020/06/04/chromium-and-selenium-in-aws-lambda/"&gt;&lt;strong&gt;using Chromium and Selenium in an AWS Lambda function&lt;/strong&gt;&lt;/a&gt;.&lt;br&gt;
To use Chromium in a Lambda function, you need to include the binaries and related libraries in the deployment package, as AWS obviously doesn't include them in the standard Python runtime. My first approach was to not use any layers by obtaining a single ZIP package of more than 80MB. Whenever I wanted to update my Lambda function code, I was forced to upload the entire package, resulting in a long wait. Considering the number of times I repeated the operation during the development phase of the project and that the source of the function was a very small part of the whole package (a few lines of code), I realize how much time I wasted!&lt;/p&gt;

&lt;p&gt;The second, much smarter approach was to use an AWS Lambda layer to include the binaries of &lt;a href="https://www.chromium.org/Home"&gt;Chromium&lt;/a&gt; and all the Python packages required in a similar way to what was seen above. The structure is this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;layer.zip
└ bin
  └ chromium
    chromedriver    
    fonts.conf      
    lib
    └ ...        
└ python
  └ selenium
    selenium-3.14.0.dist-info
    ...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To install the Python packages I used the usual PIP command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip3 install -r requirements.txt -t python
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Once the layer was created, the time required for deploying the function was significantly reduced, all in favor of productivity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Some more information on AWS Lambda Layers&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Can be used by multiple Lambdas&lt;/li&gt;
&lt;li&gt;Can be updated and a new version is created each time&lt;/li&gt;
&lt;li&gt;Versions are automatically numbered from 1 up&lt;/li&gt;
&lt;li&gt;Can be shared with other AWS Accounts and made public&lt;/li&gt;
&lt;li&gt;Are specific to an AWS Region&lt;/li&gt;
&lt;li&gt;If there are multiple layers in a Lambda, they are "merged" together in the specified order, overwriting any files already present&lt;/li&gt;
&lt;li&gt;A function can use up to 5 levels at a time&lt;/li&gt;
&lt;li&gt;Do not allow exceed &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/gettingstarted-limits.html"&gt;the limit of the size&lt;/a&gt; of the AWS distribution package&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When to Use AWS Lambda Layers
&lt;/h2&gt;

&lt;p&gt;In my specific case, using a layer has brought great benefits by reducing deployment times. So I've wondered if it's always a good idea to use AWS Lambda layers. Spoiler alert: the answer is no!&lt;/p&gt;

&lt;p&gt;There are two main reasons for using layers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the reduction of the size of AWS Lambda deployment packages&lt;/li&gt;
&lt;li&gt;the reusability of code, middleware and binaries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This last point is the most critical: what happens to Lambda functions during the lifecycle of the layers they depend on?&lt;br&gt;
&lt;strong&gt;Layers can be deleted&lt;/strong&gt;: removing a layer does not cause problems for the functions that already use it. It is possible to modify the code of the function but, if it is necessary to modify the levels on which it depends, the dependence on the layer no longer available must be removed.&lt;br&gt;
&lt;strong&gt;Layers can be upgraded&lt;/strong&gt;: creating a new version of a layer does not cause problems for functions that use previous versions. &lt;strong&gt;However, the lambda updating process is not automatic&lt;/strong&gt;: if necessary, the new layer version must be specified in the lambda definition, removing the previous one first. Although the use of layers can therefore allow the distribution of fixes and security patches related to common Lambda components, it must be taken into account that this process is not completely automated.&lt;/p&gt;
&lt;h2&gt;
  
  
  AWS Lambda layers: more complex testing?
&lt;/h2&gt;

&lt;p&gt;In addition to what has already been highlighted in the previous paragraph, the use of layers entails the need to face new challenges, especially in the context of tests.&lt;/p&gt;

&lt;p&gt;The first aspect to consider is that a layer causes the introduction of dependencies that are only available at runtime, making it more difficult to debug your code locally. The solution is to download the content of the layers from AWS and include it during the build process. Not very practical, however.&lt;/p&gt;

&lt;p&gt;Similarly, the execution of unit tests and integration tests undergoes an increase in complexity: as for local debugging, the content of the layers must be available during execution.&lt;/p&gt;

&lt;p&gt;The second aspect concerns static languages such as Java or C#, for which it is required that all dependencies are available to compile DLL or JAR. Obviously, even in this case there are more or less elegant solutions, such as loading them at runtime.&lt;/p&gt;
&lt;h2&gt;
  
  
  Security &amp;amp; Performance
&lt;/h2&gt;

&lt;p&gt;In general, the introduction of AWS Lambda layers does not involve any security disadvantages: better, it is possible to deploy new versions of existing layers to release security patches. As seen above, remember that the update process is not automatic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Particular attention should be paid to third-party layers&lt;/strong&gt;: there are different levels &lt;a href="https://github.com/mthenw/awesome-layers"&gt;made publicly available&lt;/a&gt; and dedicated to various fields. Although it is actually convenient to be able to use a layer already configured for a very specific purpose, it is obviously better to create your own layers directly so as not to fall victim to malicious code. Alternatively, it is always recommended to first check the repository of the layer you intend to use.&lt;/p&gt;

&lt;p&gt;Performance: the use of layers as an alternative to an all-in-one package has no effect even in the case of a cold start.&lt;/p&gt;
&lt;h2&gt;
  
  
  CloudFormation
&lt;/h2&gt;

&lt;p&gt;Creating AWS Lambda Layers in CloudFormation is very simple. Layers are resources of type &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-layerversion.html"&gt;AWS::Lambda::LayerVersion&lt;/a&gt;. In the Lambda function definition, the &lt;strong&gt;Layers&lt;/strong&gt; parameter allows to specify a list of (maximum 5) dependencies.&lt;/p&gt;

&lt;p&gt;Here's an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;SeleniumChromiumLayer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Lambda::LayerVersion&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;CompatibleRuntimes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;python3.7&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;python3.6&lt;/span&gt;
        &lt;span class="na"&gt;Content&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;S3Bucket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
                &lt;span class="na"&gt;Ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;BucketName&lt;/span&gt;
            &lt;span class="na"&gt;S3Key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
                &lt;span class="s"&gt;Fn::Sub: '${SourceFolder}/SeleniumChromiumLayer.zip'&lt;/span&gt;
        &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Selenium and Chromium Layer for Python3.6&lt;/span&gt;

&lt;span class="na"&gt;SampleFunction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::Lambda::Function&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python3.7&lt;/span&gt;
        &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Sample function&lt;/span&gt; 
        &lt;span class="na"&gt;Handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;src/lambda_function.lambda_handler&lt;/span&gt;
        &lt;span class="na"&gt;Role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
            &lt;span class="s"&gt;Fn::GetAtt: [ "SampleFunctionRole", "Arn" ]&lt;/span&gt;
        &lt;span class="na"&gt;Timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;15&lt;/span&gt;
        &lt;span class="na"&gt;MemorySize&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;512&lt;/span&gt;
        &lt;span class="na"&gt;Code&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;S3Bucket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
                &lt;span class="na"&gt;Ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;BucketName&lt;/span&gt;
            &lt;span class="na"&gt;S3Key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
                &lt;span class="s"&gt;Fn::Sub: '${SourceFolder}/SampleFunction.zip'&lt;/span&gt;
        &lt;span class="na"&gt;Layers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SeleniumChromiumLayer&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is it always a good idea?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;My two cents: using AWS Lambda layers certainly brings benefits in the presence of large dependencies that do not need to be updated very frequently. Moving these dependencies into a layer significantly reduces the deployment time of your Lambda function.&lt;/p&gt;

&lt;p&gt;What about sharing source code? In this case it is good to make assessments that take into account the complexity that is introduced in the application's debug and test processes: it is likely that the effort required is not justified by the benefits that can be obtained with the introduction of layers.&lt;/p&gt;

&lt;p&gt;Did we have fun? See you next time!&lt;/p&gt;

</description>
      <category>aws</category>
      <category>serverless</category>
      <category>python</category>
    </item>
  </channel>
</rss>
