Testing AWS Accelerate with Go and Graviton
Credits:
- https://aws.amazon.com/blogs/compute/accelerating-serverless-development-with-aws-sam-accelerate/
- https://docs.aws.amazon.com/lambda/latest/dg/go-image.html
Let's start with setting the correct AWS profile, just to be sure, that you don't expose working credentials (in case you have multiple environments):
export AWS_PROFILE="test"
Then let the fun begin:
Initialize the app by running sam init
command.
Choose AWS QuickStart Templates
then Zip (artifact is a zip uploaded to S3)
then go1.x
and specify the desired project name, e.g. gravitonus
.
Check if everything works already by running sam local start-api
. In my case there was already an error, stating something like:
Exception on /hello [GET]
Traceback (most recent call last):
File "/usr/local/Cellar/aws-sam-cli/1.34.1/libexec/lib/python3.8/site-packages/docker/api/client.py", line 261, in _raise_for_status
response.raise_for_status()
File "/usr/local/Cellar/aws-sam-cli/1.34.1/libexec/lib/python3.8/site-packages/requests/models.py", line 943, in raise_for_status
raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 404 Client Error: Not Found for url: http+docker://localhost/v1.35/images/create?tag=rapid-1.34.1-x86_64&fromImage=public.ecr.aws%2Fsam%2Femulation-go1.x%3Arapid-1.34.1-x86_64
It turned out that there were some docker issues - obsolete images, lack of disc space. Just as a side-note - try to get feedback as soon as possible to avoid much investigation later on.
I also simplified the hello-world template code, code, so it become something like this (and renamed hello-world
to handler
)
package main
import (
"fmt"
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
)
func handler(request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
return events.APIGatewayProxyResponse{
Body: fmt.Sprintf("Hello, world"),
StatusCode: 200,
}, nil
}
func main() {
lambda.Start(handler)
}
AWS introduced recently AWS SAM Accelerate feature to speed up development, so I wanted to test it out. This is easily achievable by running sam sync --stack-name gravitonus --region eu-west-1
. It deploys the current stack. Navigating to the API Gateway URL (you can get the URL from the console output) gives the desired "Hello, world", which means, we can go further. Let's do some changes to the template and see what else sync
can do.
So, changing the response to return "Hello, Gravitonus" and renaming HelloWorld occurrences in the template.yaml and running the sam sync command gives me the list of updates. It takes time since old CloudFormation resources are deleted and new ones created. But if I only have some code changes, e.g. after replacing "Hello, Gravitonus" to "Hello, Gravitonus v2" it takes literally seconds to update the stack.
Cool, let's switch back to the original idea. Simply changing the architecture in template.yaml to arm64 won't work, as Go is not supported yet, so we need to use a container. So let's change the template to use Docker image. In order to do so, we need to create a Dockerfile:
FROM public.ecr.aws/lambda/provided:al2 as build
# install compiler
RUN yum install -y golang
RUN go env -w GOPROXY=direct
# cache dependencies
ADD go.mod go.sum ./
RUN go mod download
# build
ADD . .
RUN go build -o /handler
# copy artifacts to a clean image
FROM public.ecr.aws/lambda/provided:al2
COPY --from=build /handler /handler
ENTRYPOINT [ "/handler" ]
Note, that in order to use arm64 architecture, we need the Amazon Linux 2 image. Here are corresponding changes to the template.yaml
file:
Resources:
GravitonusFunction:
Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
Properties:
PackageType: Image
Runtime: go1.x
Architectures:
- arm64
Tracing: Active # https://docs.aws.amazon.com/lambda/latest/dg/lambda-x-ray.html
Events:
CatchAll:
Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
Properties:
Path: /hello
Method: GET
Environment: # More info about Env Vars: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#environment-object
Variables:
PARAM1: VALUE
Metadata:
DockerTag: go1.x-v1
DockerContext: ./handler
Dockerfile: Dockerfile
I tried the sync
command, but apparently, it doesn't work yet and gives me Error: Unable to upload artifact gravitonusfunction:v1 referenced by ImageUri parameter of GravitonusFunction resource. 500 Server Error: Internal Server Error ("invalid reference format")
. So let's wait when the AWS team releases the feature. And meanwhile, I've used the regular build and deploy:
sam build
sam deploy --guided
Also, instead of blindly believing that everything works, let's get some OS info in the output. Fast googling pointed me to the "github.com/matishsiao/goInfo" repository. And here is the sample output I've got after using it:
GoOS:linux,
Kernel:Linux,
Platform:aarch64,
OS:GNU/Linux,
CPUs:2
Summary
Sync feature works for simplest cases, it's still in beta yet. I'm waiting for the release. I also doubt, that it will replace CI builds, but for small projects and fast-prototyping it definitely worth exploring.
Graviton architecture is not yet supported as a standalone (Zip) package for the Go language but works with custom containers. I'd try to use it for some of my projects and write next time about the experience gained.
Don't forget to clean-up resources and delete stack. sam delete --stack-name gravitonus --region eu-west-1
will do the job.
Code
The final result is here: https://github.com/abarbarov/gravitonus
That's all, folks!