Build a Serverless Golang Function with OpenFaaS
In this post I want to show you how to build a Serverless function in Go with our new Golang template created by the OpenFaaS community.
Embrace 👏🏿 The 👏🏻 Gopher 👏🏽
— josé nieto (@xiam) August 29, 2017
Be The Gopher
curl -s https://t.co/moky0K8uki | \
sudo tar -xzf - -C /usr/local/#golang pic.twitter.com/2ksvMqgdTz
OpenFaaS is the only Serverless framework which puts containers in the spotlight and allows any code or binary for Linux or Windows to become a serverless function.
Pre-reqs
- Go 1.8.3 or later (we use this on the host system to vendor packages)
- Docker
- OpenFaaS deployed (locally or remotely)
- OpenFaaS CLI (installed in this tutorial)
This guide assumes you have already deployed OpenFaaS on your laptop or the cloud.
You can deploy OpenFaaS by following the Swarm or Kubernetes guides here:
Go
Go (golang) is well suited to writing serverless applications because it has a modern set of libraries covering everything from Marshaling to HTTP to Image Manipulation and much more. This results in concise code which is easy to understand and also offers a simple API for concurrency through channels.
Your code can compile down to a small binary with no other external runtimes required - it can offer C-like speed and power but with type-safety.
We'll start with hello world then move onto a function which makes use of a third-party (vendored) library to create hashes from Go struct
s.
Let's get started
So let's grab the OpenFaaS CLI and create a new Go function using the language template:
- Install with
curl
for the latest version, or withbrew
$ curl -sL cli.openfaas.com | sudo sh
If you already have the CLI then run the command again so that you're up to date and in-sync.
We'll get hello world working, then move onto something more interesting where we also vendor a package.
1. Hello world
- Create a function from a template
Use faas-cli new
to create a template in one of the supported languages, or faas-cli new --lang Dockerfile
to use a custom Dockerfile.
- Create a folder for your functions
$ mkdir -p $GOPATH/src/functions && \
cd $GOPATH/src/functions
Then use the CLI to create your Go function:
$ faas-cli new --lang go gohash
Folder: gohash created.
___ ___ ___
/ _ \ _ __ ___ _ __ | ___|_ _ __ _/ ___|
| | | | '_ \ / _ \ '_ \| |_ / _` |/ _` \___ \
| |_| | |_) | __/ | | | _| (_| | (_| |___) |
\___/| .__/ \___|_| |_|_| \__,_|\__,_|___ /
|_|
Function created in folder: gohash
Stack file written: gohash.yml
We now have a folder called gohash
with a handler.go file inside:
package function
import (
"fmt"
)
// Handle a serverless request
func Handle(req []byte) string {
return fmt.Sprintf("Hello, Go. You said: %s", string(req))
}
Now you build / deploy and invoke the function.
If you are on a multi-node cluster, you'll need to edit the gohash.yml file which was generated by the CLI. Put your Docker Hub account into the
image:
section. i.e.image: alexellis2/gohash:latest
.
$ faas-cli build -f gohash.yml
The build uses your local Docker client.
If you're on a remote cluster or a multi-node cluster push the image to a registry or the Docker Hub:
$ faas-cli push -f gohash.yml
Now let's deploy the function:
$ faas-cli deploy -f gohash.yml
Note: as of 0.7.6 of the OpenFaaS CLI you can replace
faas-cli build/push/deploy
with a single command:faas-cli up
.
If this is on a remote cluster add the parameter --gateway http://...
Finally you can invoke your function using the OpenFaaS UI or the CLI with:
echo -n "test" | faas-cli invoke gohash
If you'd like to delete the function run this:
echo -n "test" | faas-cli delete gohash
2. Use a 3rd party dependency
We'll use a quick example of a project called structhash
which generates a hash in a string format of arbitrary structs. This is useful for creating checksums and hashes of graphs within memory.
structhash is a Go library for generating hash strings of arbitrary data structures.
This is a snippet from the structhash QuickStart on GitHub:
package main
import (
"fmt"
"crypto/md5"
"crypto/sha1"
"github.com/cnf/structhash"
)
type S struct {
Str string
Num int
}
func main() {
s := S{"hello", 123}
hash, err := structhash.Hash(s, 1)
if err != nil {
panic(err)
}
fmt.Println(hash)
// Prints: v1_41011bfa1a996db6d0b1075981f5aa8f
}
- Build a custom function
Let's take a quick look at the signature of our Go template:
func Handle(req []byte) string {
return fmt.Sprintf("Hello, Go. You said: %s", string(req))
}
You can see the input is of type []byte
which means you can also deal with binary data, or just convert this to a string as we are doing above.
- Vendor the library
Let's go ahead and vendor the dependency of github.com/cnf/structhash
so we can use it in our function.
Install Go's vendoring tool dep
:
$ go get -u github.com/golang/dep/cmd/dep
dep
needs Golang 1.8 or later, if you can't update from Golang 1.7 or 1.6 then please try another vendor tool like: vndr.
Invoke vendoring on the library:
$ cd $GOPATH/src/functions/gohash
$ dep init && \
dep ensure -add github.com/cnf/structhash && \
ls
cd $GOPATH/src/functions
Vendoring is Golang's approach to dependency management. It involves copying the entire source tree of any dependent projects into your project.
You'll now see these new files in the ./gohash
folder:
Gopkg.lock Gopkg.toml ./vendor
A copy of the library is copied inside the vendor folder.
Now let's update the code, build, deploy and invoke it.
- Update
gohash/handler.go
:
package function
import (
"fmt"
"github.com/cnf/structhash"
)
type S struct {
Str string
Num int
}
// Handle a serverless request
func Handle(req []byte) string {
s := S{string(req), len(req)}
hash, err := structhash.Hash(s, 1)
if err != nil {
panic(err)
}
return fmt.Sprintf("%s", hash)
}
The code populates our request and its length into the struct ready to be turned into a hash.
Build and deploy:
$ cd $GOPATH/src/functions
$ faas-cli build -f gohash.yml && \
faas-cli deploy -f gohash.yml
When you're ready invoke it with some input:
$ echo "Serverless" | faas-cli invoke gohash
If you're running into a build error, then please make sure you ran
dep
in the correct location and that you're using Go 1.8 or later as advised.
- Rinse-repeat
Now just edit your handler.go
file and run faas-cli build
followed by faas-cli deploy
for each change.
A note on pushing: If you're using a remote cluster you will also need to run
faas-cli push
so the function can be deployed on any available host.
As part of the build we check that your formatting is correct according to the Golang standard. An easy way to get correct formatting is to run the gofmt
tool or to use an IDE like VSCode.
$ gofmt -s -w gohash/handler.go
If you head over to http://localhost:8080/ you will also be able to invoke the function through the built-in UI and at http://localhost:9090 you will see all the metrics which have been recorded automatically through Prometheus.
- Want to go deeper?
Checkout the Dockerfile in the "template/golang" folder which uses Alpine Linux and multi-stage builds to produce a tiny function image.
Test everything
We've included a unit test in this post along with the sample function. So any tests you have in the function's folder are run and validated as part of your build. If you haven't quite mastered unit testing in Go you can delete the file or head over to my tutorial:
gohash/handler_test.go
package function
import "testing"
func TestHandleReturnsCorrectResponse(t *testing.T) {
expected := "v1_b8dfcbb21f81a35afde754b30e3228cf"
resp := Handle([]byte("Hello World"))
if resp != expected {
t.Fatalf("Expected: %v, Got: %v", expected, resp)
}
}
Wrapping up
We've just built a native Golang function and deployed it to OpenFaaS including vendored dependencies. OpenFaaS supports any process or code as a function but our unique templates mean you can get to serverless even faster.
"Function templates" - from my presentation at JeffConf Milan
Did you know OpenFaaS won the InfoWorld award for Best Cloud Software 2017?
The best 2017 cloud computing software in open-source: @moby, @docker, @kubernetesio, @open_faas and @HashiCorp Terraform - via @infoworld https://t.co/gkeMipFzQz
— Open FaaS (@open_faas) September 27, 2017
Star the OpenFaaS repo and find out why so many people are choosing OpenFaaS - the only Cloud Native Serverless framework for Kubernetes and Docker Swarm.
See also:
Mentions: thank you to Richard Gee for proof reading and testing these instructions.