Going Serverless with OpenFaaS and Golang - The Ultimate Setup and Workflow
Tags: go, serverless, tutorial, docker
Published: 2019-11-16 16:53:39

_Note: This was originally posted at [martinheinz.dev](https://martinheinz.dev/blog/10)_

_Serverless_ applications have been cool and hype for a while now and some interesting and useful platforms/tools/frameworks are emerging now. One of them is _OpenFaaS_ which is open source _Function as a Service_ tool for developing _cloud-native serverless applications_. 

In this blog post, I want to show you how to setup (IMHO) the ideal project and workflow for developing _OpenFaaS_ functions, as well as creating your first function using _Golang_.

_Note: Full source code for this post can be found here: <https://github.com/MartinHeinz/openfaas-functions>_

## Why OpenFaaS

You might be asking, why - out of all the frameworks like _AWS Lambda_, _Google Cloud Run_, _OpenWhisk_ etc. - would I choose _OpenFaaS_? Well, there are quite few reasons for me:

- **No Vendor-Lock** - One big reason for me to choose _OpenFaaS_ over other alternatives, is the fact, that I'm not gonna get vendor locked. Why does it matter so much, though? It matters, because at some point, you might decide, that you want to move to other platform or even completely move away from _serverless_, which you can't really do with tools like _AWS Lambda_ that forces you to use AWS.

- **Cloud Native** - Next advantage is that it runs in cloud environments like _Kubernetes_, _OpenShift_ or _k3s_ (used in this tutorial). I consider this an advantage because spinning up small _Kubernetes_ cluster is pretty cheap, fast and simple nowadays and deploying OpenFaaS in it is simple too, as you will see below.

- **Any Language** - _OpenFaaS_ is able to use any runtime, thanks to the fact that all the functions run in _Docker_ containers. You can even build your own templates from which you build your functions.

- **Performance** - Another nice thing about _OpenFaaS_, is its performance, more specifically - [_zero-scale_](https://www.openfaas.com/blog/zero-scale/) or in other words - ability to scale pods to zero when idle, and [HTTP-based (beta) templates](https://github.com/openfaas-incubator/of-watchdog) with lower latency, caching and persistent connections (you will see that in next blog post).

## Setting Up

To start building some cool functions, we first need to install few tools. Here's list of them with instructions:

- _k3s_ - To run the functions we need a cloud environment and considering that we will be using local machine, we should probably choose the most lightweight option possible, here I will use _Ranchers k3s_ (alternatively you could also use _KinD_):

  To install use following commands:
```bash
curl -SLsf https://get.k3sup.dev/ | sudo sh
```

- _OpenFaaS_ - As mentioned previously, _OpenFaaS_ is a Cloud Native platform, so we need to put it into our Cloud environment (_k3s_):

```bash
k3sup app install openfaas
```

- _`faas-cli`_ - To be able to interact with the deployed _OpenFaaS_ we also need its CLI:
   
```bash
curl -sL https://cli.openfaas.com | sudo sh
faas-cli --version
```

- _Taskfile_ - Lastly, our setup uses _Taskfile_ - replacement for _Make_ - to automate common tasks. To install it, you need to run:

```bash
cd ~
curl -sL https://taskfile.dev/install.sh | sh
task --version
```


## First Function

Now that we have all our tools ready, let's build our first _OpenFaaS_ function. Before we actually do any coding, we need to pull templates on top of which we will build the functions:

```console
~ $ faas-cli template pull  # Pull from default store
~ $ faas-cli template store list  # Check what we have available
NAME                     SOURCE             DESCRIPTION
...
go                       openfaas           Classic Golang template
...
```

Above, we can see the one template we are interested in - the `go` template. Let's create function from it:

```console
~ $ faas-cli new --lang go first-func --prefix=martinheinz
...
Function created in folder: first-func
Stack file written: first-func.yml
...
```

From the output, we can see which files were created - `handler.go` and `first-func.yml`. First of them contains function responsible for handling incoming requests, other one is _YAML_ stack file, that describes the function - it contains information like image name, path to handler, language etc. There are also more values that can be specified, those you can find [here](https://docs.openfaas.com/reference/yaml/). You probably noticed that we also specified `prefix` flag - we need that, to be able to push image of our built function to remote repository, which is in this case _Docker Hub_ and the `prefix` is _Docker Hub_ username. _OpenFaaS_ will then use this image from remote registry, when we deploy the function.

I will leave fiddling with code and implementing cool features to you and I will just build, push and deploy the _Hello World_ function as is:

```console
faas-cli build -f first-func.yml
faas-cli push -f first-func.yml
faas-cli deploy -f first-func.yml
``` 

And just like that, we have our function running in _OpenFaaS_ on _k3s_. We can view it both in UI and commandline. For UI view, you can navigate to `OPENFAAS_URL` (which should be <http://127.0.0.1:31112>) and for shell you can run following commands to display and invoke the function:

```console
~ $ echo "Martin" | faas-cli invoke first-func
Hello, Go. You said: Martin
```

I think, this was quite simple and we can build upon this. In the next blog post, we will explore how to make use of newer (beta) `of-watchdog` templates as well as how to build our own template store including our optimized templates using _Go_ modules. You can have a sneak peak in my template repository [here](https://github.com/MartinHeinz/openfaas-templates).

## Unit Tests
It might seem like we're done here, but there's one thing we are missing - _Unit Test(s)_. 

To write our unit test we simply create `handler_test.go` file alongside `handler.go` and put this test in there:

```golang
package function

import (
	"testing"
)

func TestHandleReturnsCorrectResponse(t *testing.T) {
	expected := "Hello, Go. You said: First Test!"
	response := Handle([]byte("First Test!"))

	if response != expected {
		t.Fatalf("Expected: %v, Got: %v", expected, response)
	}
}
```

You might have actually noticed, that the `go test` command was ran during _Docker_ build, so all we need to run tests is build the function like before:

```console
faas-cli build -f first-func.yml
...
RUN CGO_ENABLED=${CGO_ENABLED} GOOS=linux \
    go build --ldflags "-s -w" -a -installsuffix cgo -o handler . && \
    go test $(go list ./... | grep -v /vendor/) -cover
 ---> Running in fc879c8559f8
?   	handler	[no test files]
ok  	handler/function	0.002s	coverage: 100.0% of statements
...
```

You might think that it is redundant to test little pieces of code like these kinds of _serverless_ functions, but you should not neglect testing just because the code is simple or short, everything should be tested if possible.

## Made Simple with Taskfile

In the beginning of the post, I asked you to install _Taskfile_, but we haven't actually used it yet, so let's change that and make the workflow simpler and create, build, deploy or debug the function with _Taskfile_ targets that I already created for you:

First step - create the function with specified name, using some template:

  ```bash
  task create TPL=go FUNC=first-func
  ```

in the background this task downloads templates for you, creates the function and then adds the files to git staging area.

Next, let's build it:

  ```bash
  task build FUNC=first-func
  ```

Nothing fancy, it just builds the function, but you can also go ahead and build, push and deploy the function in one command:

  ```bash
  task run FUNC=first-func
  ```
    
Now, to check if everything works as expected, let's check logs:

  ```bash
  task logs FUNC=first-func
  ```
  
This will give description of kubernetes deployment object as well as function logs that should look like this:

```console
2019/11/12 19:33:47 Version: 0.18.1	SHA: b46be5a4d9d9d55da9c4b1e50d86346e0afccf2d
2019/11/12 19:33:47 Timeouts: read: 5s, write: 5s hard: 0s.
2019/11/12 19:33:47 Listening on port: 8080
2019/11/12 19:33:47 Writing lock-file to: /tmp/.lock
2019/11/12 19:33:47 Metrics listening on port: 8081
```

And if logs aren't enough, you can debug the function with:

  ```bash
  task debug FUNC=first-func
  ```
  
This will run the function locally in _Docker_ and attach terminal to the to `watchdog` process logs. Now, that the function is running on your machine, you can use `cURL` to invoke it:

  ```bash
  curl -vvv --header "Content-Type: application/json" \
            --request POST \
            --data '{"key":"value"}' \
            127.0.0.1:8081
  ```

To see what's happening inside the `Taskfile.yml` or to check out little more detailed documentation, please check out `Taskfile.yml` in my repository [here](https://github.com/MartinHeinz/openfaas-functions/blob/master/Taskfile.yml) and `README.md` [here](https://github.com/MartinHeinz/openfaas-functions/blob/master/README.md).

## The Simplest CI/CD

The final thing we need to setup is CI/CD pipeline and we can do that with one last _Taskfile_ target that I omitted in previous section - `task verify` - this command downloads `faas-cli`, if not present on the machine and builds each function in the repository using _YAMLs_ stored in `functions` directory:

```yaml
...
install:
  - curl -sL https://taskfile.dev/install.sh | sh

before_install:

script:
  - ./bin/task verify
...
```

For a full code listing, please see files in repository, specifically `.travis.yml` [here](https://github.com/MartinHeinz/openfaas-functions/blob/master/.travis.yml) and `verify.sh` [here](https://github.com/MartinHeinz/openfaas-functions/blob/master/verify.sh).

## Conclusion

As you can see from this article, it can be really easy to start developing _serverless_ applications and I hope you will give it a shot, considering that _serverless_ is not just hype anymore and these technologies are quite mature. If you want to see more details and complete source code from this post please see my repository [here](https://github.com/MartinHeinz/openfaas-functions) and in case you have some suggestions/improvements/issues feel free to submit issue or just star it. &#x1F642;

As mentioned earlier - in the next blog post, we will look at how to set up own template store, create custom templates and use it to create better functions.