Creating Your Own Serverless Cloud with Fn Project

In this Fn Project tutorial, you will learn the basic features of Fn Project by creating a serverless cloud and installing it on your own infrastructure. Then, you will create a couple of functions in different programming languages. This will illustrate some of the most useful concepts of Fn Project and help you get familiarized with this lightweight and simple serverless platform. 

What is Serverless? 

Serverless computing, or more simply “Serverless,” is not easy to define, but for the sake of this article, we can loosely define it as a software architecture trend that reduces the notion of infrastructure. While this trend still requires servers, developers don’t need to worry about load balancing, multithreading, or any other infrastructure subject. The chosen platform manages the resources, allowing developers to just focus on their code.

Currently, there are many frameworks and platforms for serverless solutions, such as AWS Lambda (Amazon), Azure Functions (Microsoft), Cloud Functions (Google), Cloudflare Workers (Cloudflare), and OpenWhisk (IBM). When using these frameworks, you are usually charged for the computation of your code and the number of times it gets executed. But what about a solution or platform where you are not charged for these services?

Fn Project 

The official Fn Project website defines Fn Project as “an open-source container-native serverless platform that you can run anywhere—any cloud or on-premise.” It’s important to mention that, even though it’s open-source, it has the support of Oracle.

Fn Project is different from most other solutions in that it is not tied to any specific cloud vendor; in other words, it’s cloud-agnostic. The solution can be hosted in any environment that supports Docker, like AWS, Microsoft Azure, Google Cloud Platform, and even your own server. 

