9. Going Serverless with OpenFaaS – Serverless Architectures with Kubernetes

9. Going Serverless with OpenFaaS

Learning Objectives

By the end of this chapter, you will be able to:

  • Set up the OpenFaaS framework on a Minikube cluster
  • Create, build, deploy, list, invoke, and delete functions with the OpenFaaS CLI
  • Deploy and invoke OpenFaaS functions from the OpenFaaS portal
  • Return an HTML web page from OpenFaaS functions
  • Set up the Prometheus and Grafana dashboards to monitor OpenFaaS functions
  • Configure function autoscaling to adjust the function count based on demand

In this chapter, we aim to set up the OpenFaaS framework on top of a Minikube cluster and study how we can work with OpenFaaS functions, using both the OpenFaaS CLI and OpenFaaS portal. We will also look into features such as observability and autoscaling with OpenFaaS.

Introduction to OpenFaaS

In the previous chapter, we learned about OpenWhisk, an open source serverless framework, which is part of the Apache Software Foundation. We learned how to create, list, invoke, update, and delete OpenWhisk actions. We also discussed how to automate the action invocation with feeds, triggers, and rules.

In this chapter, we will be studying OpenFaas, another open source framework used to build and deploy serverless functions on top of containers. This was started as a proof-of-concept project by Alex Ellis in October 2016, and the first version of the framework, written in Golang, was committed to GitHub in December 2016.

OpenFaaS was originally designed to work with Docker Swarm, which is the clustering and scheduling tool for Docker containers. Later, the OpenFaaS framework was rearchitected to support the Kubernetes framework, too.

OpenFaaS comes with a built-in UI named OpenFaaS Portal, which can be used to create and invoke the functions from the web browser. This portal also offers a CLI named faas-cli that allows us to manage functions through the command line. The OpenFaaS framework has built-in support for autoscaling. This will scale up the function when there is increased demand, and it will scale down when demand decreases, or even scale down to zero when the function is idle.

Now, let's take a look at the components of the OpenFaaS framework:

Figure 9.1: OpenFaaS components

OpenFaaS consists of the following components that are running on the underlying Kubernetes or Docker Swarm:

  • API Gateway:

    The API Gateway is the entry point to the OpenFaaS framework, which exposes the functions externally. It is also responsible for collecting the function metrics such as function invocation count, function execution duration, and number of function replicas. The API Gateway also handles function autoscaling by increasing or decreasing function replicas based on demand.

  • Prometheus:

    Prometheus, which is an open source monitoring and alerting tool, comes bundled with the OpenFaaS framework. This is used to store the information about the function metrics collected by the API Gateway.

  • Function Watchdog:

    The Function Watchdog is a tiny Golang web server running alongside each function container. This component is placed between the API Gateway and your function and is responsible for converting message formats between the API Gateway and the function. It converts the HTTP messages sent by the API Gateway to the "standard input" (stdin) messages, which the function can understand. This also handles the response path by converting the "standard output" (stdout) response sent by the function to an HTTP response.

    The following is an illustration of a function watchdog:

Figure 9.2: OpenFaaS function watchdog

Docker Swarm or Kubernetes can be used as the container orchestration tool with the OpenFaaS framework, which manages the containers running on the underlying Docker framework.

Getting Started with OpenFaas on Your Local Minikube Cluster

In this section, we will set up an OpenFaaS framework and CLI on our local Minikube cluster. Before starting the installation, we need to ensure that the following prerequisites are met:

  • Minikube is installed
  • Docker (version 17.05 or later) is installed
  • Helm is installed
  • A Docker Hub account is created

Once these prerequisites are ready, we can continue to install OpenFaaS. The installation of OpenFaas can be broadly classified into three steps, as follows:

  1. Installing the OpenFaaS CLI
  2. Installing the OpenFaaS framework (on a Minikube cluster)
  3. Setting up an environment variable

Let's look at each of these steps in more depth:

Installing the OpenFaaS CLI

faas-cli is the command-line utility for the OpenFaaS framework, which can be used to create and invoke OpenFaaS functions from the Terminal. We can install the latest version of faas-cli using the following command:

$ curl -sL https://cli.openfaas.com | sudo sh

The output should be as follows:

Figure 9.3: Installing faas-cli

Once the installation is complete, we can verify installation with the faas-cli version command:

$ faas-cli version

The output should be as follows:

Figure 9.4: The faas-cli version

As you can see, we have installed the faas-cli utility on the cluster and can also check the version number.

Installing the OpenFaaS Framework

Next, we need to install the OpenFaaS framework using the OpenFaaS helm repository. First, we need to add the openfaas helm repository and update it to pull any new releases. Use the following commands:

$ helm repo add openfaas https://openfaas.github.io/faas-netes/

$ helm repo update

The output should be as follows:

Figure 9.5: Adding and updating helm charts

Installing OpenFaaS requires two Kubernetes namespaces. The openfaas namespace is for the core services of the OpenFaaS framework, and the openfaas-fn namespace is for the OpenFaaS functions. Run the following commands to create the namespaces:

$ kubectl create namespace openfaas

$ kubectl create namespace openfaas-fn

The output will be as follows:

Figure 9.6: Creating namespaces

Now we are going to create the Kubernetes secret, which is required to enable basic authentication for the OpenFaaS gateway. First, we will create a random string that will be used as the password. Once the password is generated, we will echo the generated password and save it in a secure place as we need it to log in to the API Gateway later on. Run the following commands to generate the password:

