Skip to content

Latest commit

 

History

History

1

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 

Creating a simple Serverless Application

Estimated time: 15 minutes

References:

In the following lab, we will create a simple Fibonacci Serverless Function Using Java Streams and Fn SDK.

A tiling with squares whose side lengths are successive Fibonacci numbers: 1, 1, 2, 3, 5, 8, 13 and 21.

Start Fn Server

From a terminal window Check the fn version

$ fn version
Client version is latest version: 0.6.13
Server version:  0.3.749

First let's start the Fn local Server in DEBUG mode. By default, the server starts on port 8080

$ fn start --log-level DEBUG
2022/02/14 22:22:15 ¡¡¡ 'fn start' should NOT be used for PRODUCTION !!! see https://github.com/fnproject/fn-helm/
time="2022-02-14T21:22:17Z" level=info msg="Setting log level to" fields.level=DEBUG
time="2022-02-14T21:22:17Z" level=info msg="Registering data store provider 'sql'"
time="2022-02-14T21:22:17Z" level=debug msg="creating new datastore" db=sqlite3
time="2022-02-14T21:22:17Z" level=debug msg="mysql does not support sqlite3"
time="2022-02-14T21:22:17Z" level=debug msg="postgres does not support sqlite3"
time="2022-02-14T21:22:17Z" level=debug msg="mysql does not support sqlite3"
time="2022-02-14T21:22:17Z" level=debug msg="postgres does not support sqlite3"
time="2022-02-14T21:22:17Z" level=info msg="Connecting to DB" url="sqlite3:///app/data/fn.db"
time="2022-02-14T21:22:17Z" level=info msg="datastore dialed" datastore=sqlite3 max_idle_connections=256 url="sqlite3:///app/data/fn.db"
time="2022-02-14T21:22:17Z" level=debug msg="mysql does not support sqlite3"
time="2022-02-14T21:22:17Z" level=debug msg="postgres does not support sqlite3"
time="2022-02-14T21:22:17Z" level=info msg="agent starting cfg={MinDockerVersion:17.10.0-ce ContainerLabelTag: DockerNetworks: DockerLoadFile: DisableUnprivilegedContainers:false FreezeIdle:50ms HotPoll:200ms HotLauncherTimeout:1h0m0s HotPullTimeout:10m0s HotStartTimeout:5s DetachedHeadRoom:6m0s MaxResponseSize:0 MaxHdrResponseSize:0 MaxLogSize:1048576 MaxTotalCPU:0 MaxTotalMemory:0 MaxFsSize:0 MaxPIDs:50 MaxOpenFiles:0xc4201cbd18 MaxLockedMemory:0xc4201cbd30 MaxPendingSignals:0xc4201cbd38 MaxMessageQueue:0xc4201cbd40 PreForkPoolSize:0 PreForkImage:busybox PreForkCmd:tail -f /dev/null PreForkUseOnce:0 PreForkNetworks: EnableNBResourceTracker:false MaxTmpFsInodes:0 DisableReadOnlyRootFs:false DisableDebugUserLogs:false IOFSEnableTmpfs:false EnableFDKDebugInfo:false IOFSAgentPath:/iofs IOFSMountRoot:/Users/nono/.fn/iofs IOFSOpts: ImageCleanMaxSize:0 ImageCleanExemptTags: ImageEnableVolume:false}"
time="2022-02-14T21:22:17Z" level=info msg="no docker auths from config files found (this is fine)" error="open /root/.dockercfg: no such file or directory"
time="2022-02-14T21:22:17Z" level=info msg="available memory" cgroup_limit=9223372036854771712 head_room=673241497 total_memory=6732414976
time="2022-02-14T21:22:17Z" level=info msg="ram reservations" avail_memory=6059173479
time="2022-02-14T21:22:17Z" level=info msg="available cpu" avail_cpu=6000 total_cpu=6000
time="2022-02-14T21:22:17Z" level=info msg="cpu reservations" cpu=6000
time="2022-02-14T21:22:17Z" level=info msg="\n        ______\n       / ____/___\n      / /_  / __ \\\n     / __/ / / / /\n    /_/   /_/ /_/\n"
time="2022-02-14T21:22:17Z" level=info msg="Fn serving on `:8080`" type=full version=0.3.749

