Part II: Fn Load Balancer
Tags: programming, beginners, serverless, kubernetes
Published: 2018-05-21 23:23:56

---
title: Part II: Fn Load Balancer
published: true
description: This article talks about how to run the Fn load balancer locally and how to deploy it to the Kubernetes cluster
tags: programming, beginners, serverless, kubernetes
---
---
**Table of Contents**

* [Part I: Getting started with the Fn project](https://dev.to/peterj/playing-with-the-fn-project-5e3f)

* **Part II: Fn Load Balancer (this post)**

---

If you're looking to get up and running with the Fn project, check out the [first post](https://dev.to/peterj/playing-with-the-fn-project-5e3f) in this series. In this post I am going to talk about Fn load balancer - what is it, how to run it locally and how to deploy it to a Kubernetes cluster.

---

The purpose of the Fn load balancer is to intelligently route traffic to clusters of Fn servers. This is not a simple round robin routing as it has some logic that determines to which nodes to route the traffic based on the requests that are coming in. For example, if your function is "hot" on node2, the load balancer will ensure it gets routed to that node, instead of another node where the function is not loaded. That way you get much better performance.
Check the simple diagram below to see how load balancer falls into place with the Fn server(s) and the Fn database.

![LB diagram](https://cdn-images-1.medium.com/max/800/1*e0P5kgq0kGHsghzGjsngVw.png)

Load balancer also gathers information about the entire cluster and exposes the list of nodes and their states, so you can use that information to scale out your cluster by adding more Fn servers or scale in - decrease the number of servers in your cluster.


## Running locally
The quickest way to get multiple Fn servers up and running behind the load balancers and to try things out is to run it locally on your machine using Docker.
The image you want to pull and run is called `fnproject/fnlb`. At the time of writing this, the latest version/tag is `0.0.268`.

Let's try running a couple of Fn servers and a load balancer. We will run both the Fn server(s) and the load balancer in containers - we will link the load balancer to the Fn server instance, so it can discover it.

If you don't want to manually copy/paste the commands in this section, you can get the script [here](https://gist.githubusercontent.com/peterj/25f658880c4415f6da8769c095d16b9f/raw/4d780fb5e810810ec18c16659e1d149f71d16c41/run-fn-lb.sh) - this script will automatically start N Fn server containers and a single Fn load balancer.

{% gist
https://gist.github.com/peterj/25f658880c4415f6da8769c095d16b9f 
%}


>Note: Make sure to run fn start in the same folder you're planning to run the >above script in. The reason for that is, so Fn can create the database file for >us. If you don't do this, you'll get errors starting the Fn server containers.

Start by running a couple of Fn server containers. We don't need to expose any ports to the host, as we are going to be accessing the Fn server through the load balancer:

```
docker run -it --rm --name fnserver1 -v $PWD/data/fn.db:/app/data/fn.db  -v /var/run/docker.sock:/var/run/docker.sock fnproject/fnserver:latest
```

Similarly, run the second Fn server from a new terminal window. Make sure you run the second server from the same folder as they need to share the same database:

```
docker run -it --rm --name fnserver2 -v $PWD/data/fn.db:/app/data/fn.db  -v /var/run/docker.sock:/var/run/docker.sock fnproject/fnserver:latest
```

>Note: if you don't feel like opening multiple new terminal windows, you can add -d >to each Docker command to run the containers in detached mode. I prefer seeing >logs from all containers when trying things out.

Finally, let's run the load balancer and link to the Fn servers we have running:

```
docker run -it --rm --name fnlb --link fnserver1 --link fnserver2 -p 8080:8081 fnproject/fnlb:latest --nodes fnserver1:8080, fnserver2:8080
```

We are using `--link` to link the containers together - this makes the Fn server containers accessible from the Fn load balancer container using their names (`fnserver1` and `fnserver2`). We are also exposing port `8080` to the host and linking it to the port `8081` inside of the container (that's the port load balancer runs on inside of the container).

If all is good, you should see the following output from the load balancer container telling you that both Fn servers are running, healthy and accessible from the load balancer:

![LB discovering nodes](https://cdn-images-1.medium.com/max/800/1*3JkMGs6b50ZeDf5XpjXeCw.png)

Let's try this out! Since load balancer is exposed on port `8080`, we can simply run the `fn` CLI commands to deploy the apps (we could have used a different port for the load balancer and then set the `FN_API_URL` environment variable to point to the actual host and port).

At this point you can create and deploy a couple of new functions:
```
fn init --runtime go testapp1
fn init --runtime go testapp2
fn init --runtime go testapp3
cd testapp1
fn deploy --local --app testapp1
cd .. 
cd testapp2
fn deploy --local --app testapp2
cd ..
cd testapp3
fn deploy --local --app testapp3
cd ..
```

And then try calling them to see that the calls are being routed to both Fn server instances. I used this command to continuously call the endpoints from multiple terminal windows:

```
while true; do sleep 1; curl http://localhost:8080/r/testapp3/testapp3; echo $(date);done
```

Make sure you replace the app and function name (`testappX`) and eventually you should see logs being outputted from both Fn server instances.

## Management endpoints

Fn load balancer also exposes a management endpoint that's accessible on path `/1/lb/nodes` and supports `PUT`, `DELETE` and `GET` methods. With `PUT` you could dynamically add a node to the list of nodes load balancer maintains, the `DELETE` method will remove a specified node and with `GET` you can get a list of nodes and their states. You can imagine implementing some sort of a watcher that uses certain events to add/delete nodes from the load balancer.


# Running on Kubernetes

How would we replicate a similar set up, but instead of running it locally, we would run it on Kubernetes?

Fn load balancer supports a Kubernetes mode of operation that can be activated by passing in the `-db=k8s` flag - in this mode the `--nodes` flag will be ignored and you will be able to use labels and label-selectors to pick out the pods that are running Fn servers. You could manually go and create the files necessary to deploy all this on Kubernetes, but the good news is that the Fn team already did this for you!

There's an [Fn Helm chart](https://medium.com/r/?url=https%3A%2F%2Fgithub.com%2Ffnproject%2Ffn-helm) available and you can use that to install Fn on your Kubernetes cluster.
Helm chart on the link above will also install an instance of MySQL and Redis on your cluster as well as the UI and Fn Flow - so you get the full deal with one Helm command.
Instructions on how to install it are [here](https://medium.com/r/?url=https%3A%2F%2Fgithub.com%2Ffnproject%2Ffn-helm), but in short once you have Helm installed you run the following commands to initialize Helm, clone the repo, install the chart dependencies and finally install the Fn chart:

```
helm init
git clone git@github.com:fnproject/fn-helm.git && cd fn-helm
helm dep build fn
helm install --name myfn fn
```

Once installed, you will also see the instructions on how to set the `FN_API_URL` environment variable that points to the load balancer. If you're using Docker for Mac you can access the load balancer on `http://localhost:80`.

If you're watching the logs from the Fn load balancer (pod that's created as part of the deployment `fn-fnlb-depl`), you should see a bunch of activity that indicates new pod was detected and there should be a line that look like this:

```
time="2018-05-21T22:31:49Z" level=info msg="is healthy" node="10.1.0.23:80"
```

and indicates that Fn load balancer was able to connect to an instance of the Fn server.
The great thing if you're using Kubernetes that's part of Docker for Mac is that the Fn load balancer service is accessible via `http://localhost:80`:

```
$ curl localhost:80/version
{"version":"0.3.455"}
```

>Note: if you're deploying this to a "real" cluster, you can look up the external >IP of the `fn-fn-api` service by running `kubectl get svc` and checking the >`EXTERNAL-IP` column.

## Hooking up the Fn CLI
Assuming you don't have any Fn servers running locally anymore, you will get an error if you try to run an Fn CLI command. For example:
```
# Try to get the list of apps
$ fn apps l
ERROR: Get http://localhost:8080/v1/apps: dial tcp [::1]:8080: connect: connection refused
```

Since we have Fn load balancer exposed on localhost port 80, we can hook it up with the CLI by setting the environment variable `FN_API_URL` or running the CLI with the variable pre-pended:

```
FN_API_URL=http://localhost:80 fn apps l
```

Because we haven't deployed anything yet, the above command will return "no apps found". Set the `FN_REGISTRY` to your Docker hub repository and then deploy the testapp1 from before:

```
FN_API_URL=http://localhost:80 fn deploy --app testapp1
```

>Note: we had to set the `FN_REGISTRY`, because we won't be deploying the apps to a >"local" Fn server like we did before. We will actually build and push the image to >the Docker hub, so Fn server running in the Kubernetes cluster can pull it and run >it.

Last thing to do is to try and run the deployed function. You can either list the routes of the app we deployed to get the full endpoint (e.g. `http://localhost:80/r/testapp1/testapp1`) or use the Fn CLI to invoke the function like this:

```
$ FN_API_URL=http://localhost:80 fn call testapp1 testapp1
{"message1":"Hello World"}
```

# Conclusion
In this article I talked about Fn load balancer and how to set it up either locally for development and just to try things out as well as how to deploy it using a Helm chart on a Kubernetes cluster.
With the knowledge from the previous post you could create your custom Fn server image (including your custom extensions) then deploy and run it inside the Kubernetes cluster and perhaps expose the functionality to the end users and allow them to use your platform to deploy their services.

# Thanks for Reading!
Any feedback on this article is more than welcome! You can also follow me on [Twitter](https://twitter.com/pjausovec) and [GitHub](https://github.com/peterj). If you liked this and want to get notified when I write more stuff, you should subscribe to [my newsletter](https://tinyletter.com/pjausovec)!