$ PASSWORD=$(head -c 12 /dev/urandom | shasum| cut -d' ' -f1)

$ echo $PASSWORD

The output will be as follows:

Figure 9.7: Generating the password

After generating the password, we will create a Kubernetes secret object to store the password.

Note:

A Kubernetes secret object is used to store sensitive data such as a password.

Execute the following command to create a Kubernetes secret named basic-auth:

$ kubectl -n openfaas create secret generic basic-auth \

    --from-literal=basic-auth-user=admin \

    --from-literal=basic-auth-password="$PASSWORD"

The output will be as follows:

Figure 9.8: Creating the basic-auth secret

We can now deploy the OpenFaaS framework from the helm chart. The helm upgrade openfaas command starts the deployment of OpenFaaS and will start deploying the OpenFaaS framework on your local Minikube cluster. This will take between 5 and 15 minutes depending on the network speed. Run the following commands to install OpenFaaS:

$ helm upgrade openfaas \

    --install openfaas/openfaas \

    --namespace openfaas \

    --set functionNamespace=openfaas-fn \

    --set basic_auth=true

The preceding command prints a lengthy output, and, at the bottom, it provides a command to verify the installation, as you can see in the following screenshot:

Figure 9.9: OpenFaaS installation

You can verify the deployment state from the following command:

$ kubectl --namespace=openfaas get deployments -l "release=openfaas, app=openfaas"

The output will be displayed as follows:

Figure 9.10: Verifying the OpenFaaS installation

Once the installation has been successfully completed and all services are running, we then have to log in to the OpenFaaS gateway with the credentials we created in the preceding steps. Run the following command to log in to the OpenFaas gateway:

$ faas-cli login --username admin --password $PASSWORD

The output should be as follows:

Figure 9.11: Logging in to the OpenFaaS gateway

Setting the Environment Variables

There are several environment variables related to OpenFaaS, and we will set two environment variables in this section. These environment variables can be overridden using the command-line flags of faas-cli, if necessary.

  • OPENFAAS_URL: This should point to the API Gateway component.
  • OPENFAAS_PREFIX: This is the Docker ID of your Docker Hub account.

Open the ~/.bashrc file with your favorite text editor and add the following two lines at the end of the file. Replace <your-docker-id> with your Docker ID in the following commands:

export OPENFAAS_URL=$(minikube ip):31112

export OPENFAAS_PREFIX=<your-docker-id>

Then, you need to source the ~/.bashrc file to reload the newly configured environment variables, as shown in the following command:

$ source ~/.bashrc

The command should appear as follows:

Figure 9.12: Source the bashrc file

OpenFaaS Functions

OpenFaaS functions can be written in any language supported by Linux or Windows, and they can then be converted to a serverless function using Docker containers. This is a major advantage of the OpenFaaS framework compared to other serverless frameworks that support only predefined languages and runtimes.

OpenFaaS functions can be deployed with either faas-cli or the OpenFaaS portal. In the following sections, we are first going to discuss how we can build, deploy, list, invoke, and delete OpenFaaS functions using the faas-cli command-line tool. Then, we will discuss how to deploy and invoke functions with the OpenFaaS portal.

Creating OpenFaaS Functions

As we discussed previously, OpenFaaS functions can be written in any language supported by Linux and Windows. This requires us to create the function code, add any dependencies, and create a Dockerfile to build the Docker image. It requires a certain amount of understanding of the OpenFaaS platform in order to be able to perform the previously mentioned tasks. As a solution, OpenFaaS has a template store that includes prebuilt templates for a set of supported languages. This means that you can download these templates from the template store, update the function code, and then the CLI does the rest to build the Docker image.

First of all, we need to pull the OpenFaaS templates with the faas-cli template pull command. This will fetch the templates from the official OpenFaaS template repository at https://github.com/openfaas/templates.git.

Now, let's create a new folder and pull the templates to the newly created folder with the following commands:

$ mkdir chapter-09

$ cd chapter-09/

$ faas-cli template pull

The output will be as follows:

Figure 9.13: Creating directories

Let's check the folder structure with the tree -L 2 command that will print the folder tree with two levels of depth, as you can see in the following screenshot:

Figure 9.14: The tree view of the folder

Within the template folder, we can see 17 folders each for a specific language template.

Now, we can use the faas-cli new command to create the structure and files for a new function using the downloaded templates as follows:

$ faas-cli new <function-name> --lang=<function-language>

<function-language> can be replaced by any programming language supported by OpenFaaS templates. faas-cli new --list can be used to get a list of supported programming languages, as displayed in the following figure:

Figure 9.15: Listing supported programming language templates

Let's create our first OpenFaaS function named hello with the go language template using the following command:

$ faas-cli new hello --lang=go

The output will be as follows:

Figure 9.16: Creating the hello function template

As per the output, the preceding command will create multiple files and directories inside the current folder. Let's execute the tree -L 2 command again to identify the newly created files and directories:

Figure 9.17: The tree view of the folder

We can see a file named hello.yml, a folder named hello, and a handler.go file inside the hello folder.

First, we will look into the hello.yml file, which is called the function definition file:

version: 1.0

provider:

  name: openfaas

  gateway: http://192.168.99.100:31112

functions:

  hello:

    lang: go

    handler: ./hello

    image: sathsarasa/hello:latest

This file has three top levels named version, provider, and functions.

Inside the provider section, there is a name: faas tag, which defines the provider name as faas. This is the default and only valid value for the name tag. The next one is the gateway tag, which points to the URL where the API Gateway is running. This value can be overridden at deployment time with the --gateway flag or the OPENFAAS_URL environment variable.