Create Fn Application

In another terminal window, Create your first fn Application

$  mkdir graal-fn-demo
$ cd graal-fn-demo/

Now initialize your first Fn application

$ $ fn create app graal-fn-demo
Successfully created app:  graal-fn-demo

Check the created application

$ fn list app
NAME  ID
graal-fn-demo 01FVX21T29NG8G00GZJ0000002

applications act as a container for Fn functions, In the graal-fn-demo app, let's create the Fibonacci Function

Create a subdirectory graal-fn-demo/fibonacci to encapsulate the function objects.

$ mkdir fibonacci
$ cd fibonacci 

Now, Initialize the fibonaci java function with version 1.0.0

$ fn init --runtime java11 --trigger http --name fibonacci
Function boilerplate generated.
func.yaml created.

The function is created with a couple of files and directories

nono-mac:fibonacci nono$ tree
.
├── func.yaml
├── pom.xml
└── src
    ├── main
    │   └── java
    │       └── com
    │           └── example
    │               └── fn
    │                   └── HelloFunction.java
    └── test
        └── java
            └── com
                └── example
                    └── fn
                        └── HelloFunctionTest.java

11 directories, 4 files

Edit the yaml file to match the following constraints

1️⃣ Rename package from com.example.fn to com.oracle.graalvm.fn
2️⃣ Rename the HelloFunction Class to FibonacciFunction
3️⃣ In the pom.xml file change the application groupId to <groupId>com.oracle.graalvm.fn</groupId>
4️⃣ In the pom.xml file rename the artifactId to <artifactId>fibonacci</artifactId>
5️⃣ Edit the cmd starting command in the func.yaml file accordingly cmd: com.oracle.graalvm.fn.FibonacciFunction::handleRequest
6️⃣ Update the com.oracle.graalvm.fn.FibonacciFunction::handleRequest
with the following code snipet

package com.oracle.graalvm.fn;
import java.util.Comparator;
import java.util.stream.Stream;

    public Long handleRequest(String input) {
        try {
            // Convert the received string input to Integer Type
            Integer inputInt = Integer.valueOf(input);
            long result = 0;
            //Compute the associated Fibonacci Number
            result = Stream.iterate( new int[]{0,1}, fib-> new int[]{fib[1], fib[0]+fib[1]} )
                    .limit(inputInt)
                    .map(x->x[1])
                    .max(Comparator.naturalOrder())
                    .get()
                    .longValue();
            return result;
        }catch ( NumberFormatException nfe ){
            System.out.println("Invalid parameter received " + input);
            nfe.printStackTrace();
        }
        // In case of error return -1l
        return -1l;
    }
}

7️⃣ Update the test class com.oracle.graalvm.fn.FibonacciFunctionTestwith

package com.oracle.graalvm.fn;
import com.fnproject.fn.testing.*;
import org.junit.*;

import static org.junit.Assert.*;

public class FibonacciFunctionTest {
    @Rule
    public final FnTestingRule testing = FnTestingRule.createDefault();
    @Test
    public void shouldReturnFibonaciNumber() {
        testing.givenEvent()
        //Compute Fib(8) - The most important portion of the yello square
                .withBody("8")
                .enqueue();
        testing.thenRun(FibonacciFunction.class, "handleRequest");
        FnResult result = testing.getOnlyResult();
        // should be 21
        assertEquals("21", result.getBodyAsString());
    }
}

8️⃣ Use GraalVM SDK as your IDE Java and Test your function using Maven

$ sdk use java 22.0.0-ee11
Using java version 22.0.0-ee11 in this shell.

