<?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: Mohamed Mahmoud</title>
    <description>The latest articles on DEV Community by Mohamed Mahmoud (@mohamedmahmoud97).</description>
    <link>https://dev.to/mohamedmahmoud97</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%2F63222%2Fe0154a18-f45d-4c15-b1df-be17708b7627.jpg</url>
      <title>DEV Community: Mohamed Mahmoud</title>
      <link>https://dev.to/mohamedmahmoud97</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mohamedmahmoud97"/>
    <language>en</language>
    <item>
      <title>From Zero to Serverless</title>
      <dc:creator>Mohamed Mahmoud</dc:creator>
      <pubDate>Mon, 01 Jun 2020 14:27:18 +0000</pubDate>
      <link>https://dev.to/mohamedmahmoud97/from-zero-to-serverless-20g1</link>
      <guid>https://dev.to/mohamedmahmoud97/from-zero-to-serverless-20g1</guid>
      <description>&lt;p&gt;I was first exposed to Lambda functions at work when the team needed to implement an API service that will receive requests from other services, parse the data, and then sends this data to Kafka. Now this service is handling around 1M request each month 🎉🎉&lt;/p&gt;

&lt;p&gt;I was very confused about where to start or how to get this done to work in development, testing, and production environments. I saw &lt;a href="https://serverless.com" rel="noopener noreferrer"&gt;Serverless Framework&lt;/a&gt;, &lt;a href="https://knative.dev/" rel="noopener noreferrer"&gt;Knative&lt;/a&gt;, and &lt;a href="https://kubeless.io/" rel="noopener noreferrer"&gt;Kubeless&lt;/a&gt; on the internet and took much time to decide what to choose since we are using K8s in our environments.&lt;/p&gt;

&lt;p&gt;At last, I found &lt;a href="https://serverless.com" rel="noopener noreferrer"&gt;Serverless&lt;/a&gt; the most suitable one to use in all our environments so I started learning how to build a serverless function using it. It's pretty easy to learn, use, and even read your configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  The directory structure
&lt;/h3&gt;

&lt;p&gt;It's better to abstract each function in a separate directory with its components. You will also see &lt;code&gt;Dockerfile&lt;/code&gt; and &lt;code&gt;.docker&lt;/code&gt; directory which are used in the development environment. You will also see we are using Jenkins in our CI/CD pipeline. The programming language we used is &lt;code&gt;NodeJS&lt;/code&gt;. We will talk about each environment setup next.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F62zwv5yfuom5odeqnocn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F62zwv5yfuom5odeqnocn.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The function handler's code is always inside &lt;code&gt;handler.js&lt;/code&gt;. We create as many files inside &lt;code&gt;lib/&lt;/code&gt; as necessary. The corresponding tests to all this code are under &lt;code&gt;tests/&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The &lt;code&gt;serverless.yml&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Let me start by sharing our "simple" serverless.yml 😅, and then I'll start commenting on pieces of it and explain how we are using this file for all of our environments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api-service&lt;/span&gt;
&lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws&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;nodejs12.x&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;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${opt:stage, 'dev'}&lt;/span&gt;
  &lt;span class="na"&gt;logRetentionInDays&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${self:custom.logRetention.${self:provider.stage}, self:custom.logRetention.other}&lt;/span&gt;
  &lt;span class="na"&gt;vpc&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;subnetIds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;${self:custom.subnetIds.${self:provider.stage}, self:custom.subnetIds.other}&lt;/span&gt;
    &lt;span class="na"&gt;securityGroupIds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;${self:custom.securityGroupIds.${self:provider.stage}, self:custom.securityGroupIds.other}&lt;/span&gt;
  &lt;span class="na"&gt;deploymentBucket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api-service&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, you will have to specify the service name to which you want to name the service. In the provider part, you will need to name your cloud provider you are going to use. I am AWS as shown above but if you are using GCP it will be &lt;code&gt;google&lt;/code&gt;. You can see an example for GCP &lt;a href="https://github.com/serverless/examples/tree/master/google-node-simple-http-endpoint" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Then, the runtime your serverless application will use. Don't forget to set the version like the one you are using in the developent environment. &lt;/p&gt;

&lt;p&gt;One of the important options in the provider block is adding a &lt;code&gt;memorySize&lt;/code&gt; value. The default value is 1024 but sometimes your function is not going to use this much of memory so it's good to save some money here. Next the &lt;code&gt;stage&lt;/code&gt; option, you can set it with passing an sls argument during the deployment or use a value to fallback to as above, &lt;code&gt;dev&lt;/code&gt; in our case. &lt;/p&gt;