Next is the functions section, which is used to define one or more functions to be deployed with the OpenFaaS CLI. In the preceding code, the hello.yml file has a single function named hello written in the Go language (lang: go). The handler of the function is defined with handler: ./hello section, which points to the folder where the source code of the hello function (hello/handler.go) resides. Finally, there is the image tag that specifies the name of the output Docker image. The Docker image name is prepended with your Docker image ID configured using the OPENFAAS_PREFIX environment variable.

Next, we will discuss the handler.go file that was created inside the hello folder. This file contains the source code of the function written in the Go language. This function accepts a string parameter and returns the string by prepending it with Hello, Go. You said:, as displayed in the following code snippet:

package function

import (

"fmt"

)

// Handle a serverless request

func Handle(req []byte) string {

return fmt.Sprintf("Hello, Go. You said: %s", string(req))

}

This is just a sample function generated by the template. We can update it with our function logics.

Building OpenFaaS Functions

Once the function definition file (hello.yml) and function source code (hello/handler.go) are ready, the next step is to build the function as a Docker image. The faas-cli build CLI command is used to build the Docker image, which has the following format:

$ faas-cli build -f <function-definition-file>

This initiates the process of building the Docker image and will invoke the docker build command internally. A new folder named build will be created during this step with all the files required for the build process.

Now, let's build the hello function that we created in the previous section:

$ faas-cli build -f hello.yml

We will receive an output similar to the following:

     [0] > Building hello.

Clearing temporary build folder: ./build/hello/

Preparing ./hello/ ./build/hello/function

Building: sathsarasa/hello with go template. Please wait..

Sending build context to Docker daemon  6.656kB

Step 1/24 : FROM openfaas/classic-watchdog:0.15.4 as watchdog

---> a775beb8ba9f

...

...

Successfully built 72c9089a7dd4

Successfully tagged sathsarasa/hello:latest

Image: sathsarasa/hello built.

[0] < Building hello done.

[0] worker done.

Once we receive the build success message, we can list the Docker image using the docker images command as follows:

$ docker images | grep hello

The output is as follows:

Figure 9.18: Verifying the Docker image

Pushing the OpenFaaS Function Image

The next step of the process is to push the Docker image of the function to a Docker registry or to the Docker Hub. We can use either the faas-cli push or docker push commands to push the image.

Note

Docker Hub is a free service for storing and sharing Docker images.

Let's push the image with the faas-cli push command:

$ faas-cli push -f hello.yml

The output will be as follows:

Figure 9.19: Pushing the Docker image

We can verify that the image is pushed successfully by visiting the Docker Hub page at https://hub.docker.com/.

The output should be as follows:

Figure 9.20: Verifying from Docker Hub

Thus, we have successfully pushed the Docker image function to Docker Hub.

Deploying the OpenFaaS Functions

Now, we are ready to deploy the hello function into the OpenFaaS framework using the faas-cli deploy command. This command also requires the function specification file with the -f flag similar to other faas-cli commands that we executed previously:

$ faas-cli deploy -f hello.yml

The output should be as follows:

Figure 9.21: Deploying the hello function

We will receive a 202 Accepted output along with the function URL, which we can use to invoke the function.

At this step, there will be a number of Kubernetes objects, including pods, services, deployments, and replica sets created in the openfaas-fn namespace. We can view all these Kubernetes objects with the following command:

$ kubectl get all -n openfaas-fn

The output should be as follows:

Figure 9.22: Verifying the Kubernetes objects

Hence, we have successfully deployed the hello function to the OpenFaaS framework.

Listing the OpenFaaS Functions

The faas-cli list command is used to list all the functions deployed on the OpenFaaS framework:

$ faas-cli list

The output should be as follows:

Figure 9.23: Listing the OpenFaaS functions

The output of the faas-cli list command will include the following columns:

  • Function – The name of the function
  • Invocations – The number of times the function has been invoked
  • Replicas – The number of Kubernetes pod replicas of the function

The value of the Invocations column will increase each time we invoke the function. The value of the Replicas column will increase automatically if the invocation rate increases.

The --verbose flag can be used with faas-cli list if you want to get an additional column named Image, which lists the Docker image used to deploy the function, as shown in the following command:

$ faas-cli list --verbose

The output should be as follows:

Figure 9.24: Listing the OpenFaaS functions with the verbose output

If we want to get details about a specific function, we can use the faas-cli describe CLI command:

$ faas-cli describe hello

The output should be as follows:

Figure 9.25: Describing an OpenFaaS function

Invoking OpenFaaS Functions

Now, the function is deployed and ready to be invoked. A function can be invoked with the faas-cli invoke command, which has the following format:

$ faas-cli invoke <function-name>

Now, let's invoke the hello function we deployed in the previous step.

Run the following command to invoke the hello function:

$ faas-cli invoke hello

Once the function is invoked, it will ask you to enter the input parameters and press Ctrl + D to stop reading from the standard input. The output should be as follows:

Figure 9.26: Invoking the hello function

We can also send the input data to the function, as shown in the following command:

$ echo "Hello with echo" | faas-cli invoke hello

The output should be as follows:

Figure 9.27: Invoking the hello function with piping the input

The curl command can also be used to invoke the functions, as follows:

$ curl http://192.168.99.100:31112/function/hello -d "Hello from curl"

The output should be as follows:

Figure 9.28: Invoking the hello function with curl

