info@lemonade.be +32 476 522 867 Follow us on LinkedIn Follow us on Instagram

Function as a Service (FaaS) in action with Fission

Function as a Service (FaaS) is a category of cloud computing services that provides a platform to develop, run and manage application functionalities without the complexity of a typical app (servers, networks, etc.). With this paradigm, developers write modular pieces of code that can be executed in response to certain events (triggers) such as HTTP requests, MQTT messages or Cron events.

FaaS

We have a big variety of platform providers in the public cloud or on-premises. In this post, we will use Fission (https://fission.io/) to install a FaaS platform over a Kubernetes cluster and create our own functions. Fission is an open source FaaS platform, which allows us to work on-premises.

Installing Fission

To install Fission, we need a Kubernetes cluster. For this post we are going to use Minikube. This is a very good alternative to a Kubernetes cluster for dev or demo purposes.

All releases of Fission are available at GitHub (https://github.com/fission/fission/releases/). We are going to install Fission 1.14.1 (latest stable version) in two steps: install Fission to Kubernetes and install Fission CLI tool.

To install Fission to your Kubernetes cluster, use the following instructions. With the last step, you have to use a different version of file ‘fission-all-1.14.1-minikube.yaml’ if you are not using a Minikube cluster.

kubectl create -k "github.com/fission/fission/crds/v1?ref=1.14.1"
export FISSION_NAMESPACE="fission"
kubectl create namespace $FISSION_NAMESPACE
kubectl config set-context --current --namespace=$FISSION_NAMESPACE
kubectl apply -f https://github.com/fission/fission/releases/download/1.14.1/fission-all-1.14.1-minikube.yaml

To install Fission CLI tool, follow these instructions.

curl -Lo fission https://github.com/fission/fission/releases/download/1.14.1/fission-1.14.1-linux-amd64 \
    && chmod +x fission && sudo mv fission /usr/local/bin/

Creating our first NodeJS function

The best way to understand how FaaS works is by an example. The first step to create our first function is to configure the environment. The environment contains just enough software to build and run functions – in our case, NodeJS functions. To do that, we are going to use the Fission CLI tool to create a new environment named ‘nodejs’ using the docker image ‘fission/node-env:latest’. We have all environments at GitHub<https://github.com/fission/environments> and we can create our environments if needed.

fission env create --name nodejs --image fission/node-env:latest

We can list all our environments with the ‘fission env list’ command, and update it with ‘fission env update’ or ‘fission env delete’ if needed. All environment commands are available with the ‘fission env –help’ command.

Now we can code our first function in the file ‘hello.js.

module.exports = async function(context) {
    return {
        status: 200,
        body: "bye, world!\n"
    };
}

We can then install to Fission using the following. If our function uses more than one file we can use ‘–code’ parameter as many times as we need or attach a zip file.

fission function create --name hello --env nodejs --code hello.js

Now we can test and see the function logs. We can consult all function operations such as delete using the command ‘fission function –help’.

fission function test --name hello
fission function log --name hello

Attaching the function to a route is simple, and will be available at
http://${fission_router_service}/hello.

fission route create --function hello --url /hello --name hello

Last step! Update the function with an example to access URL, parameters, headers, etc. We then edit the ‘hello.js’ file with the new version.

var url = require('url');
 
String.prototype.replaceAll = function(search, replacement) {
    var target = this;
    return target.replace(new RegExp(search, 'g'), replacement);
};
 
module.exports = async function(context) {
    var ret="<h1>Test Hello</h1>";
 
    ret+="<h2>Headers</h2>"
    ret+=JSON.stringify( context.request.headers, null, 4 ).replaceAll('\n','<br/>').replaceAll(' ','&nbsp;');
 
    ret+="<h2>URL "+context.request.headers["x-fission-full-url"]+"</h2>"
    var urlParts = url.parse(context.request.headers["x-fission-full-url"], true);
    ret+=JSON.stringify( urlParts, null, 4 ).replaceAll('\n','<br/>').replaceAll(' ','&nbsp;');
 
    ret+="<h2>Body</h2>"
    ret+=JSON.stringify( context.request.body, null, 4 ).replaceAll('\n','<br/>').replaceAll(' ','&nbsp;');
 
    return {
        status: 200,
        headers: {
            'x-my-header': 'my header'
            },
        body: ret
    };
}

And execute.

fission function update --name hello --code hello.js

Creating a Java function

Java is our main programming language here at Lemonade and a good example of functions that require compilation. Now we will repeat the flow to create our new function in Java. We create the environment using the following command.

fission environment create --name java --image fission/jvm-env --version 2 --keeparchive --builder fission/jvm-builder

The main difference here is that we are going to use a ‘builder’ docker image to compile our function. The next step is creating the following two files. Remember to create the correct folder structure.

pom.xml (with the maven dependencies):

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.lemonade.techfridays.faas</groupId>
	<artifactId>hello-world</artifactId>
	<version>1.0-SNAPSHOT</version>
	<packaging>jar</packaging>
 
	<name>hello-world</name>
	<url>http://maven.apache.org</url>
 
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>
 
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
			<version>2.0.1.RELEASE</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>io.fission</groupId>
			<artifactId>fission-java-core</artifactId>
			<version>0.0.2-SNAPSHOT</version>
			<scope>provided</scope>
		</dependency>
	</dependencies>
 
	<build>
		<plugins>
			<plugin>
				<artifactId>maven-assembly-plugin</artifactId>
				<configuration>
					<descriptorRefs>
						<descriptorRef>jar-with-dependencies</descriptorRef>
					</descriptorRefs>
				</configuration>
				<executions>
					<execution>
						<id>make-assembly</id> <!-- this is used for inheritance merges -->
						<phase>package</phase> <!-- bind to the packaging phase -->
						<goals>
							<goal>single</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
			<plugin>
            			<groupId>org.apache.maven.plugins</groupId>
            			<artifactId>maven-surefire-plugin</artifactId>
            			<version>2.22.1</version>
            			<configuration>
                			<useSystemClassLoader>false</useSystemClassLoader>
            			</configuration>
        		</plugin>
		</plugins>
	</build>
 
	<!-- Adding Sonatype repository to pull snapshots -->
	<repositories>
		<repository>
			<id>fission-java-core</id>
			<name>fission-java-core-snapshot</name>
			<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
		</repository>
	</repositories>
 
</project>

src/main/java/com/lemonade/techfridays/faas/Hello.java:

package com.lemonade.techfridays.faas;
 
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
 
import io.fission.Function;
import io.fission.Context;
 
public class Hello implements Function {
 
  @Override
  public ResponseEntity<?> call(RequestEntity req, Context context) {
    String ret="<h1>Test Hello</h1>";
 
    return ResponseEntity.ok( ret );
  }
 
}

As we are using different files and a folder structure, we have to create a zip file ‘zip -r java-src-pkg.zip *’ and create a package. This step is just for functions that require compilation.

fission package create --sourcearchive java-src-pkg.zip --env java --name hello-pkg

We have to wait until the package compilation indicates ‘success’. We can see this by showing the package info or at the package lists

fission package info --name hello-pkg<br/>fission package list

Once our package has been compiled successfully, we can create our function.

fission fn create --name hello-fn --pkg hello-pkg --env java --entrypoint<br/>com.lemonade.techfridays.faas.Hello

And finally we can create our trigger. Obviously, we can test and log as we did with our NodeJS function.

fission route create --name hello-rt --function hello-fn --url /hellofn

Deploying your function using YAML declarative specifications

If required for your project, you may deploy your application using declarative specifications. Basically, the process is as follows.

Step one: We execute ‘fission spec init’ to initialize our specifications folder.

Step two: Creating our environments, functions, routes, etc. with the CLI tool and adding the parameter ‘–spec’. This parameter creates the YAML files inside the folder ‘spec’ instead of executing the order.

Step three: Once we have all YAML files configured we can deploy them using the command ‘fission spec apply –wait’.

Albert Coronado

Written by Albert Coronado
Architecture Office at Lemonade

Maybe you are also interested in...

 
Software Development Case Studies

Docker - A quick guide through docker’s amazing world for devs

Software Development Case Studies

ICS2: What is it and what does it do?

Software Development Case Studies

Real Time Business Statistics with Kafka, KSQL and Elasticsearch

 

Your passionate advanced software development team