Everyone, the day has come.
AWS Lambda is finally. Compatible. With Golang. 🖖
Here's how you can start using Go with the Serverless Framework RIGHT NOW and deploy Lambdas to your heart's content.
Get Started
First things first, you'll be needing the Serverless Framework installed, and an AWS account.
(If it's your first time using the Serverless Framework, our first time deployment post has a quick setup guide. Takes like 5 minutes, we promise.)
Use the Go template
The Framework will configure AWS for Go on your behalf.
There are a couple Go templates already included with the Framework as of v1.26—aws-go
for a basic service with two functions, and aws-go-dep
for the basic service using the dep
dependency management tool. Let's try the aws-go-dep
template. You will need dep
installed.
Make sure you're in your ${GOPATH}/src
directory, then run:
$ serverless create -t aws-go-dep -p myservice
Change into your new service directory and compile the function:
$ cd myservice
$ make
The default command in the included Makefile will gather your dependencies and build the proper binaries for your functions. You can deploy now:
$ serverless deploy
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Creating Stack...
Serverless: Checking Stack create progress...
.....
Serverless: Stack create finished...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service .zip file to S3 (4.43 MB)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
........................
Serverless: Stack update finished...
Service Information
service: myservice
stage: dev
region: us-east-1
stack: myservice-dev
api keys:
None
endpoints:
None
functions:
hello: myservice-dev-hello
world: myservice-dev-world
Finally, invoke your function:
$ serverless invoke -f hello
{
"message": "Go Serverless v1.0! Your function executed successfully!"
}
Nice!
Building a Web API with Go + Lambda
The basic example is nice, but let's try something a little more useful.
Lambda + API Gateway is awesome for quickly spinning up endpoints to retrieve or ingest data. So we're going to build an example endpoint.
For our friends coming from interpreted, dynamically-typed languages (looking at you, Pythonistas & Javascript-lovers!), the Golang approach is a little different. You have to be a more intentional about the input & output of your functions. Don't worry, we'll take it slow. 😉
We're going to make an HTTP endpoint that accepts a POST request at the path /echo
, logs the POST body, and echoes the body back to the client.
First, let's fix our serverless.yml
to attach an HTTP event:
# serverless.yml
service: myservice
provider:
name: aws
runtime: go1.x
package:
exclude:
- ./**
include:
- ./bin/**
functions:
hello:
handler: bin/hello
events:
- http:
path: hello
method: post
We'll need to update our function in hello/main.go
.
Remember, Golang is a compiled, statically-typed language, so we need to define the event
object that's coming into our function. Fortunately, AWS has provided a number of event types in a Github repo. 💥 We can just use those.
Update your hello/main.go
to have the following code:
# hello/main.go
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) {
fmt.Println("Received body: ", request.Body)
return events.APIGatewayProxyResponse{Body: request.Body, StatusCode: 200}, nil
}
func main() {
lambda.Start(Handler)
}
Our Handler()
function now takes an APIGatewayProxyRequest
object and returns a APIGatewayProxyResponse
object. In our function code, we're printing the request body, then returning a response with the request body.
Recompile & deploy again:
$ make
dep ensure
env GOOS=linux go build -ldflags="-s -w" -o bin/hello hello/main.go
env GOOS=linux go build -ldflags="-s -w" -o bin/world world/main.go
$ sls deploy
Serverless: Packaging service...
... <snip> ...
...............................
Serverless: Stack update finished...
Service Information
service: myservice
stage: dev
region: us-east-1
stack: myservice-dev
api keys:
None
endpoints:
POST - https://24k8pql1le.execute-api.us-east-1.amazonaws.com/dev/hello
functions:
hello: myservice-dev-hello
world: myservice-dev-world
Notice that you now have an endpoint
listed in your Service Information output.
Let's use curl
to hit your endpoint and get a response:
$ curl -X POST https://24k8pql1le.execute-api.us-east-1.amazonaws.com/dev/hello -d 'Hello, world!'
Hello, world!
Great! This should get you started on a web API. Feel free to check out the other Lambda events in Golang.
Why use Go for your Lambdas?
Golang support for Lambda has been one of the most anticipated releases. The crowd at re:Invent was ecstatic when Werner announced Golang support was coming soon.
Why do people care about Golang so much? Simple: the combination of safety + speed.
As we saw above, Golang is a compiled, statically-typed language. This can help catch simple errors and maintain correctness as your application grows. This safety is really useful for production environments.
However, we've had Java and C# support in Lambda for years. These are both compiled, static languages as well. What's the difference?
Java and C# both have notoriously slow cold-start time, in terms of multiple seconds. With Go, the cold-start time is much lower. In my haphazard testing, I was seeing cold-starts in the 200-400ms range, which is much closer to Python and Javascript.
Speed and safety. A pretty nice combo.
A Gateway to all the runtimes
There's one final note about the Golang implementation on Lambda that's really interesting.
The main()
function which is the entrypoint to our Golang binary isn't our Lambda handler function. It's a little RPC server that wraps our handler function:
func main() {
lambda.Start(Handler)
}
Under the covers, it looks like Lambda starts up your executable on a coldstart. The executable listens on a given port, receives input via JSON, and sends a response via JSON.
This opens up a lot of possibilities to bring other runtimes into Lambda. You just need to pull in an executable that implements the desired RPC interface.
Erica Windisch, CTO at IOpipes, is already making progress on this by pulling NodeJS 8 into Lambda:
NodeJS 8 running natively on AWS Lambda... 🛠️⚙️🏗️
— Erica Windisch (@ewindisch) January 16, 2018
This is really exciting; can't wait to see what the serverless community builds!