How to migrate Go-based Lambda to ARM/Graviton2 on AWS.

image of author

Youssef CHLIH

Introduction #

In this blog post, I will show you how to migrate blank-go from x86_64 architecture to run on ARM/Graviton2 architecture.
According to AWS, Lambda functions powered by ARM/Graviton2 are designed to deliver up to 19 percent better performance at 20 percent lower cost.

The example of blank-go was picked for clarity purposes, as It is present in the AWS official guide, and probably widely used.

Implementation #

Here is the CloudFormation template of the blank-go function :

    AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: An AWS Lambda application that calls the Lambda API.
Resources:
function:
Type: AWS::Serverless::Function
Properties:
Handler: main
Runtime: go1.x
CodeUri: function/.
Description: Call the AWS Lambda API
Timeout: 5
Policies:
- AWSLambdaBasicExecutionRole
- AWSLambda_ReadOnlyAccess
- AWSXrayWriteOnlyAccess
Tracing: Active

The template is pretty simple, it contains a go serverless function ...

The runtime go1.x is based on Amazon Linux 1, hence it doesn't support arm64 architecture.
In order to solve this problem, Amazon is providing us with a way to create a custom runtime with Amazon Linux 2.
We need to implement and provide our own runtime on top of Amazon Linux 2, we need first to update and change the runtime from go1.x to provided.al2, which means that Golang runtime will be provided, and will run on Amazon Linux 2.

    Runtime: provided.al2

When you build a Go program, the resulting binary is self-contained and includes its runtime, which means the runtime will be included in the function, but for other technologies (e.g C++, nodejs ...), the runtime can also be included in a layer.
If the runtime is included in the function, Lambda will look for a file called bootstrap and run it.
So we need to rename our handler to bootstrap, and also the resulting binary of our go build.

    Handler: bootstrap

We still need to specify the architecture :

    Architectures: ['arm64']

The final CloudFormation template should look like this:

    AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: An AWS Lambda application that calls the Lambda API.
Resources:
function:
Type: AWS::Serverless::Function
Properties:
Handler: bootstrap
Runtime: provided.al2
CodeUri: function/.
Architectures: ['arm64']
Description: Call the AWS Lambda API
Timeout: 5
Policies:
- AWSLambdaBasicExecutionRole
- AWSLambda_ReadOnlyAccess
- AWSXrayWriteOnlyAccess
Tracing: Active

In Order to rename the Go binary into bootstrap, you can change it in the build command present in the file 2-deploy.sh, also specify GOARCH=arm64, if you are building in a none ARM machine.
GOOS and GOARCH are here to allow you to compile a golang binary for other platforms.
The final command should look like this :

    GOOS=linux GOARCH=arm64 go build main.go -o bootstrap

To deploy you can follow the instruction in README from the blank-go repository or just run 1-create-bucket.sh to create the deployment bucket and then 2-deploy.sh to deploy.
In the AWS console, you should be able to see that the deployed Lambda is now running on arm :

Go-Lambda-On-Arm

The Go lambda can be tested by running the script 3-invoke.sh.

Conclusion #

As development effort is minimal, you can easily migrate Go Lambdas to run on Arm/Gravtion2, and save cost as it's 20% cheaper than x86.
The cost saving will start showing immediately in the cost usage report:

Go-Lambda-arm-cost

You can find the blank-go project with ARM architecture on Github.


image of author
Written by Youssef CHLIH

Platform Engineer at Autoscout24