Hence, we have successfully invoked the hello function using both the faas-cli invoke command and the curl command.

Deleting OpenFaaS Functions

The faas-cli remove command is used to delete a function from the OpenFaaS cluster either by specifying the function definition file with the -f flag, or by explicitly specifying the function name, as shown in the following command:

$ faas-cli remove <function-name>

Or, alternatively, with the following command:

$ faas-cli remove -f <function-definition-file>

We can remove the hello function we created earlier with the following command:

$ faas-cli remove hello

The output should be as follows:

Figure 9.29: Deleting the hello function

In these sections, we learned to create, deploy, list, invoke, and delete OpenFaaS functions using the faas-cli command line. Now, let's move on to an exercise where we will be creating our first OpenFaaS function.

Exercise 30: Creating an OpenFaaS Function with Dependencies

In this exercise, we are going to create a Python function that can print the source IP address by invoking an external API. We will be using the requests Python module to invoke this API:

  1. Create a new function named ip-info using the Python3 template:

    $ faas-cli new ip-info --lang=python3

    The output should be as follows:

    Figure 9.30: Creating the ip-info function template
  2. Update the ip-info/requirements.txt file to add the requests pip module, which we need to invoke HTTP requests from our function:

    requests

  3. Update the ip-info/handler.py file to invoke the https://httpbin.org/ip endpoint. This endpoint is a simple service that will return the IP of the originating request. The following code will send an HTTP GET request to the https://httpbin.org/ip endpoint and return the origin IP address:

    import requests

    import json

    def handle(req):

        api_response = requests.get('https://httpbin.org/ip')

        json_object = api_response.json()

        origin_ip = json_object["origin"]

        return "Origin IP is " + origin_ip

  4. Build, push, and deploy the ip-info function with the faas-cli up command. The faas-cli up command will execute the faas-cli build, faas-cli push, and faas-cli deploy commands in the background to build the function, push the Docker images to the Docker registry, and deploy the function on the OpenFaaS framework:

    $ faas-cli up -f ip-info.yml

    The faas-cli up command will print the following output, which lists the steps of building, pushing, and deploying the ip-info function:

    [0] > Building ip-info.

    Clearing temporary build folder: ./build/ip-info/

    Preparing ./ip-info/ ./build/ip-info//function

    Building: sathsarasa/ip-info:latest with python3 template. Please wait..

    Sending build context to Docker daemon  9.728kB

    ...

    Successfully built 1b86554ad3a2

    Successfully tagged sathsarasa/ip-info:latest

    Image: sathsarasa/ip-info:latest built.

    [0] < Building ip-info done.

    [0] worker done.

    [0] > Pushing ip-info [sathsarasa/ip-info:latest].

    The push refers to repository [docker.io/sathsarasa/ip-info]

    ...

    latest: digest: sha256:44e0b0e1eeca37f521d4e9daa1c788192cbc0ce6ab898c5e71cb840c6d3b4839 size: 4288

    [0] < Pushing ip-info [sathsarasa/ip-info:latest] done.

    [0] worker done.

    Deploying: ip-info.

    WARNING! Communication is not secure, please consider using HTTPS. Letsencrypt.org offers free SSL/TLS certificates.

    Deployed. 202 Accepted.

    URL: http://192.168.99.100:31112/function/ip-info

  5. Invoke the ip-info function using the curl command as follows:

    $ curl http://192.168.99.100:31112/function/ip-info

    The output should be as follows:

    Figure 9.31: Invoking the ip-info function template
  6. Finally, remove the ip-info function:

    $ faas-cli remove ip-info

Thus, we have created, deployed, and invoked an OpenFaaS function named ip-info, which will print the source IP address of the function invoker.

Deploying and Invoking Functions with OpenFaaS Portal

The OpenFaaS framework comes with a built-in UI that allows us to deploy and invoke functions from the web browser. It can be used to either deploy a custom function or a function from the function store. The OpenFaaS function store is a freely available set of prebuilt functions. These functions can be deployed easily on our existing OpenFaaS cluster.

The format of the OpenFaaS portal URL is http://<openfaas-gateway-endpoint>/ui. Let's use the following command to derive the OpenFaaS portal URL from the $OPENFAAS_URL environment variable that we set up previously:

echo $OPENFAAS_URL/ui/

The output should be as follows:

Figure 9.32: Generating the OpenFaaS portal URL

Let's navigate to the output URL of http://192.168.99.100:31112/ui/.

You should be able to see a portal similar to the following, which we will use in the following steps to deploy and invoke OpenFaaS functions:

Figure 9.33: Navigating to the OpenFaaS portal URL

Deploying a Function from the Function Store

In this section, we will learn how to deploy a function from the function store. First, click on the Deploy New Function button in the OpenFaaS portal. This will prompt you with a dialog box that lists all the functions available in the function store. In this section, we are going to deploy the Figlet function, which can generate ASCII logos from the string input provided. Select Figlet from the function list and click on the DEPLOY button, as shown in the following figure:

Figure 9.34: Deploying the figlet function

That's all you need to do! This will deploy the Figlet function into our existing OpenFaaS cluster. Now, you will be able to see a new function named figlet in the left-hand sidebar of the OpenFaaS portal, as shown in the following figure:

Figure 9.35: Verifying the figlet function

Let's invoke the function from the OpenFaaS portal. You need to click on the function name, and then the right-hand panel of the screen will display information about the function, including the function status, invocation count, replica count, function image, and the function URL:

Figure 9.36: Figlet function description