&lt;p&gt;Next is setting a retention policy to your function (&lt;code&gt;logRetentionInDays&lt;/code&gt;). Some people forget to use this option but it's a good way to get rid of old logs and save money from decreasing the tons of logs stored on Cloudwatch. We are setting a different value in testing other than the production as we need the production logs to be kept longer. The values here are set by values in the custom section. We are setting a value depending on the stage we are deploying to. We are using this approach in most of our fields. It's good to write a generic &lt;code&gt;serverless.yml&lt;/code&gt; file that could handle multiple stages/environments. So if you are using prod stage, it will get the value from &lt;code&gt;custom.logRetention.prod&lt;/code&gt;. It will go and look if we have &lt;code&gt;custom.logRetention.prod&lt;/code&gt; already defined. If not, it will set the value with &lt;code&gt;custom.logRetention.other&lt;/code&gt; which we are using for all the testing environments.&lt;/p&gt;

&lt;p&gt;For the &lt;code&gt;subnetIDs&lt;/code&gt; and &lt;code&gt;securityGroupIds&lt;/code&gt; options, you will use them if you need to connect the application to an internal resource in your cluster, so skip it if not. You will have to grab the subnets and security-groups that your other resources is hosted in. &lt;/p&gt;

&lt;p&gt;Be carefull, as soon as you are connecting your function with a VPC, the function is no longer able to access the Internet, even if you are choosing a public subnet with a route to the Internet gateway for your function (see &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/configuration-vpc.html#vpc-internet" rel="noopener noreferrer"&gt;Internet Access for Lambda Functions&lt;/a&gt; to learn more). So you can’t have both: access to resources within your VPC and through the Internet.&lt;/p&gt;

&lt;p&gt;To be able to access your VPC as well as the Internet, you need to spin up a NAT Gateway. Or in some cases, you might get away with a VPC Endpoint.&lt;/p&gt;

&lt;p&gt;Last part here, is adding your &lt;code&gt;deploymentBucket&lt;/code&gt;. It's important to put all of your deployments in one bucket. You will have each stage, function, and deployment in an organized directories with timestamps. If your going to skip this one, it will create a random bucket for each deployment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;package&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;functions/api-service/.env.defaults&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;functions/api-service/app.js&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;functions/api-service/handler.js&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;functions/api-service/lib/**&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;functions/api-service/node_modules/**&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;package&lt;/code&gt; option is used to include and also exclude files and directories from your functions and can also help you in decreasing your application size that will be uploaded every deployment to S3.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;functions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;api-service&lt;/span&gt;&lt;span class="pi"&gt;:&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;functions/api-service/handler.api-service&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;this function will receive data from multiple services&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;KAFKA_TOPIC&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api.service&lt;/span&gt;
      &lt;span class="na"&gt;KAFKA_BROKERS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${self:custom.KAFKA_BROKERS.${self:provider.stage}, self:custom.KAFKA_BROKERS.other}&lt;/span&gt;
    &lt;span class="na"&gt;events&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api-service&lt;/span&gt;
          &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;post&lt;/span&gt;
          &lt;span class="na"&gt;cors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;origins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;https://*.example.com&lt;/span&gt;
            &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Content-Type&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;X-Amz-Date&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Authorization&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;X-Api-Key&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;X-Amz-Security-Token&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;X-Amz-User-Agent&lt;/span&gt;
            &lt;span class="na"&gt;allowCredentials&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The most interseting part is defining your functions array. In our case we are using just one function but you can add as many as you need depending on the pipeline you are working on or you are kinda nerd and using step functions 🤓. I recommend looking &lt;a href="https://www.serverless.com/blog/how-to-manage-your-aws-step-functions-with-serverless/" rel="noopener noreferrer"&gt;here&lt;/a&gt; if you are interested to play with step functions.&lt;/p&gt;

&lt;p&gt;Name your function and add it alongside with its &lt;code&gt;description&lt;/code&gt; and &lt;code&gt;handler&lt;/code&gt;. Make sure you are writing the right handler function not the file name. Then, you can use &lt;code&gt;environment&lt;/code&gt; option to export some environment variables to use while your function is running. You will realize we are using the same approach in handling the same environment variable with multiple stages like in &lt;code&gt;${self:custom.KAFKA_BROKERS.${self:provider.stage}, self:custom.KAFKA_BROKERS.other}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;events&lt;/code&gt; section, it depends on what is triggering your function: api gateway, websocket, kinesis &amp;amp; dynamodb ,s3 , etc. Look &lt;a href="https://www.serverless.com/framework/docs/providers/aws/events/" rel="noopener noreferrer"&gt;here&lt;/a&gt; for more details. &lt;/p&gt;

&lt;p&gt;We are using an API gateway with http endpoint handling only POST requests on &lt;code&gt;/api-service&lt;/code&gt;. You can also add some CORS configurations to your function as we you see above.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;serverless-domain-manager&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;serverless-offline&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A good thing serverless framework provide you with, is plugins. We use &lt;code&gt;serverless-offline&lt;/code&gt; for running our setup locally in the development environment and &lt;code&gt;serverless-domain-manager&lt;/code&gt; plugin to handle the API gateway configuration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;custom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;customDomain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;domainName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${self:custom.subDomain.${self:provider.stage}, self:custom.subDomain.other}.${self:custom.domain.${self:provider.stage}, self:custom.domain.other}&lt;/span&gt;
    &lt;span class="na"&gt;certificateName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*.${self:custom.domain.${self:provider.stage},&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;self:custom.domain.other}"&lt;/span&gt;
    &lt;span class="na"&gt;basePath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${self:custom.basePath.${self:provider.stage},&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;self:custom.basePath.other}"&lt;/span&gt;
    &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${self:provider.stage}&lt;/span&gt;
    &lt;span class="na"&gt;createRoute53Record&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;domain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;prod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example-prod.com&lt;/span&gt;
    &lt;span class="na"&gt;other&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example.com&lt;/span&gt;
  &lt;span class="na"&gt;subDomain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;prod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api-service&lt;/span&gt;
    &lt;span class="na"&gt;other&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api-service&lt;/span&gt;
  &lt;span class="na"&gt;basePath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;prod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
    &lt;span class="na"&gt;other&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${self:provider.stage}&lt;/span&gt;
  &lt;span class="na"&gt;subnetIds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;prod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;subnet-xxxxxxx&lt;/span&gt;
    &lt;span class="na"&gt;other&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;subnet-yyyyyyy&lt;/span&gt;
  &lt;span class="na"&gt;securityGroupIds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;prod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sg-xxxxxxx'&lt;/span&gt;
    &lt;span class="na"&gt;other&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sg-yyyyyyy'&lt;/span&gt;
  &lt;span class="na"&gt;KAFKA_BROKERS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;prod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kafka-prod.example.com&lt;/span&gt;
    &lt;span class="na"&gt;other&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kafka-${self:provider.stage}.example.com&lt;/span&gt;
  &lt;span class="na"&gt;logRetention&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;prod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;14&lt;/span&gt;
    &lt;span class="na"&gt;other&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;7&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the &lt;code&gt;custom&lt;/code&gt; section, it's like declaring variables. Variables allow you to dynamically replace config values in &lt;code&gt;serverless.yml&lt;/code&gt; config. We declare one for the &lt;code&gt;prod&lt;/code&gt; and &lt;code&gt;other&lt;/code&gt; to fallback to when the stage is not prod. Like when we used &lt;code&gt;${self:custom.subnetIds.${self:provider.stage}, self:custom.subnetIds.other}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;customDomain&lt;/code&gt; variable is used here to define the API gateway config. So, we are setting &lt;code&gt;domainName&lt;/code&gt;, &lt;code&gt;certificateName&lt;/code&gt;, &lt;code&gt;basePath&lt;/code&gt; which is very helpful if the certificate you are using is not handling alternate domain names (CNAMEs) look &lt;a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/CNAMEs.html#alternate-domain-names-requirements" rel="noopener noreferrer"&gt;here&lt;/a&gt; for more info,and the &lt;code&gt;stage&lt;/code&gt; field. You can also set &lt;code&gt;createRoute53Record&lt;/code&gt; with &lt;code&gt;true&lt;/code&gt; to have the framework create for you Route53Records to use in your other external services.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up the development environment
&lt;/h3&gt;

&lt;p&gt;For the development environment, I preferred to run the serverless function using Docker to better handle the dependencies. So this is how the Dockerfile and the entrypoint look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:12.15.0-stretch&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; ./.docker/startup.sh /etc/&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x /etc/startup.sh
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; /etc/startup.sh&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





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

&lt;span class="nb"&gt;cd&lt;/span&gt; /var/www/app
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; serverless
npm &lt;span class="nb"&gt;install
&lt;/span&gt;sls offline &lt;span class="nt"&gt;-o&lt;/span&gt; api-service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will see above how simple the setup is in the development environment. We are just running the &lt;code&gt;sls&lt;/code&gt; command with the &lt;code&gt;serverless-offline&lt;/code&gt; plugin. This will let a local API gateway to run on port &lt;code&gt;3000&lt;/code&gt; inside the container and triggers the function once we hit it. &lt;/p&gt;

&lt;p&gt;Our parameters are stored in &lt;code&gt;functions/api-service/.env.defaults&lt;/code&gt;. We will handle our parameters in a different way in the other environments.&lt;/p&gt;

&lt;p&gt;To run this setup locally, you can either use only docker command or you can also use docker-compose instead.&lt;/p&gt;

&lt;h4&gt;
  
  
  Docker command:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; api-service &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;--name&lt;/span&gt; api-service &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 80:3000 &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PWD&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;:/var/www/app api-service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  docker-compose:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.7'&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# api service&lt;/span&gt;
  &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;80:3000"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.:/var/www/app&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker-compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Setting up testing and production environments
&lt;/h3&gt;

&lt;p&gt;Luckily, because we have written a very cool and generic &lt;code&gt;serverless.yml&lt;/code&gt; file. We don't have much of steps to do in these environments. You will just need to run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sls deploy -s [stage] -v
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This step will create a &lt;code&gt;.serverless&lt;/code&gt; directory that will have the cloudformation template generated, serverless-state and the zip file that will be uploaded to the S3 bucket. I also like the &lt;code&gt;-v&lt;/code&gt; option to see every resource created and every step serverless is making 👀&lt;/p&gt;

&lt;h3&gt;
  
  
  Automating production deployments
&lt;/h3&gt;

&lt;p&gt;If you want to step higher with your pipeline and add some automation, you can use &lt;a href="https://www.jenkins.io/" rel="noopener noreferrer"&gt;Jenkins&lt;/a&gt; to automate your CI/CD pipeline or autodeploy your application when merging your changes to master for example.&lt;/p&gt;

&lt;p&gt;An example of a jenkinsfile is as below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;pipeline&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;kubernetes&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="s1"&gt;'api-service'&lt;/span&gt;
      &lt;span class="n"&gt;defaultContainer&lt;/span&gt; &lt;span class="s1"&gt;'jnlp'&lt;/span&gt;
      &lt;span class="n"&gt;yaml&lt;/span&gt; &lt;span class="s2"&gt;"""
apiVersion: v1
kind: Pod
metadata:
labels:
  component: ci
spec:
  # Use service account that can deploy to all namespaces
  containers:
  - name: node
    image: node:12.15.0-stretch
    command:
    - cat
    tty: true
  - name: awscli
    image: organs/awscli
    command:
    - cat
    tty: true

"""&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;stages&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'build'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;environment&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
          &lt;span class="n"&gt;BRANCH_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt; &lt;span class="nl"&gt;script:&lt;/span&gt; &lt;span class="s2"&gt;"echo ${env.GIT_BRANCH}|  rev  | cut -f1 -d '/' | rev | sed 's/ *//g'"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;returnStdout:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;trim&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
      &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'node'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s2"&gt;"""
               npm install
               """&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'awscli'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s2"&gt;"""
               # deploy to prod stage if branch is master
               if [ "${env.BRANCH_NAME}" == "master" ]; then
                 sls deploy -s prod -v
               fi
               """&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Closing
&lt;/h3&gt;

&lt;p&gt;Serverless is an amazing technology, or a mindset if you will. It's a major step towards delegating infrastructure problems to companies that are much better positioned to deal with them. No matter how good you become at DevOps, you sometimes need those companies to handle and manage your applications you don't want to bother yourself with. And the main point here is building your architecture without spending ages building it yourself and saving tons of money hosting it.&lt;/p&gt;

&lt;p&gt;I will continue writing about various serverless topics and case scenarios that I have faced that I also see it interesting and challenging. &lt;/p&gt;

&lt;p&gt;Appreciate your comments and feedback 😊&lt;/p&gt;

</description>
      <category>devops</category>
      <category>serverless</category>
      <category>aws</category>
      <category>lambda</category>
    </item>
  </channel>
</rss>
