Using Golang for your AWS Lambda Functions

nr18

Joris Conijn

Posted on August 4, 2023

Using Golang for your AWS Lambda Functions

I was sparked on a XKE to do a short experiment with using Golang for my AWS Lambda Functions. The trigger for this was something my colleague Mark van Holsteijn said. We where talking about sustainability. During this talk Mark made the comment that we should drop Python for our functions then. (He also recently wrote a blog on Golang using Golang for custom providers.)

Let me compare some numbers between Python and Go. (source: Which Programming Languages Use the Least Electricity?))

  • Energy: 75.88 vs 3.23
  • Time: 71.90 vs 2.83
  • Memory: 2.80 vs 1.05

As you can see, based on these numbers Golang is clear winner. But are there other advantages? So, like always everything comes with it's own pros and cons. Lets explore those when you do chose for Golang.

Pre-compiled binary vs compiled at run time

Let's start with the obvious one, a pre compiled binary vs code that is compiled at run time. This is one of the reasons why Golang is more energy efficient as you only compile once vs every time you run it. But the advantage of Python is that you can actually see the source code in the AWS Console and tweak it. This makes it a great candidate for easy development and fast testing. This also a problem! When you are running production workloads. You don't want your developers altering your code through the console. Yes, IAM can help you here but altering a binary is not possible through the console.

You need to compile

Why is the need to compile an advantage? Next to the performance improvement you actually check if your program compiles. If it does not compile you cannot deploy it. Compare this with Python, you can upload malformed code. This would than break your workload because of an indent issue. You can build in checks to prevent this from happening, and you should! But you need to do something extra, with Golang this comes out of the box.

Dependencies

In python you have the option to do inline code in AWS CloudFormation templates. I would never recommend doing this. When you needs other dependencies than that there are available. You will need to bundle them yourself and upload them as a zipfile. When you are missing a dependency the invocation of your function will fail at runtime. When you use Golang the dependencies are all build into the binary. Especially when you set the CGO_ENABLED=0 option in your compile options. This will make sure that all the needed libraries are included.

Tutorial

Let's deploy a small stack including a single Lambda function. We will be using AWS SAM for the deployment, so please make sure you have that installed.

Prepare the file structure

Create the folders and files we will be needing:

mkdir aws-lambda-sample-golang
cd aws-lambda-sample-golang
touch template.yaml
mkdir my-function
touch my-function/Makefile
touch my-function/main.go
Enter fullscreen mode Exit fullscreen mode

SAM Template

A SAM Template is nothing more that a CloudFormation template. But it contains the AWS::Serverless-2016-10-31 transformation. This allows you to use the AWS::Serverless::Function resource. When CloudFormation parses this resource it will converted to CloudFormation resources.

Add the following to the template.yaml file:

Transform: AWS::Serverless-2016-10-31
Resources:

  MyFunction:
    Type: AWS::Serverless::Function
    Metadata:
      BuildMethod: makefile
    Properties:
      Runtime: provided.al2
      CodeUri: ./my-function
      Handler: bootstrap

  MyFunctionLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub /aws/lambda/${MyFunction}
      RetentionInDays: 7
Enter fullscreen mode Exit fullscreen mode

A few things are going on here. You can see the BuildMethod set in the Metadata property. With this configuration you are saying to SAM that you want to use a Makefile. This Makefile is used to compile the lambda function.

  • CodeUri will point to the root of your Lambda functions code.
  • Handler will point to your binary output file.

Lets create the Makefile, we need to place it in the root folder of the function my-function. Add the following content to the file:

build-MyFunction:
    GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o bootstrap
    cp ./bootstrap $(ARTIFACTS_DIR)/.
Enter fullscreen mode Exit fullscreen mode

The syntax used here is build-<LOGICAL-ID-OF-CLOUDFORMATION>. So, if your LogicalId in the template is HelloWorld. Your Makefile target would be: build-HelloWorld. The go build command will build your binary. The copy command will copy the binary in the artifacts directory. This is used when you deploy your template.

Note the GOARCH if you use an ARM function you need to specify it here as well.

Lambda Code

We need to initialize our go module, and pull in the github.com/aws/aws-lambda-go dependency:

cd my-function
go mod init my-function
go get github.com/aws/aws-lambda-go
cd ..
Enter fullscreen mode Exit fullscreen mode

Now we can fill the my-function/main.go with some business logic:

package main

import (
    "context"
    "log"

    "github.com/aws/aws-lambda-go/lambda"
)

type MyEvent struct {
    Name string `json:"name"`
}

func Handler(ctx context.Context, event MyEvent) {
    log.Printf("Welcome %s", event.Name)
}

func main() {
    lambda.Start(Handler)
}
Enter fullscreen mode Exit fullscreen mode

And done!

Deploy the template

To deploy the template we first need to build the binary. AWS SAM as build in support for building and packaging Lambda functions.

Note: AWS SAM will create a bucket in your account used for uploading the artifacts.

sam build
sam deploy \
    --stack-name aws-lambda-sample-golang \
    --capabilities CAPABILITY_IAM \
    --resolve-s3
Enter fullscreen mode Exit fullscreen mode

Testing

You can now invoke the Lambda function, with a {"name": "Joris Conijn"} payload. The logs should now show:

Welcome Joris Conijn

For a full working copy you could also clone my Nr18/aws-lambda-sample-golang repository.

Conclusion

For me Golang would become my default choice for AWS Lambda functions. The fact that it's "more sustainable" and that it protects you from obvious failures convinced me. That beings said I have a software engineering background. So, for me it's relatively easy to switch languages. How about you, would you use Golang as your default choice?

Photo by Narcisa Aciko

💖 💪 🙅 🚩
nr18
Joris Conijn

Posted on August 4, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related