We can invoke this function by clicking on the INVOKE button available under the Invoke function section. If the function requires an input value, you can provide it under the Request Body section before invoking the function.

Let's invoke the figlet function by providing the OpenFaaS string as the request body, as shown in the following figure:

Figure 9.37: Invoking the figlet function

Now, you can see the expected output of the function. This will be the ASCII logo for the input value we provided when invoking the function. Additionally, the UI will provide you with the response status code and the execution duration for the function invocation.

Deploying a Custom Function

Now, let's deploy a custom function named hello using the Docker image that we built previously. Before deploying the functions from the OpenFaaS portal, we should have our functions written, and the Docker images built and pushed using the faas-cli command.

Click on the Deploy New Function button again, and, this time, select the CUSTOM tab from the dialog box. Now, we need to provide the Docker image name and function name as mandatory fields. Let's provide the hello Docker image we built previously (<your-docker-id>/hello) and provide hello-portal as the function name and click on the DEPLOY button:

Figure 9.38: Deploying the hello-portal function

Then, you will see the hello-portal function added to the left-side menu of the OpenFaaS portal:

Figure 9.39: Verifying the hello-portal function

Now, you can follow similar steps to the ones that we discussed previously to invoke the hello-portal function.

OpenFaaS Functions with HTML Output

In this section, we are going to set up an OpenFaaS function to return HTML content. This allows us to create both static and dynamic websites using the OpenFaaS framework.

First, we will create the html-output function using the php7 template, as shown in the following command:

$ faas-cli new html-output --lang=php7

The output should be as follows:

Figure 9.40: Creating the html-output function

Then, we will update the generated Handler.php file to return a hardcoded HTML string using the following command:

Open the html-output/src/Handler.php file using your favorite text editor. The following command will open this file with the vi editor:

$ vi html-output/src/Handler.php

Add the following content to the file. This is a simple PHP code that will return the text, OpenFaaS HTML Output, formatted as HTML header text:

<?php

namespace App;

/**

* Class Handler

* @package App

*/

class Handler

{

    /**

     * @param $data

     * @return

     */

    public function handle($data) {

        $htmlOutput = "<html><h1>OpenFaaS HTML Output</h1></html>";

        return $htmlOutput;

    }

}

Now, the PHP function is ready with the HTML output. The next step is to configure Content-Type of the function as text/html. This can be done by updating the environment section of the function definition file. Let's update the html-output.yml file with content_type: text/html inside the environment section, as shown in the following code:

$ vi html-output.yml

provider:

  name: faas

  gateway: http://192.168.99.100:31112

functions:

  html-output:

    lang: php7

    handler: ./html-output

    image: sathsarasa/html-output:latest

    environment:

      content_type: text/html

Now, let's build, push, and deploy the html-output function with the faas-cli up command:

$ faas-cli up -f html-output.yml

Once the preceding command is executed, we will receive an output similar to the following:

[0] > Building html-output.

Clearing temporary build folder: ./build/html-output/

Preparing ./html-output/ ./build/html-output//function

Building: sathsarasa/html-output:latest with php7 template. Please wait..

Sending build context to Docker daemon  13.31kB

...

Successfully built db79bcf55f33

Successfully tagged sathsarasa/html-output:latest

Image: sathsarasa/html-output:latest built.

[0] < Building html-output done.

[0] worker done.

[0] > Pushing html-output [sathsarasa/html-output:latest].

The push refers to repository [docker.io/sathsarasa/html-output]

b7fb7b7178f2: Pushed

06f1d60fbeaf: Pushed

b2f016541c01: Pushed

1eb73bc41394: Pushed

dc6f559fd649: Mounted from sathsarasa/php7

e50d92207970: Mounted from sathsarasa/php7

9bd686c066e4: Mounted from sathsarasa/php7

35b76def1bb4: Mounted from sathsarasa/php7

34986ef73af3: Mounted from sathsarasa/php7

334b08a7c2ef: Mounted from sathsarasa/php7

5833c19f1f2c: Mounted from sathsarasa/php7

98d2cfd0a4c9: Mounted from sathsarasa/php7

24291ffdb574: Mounted from sathsarasa/php7

eb2c5ec03df0: Pushed

3b051c6cbb79: Pushed

99abb9ea3d15: Mounted from sathsarasa/php7

be22007b8d1b: Mounted from sathsarasa/php7

83a68ffd9f11: Mounted from sathsarasa/php7

1bfeebd65323: Mounted from sathsarasa/php7

latest: digest: sha256:ec5721288a325900252ce928f8c5f8726c6ab0186449d9414baa04e4fac4dfd0 size: 4296

[0] < Pushing html-output [sathsarasa/html-output:latest] done.

[0] worker done.

Deploying: html-output.

WARNING! Communication is not secure, please consider using HTTPS.

Letsencrypt.org offers free SSL/TLS certificates.

Deployed. 202 Accepted.

URL: http://192.168.99.100:31112/function/html-output

The function has now been deployed successfully. Now, we can visit the function URL at http://192.168.99.100:31112/function/html-output from a web browser to view the output, as shown in the following figure:

Figure 9.41: Invoking the html-output function

Exercise 31: Returning HTML Based on Path Parameters