$ java -version
java version "11.0.14" 2022-01-18 LTS
Java(TM) SE Runtime Environment GraalVM EE 22.0.0 (build 11.0.14+8-LTS-jvmci-22.0-b03)
Java HotSpot(TM) 64-Bit Server VM GraalVM EE 22.0.0 (build 11.0.14+8-LTS-jvmci-22.0-b03, mixed mode, sharing)

Build and deploy the Serverless Application Locaaly

Run the unit test with Maven

$ mvn test
[INFO] Scanning for projects...
[INFO] 
[INFO] ------------------< com.oracle.graalvm.fn:fibonacci >-------------------
[INFO] Building fibonacci 1.0.0
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ fibonacci ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /Users/nono/Projects/Workshops/EMEA-HOL-GraalVMServerless/graalvm-serverless/1/graal-fn-demo/fibonacci/src/main/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.3:compile (default-compile) @ fibonacci ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ fibonacci ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /Users/nono/Projects/Workshops/EMEA-HOL-GraalVMServerless/graalvm-serverless/1/graal-fn-demo/fibonacci/src/test/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.3:testCompile (default-testCompile) @ fibonacci ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /Users/nono/Projects/Workshops/EMEA-HOL-GraalVMServerless/graalvm-serverless/1/graal-fn-demo/fibonacci/target/test-classes
[INFO] 
[INFO] --- maven-surefire-plugin:2.22.1:test (default-test) @ fibonacci ---
[INFO] 
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.oracle.graalvm.fn.FibonacciFunctionTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.46 s - in com.oracle.graalvm.fn.FibonacciFunctionTest
[INFO] 
[INFO] Results:
[INFO] 
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  2.504 s
[INFO] Finished at: 2022-02-14T23:31:05+01:00
[INFO] ------------------------------------------------------------------------

Before building our function, let us explore and customize the local context

Contexts Management

$ fn list contexts
CURRENT NAME        PROVIDER API URL       REGISTRY
default default  http://localhost:8080     

Add your docker-id as registry in the default context

fn update context registry <your-docker-id>

For me

fn update context registry nelvadas

The context is updated accordingly

$ fn list contexts
CURRENT NAME        PROVIDER API URL       REGISTRY
default default  http://localhost:8080     nelvadas

Build

Now let's package the fibonacci function

