Functions as a Service are a new paradigm for application composition and deployment. Functions are a smaller deployable unit than microservices and especially traditional monoliths. Project Riff is a new Pivotal project that runs functions in a cloud environment like Pivotal Container Services (Kubernetes). Lets get started by setting up a new Riff environment and then creating some functions.
minikube
is the tool that we’ll use to run Kubernetes locally.
In this demo, we assume a configuration containing Minikube 0.25
and Helm/tiller 2.7.2
. Make sure to start Minikube:
minikube start \
--memory=4096 \
--cpus=2
All of the steps to manually prepare the environment locally are outlined in the Riff Github Repo. Helm is a package manager for Kubernetes. Riff provides helm packages ( Helm "charts" ) to ease installation and migration. For further information on installing Helm, refer to the project’s Installation guide.
Homebrew can be used to install helm in a jiffy:
brew install kubernetes-helm
Linux and Windows users may use alternative methods, or your simply want to install things manually on OSX, follow the instructions for Helm.
Then, add the Helm chart for Riff:
helm repo add riffrepo https://riff-charts.storage.googleapis.com
helm repo update
Install Riff with the release name demo
:
helm install riffrepo/riff --name demo
Once this command is complete, you will have a functioning Riff environment. You can enumerate the Riff componenets using this command:
kubectl get pods --show-labels -l app=riff
You’ll notice several pods in service. The entry point to your functions
is demo-riff-http-gateway
. Enter the following command to enumerate
Kubernetes services and ensure it is accepting connections from localhost
;
kubectl get svc -l app=riff
Check that demo-riff-http-gateway
has a TYPE
of NodePort
.
If your riff-http-gateway says that EXTERNAL-IP is Pending and fails to resolve after several minutes, then you will need to change the service type.
kubectl edit service demo-riff-http-gateway
Update the type
field from LoadBalancer
to NodePort
, and save.
This will expose your riff-http-gateway to the local network.
Additional networking configuration may be necessary as you might have subnetting/firewalls in place, especailly in a controlled environtment.
In order to use Riff, you’ll need the riff
command line client interface (CLI).
Clone the git
repository and add it to your PATH
.
Now you can use riff
in the shell.
Riff maintains a registry of functions. These functions can optionally accept payloads as requests and optionally produce replies. Riff will forward requests to your functions and deliver replies from your functions over message channels. Messages originate from a number of different sources: HTTP, Kafka, RabbitMQ, etc. As of this writing, while message payloads are routed through Kafka topics, communication to the front-end of Riff take HTTP requests only.
At the moment, Riff supports the following languages and runtimes: Python, Node.js, /bin/bash
, and Java.
We will focus on Java in this example. Riff supports three standard Java 8 functional interfaces:
-
java.util.function.Supplier
: returns a value but does not accept an argument -
java.util.function.Function
: returns a value and does accept an argument -
java.util.function.Consumer
: does not return a value but does accept an argument.
Let’s look at a simple Function<String,String>
, which both accepts an input argument and
produces a reply. This is the only one that we will deploy in this article.
DemoFunction.java
package mcp;
import java.util.function.Function;
public class DemoFunction implements Function<String, String> {
public String apply(String s) {
return new StringBuilder(s).reverse().toString();
}
}
We will need to create and deploy our function in a container. We can create our own Dockerfile
,
service description (.yaml
) configuration, and container images manually if we wanted to.
There is no need to, though, as Riff 0.0.3 will do all this for us!
riff create --name demofn --input reverse-in \
--protocol pipes --artifact target/demofn-0.0.1.jar \
--handler mcp.DemoFunction
This command creates a function. You specify the input and output topics, a fully
qualified classname and method, and the artifact (the .jar
) that contains the class.
Your topics are logical names for pipes that conduct requests and replies between
functions.
The following command will locate your riff-http-gateway’s IP and port,
then create an HTTP request that will be sent to the `reverse-in
topic.
We tell Riff to wait for a response payload from the input topic with
the --reply
parameter.
riff
CLI.riff publish --input reverse-in --data GNIRPS --reply
Riff will publish 'GNIRPS' data to the 'reverse-in' topic. By
specifying --reply
in the riff command, we are requesting that riff
await for retured data to print to the console.
Riff uses the function-controller to replicate (or scale) your
functions as request rates increase, and decrease over time.
The default behavior implements replication factor equal to the number
of partitions of the input topic. You can edit a topic resource
by using kubectl edit topic MY_TOPIC
command and
change spec.partitions
to your needs. Alternately, the default
behavior can be modified by editing the function’s configuration,
setting spec.maxReplicas
to a desired value.
We’ve only begun to scratch the surface in this look at Project Riff. It would be worth in another installment looking at using some of the other types of functions - `Consumer`s and `Supplier`s - and look at when and how they’re used. In another installment we could look at how functions connect with each other. In another installment, we could look at how function-capable frameworks like Spring Cloud Function work in the Riff environment.