In this exercise, we will create a function that can return one of the two static HTML files based on the path parameters of the function URL:

  1. Create a new function named serverless-website based on the php7 template:

    $ faas-cli new serverless-website --lang=php7

    The output should be as follows:

    Figure 9.42: Creating the serverless-website function
  2. Create the HTML folder inside serverless-website to store all the HTML files:

    $ mkdir serverless-website/src/html

  3. Create the first HTML file for the home page (serverless-website/src/html/home.html) with the following code. This HTML page will output the text, Welcome to OpenFaaS Home Page, as the page header, and OpenFaaS Home as the page title:

    <!DOCTYPE html>

    <html>

      <head>

        <title>OpenFaaS Home</title>

    </head>

    <body>

        <h1>Welcome to OpenFaaS Home Page</h1>

    </body>

    </html>

  4. Create the second HTML file for the login page (serverless-website/src/html/login.html). This HTML page will output a simple login form with two fields for username and password and a Login button to submit the form:

    <!DOCTYPE html>

    <html>

    <head>

        <title>OpenFaaS Login</title>

    </head>

    <body>

        <h1>OpenFaaS Login Page</h1>

        <form id="contact_us_form">

           <label for="username">Username:</label>

           <input type="text" name="username" required>

           <label for="password">Password:</label>

           <input type="text" name="password" required>

           <input type="submit" value="Login">

        </form>

    </body>

    </html>

  5. Update the handler file (serverless-website/src/Handler.php) to return the appropriate HTML file based on the path parameters of the function URL with the following code. This function will receive either home or login as the path parameter while invoking. It will then read the path parameter and set the HTML page name accordingly based on the path parameter provided. The next step is to open the HTML file, read the content of the file, and finally return the content of the file as the function response:

    <?php

    namespace App;

    class Handler

    {

        public function handle($data) {

         // Retrieve page name from path params

    $path_params = getenv('Http_Path');

    $path_params_array = explode('/',$path_params);

    $last_index = count($path_params_array);

    $page_name = $path_params_array[$last_index-1];

    // Set the page name

    $current_dir = __DIR__;

    $html_file_path = $current_dir . "/html/" . $page_name . ".html";

    // Read the file

    $html_file = fopen($html_file_path, "r") or die("Unable to open HTML file!");

    $html_output = fread($html_file,filesize($html_file_path));

    fclose($html_file);

    // Return file content

    return $html_output;

        }

    }

  6. Set content_type as text/html in serverless-website.yml:

    version: 1.0

    provider:

      name: openfaas

      gateway: http://192.168.99.100:31112

    functions:

      serverless-website:

        lang: php7

        handler: ./serverless-website

        image: sathsarasa/serverless-website:latest

        environment:

          content_type: text/html

  7. Build, push, and deploy the serverless-website function using the following command:

    $ faas-cli up -f serverless-website.yml

    The following is the output of the preceding command:

    [0] > Building serverless-website.

    Clearing temporary build folder: ./build/serverless-website/

    Preparing ./serverless-website/ ./build/serverless-website//function

    Building: sathsarasa/serverless-website:latest with php7 template. Please wait..

    Sending build context to Docker daemon  16.38kB

    ...

    Successfully built 24fd037ce0d0

    Successfully tagged sathsarasa/serverless-website:latest

    Image: sathsarasa/serverless-website:latest built.

    [0] < Building serverless-website done.

    [0] worker done.

    [0] > Pushing serverless-website [sathsarasa/serverless-website:latest].

    The push refers to repository [docker.io/sathsarasa/serverless-website]

    ...

    latest: digest: sha256:991c02fa7336113915acc60449dc1a7559585ca2fea3ca1326ecdb5fae96f2fc size: 4298

    [0] < Pushing serverless-website [sathsarasa/serverless-website:latest] done.

    [0] worker done.

    Deploying: serverless-website.

    WARNING! Communication is not secure, please consider using HTTPS. Letsencrypt.org offers free SSL/TLS certificates.

    Deployed. 202 Accepted.

    URL: http://192.168.99.100:31112/function/serverless-website

  8. Verify by invoking both the home page and login page on the following URLs:

    http://192.168.99.100:31112/function/serverless-website/home

    The home page should appear as follows:

Figure 9.43: Invoking the home page of the serverless website function

Next, run the following URL: http://192.168.99.100:31112/function/serverless-website/login.

The login page should look as follows:

Figure 9.44: Invoking the login page of the serverless website function

Thus, we have successfully parsed HTML based on the path parameters.

OpenFaaS Function Observability

Observability is a critical feature of every production system. This allows us to observe the health of the system and activities performed thereon. Once our applications are deployed and running in production, we need to make sure they are running as expected in terms of functionality and performance. Any service downtime can have a negative impact on the organization. So, it is very critical to observe the important application metrics, such as CPU usage, memory usage, request count, response duration over time, and then analyze for any anomalies.

OpenFaaS comes built-in with Prometheus, which can be used to collect function metrics. Prometheus contains a time series database, which can be used to store various metrics over time. The OpenFaaS API gateway collects metrics related to the function invocation and stores them in Prometheus. The following table shows the metrics exposed by the OpenFaaS API Gateway and stored with Prometheus:

Figure 9.45: Function metrics with descriptions

We can use the Prometheus dashboard to visualize these metrics.

First, we need to expose the Prometheus deployment created during the installation. Execute the following command to expose Prometheus as a NodePort service:

$ kubectl expose deployment prometheus -n openfaas --type=NodePort --name=prometheus-ui

This will expose the Prometheus deployment on a random port above 30,000. Execute the following commands to get the URL of the Prometheus UI:

$ MINIKUBE_IP=$(minikube ip)

$ PROMETHEUS_PORT=$(kubectl get svc prometheus-ui -n openfaas -o jsonpath="{.spec.ports[0].nodePort}")