$$ fn build -v
Using Container engine docker
Building image nelvadas/fibonacci:0.0.1
FN_REGISTRY:  nelvadas
Current Context:  default
[+] Building 1.3s (15/15) FINISHED
 => [internal] load build definition from Dockerfile214563689                                                                                                                  0.0s
 => => transferring dockerfile: 729B                                                                                                                                           0.0s
 => [internal] load .dockerignore                                                                                                                                              0.0s
 => => transferring context: 2B                                                                                                                                                0.0s
 => [internal] load metadata for docker.io/fnproject/fn-java-fdk:jre11-1.0.145                                                                                                 1.2s
 => [internal] load metadata for docker.io/fnproject/fn-java-fdk-build:jdk11-1.0.145                                                                                           1.2s
 => [build-stage 1/6] FROM docker.io/fnproject/fn-java-fdk-build:jdk11-1.0.145@sha256:d1a3e06e1f3fb4c3aa4bbe776b90cb074f9e605eedac9ff1db70060873025165                         0.0s
 => [stage-1 1/3] FROM docker.io/fnproject/fn-java-fdk:jre11-1.0.145@sha256:44e31570ba93566ed6862b6ebb036c9d622af68a5d7c9da238bb9e2f4bcc0fde                                   0.0s
 => [internal] load build context                                                                                                                                              0.0s
 => => transferring context: 710B                                                                                                                                              0.0s
 => CACHED [stage-1 2/3] WORKDIR /function                                                                                                                                     0.0s
 => CACHED [build-stage 2/6] WORKDIR /function                                                                                                                                 0.0s
 => CACHED [build-stage 3/6] ADD pom.xml /function/pom.xml                                                                                                                     0.0s
 => CACHED [build-stage 4/6] RUN ["mvn", "package", "dependency:copy-dependencies", "-DincludeScope=runtime", "-DskipTests=true", "-Dmdep.prependGroupId=true", "-DoutputDire  0.0s
 => CACHED [build-stage 5/6] ADD src /function/src                                                                                                                             0.0s
 => CACHED [build-stage 6/6] RUN ["mvn", "package"]                                                                                                                            0.0s
 => CACHED [stage-1 3/3] COPY --from=build-stage /function/target/*.jar /function/app/                                                                                         0.0s
 => exporting to image                                                                                                                                                         0.0s
 => => exporting layers                                                                                                                                                        0.0s
 => => writing image sha256:183aaaefb8229f64c8c56862beb33b693bf35306852b481a4583e18c87222211                                                                                   0.0s
 => => naming to docker.io/nelvadas/fibonacci:0.0.1                                                                                                                            0.0s

Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them

Function nelvadas/fibonacci:0.0.1 built successfully

The function is build and a new docker image is produced. In this Docker image, the application Jar file is packaged as an additonnal layer of an image containing a Java Runtime Environment fnproject/fn-java-fdk:jre11-1.0.145

Navigate through the generated image using the divetool

dive nelvadas/fibonacci:0.0.1

Local Deployment

Deploy the function on the local running server using fn deploy

$ fn deploy --app graal-fn-demo --local --no-bump
Using Container engine docker
Building image nelvadas/fibonacci:0.0.1 ..
Updating function fibonacci using image nelvadas/fibonacci:0.0.1...
Successfully created function: fibonacci with nelvadas/fibonacci:0.0.1
Successfully created trigger: fibonacci
Trigger Endpoint: http://localhost:8080/t/graal-fn-demo/fibonacci

The no-bump argument assumes external version management .

The functions list of the graal-fn-demo applications is updated with the new function

$ fn list functions graal-fn-demo
NAME  IMAGE    ID
fibonacci nelvadas/fibonacci:0.0.1 01FVX74J8XNG8G00GZJ0000003

Invoke the function

  • Inspect
$ fn inspect function graal-fn-demo fibonacci
{
 "annotations": {
  "fnproject.io/fn/invokeEndpoint": "http://localhost:8080/invoke/01FVX74J8XNG8G00GZJ0000003"
 },
 "app_id": "01FVX21T29NG8G00GZJ0000002",
 "created_at": "2022-02-14T22:55:00.893Z",
 "id": "01FVX74J8XNG8G00GZJ0000003",
 "idle_timeout": 30,
 "image": "nelvadas/fibonacci:0.0.1",
 "memory": 128,
 "name": "fibonacci",
 "timeout": 30,
 "updated_at": "2022-02-14T22:55:00.893Z"
}

Invoke the function

  • with fn invoke
$ echo -n '10' | fn invoke graal-fn-demo  fibonacci
55
  • With curl: Hit the function's http trigger
$ curl  -X POST --data 8  http://localhost:8080/t/graal-fn-demo/fibonacci
21

Fn Performance

Let's run a benchmark on the existing serverless function with hey() For each function invocation, a docker container is created to serve the request.

Create an Body file for the benchmark; for this we want to get Fibonaci(100)

$ echo 100 > data.txt
$ cat data.txt
100

Now run 1000 requests with 100 cucurrent calls.

$ hey -n 1000 -c 100  -m POST -D data.txt  http://localhost:8080/t/graal-fn-demo/fibonacci

Summary:
  Total: 32.8704 secs
  Slowest: 19.9812 secs
  Fastest: 0.0212 secs
  Average: 2.1615 secs
  Requests/sec: 30.4226

  Total data: 1980 bytes
  Size/request: 2 bytes

Response time histogram:
  0.021 [1] |
  2.017 [734] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
  4.013 [68] |■■■■
  6.009 [47] |■■■
  8.005 [35] |■■
  10.001 [24] |■
  11.997 [18] |■
  13.993 [9] |
  15.989 [14] |■
  17.985 [11] |■
  19.981 [6] |


Latency distribution:
  10% in 0.1277 secs
  25% in 0.2228 secs
  50% in 0.4512 secs
  75% in 1.7373 secs
  90% in 7.1375 secs
  95% in 11.4712 secs
  99% in 17.8666 secs

Details (average, fastest, slowest):
  DNS+dialup: 0.0009 secs, 0.0212 secs, 19.9812 secs
  DNS-lookup: 0.0004 secs, 0.0000 secs, 0.0056 secs
  req write: 0.0001 secs, 0.0000 secs, 0.0034 secs
  resp wait: 2.1604 secs, 0.0211 secs, 19.9667 secs
  resp read: 0.0001 secs, 0.0000 secs, 0.0007 secs

Status code distribution:
  [200] 966 responses
  [502] 1 responses

Error distribution:
  [33] Post "http://localhost:8080/t/graal-fn-demo/fibonacci": context deadline exceeded (Client.Timeout exceeded while awaiting headers)

Watch the running and exited containers.

$ $ docker ps -a
CONTAINER ID   IMAGE                                  COMMAND                  CREATED          STATUS                      PORTS                                                 NAMES
1ff76ca5df5c   nelvadas/fibonacci:0.0.1               "/usr/java/openjdk-1…"   18 seconds ago   Up 8 seconds (Paused)                                                             01FVY7SA1VNG8G00GZJ00000CY
a43a02018f83   nelvadas/fibonacci:0.0.1               "/usr/java/openjdk-1…"   18 seconds ago   Up 8 seconds (Paused)                                                             01FVY7SA1VNG8G00GZJ00000CX
f8fbc1cab450   nelvadas/fibonacci:0.0.1               "/usr/java/openjdk-1…"   18 seconds ago   Up 10 seconds                                                                     01FVY7S9VVNG8G00GZJ00000CN
e3e5a8e25de9   nelvadas/fibonacci:0.0.1               "/usr/java/openjdk-1…"   18 seconds ago   Up 10 seconds                                                                     01FVY7SA1TNG8G00GZJ00000CW
ce6e99e640a8   nelvadas/fibonacci:0.0.1               "/usr/java/openjdk-1…"   18 seconds ago   Up 10 seconds                                                                     01FVY7SA1XNG8G00GZJ00000CZ
47a06f25000e   nelvadas/fibonacci:0.0.1               "/usr/java/openjdk-1…"   18 seconds ago   Up 10 seconds                                                                     01FVY7SA1TNG8G00GZJ00000CV
d32c6ae9737e   nelvadas/fibonacci:0.0.1               "/usr/java/openjdk-1…"   18 seconds ago   Up 10 seconds                                                                     01FVY7S9VVNG8G00GZJ00000CQ
117536ae72c8   nelvadas/fibonacci:0.0.1               "/usr/java/openjdk-1…"   18 seconds ago   Up 10 seconds (Paused)                                                            01FVY7S9VWNG8G00GZJ00000CR
0bd183ee354a   nelvadas/fibonacci:0.0.1               "/usr/java/openjdk-1…"   18 seconds ago   Up 9 seconds                                                                      01FVY7S9VJNG8G00GZJ00000CA
b3244633d25b   nelvadas/fibonacci:0.0.1               "/usr/java/openjdk-1…"   18 seconds ago   Up 8 seconds (Paused)                                                             01FVY7S9VVNG8G00GZJ00000CP
f0af23abf681   nelvadas/fibonacci:0.0.1               "/usr/java/openjdk-1…"   18 seconds ago   Up 9 seconds                                                                      01FVY7S9VQNG8G00GZJ00000CH
                                                          
...

In average we can comple 30.42 req/s with a latency ~17.87 for 99% of the requests.

Wrap Up

Congratulations! you just built a Fibonacci serverless function with Fn SDK Build the function with Docker and package it in a container along with a Java Virtual Machine .

Next, we'll try to create a Optimize the function with GraalVM Enterprise features..


References

https://docs.oracle.com/en-us/iaas/Content/Functions/Tasks/functionsusingwithfncli.htm