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
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
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)/.
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 ..
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)
}
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
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
Top comments (4)
Yes:
tecracer.com/blog/2023/07/custom-r...
Another argument - thinking long term - is that the runtime for python changes every few years. GO has a backward compatibility promise.
But as maintainer go-on-aws.com/ I am biased :)
That is true once compiled it will work for your compiled os and architecture. No need to have dependencies available at runtime anymore.
Love love love Golang and Lambda. Nice article! Keep them coming!
Thanks for your post. Sometime back I wrote this post might be interesting for you.
awsmantra.com/100-millions-lambda-...