$ PROMETHEUS_URL=http://$MINIKUBE_IP:$PROMETHEUS_PORT/graph

$ echo $PROMETHEUS_URL

The output should be as follows:

Figure 9.46: Generating the Prometheus URL

For me, the PROMETHEUS_URL output value is http://192.168.99.100:30479/graph. But the <minikube-ip> and <node-port> values may be different.

We can view the metrics exposed by Prometheus using the UI, as shown in the following figure:

Figure 9.47: Prometheus UI

Type gateway_function_invocation_total in the Expression area and click on the Execute button. This will list the results under the Console tab. We can click on the Graph tab as we need to view the function invocation count in a line graph. Click on the Add Graph button available in the lower-left corner if you want to add this graph permanently to the Prometheus dashboard, as shown in the following figure:

Figure 9.48: Prometheus graph for the gateway_function_invocation_total metric

Note

Invoke the available functions multiple times so that we can view the statistics of these invocations from the Prometheus dashboard.

In addition to the Prometheus dashboards that we discussed, we can also use Grafana to visualize the metrics stored in Prometheus. Grafana is an open source tool used to analyze and visualize metrics over a period of time. It can be integrated with multiple data sources such as Prometheus, ElasticSearch, Influx DB, or MySQL. In the next exercise, we are going to learn how to set up Grafana with OpenFaaS and create dashboards to monitor the metrics stored in the Prometheus data source.

Exercise 32: Installing an OpenFaaS Grafana Dashboard

In this exercise, we are going to install a Grafana dashboard to view the metrics from the Prometheus data source. Then, we will import another OpenFaaS dashboard into Grafana:

  1. Create the grafana deployment in the openfaas namespace using the stefanprodan/faas-grafana:4.6.3 Docker image:

    kubectl run grafana -n openfaas \

        --image=stefanprodan/faas-grafana:4.6.3 \

        --port=3000

    The output should be as follows:

    Figure 9.49: Creating the Grafana deployment
  2. Expose the grafana deployment using the NodePort service:

    kubectl expose deployment grafana -n openfaas  \

        --type=NodePort \

        --name=grafana

    The output should be as follows:

    Figure 9.50: Exposing the grafana port
  3. Find the URL of the grafana dashboard using the following commands:

    $ MINIKUBE_IP=$(minikube ip)

    $ GRAFANA_PORT=$(kubectl get svc grafana -n openfaas -o jsonpath="{.spec.ports[0].nodePort}")

    $ GRAFANA_URL=http://$MINIKUBE_IP:$GRAFANA_PORT/dashboard/db/openfaas

    $ echo $GRAFANA_URL

    The output should be as follows:

    Figure 9.51: Generating the grafana URL
  4. Navigate to the grafana URL using the URL printed in the previous step:
    Figure 9.52: Grafana UI
  5. Log in to Grafana using the default credentials (the username is admin and the
  6. password is admin). The output should be as follows:
    Figure 9.53: Grafana dashboards

    From the Grafana menu () in the top-left corner, as highlighted in Figure 9.53, select Dashboards > Import. Provide the ID of 3434 in the Grafana.com Dashboard input box and wait for a few seconds to load the dashboard data:

    Figure 9.54: Importing the new dashboard
  7. From this screen, select faas as the Prometheus data source and click on Import, as shown in the following figure:
    Figure 9.55: Importing the new dashboard
  8. Now you can see the metrics in the new dashboard:
Figure 9.56: OpenFaaS serverless Grafana dashboard

Thus, we have successfully set up Grafana dashboards to visualize the metrics stored in Prometheus.

OpenFaaS Function Autoscaling

Autoscaling is a feature available in OpenFaaS that scales up or scales down function replicas based on demand. This feature was built using both Prometheus and the Alert Manager components available with the OpenFaaS framework. Alert Manager will fire alerts when the function invocation frequency exceeds the defined threshold.

While deploying the functions, the following labels are used to control the number of minimum replicas, maximum replicas, and the increase/decrease factor of the functions:

  • com.openfaas.scale.min – This defines the initial number of replicas, which is 1 by default.
  • com.openfaas.scale.max – This defines the maximum number of replicas.
  • com.openfaas.scale.factor – This defines the percentage of pod replica increase (or decrease) when the Alert Manager fires the alerts. By default, this is set to 20% and should have a value between 0 and 100.

When OpenFaaS is deployed on Kubernetes, the Horizontal Pod Autoscaling feature from the Kubernetes framework can also be used to autoscale functions based on demand, as an alternative to the built-in autoscaling feature available with the OpenFaaS framework.

Let's now deploy the figlet function from the OpenFaaS function store to check the autoscaling feature in action:

faas-cli store deploy figlet \

    --label com.openfaas.scale.min=1 \

    --label com.openfaas.scale.max=5

The output should be as follows:

Figure 9.57: Deploying the figlet function

Now we can put a load on the figlet function by invoking it 1,000 times, as shown in the following code. The following script will invoke the figlet function 1,000 times by providing the OpenFaaS string as the input for the function and sleeps for 0.1 seconds in between each invocation:

for i in {1..1000}

do

   echo "Invocation $i"

   echo OpenFaaS | faas-cli invoke figlet

   sleep 0.1

done

Navigate to the Grafana portal and observe the increasing number of replicas for the figlet function. Once the load completes, the replica count will start scaling down and go back to the com.openfaas.scale.min count of 1 function replica.

The output should be as follows:

Figure 9.58: Verifying the autoscaling feature

