Going Serverless with OpenFaaS and Golang -  Building Optimized Templates
Tags: docker, go, serverless, kubernetes
Published: 2019-11-29 16:39:14

In the [last blog post about _OpenFaaS_ function](https://dev.to/martinheinz/going-serverless-with-openfaas-and-golang-the-ultimate-setup-and-workflow-2hd1) we explored what we can do with _OpenFaaS_ - we created our first OpenFaaS function in _Golang_, we wrote unit test for it, set up CI/CD with _Travis_ and automated common tasks with _Taskfile_. Now, it's time to go little deeper.

In this blog post, we will look into how we can build custom templates, optimize them, build functions from them and finally, we will also incorporate _Go_ modules into our new template. So, let's do it!

_TL;DR: Full source code for this post, including docs can be found here: <https://github.com/MartinHeinz/openfaas-templates>_

## Deeper Look at Templates
If we want to build custom templates, then we first need to understand how they work. Let's first look at what files the template consists of:

- Template Manifest - the `template.yml` is a file that describes the template itself, so that _OpenFaaS_ knows how to create and deploy functions from it. In this file you can find information such as `language`, required `packages` or `fprocess`
- Entrypoint - Next, for the function to be able to work, we need some file (entrypoint) which either listens to HTTP requests or reads _STDIO_ and forwards these request to function itself. What this entrypoint file is, depends on the language, so you can have e.g. `main.go`, `index.py`, `index.js`, etc.
- Handler - Now, that we have an entrypoint, which passes requests to our function, we need to somehow _handle_ it. For that we have `handler.{go,js,py...}` which does the actual work and returns response from the function
- Dependencies - Most of the time, your function will use some packages. These can be specified in language specific way, for example using `package.json` for _JavaScript_ or using `go.mod` for _Golang_
- `Dockerfile` - Finally, to package all these files into a function we have `Dockerfile`, which both builds, tests and creates final _runner_ image that can be deployed to _OpenFaaS_.

Now that we know what the function consists of, how does it actually work? There are two versions of _OpenFaaS_ templates - _classic_ (`watchdog`) and new _beta_ (`of-watchdog`) templates. Both of them operate by creating tiny _Golang_ webserver, which marshals a HTTP request accepted on the API Gateway, which is then forwarded to invoked function. That's that for similarities, now let's look at the differences:

- `watchdog`: _Classic_ templates operate by forking one process per request. Once your process is forked the watchdog passes in the HTTP request via `stdin` and reads a HTTP response via `stdout`.

    <p align="center">
        <img src="https://res.cloudinary.com/martinheinz/image/upload/v1574790497/blog_posts/watchdog-forking_vgd2d6.png"/>
    </p>
    
- `of-watchdog`: On the other hand, the new templates fork a process when watchdog starts, we then forward any request incoming to the watchdog to a HTTP port within the container.

    <p align="center">
        <img src="https://res.cloudinary.com/martinheinz/image/upload/v1574790497/blog_posts/watchdog-http_inxbrq.png"/>
    </p>

_Images above taken from OpenFaaS Architecture Docs: <https://docs.openfaas.com/architecture/watchdog/>_

If you want to delve deeper into how the `watchdog` (both old and new) works, then go to [here](https://github.com/openfaas/faas/blob/master/watchdog/README.md) or [here](https://github.com/openfaas-incubator/of-watchdog/blob/master/README.md)

## Creating Template
First of all, templates have specific file/directory structure, which we need to follow, this is how it looks:

```
template
└── my-template
    ├── Dockerfile
    ├── function
    │   └── handler.go
    ├── main.go
    └── template.yml

```

Using the structure above - the `template` directory is the place where all templates reside. In it, we have one template named `my-template` and inside it are the files necessary for it to work - all of those we already described in previous section. This is very minimal example and we will be adding more files to that as we go.

That's it for structure, now I think it's finally time to create a template, let's start with _"classic `watchdog` style"_ template. We can create one by just copying one from official template store [here](https://github.com/openfaas/templates/tree/master/template). Considering that this is guide for _Golang_ templates, I will take the basic `go` template (it's redundant to copy it here, you can see the most up-to-date version of the template [here](https://github.com/openfaas/templates/tree/master/template/go)).

When we have it copied into `template` folder, we should check if it actually works, right? So let's build it:

```console
~ $ cd template/go
template/go $ ls -la
...
-rw-rw-r-- 1 martin martin 1173 nov 23 09:21 Dockerfile  <- We need this to build it
...
template/go $ docker build -t martinheinz/go .
Sending build context to Docker daemon  7.168kB
...
Successfully built 586fd7a36c0c
Successfully tagged martinheinz/go:latest
```

Looks good, now let's deploy the function, shall we?
```console
~ $ docker push martinheinz/go:latest
~ $ faas-cli deploy --image=martinheinz/go:latest --name=go-test-func
~ $ echo "It's Working!" | faas-cli invoke go-test-func
Hello, Go. You said: It's Working!
```

And just like that, we have created our own template (well, we really just borrowed it from official store) and deployed function made from it. The whole process however, feels quite clunky, as we had to use quite a few commands to run it as well as call `docker` commands directly instead of e.g. `faas-cli build` and `faas-cli push`. We will simplify this process with _Taskfile_ and few scripts in the next blog post. For the time being though, let's optimize the template, that we have...

## What Could We Improve?
The _classic_ template from previous section is nice and all, but there are quite few issues with it:

- _No Go modules_: This template doesn't use _Golang_ module system introduced in 1.11, which means that impractical and outdated dependency management has to be used.
- _Non-optimal `Dockerfile`_: `Dockerfile` for this template has quite a few unnecessary layers making it slow to build and little _thicker_ then needed.
- _`of-watchdog` is faster_: As mentioned above, it's a _classic_ template, so it doesn't use `of-watchdog`. Therefore we can improve it's performance by switching to `of-watchdog` and using HTTP mode.

So, let's fix those!

_**Note**: Here is a good time to mention, that OpenFaaS community already did a lot of work on these templates. Classic one is legacy template and you should be using middleware or http versions of it and possibly applying tweaks and changes presented below._

Some of those issues can be solved by just using `openfaas-incubator/golang-http-template` [here](https://github.com/openfaas-incubator/golang-http-template), but we can push it a little further, so let's take the `golang-http` from [here](https://github.com/openfaas-incubator/golang-http-template/tree/master/template/golang-http) and build on top of that.

Here I will just show you the final `Dockerfile` I created using [this](https://github.com/openfaas-incubator/golang-http-template/blob/master/template/golang-http/Dockerfile) as a base and I will walk you through all the changes and rationale behind them:

```dockerfile
FROM openfaas/of-watchdog:0.7.2 as watchdog
FROM golang:1.13.1-alpine3.10 as build

ARG GO111MODULE="on"
ENV CGO_ENABLED=0

RUN apk --no-cache add git

COPY --from=watchdog /fwatchdog /usr/bin/fwatchdog
RUN chmod +x /usr/bin/fwatchdog

WORKDIR /go/src/handler
COPY . .

# Run go test, gofmt, go vet
RUN chmod +x test.sh \
    && ./test.sh \
    && go build --ldflags "-s -w" -a -installsuffix cgo -o handler .

# Final runner image
FROM alpine:3.10
# Add non root user and certs
RUN apk --no-cache add ca-certificates \
    && addgroup -S app && adduser -S -g app app \
    && mkdir -p /home/app \
    && chown app /home/app

WORKDIR /home/app
COPY --from=build /go/src/handler/handler /usr/bin/fwatchdog /go/src/handler/function/ ./
RUN chown -R app /home/app

USER app
ENV fprocess="./handler" mode="http" upstream_url="http://127.0.0.1:8082"
CMD ["./fwatchdog"]
```

_Note: You can view the `Dockerfile` in the repository [here](https://github.com/MartinHeinz/openfaas-templates/blob/master/template/golang-mod/Dockerfile)_

We start with 2 `FROM` commands as we need both _Golang_ image for building the function as well as `watchdog` binary. Next, we turn Go modules on and disable _CGO_, which would allow us to call _C_ code which we don't need here. Following line installs `git` into the builder image as we need it to download _Golang_ packages during build. On next 2 lines, we copy `watchdog` binary and make it executable. Now it's time to build our function binary - we first copy the source code to `/go/src/handler`, then we run 2 commands against it - _test_ and _build_. For testing, we use `test.sh` script included in template. Let's see what it does:

```bash
# Collect test targets
SRC_DIRS="function"
TARGETS=$(for d in "$SRC_DIRS"; do echo ./$d/...; done)

# Run tests
echo "Running tests:"
go test -installsuffix "static" ${TARGETS} 2>&1
echo

# Collect all `.go` files and run `gofmt` against them. If some need formatting - print them.
echo -n "Checking gofmt: "
ERRS=$(find "$SRC_DIRS" -type f -name \*.go | xargs gofmt -l 2>&1 || true)
if [ -n "${ERRS}" ]; then
    echo "FAIL - the following files need to be gofmt'ed:"
    for e in ${ERRS}; do
        echo "    $e"
    done
    echo
    exit 1
fi
echo "PASS"
echo

# Run `go vet` against all targets. If problems are found - print them.
echo -n "Checking go vet: "
ERRS=$(go vet ${TARGETS} 2>&1 || true)
if [ -n "${ERRS}" ]; then
    echo "FAIL"
    echo "${ERRS}"
    echo
    exit 1
fi
echo "PASS"
echo
```
_Note: You can view the `test.sh` file in the repository [here](https://github.com/MartinHeinz/openfaas-templates/blob/master/template/golang-mod/test.sh)_

First it collects test _targets_, which is our source code from `function` directory. Then it runs `go test` against these target files, if tests pass, then it's time to check formatting and look for suspicious constructs in code, that's what `gofmt` and `go vet` are for. At that point - after tests pass successfully - going back to `Dockerfile` - we build the binary called `handler`.

Final part of the `Dockerfile` is _runner_ image. Here, commands that we run _really_ start to matter. We want to reduce all calls to `RUN`, `COPY`, and `ADD` as those create extra layers and would bloat the final image. So, first we use single `RUN` command to add CA, add user called `app` under which the function will run and move to it's home directory. Next we `COPY` all the needed files from builder image to runner directory from which function will run in single layer, this includes _Golang_ binary, `watchdog` binary and everything in function directory. Finally, we set our user, set environment variables and set `watchdog` binary as default startup command.

Now, that we have `Dockerfile` out of the way, we can have a look at the source code. For the sake of simplicity, I just use both `main.go` and `handler.go` from previously mentioned [golang-http](https://github.com/openfaas-incubator/golang-http-template/tree/master/template/golang-http), as there is not much to change when it comes to template code. What is missing though, is some unit test template, so let's see what we can do about that...
    
## Time For Testing
If you read any of my previous posts, you already know what's coming - _unit testing_. The template can include any code - which includes tests, so I think it's appropriate to add simple test template to have some starting point for testing your functions. So, here is example test:

```golang
package function

import (
	"github.com/openfaas-incubator/go-function-sdk"
	"github.com/stretchr/testify/assert"
	"net/http"
	"testing"
)

func TestHandleReturnsCorrectResponse(t *testing.T) {
	expected := handler.Response{Body: []byte("Hello world, input was: John"), StatusCode: http.StatusOK}
	response, err := Handle(handler.Request{Body: []byte("John"), Method: "GET"})

	assert.Nil(t, err)
	assert.Equal(t, response.StatusCode, expected.StatusCode)
	assert.Equal(t, response.Body, expected.Body)
}
```

This test lives in `handler_test.go` file right next to `handler.go`. This test covers the basic checks that one would expect when doing HTTP request/response, that being - status code check and body of response validation. So, first we create expected value using `handler.Response`, next we do the request to our function represented by `Handle` function, passing in our request, and finally we check it's response against expected values.

## Bringing It All Together

We finally have all the source code needed for our template, but we can't use it quite yet. We are missing `template.yml` which we need to build functions. Here it is:

```yaml
language: golang-mod
fprocess: ./handler
welcome_message: |
  You created Golang function, that uses Go modules.
  It includes simple handler as well as example unit test.

```

Not much to talk about here. We specify language, binary that will be ran as `fprocess` and a simple welcome message. We could also include `build_options` and `packages` here, but in this case it is not necessary.

Next thing we need to complete the template are the dependencies and considering that we are using Go modules, all we need to do is create `go.mod` file alongside `main.go`, that looks like this
 
```golang
module handler

go 1.12

replace handler/function => ./function
```

And then run `go mod tidy` which will populate the file with all our dependencies (`openfaas-incubator/go-function-sdk` and `stretchr/testify`)

Only thing left to do now is to build it, push it and deploy it:

```console
.../golang-mod $ docker build -t martinheinz/golang-mod .
...
Running tests:
ok  	handler/function	0.002s  <- Out test passed!

Checking gofmt: PASS

Checking go vet: PASS
...
Successfully built 26847942c5c4
Successfully tagged martinheinz/golang-mod:latest
.../golang-mod $ docker push martinheinz/golang-mod:latest
.../golang-mod $ faas-cli deploy --image=martinheinz/golang-mod:latest --name=golang-mod-test
.../golang-mod $ echo "It's Working!" | faas-cli invoke golang-mod-test
Hello world, input was: It's Working!
```

## Conclusion

And just like that, we created custom template that we can use to build and deploy _OpenFaaS_ functions. With that said, there are few things left to do... In the next blog post we will build personal template store, where we can put all our custom templates, then add automatic validation of templates and include it in CI/CD pipeline, and finally we will simplify all the tasks related to building and running templates with _Taskfile_ like we did with functions in previous post. For a sneak peek at source code, see [my repository here](https://github.com/MartinHeinz/openfaas-templates) and feel free to leave feedback or just star the repository if you like these kinds of posts. &#x1F609;