Deploy lambda using serverless

Deploy lambda using serverless

We will cover briefly:

  1. Options for deploying lambda
  2. Setup serverless
  3. Create lambda (upload image to s3 bucket) using serverless
  4. Deploy lambda using serverless

Options for deploying lambda

As of writing this article, there are two ways of deploying lambdas onto AWS

Deploy lambda using serverless
Deploy lambda using serverless

The AWS Serverless Application Model (AWS SAM) is an open-source framework that you can use to build serverless applications on AWS

One of the key benefits of SAM is Local debugging and testing

It lets you locally build, test, and debug serverless applications that are defined by AWS SAM templates. which helps you catch issues upfront by providing parity with the actual Lambda execution environment

Develop, deploy, troubleshoot, and secure your serverless applications with radically less overhead and cost by using the Serverless Framework. The Serverless Framework consists of an open-source CLI.

Serverless allows us to focus on the code, while it takes care of the setting up policies, and provisioning of required infrastructure onto AWS

Setup serverless

To install serverless, follow here. Personally, I configured using the below

npm install -g serverless
  • Once installed you can access it by typing sls on your terminal
  • We now have access to multiple templates by this command
sls create --help

We will focus on the Go specific one, which is aws-go-dep Let’s use this template to create a simple lambda. The final command for that is

sls create --template aws-go-dep --path upload-s3

--template is used to specify the template name

--path (Optional) is used to specify the directory in which your code is to be placed

  • We should see a folder structure like this
Golang serverless folder structure
Golang serverless folder structure
  • By default, the aws-go-dep a template gives us two http (GET)based endpoints, which can be seen in the serverless.yml
functions:
  hello:
    handler: bin/hello
    events:
      - httpApi:
          path: /hello
          method: get
  world:
    handler: bin/world
    events:
      - httpApi:
          path: /world
          method: get

The equivalent files correspond to hello/main.go and world/main.go 

  • In case you see errors, try to run the following commands
go mod init <SOME NAME HERE>
go mod tidy

This will help in setting the go.mod which is required for any go program

Create lambda (upload image to s3 bucket) using serverless

We will modify our lambda to upload an image to an S3 bucket.

  • We delete the unnecessary endpoints from the serverless.yml and unwanted folders like hello/main.go and world/main.go 
  • Create a new folder called upload and inside it creates a file main.go 
Our final upload lambda structure
Our final upload lambda structure
  • We will define a request body struct that will accept filename and the body as input. (Note: the body would be the base64 encoder of an image)

For getting the base64 of any image, check here

type ImageRequestBody struct {
 FileName string `json:"filename"`
 Body     string `json:"body"`
}

We will parse the body from the input and decode it using DecodeString which returns the bytes represented by the base64 string

decoded, err := base64.StdEncoding.DecodeString(bodyRequest.Body)

All our image-related functions are inside img_utils.go. After decoding the image into bytes from the previous step, we now take those bytes and form a temporary file

func imageUpload(data []byte) {
  tmpFileName := fmt.Sprintf(`/tmp/%s`, bodyRequest.FileName)
  fileErr := ioutil.WriteFile(tmpFileName, []byte(data), 0644)
  
  // CALL THE UPLOAD FUNCTION
  UploadImage(tmpFileName)
}

In the first line, we are specifying the file at a location tmp . This is an important step and as per AWS documentation

The Lambda execution environment provides a file system for your code to use at /tmp. This space has a fixed size of 512 MB. Each time a new execution environment is created, this area is deleted.

Next, we write the file with the bytes using WriteFile 

WriteFile writes data to a file named by filename. If the file does not exist, WriteFile creates it with permissions perm (before umask); otherwise WriteFile truncates it before writing, without changing permissions.

So now we have our temporary file created!

  • Now we will upload the file, onto the AWS S3 bucket. For that, we will be using the AWS GO SDK and in case you don’t have it installed follow this
go get github.com/aws/aws-sdk-go

We will pass our file to the upload function

func uploadToS3Bucket(file io.Reader, fileName string) {
 bucketName := os.Getenv("bucket_name")
 region := "us-east-2"
 conf := aws.Config{Region: &region}
 sess, _ := session.NewSession(&conf)
 uploader := s3manager.NewUploader(sess)
 upParams := &s3manager.UploadInput{
   Bucket: &bucketName,
   Key:    &fileName,
   Body:   file,
 }
 result, err := uploader.Upload(upParams)
}
  • We need to specify the region using aws.Config . In our case it was us-east-2.

session.Newsession returns a new Session created from SDK defaults, config files, environment, and user-provided config files.

aws-sdk-go gives us s3Manager which helps in uploading the file. It takes in

Bucket : Your AWS bucket name, in my case I get from serverless.yml environment variable

Key : Object key for which the PUT action was initiated. In our case, it’s the filename.

Body : What is the body payload to send to S3. In our case, it’s the file itself

  • Finally, using the above parameters, we call the Uploader.Upload 
  • We return the response from the main.go back to the caller

Deploy lambda using serverless

Till now we were creating our lambda, but now it’s time to deploy it onto AWS.

  • Let’s revisit our serverless.yml and make some changes
functions:
  upload:
    handler: bin/upload
    events:
      - httpApi:
          path: /uploadImage
          method: post

We add in the post endpoint uploadImage for our lambda

  • Next, since we are using the S3 bucket from inside lambda, we will add some IAMRoleStatements
custom:
  s3Bucket: test-bucket--aseem
  
iamRoleStatements:
    - Effect: Allow
      Action:
        - s3:*
      Resource: "arn:aws:s3:::${self:custom.s3Bucket}/*"

This specifies the bucket name using s3Bucket and allows executing the operations on this bucket

  • Finally, for specifying the environment variables, we make use of
environment:
    bucket_name: test-bucket--aseem

We will create a Makefile and put the following

.PHONY: build clean deploy
deploy: clean build
 sls deploy --verbose
remove:
 sls remove

This will help in easing the lambda deployment, by simply calling

make deploy

If everything was successful, you should see something like this

Deploy lambda using serverless
Deploy lambda using serverless

For removing the lambda, we can call

make remove

For testing the lambda, use the

curl -X POST -H "Content-Type: application/json" \
-d '{"filename": "xyz.png", "body": "BASE_64_ENCODING_OF_AN_IMAGE"}' \
<YOUR_LAMBDA_URL>
Image uploaded to S3
Image uploaded to S3
Source Code