In this section, we covered function autoscaling, we discussed what function autoscaling is, and the configuration we can use to set the minimum replica count, the maximum replica count, and the scale factor. Finally, we deployed a sample function, performed a load on the function, and observed the autoscaling functionality on a Grafana dashboard.

Activity 9: OpenFaaS Form Processor

In this activity, we will be creating a website for a brand that will have a contact form for potential customers to contact the brand personnel. We will be using OpenFaas extensively for this website.

Imagine that you are a freelancer and you want to create a website to increase your brand visibility. This website needs to have a "Contact Us" form that allows potential customers to contact you. You decided to create this website using serverless technologies and OpenFaaS was selected as the framework for this task.

Execute the following steps to complete this activity:

  1. Create a SendGrid (https://sendgrid.com) account to send emails and save the API key.
  2. Create the "Contact Us" form using HTML and return the HTML using an OpenFaaS function. The following is sample code that achieves the functionality of an HTML form with input fields for name, email, and message and a submit button; CSS to add styles to the HTML form; and a JavaScript function, which will be triggered when the user clicks on the Submit button and sends the form data as a POST request to the form-processor function:

    <!DOCTYPE html>

    <html>

      <head>

        <meta charset="UTF-8">

        <title>OpenFaaS Contact Us  Form</title>         

        <style>

          /** Page  background colour */

          body  {

            background-color: #f2f2f2;

          }  

          /** Style the h1  headers */

          h1 {  

            text-align: center;

            font-family: Arial;

          

          /** CSS for the input box and textarea */

          input[type=text], input[type=email], textarea {

            width: 100%;

            margin-top: 10px;   

            margin-bottom: 20px;

            padding: 12px;

            box-sizing: border-box;

            resize: vertical

          }

          /** Style the submit  button */

          input[type=submit] {

            color: white;

            background-color: #5a91e8;

            padding: 10px 20px;

            border: none;

            border-radius: 4px;

            cursor: pointer;

          }

          /** Change submit button  color for mouse hover */

          input[type=submit]:hover  {

            background-color: #2662bf;

          }

          /** Add padding around the form */

           container {

            padding: 20px;

            border-radius: 5px;

          }

         /** Bold font for response and add margin */

      #response {

        font-weight: bold;

    margin-bottom: 20px;

      }

          </style>

        </head>

        <body>

          <h1>OpenFaaS Contact Form</h1>

          <div class="container">

    <!-- Placeholder for the response -->

            <div id='response'></div>  

            <form id="contact_us_form">

              <label for="name">Name:</label>

              <input type="text" id="name" name="name" required>

              <label for="email">Email:</label>

              <input type="email" id="email" name="email" required>

              <label for="message">Message:</label>

              <textarea id="message" name="message" required></textarea>

              <input type="submit" value="Send Message">

              </form>

          </div>

          <script src="http://code.jquery.com/jquery-3.4.1.min.js"></script>

          <script>     

            $(document).ready(function(){

            $('#contact_us_form').on('submit', function(e){

              // prevent form from submitting.

                e.preventDefault();

    $('#response').html('Sending message...');

                // retrieve values from the form field

                var name = $('#name').val();

                email = $('#email').val();

                var message = $('#message').val();

                var formData = {

                  name: name,

                  email: email,

                  message: message

                };

                // send the ajax POST request         

                $.ajax({

                  type: "POST",

                  url: './form-processor',

                  data: JSON.stringify(formData)

                })

                 done(function(data) {

                  $('#response').html(data);

                })

                 fail(function(data) {

                  $('#response').html(data);

                });

              });

            });

            </script>  

        </body>

    </html>

  3. Create the form-processor function, which takes the form values from the Contact Us form and sends an email to a specified email address with the information provided.
  4. Invoke the Contact Us form function using a web browser and verify the email delivery.

    The contact form should look as shown in the following figure:

Figure 9.59: The Contact Us form

The email received from the contact form should look as shown in the following screenshot:

Figure 9.60: Email received from Contact Us form

Note

The solution to the activity can be found on page 444.

Summary

We started this chapter with an introduction to the OpenFaaS framework and continued with an overview of the components available with the OpenFaaS framework. Next, we looked at how to install faas-cli and the OpenFaaS framework on a local Minikube cluster.

Then, we started looking at OpenFaaS functions. We discussed how we can use faas-cli to create the function templates, build and push function Docker image, and deploy the function to the OpenFaaS framework. Then, we learned how to invoke the deployed functions with the faas-cli command and curl command. Next, we introduced the OpenFaaS portal, which is the built-in UI for the OpenFaaS framework.

We also learned how we can set up an OpenFaaS function to return HTML content and return different content based on provided parameters. We configured the Prometheus and Grafana dashboards to visualize the function metrics, including invocation count, invocation duration, and replica counts. Then, we discussed the function autoscaling feature, which scales up or scales down function replicas based on demand. We performed a load test on a function and observed autoscaling in action with Grafana dashboards.

Finally, in the activity, we built the frontend and backend of a Contact Us form of a website using the OpenFaaS framework.

Through the concepts and the various exercises and activities presented in this book, we have equipped you with all the skills you need to use serverless architectures and the state-of-art container management system, Kubernetes.

We are confident that you will be able to apply this knowledge toward building more robust and effective systems and host them on cloud providers such as AWS Lambda, Google Cloud Function, and more. You will also be able to use the highly effective features of best-in-class frameworks such as OpenFaaS, OpenWhisk, Kubeless, and more.