Let’s imagine you have a server that is underused. By taking 15 minutes to install and set up Fn Project on that server, you can create your own FaaS platform for your developers without any computing or execution costs in the future. They can start coding and deploying their functions written in several programming languages (Go, Java, Node.js, Python, Ruby, C#, Kotlin, PHP, Rust, etc.) without needing to worry about infrastructure. 

Getting Started with Fn Project 

Installing Fn Project

The only prerequisite to install Fn Project is to have Docker v.17.0.

You can download and install Fn Project with the following command:

$ curl -LSs https://raw.githubusercontent.com/fnproject/cli/master/install | sh

Then, it’s time to start the Fn Server.

$ fn start

There you go! You can check if Fn Project is running by accessing http://localhost:8080.

Fn Project

And that’s all you need to do! You now have Fn Project installed, and you are ready to start coding and deploying your functions on your own serverless platform. 

Of course, there are some more advanced configurations like changing the port or customizing the registry and context, but that’s another story for another blog post. If you want to dive in deeper, you can check out this article.

Your First App and Functions

As I mentioned before, Fn Project allows you to create functions in different programming languages. On this occasion, we will create a basic “Hello World” function in Node.js and then a less basic “Math Calculations” function in Java.  

Let’s start by creating a directory that will contain the code of our functions.

$ mkdir myFnApp

Then, we can create our very first function in Fn Project.

$ mkdir myfirstfn
$ cd myfirstfn
$ fn init --runtime node

This will create the structure of the Node.js project in the current folder. These are the automatically generated boilerplate files:

$ find .
./func.yaml
./package.json
./func.js

Now for the “Hello World”  function:

const fdk=require('@fnproject/fdk');

fdk.handle(function(input){
  let name = 'World';
  if (input.name) {
    name = input.name;
  }
  return {'message': 'Hello ' + name}
})

There you go! We have our first function in Node.js with just a couple lines of code. Now, I would like to provide more details regarding the initialization of the project. Let’s use our second function as an example of that.

$ cd ..
$ fn init --runtime java --trigger http --name math-calculations mathcalculation

The previous command created a new directory, “mathcalculation”, inside of which is a Maven project with a func.yaml file in the root directory. The function is named “math-calculations”. Let’s see what was created.

$ cd mathcalculations
$ find .
./func.yaml
./pom.xml
./src/test/java/com/example/fn/HelloFunctionTest.java
./src/main/java/com/example/fn/HelloFunction.java

If we open the HelloFunction.java file, this is its default content.

package com.example.fn;
public class HelloFunction {
    public String handleRequest(String input) {
        String name = (input == null || input.isEmpty()) ? "world"  : input;
        return "Hello, " + name + "!";
    }
}

Let’s start to code our function! Since a normal “Hello World” example is very boring, we are going to replace the HelloFunction with something more interesting. In the real world, serverless functions are usually used for heavier computational procedures, so we can try to simulate that by creating a function that receives an input number and performs a set of mathematical operations on that number. The operations in this example will be called squareRoot, squarePow, fibonacci, summation, and factorial

MathCalculationFunction.java

package com.example.fn;

import com.example.fn.model.NumberRequest;
import com.example.fn.model.NumberResponse;

public class MathCalculationFunction {

    public CalculationResponse handleRequest(CalculationRequest input) {
        Integer theNumber = input.getNumber();
        if(theNumber != null){           CalculationResponse response = new CalculationResponse();
            response.setNumber(theNumber);
            response.setSquarePow(MathCalculationUtil.squarePow(theNumber));
            response.setSquareRoot(MathCalculationUtil.squareRoot(theNumber));
            response.setFibonacci(MathCalculationUtil.fibonacci(theNumber));
            response.setSummation(MathCalculationUtil.summation(theNumber));
            response.setFactorial(MathCalculationUtil.factorial(theNumber));
            return response;
        }
        
        CalculationResponse response = new CalculationResponse();
        response.setNumber(0);
        return response;
    }
}

MathCalculationUtil.java

package com.example.fn;

public class MathCalculationUtil {
    
    public static long squarePow(int number){
        return (long) Math.pow(number, 2);
    }
    
    public static double squareRoot(int number){
        return Math.sqrt(number);
    }
    
    public static long summation(int number){
        long result = 0;
        for(int i=1; i <= number; i++){
            result+=i;
        }
        return result;
    }
    
    public static long factorial(int number){
        long result = 0;
        for(int i=1; i <= number; i++){
            result*=i;
        }
        return result;
    }
    
    public static long fibonacci(int n) {
        if (n <= 1) return n;
        else return fibonacci(n - 1) + fibonacci(n - 2);
    }
}

CalculationRequest

package com.example.fn.model;

public class CalculationRequest {
    private Integer number;
    //Getters and Setters 
}

CalculationResponse

package com.example.fn.model;

public class CalculationResponse {
    private int number;
    private long squarePow;
    private double squareRoot;
    private long fibonacci;
    private long summation;
    private long factorial;
    //Getters and Setters
}

After adding the above Java classes to the project, the project structure should now look like this:

$ find .
./func.yaml
./pom.xml
./src/test/java/com/example/fn/HelloFunctionTest.java
./src/main/java/com/example/fn/HelloFunction.java
./src/main/java/com/example/fn/MathCalculationUtil.java
./src/main/java/com/example/fn/MathCalculationFunction.java
./src/main/java/com/example/fn/model/CalculationRequest.java
./src/main/java/com/example/fn/model/CalculationResponse.java

Finally, it’s necessary to set the new MathCalculationFunction class as the entry point to our function. The file func.yaml is in charge of our function’s configuration, so we will modify it in order to set the new entry point.

func.yaml

schema_version: 20180708
name: math-calculations
version: 0.0.1
runtime: java
build_image: fnproject/fn-java-fdk-build:jdk11-1.0.95
run_image: fnproject/fn-java-fdk:jre11-1.0.95
cmd: com.example.fn.MathCalculationFunction::handleRequest
triggers:
- name: math-calculations
  type: http
  source: /math-calculations

For a complete list of all the different values that can be set on func.yaml, check out this page.

Deploying a Function

It’s time to publish our functions and make them accessible to other applications or users. The command to do this is: $ fn deploy –app <app name> –local.

Before deploying our functions, we will need to create an application to organize them:

$ cd ..
$ fn create app my-first-app

Now we can deploy our functions!

• The “Hello World” function coded in Node.js.

$ cd myfirstfn
$ fn deploy --app my-first-app --local

• The “Math Calculation” function coded in Java.

$ cd ..
$ cd mathcalculation
$ fn deploy --app my-first-app --local

The parameter “- – local” indicates the function will be deployed to the local Fn server, and it won’t push the Docker image to Docker Hub. 

Invoking our Functions

Finally, we are ready to invoke our functions. There are two ways we can call our “Math Calculations” function: 

1. Executing the function by its function ID: 

We have to find out the function ID, which can be done via the Fn command line interface using the command “fn list functions <my-app>”.

$ fn list functions my-first-app
NAME			IMAGE			ID
math-calculations	math-calculations:0.0.5	01DAW3K3N9NG8G00GZJ0000007
myfirstfn		myfirstfn:0.0.2		01DAW3HEK2NG8G00GZJ0000006

Once we have identified the ID of the function, we can invoke the function by using the URL http://<fn server>:8080/invoke/<function-id>.

curl -X POST -d '{"number":9}' http://localhost:8080/invoke/01DAW3K3N9NG8G00GZJ0000007

2.  Executing the function by the defined trigger route.

Where is the trigger route, you might be wondering? We added it to the func.yaml file a few steps before.

triggers:
- name: math-calculations
  type: http
  source: /math-calculations

Once we have identified the ID of the function, we can invoke the function by using the URL http://<fn server>:8080/t/<my-app>/<trigger route>.

curl -X POST -d '{"number":9}' http://localhost:8080/t/my-first-app/math-calculations

Both invocations will return the same JSON result.

{
  "number": 9,
  "squarePow": 81,
  "squareRoot": 3,
  "fibonacci": 34,
  "summation": 45,
  "factorial": 362880
}

Fn UI and Monitor

Fn Project comes with a basic UI tool that lets you visually run and check the status of the applications and functions you have deployed. In order to install and run this UI tool, just execute the following command:

$ docker run --rm -it --link fnserver:api -p 4000:4000 -e "FN_API_URL=http://api:8080" fnproject/ui

Then, access the UI tool at http://localhost:4000.

Fn Project UI tool

There, we can create, edit, and remove apps. If we open an app, we will be able to see the list of functions for that application. We can use this UI to see statistics about how many times a function has been executed, monitor the current running process, and even invoke a function.

What Else is There?

This article is only the tip of the Fn Project iceberg. There are other topics for further reading that I would also like to mention:.

 Serverless Framework supports creating, running and deploying functions on Fn Project.

• Fn Server provides Prometheus metrics out of the box just by accessing the endpoint http://<fn-server>:<fn-port>/metrics.

• There is official support for Fn Project in Kubernetes, which can be installed using Helm and deployed on a Kubernetes Cluster.

Due to the grand variety of serverless platforms, it’s not possible to determine if one is better than another. All of them have their advantages, and finding the best fit for your solution depends on your specific requirements. That being said, Fn Project is now an option you can keep in mind in the future!

References for Additional Reading

1. https://hackernoon.com/what-the-hell-does-serverless-mean-219a5f6e3c6a

2. https://fnproject.io/tutorials/

3. https://github.com/vladimir-dejanovic/java-in-fn-project

4. https://github.com/fnproject/docs/blob/master/fn/develop/func-file.md

5. https://medium.com/fnproject/announcing-spring-cloud-function-support-for-fn-project-921e54f49d99

 

Subscribe to our Blog

Adam Gamboa
Adam Gamboa
Adam is a Java developer with 8 years of experience oriented towards Java EE, back-end and Rest API development.

Deliver off-the-chart results.

WordPress Video Lightbox Plugin