All Projects → redhat-developer-demos → faas-tutorial

redhat-developer-demos / faas-tutorial

Licence: Apache-2.0 License
Java FaaS demos with OpenWhisk and OpenShift

Programming Languages

java
68154 projects - #9 most used programming language
javascript
184084 projects - #8 most used programming language
python
139335 projects - #7 most used programming language

Projects that are alternatives of or similar to faas-tutorial

Openwhisk
Apache OpenWhisk is an open source serverless cloud platform
Stars: ✭ 5,499 (+12688.37%)
Mutual labels:  faas, openwhisk
openwhisk-package-kafka
Apache OpenWhisk package for communicating with Kafka or Message Hub
Stars: ✭ 35 (-18.6%)
Mutual labels:  faas, openwhisk
serverless-company
Serverless. FaaS. Spring Boot. Spring Cloud Function
Stars: ✭ 15 (-65.12%)
Mutual labels:  faas, openwhisk
openwhisk-runtime-java
Apache OpenWhisk Runtime Java supports Apache OpenWhisk functions written in Java and other JVM-hosted languages
Stars: ✭ 43 (+0%)
Mutual labels:  faas, openwhisk
openwhisk-catalog
Curated catalog of Apache OpenWhisk packages to interface with event producers and consumers
Stars: ✭ 30 (-30.23%)
Mutual labels:  faas, openwhisk
openwhisk-runtime-go
Apache OpenWhisk Runtime Go supports Apache OpenWhisk functions written in Go
Stars: ✭ 31 (-27.91%)
Mutual labels:  faas, openwhisk
Openwhisk Deploy Kube
The Apache OpenWhisk Kubernetes Deployment repository supports deploying the Apache OpenWhisk system on Kubernetes and OpenShift clusters.
Stars: ✭ 231 (+437.21%)
Mutual labels:  openshift, faas
openwhisk-runtime-docker
Apache OpenWhisk SDK for building Docker "blackbox" runtimes
Stars: ✭ 23 (-46.51%)
Mutual labels:  faas, openwhisk
openwhisk-runtime-dotnet
Apache OpenWhisk Runtime .Net supports Apache OpenWhisk functions written in .Net languages
Stars: ✭ 23 (-46.51%)
Mutual labels:  faas, openwhisk
fiware-meteoroid
Meteoroid realizes integrating Function as a Service(FaaS) capabilities in FIWARE. It provides a management interface specialized for FaaS and FIWARE.
Stars: ✭ 13 (-69.77%)
Mutual labels:  faas, openwhisk
openwhisk-runtime-python
Apache OpenWhisk Runtime Python supports Apache OpenWhisk functions written in Python
Stars: ✭ 39 (-9.3%)
Mutual labels:  faas, openwhisk
archi cloudnative
Cloud Native Architectural Models using Archi. Contains models for CAAS, Cloud Native Applications, 12/15 Factor Applications with CI/CD/CS, monitoring and log management. Infrastructure components include Red Hat OpenShift, Red Hat Storage, Red Hat Ansible Tower, Red Hat Cloudforms, Red Hat Satellite, Red Hat JBoss Middleware.
Stars: ✭ 55 (+27.91%)
Mutual labels:  openshift
cfimagehost-on-openshift
CF Image Host on Red Hat OpenShift PAAS
Stars: ✭ 13 (-69.77%)
Mutual labels:  openshift
PothosDemos
Pothos demonstration applications
Stars: ✭ 24 (-44.19%)
Mutual labels:  demos
template2helm
Converts an OpenShift template into a Helm chart
Stars: ✭ 28 (-34.88%)
Mutual labels:  openshift
metagraf
metaGraf is a opinionated specification for describing a software component and what its requirements are from the runtime environment. The mg command, turns metaGraf specifications into Kubernetes resources, supporting CI, CD and GitOps software delivery.
Stars: ✭ 15 (-65.12%)
Mutual labels:  openshift
openshift4-vmware-upi
Ansible Playbooks and Documentation to Support the Automated Installation of OpenShift 4 on VMware
Stars: ✭ 45 (+4.65%)
Mutual labels:  openshift
user-guide-faas-serverless
[Cloudframeworks]Function as a Service & Serverless Architectures / [云框架]FaaS & Serverless架构
Stars: ✭ 22 (-48.84%)
Mutual labels:  faas
wpf-demos
This repository contains the samples for Syncfusion WPF UI Controls and File Format libraries and the guide to use them.
Stars: ✭ 128 (+197.67%)
Mutual labels:  demos
ocp-flyway-db-migration
Database Migration Sample with Flyway, Docker and Kubernetes in Openshift Container Platform
Stars: ✭ 17 (-60.47%)
Mutual labels:  openshift

Function as a Service(FaaS) Tutorial

Overview

This tutorial walks you through on how to build a Java functions on a Function as a Service(FaaS) platform Apache OpenWhisk.

Prerequisites

You will need in this tutorial

Tools

Setup minishift

Local development and testing can be done using minishift. Minishift is a tool that helps you run OpenShift locally by running a single-node OpenShift cluster inside a VM. Details on minishift and installation procedures can be found here.

Minishift Profile Setup

#!/bin/bash

# Don't foget to add the location of minishift executable to PATH

minishift profile set faas-tutorial
minishift config set memory 8GB
minishift config set cpus 3
minishift config set image-caching true
minishift addon enable admin-user
minishift addon enable anyuid # # # (1)

minishift start

minishift ssh -- sudo ip link set docker0 promisc on # # # (2)
  1. Some images that are in Apache OpenWhisk Docker hub requires anyuid SCC in OpenShift

  2. This is needed for pods to communicate with each other within the cluster (TODO: need to add more clear details here)

minishift ssh — sudo ip link set docker0 promisc on command needs to be execute each and every time minishift restarted

If your minishift is running OpenShift 3.9.0, you’ll need to fix another bug:

    minishift openshift config set --patch \
        '{"admissionConfig":
            {"pluginConfig":
                {"openshift.io/ImagePolicy":
                    {"configuration":
                        {"apiVersion": "v1",
                         "kind": "ImagePolicyConfig",
                         "resolveImages": "AttemptRewrite"}}}}}'

Setup environment

#!/bin/bash

eval $(minishift oc-env) && eval $(minishift docker-env)
oc login $(minishift ip):8443 -u admin -p admin

Setup OpenWhisk

The project OpenWhisk on OpenShift provides the OpenShift templates required to deploy Apache OpenWhisk.

oc new-project faas # # # (1)
oc project -q # # # (2)
oc process -f https://git.io/openwhisk-template | oc create -f - # # # (3)
oc adm policy add-role-to-user admin developer -n faas # # # (4)
  1. Its always better to group certain class of applications, create a new OpenShift project called faas to deploy all OpenWhisk applications

  2. Make sure we are in right project

  3. Deploy OpenWhisk applications to openwhisk project

  4. (Optional) Add developer user as admin to faas project so as to allow you to login with developer user and access faas project

📎

You need to wait for sometime to have all the required OpenWhisk pods come up and the FaaS is ready for some load. You can watch the status using watch -n 5 'oc logs -f controller-0 -n faas | grep "invoker status changed"'

Verify Deployment

Launch OpenShift console via minishift console. Navigate to the faas project by clicking the name in the upper right corner. A successful deployment will look like:

OpenWhisk Pods
OpenWhisk Pods

Configure WSK CLI

Download OpenWhisk CLI and add it your PATH. Verify your path using the command wsk --help

The OpenWhisk CLI needs to be configured to know where the OpenWhisk is located and the authorization that could be used to invoke wsk commands. Run the following command to have that setup:

#!/bin/bash

AUTH_SECRET=$(oc get secret whisk.auth -o yaml | grep "system:" | awk '{print $2}' | base64 --decode)
wsk property set --auth $AUTH_SECRET --apihost $(oc get route/openwhisk --template="{{.spec.host}}")

Successful setup of WSK CLI will show output like:

WSK CLI

In this case the OpenWhisk API Host is pointing to the local minishift nip.io address

To verify if wsk CLI is configured properly run wsk -i action list. This will list some actions which are installed as part of the OpenWhisk setup. If you see empty result, please see Reinstall default Catalog

💡

The nginx in OpenWhisk deployment uses a self-signed certificate. To avoid certificate errors when using wsk, you need to add wsk -i to each of your wsk commands. For convenience, you can add an alias to your profile with alias wsk='wsk -i $@'.

Setup your Development environment

Clone the complete project from git clone https://github.com/redhat-developer-demos/faas-java-tutorial, we will refer to this location as $PROJECT_HOME through out the document for convenience.

What is an Action ?

Actions are stateless code snippets that run on the OpenWhisk platform. They are analogous to methods in Java idioms. OpenWhisk Actions are thread-safe meaning at a given point of time only one invocation happens.

Fore more details refer the official documentation here.

Your first Action

Let’s quickly create a simple function in JavaScript to see it all working:

mkdir -p getstarted
cd $PROJECT_HOME/getstarted

Create a file called $PROJECT_HOME/getstarted/greeter.js and add the following content to it:

function main() {
    return {payload: 'Welcome to OpenWhisk on OpenShift'};
}

Create an action called greeter:

wsk -i action update greeter greeter.js

Lets invoke the action using command:

wsk -i action invoke greeter --result

The action invoke should respond with the following JSON:

{
    "payload": "Welcome to OpenWhisk on OpenShift"
}

Java Actions

Install Maven Archetype

Maven Archetype can be used to generate the template Java Action project, as of writing this tutorial the archetype is not maven central hence it need to install it locally,

git clone https://github.com/apache/incubator-openwhisk-devtools
cd incubator-openwhisk-devtools/java-action-archetype
mvn -DskipTests clean install
cd $PROJECT_HOME

Your first Java Action

Let’s now create the first Java Action a simple "hello world" kind of function, have it deployed to OpenWhisk and finally invoke to see the result. This section will also details the complete Create-Update-Delete cycle of Java Actions on OpenWhisk.

📎

For easier jar names all the examples will be using maven <finalName>. If you generating new project following the instructions just be sure to update the default <finalName> in pom.xml to ${artifactId} to make the command instructions in subsequent section work without any changes.

Create Java Action

cd $PROJECT_HOME
mvn archetype:generate \
  -DarchetypeGroupId=org.apache.openwhisk.java \
  -DarchetypeArtifactId=java-action-archetype \
  -DarchetypeVersion=1.0-SNAPSHOT \
  -DgroupId=com.example \
  -DartifactId=hello-openwhisk \
  -Dversion=1.0-SNAPSHOT \
  -DinteractiveMode=false

Build

cd hello-openwhisk
mvn clean package

Deploy to OpenWhisk

Create
wsk -i action create hello-openwhisk target/hello-openwhisk.jar --main com.example.FunctionApp

Invoke and Verify the result

Synchronously
wsk -i action invoke hello-openwhisk --result

As all the OpenWhisk actions are asynchronous, we need to add --result to get the result shown on the console.

Successful execution of the command will show the following output:

{"greetings":  "Hello! Welcome to OpenWhisk" }
Asynchronously
wsk -i action invoke hello-openwhisk

A successful action invoke will return an activation id :

Action with Activation ID

We can then use the to activation id check the response using wsk CLI:

wsk -i activation result <activation_id>

e.g.

wsk -i activation result ffb2966350904356b29663509043566e

Successful execution of the command will show the same output like Action Response.

Update

Update the FunctionApp class response with the String:

    response.addProperty("greetings", "Hello! Welcome to OpenWhisk on OpenShift");

Update the FunctionAppTest Test class to match the same String:

    assertEquals("Hello! Welcome to OpenWhisk on OpenShift", greetings);
cd $PROJECT_HOME/hello-openwhisk
mvn clean package
wsk -i action update hello-openwhisk target/hello-openwhisk.jar --main com.example.FunctionApp

Successful update should show a output like:

ow action update result

Repeating the Invocation and Verification steps should result in the updated response ("…​ on OpenShift") like:

{
    "greetings": "Hello! Welcome to OpenWhisk on OpenShift"
}
Delete
wsk -i action delete hello-openwhisk

A successful delete should show output like:

ow action delete result

Web Action

WebActions allow the OpenWhisk action to be invoked via HTTP verbs like GET, POST, PUT etc. The WebActions can be enabled for any Action using the parameter --web=true during the creation of the action using WSK CLI.

cd $PROJECT_HOME
mvn archetype:generate \
  -DarchetypeGroupId=org.apache.openwhisk.java \
  -DarchetypeArtifactId=java-action-archetype \
  -DarchetypeVersion=1.0-SNAPSHOT \
  -DgroupId=com.example \
  -DartifactId=hello-web \
  -Dversion=1.0-SNAPSHOT \
  -DinteractiveMode=false

Update the FunctionApp class response to show application’s arguments:

    response.add("response", args);

Update the FunctionAppTest testFunction method with code:

  @Test
  public void testFunction() {
    JsonObject args = new JsonObject();
    args.addProperty("name", "test");
    JsonObject response = FunctionApp.main(args);
    assertNotNull(response);
    String actual = response.get("response").getAsJsonObject().get("name").getAsString();
    assertEquals("test", actual);
  }

Build

cd hello-web
mvn clean package

Deploy to OpenWhisk

wsk -i action update --web=true hello-web target/hello-web.jar --main com.example.FunctionApp

Invoke and Verify the result

WEB_URL=`wsk -i action get hello-web --url | awk 'FNR==2{print $1".json"}'` # # # (1)
AUTH=`oc get secret whisk.auth -n faas -o yaml | grep "system:" | awk '{print $2}'` # # # (2)
  1. Get the HTTP URL for invoking the action, the returned URL will have .json suffix to allow the WSK to set the right Content-Type and Accept headers

  2. Some resources requires authentication, for those requests its required to add Authorization header with value as $AUTH

curl -k $WEB_URL

You can also access the url via browser using address held in variable $WEB_URL.

📎

The following section shows some example requests and their expected responses

Without any request data

{
  "response": {
    "__ow_method": "get",
    "__ow_headers": {
      "x-forwarded-port": "443",
      "accept": "*/*",
      "forwarded": "for=192.168.64.1;host=openwhisk-faas.192.168.64.67.nip.io;proto=https",
      "user-agent": "curl/7.54.0",
      "x-forwarded-proto": "https",
      "host": "controller.faas.svc.cluster.local:8080",
      "x-forwarded-host": "openwhisk-faas.192.168.64.67.nip.io",
      "x-forwarded-for": "192.168.64.1"
    },
    "__ow_path": ""
  }
}

With any JSON request data

curl -k -X POST -H 'Content-Type: application/json' -d '{"name": "test"}' $WEB_URL.json
{
  "response": {
    "__ow_method": "post",
    "__ow_headers": {
      "x-forwarded-port": "443",
      "accept": "*/*",
      "forwarded": "for=192.168.64.1;host=openwhisk-faas.192.168.64.67.nip.io;proto=https",
      "user-agent": "curl/7.54.0",
      "x-forwarded-proto": "https",
      "host": "controller.faas.svc.cluster.local:8080",
      "content-type": "application/json",
      "x-forwarded-host": "openwhisk-faas.192.168.64.67.nip.io",
      "x-forwarded-for": "192.168.64.1"
    },
    "__ow_path": "",
    "name": "test"
  }
}

With request data and an invalid content type

curl -k -X POST -H 'Content-Type: application/something' -d '{"name": "test"}' $WEB_URL.json

Invoke via curl like above , with request data you will see the response like:

{
  "response": {
    "__ow_method": "post",
    "__ow_headers": {
      "x-forwarded-port": "443",
      "accept": "*/*",
      "forwarded": "for=192.168.64.1;host=openwhisk-faas.192.168.64.67.nip.io;proto=https",
      "user-agent": "curl/7.54.0",
      "x-forwarded-proto": "https",
      "host": "controller.faas.svc.cluster.local:8080",
      "content-type": "application/something",
      "x-forwarded-host": "openwhisk-faas.192.168.64.67.nip.io",
      "x-forwarded-for": "192.168.64.1"
    },
    "__ow_path": "",
    "__ow_body": "eyJuYW1lIjogInRlc3QifQ==" //(1)
  }
}
  1. for unknown content-type the request body will be sent as base64 encoded string

Chaining Actions

Apache OpenWhisk allows chaining of actions which are called in the same sequence as they are defined. We will now create a simple sequence of actions which will split, convert to uppercase, and sort a comma separated string.

All the three projects can be co-located in same directory for clarity and easy building:

cd ..
mkdir -p sequence-demo
cd sequence-demo
wsk -i package create redhat-developers-demo # # (1)
  1. Create a new package to hold our actions, this gives a better clarity on which actions we add to our sequence. For more details refer to the Packages documentation.

Create Split Action

This Action will receive a comma separated string as a parameter and return a array of Strings as a response.

cd $PROJECT_HOME/sequence-demo
mvn archetype:generate \
  -DarchetypeGroupId=org.apache.openwhisk.java \
  -DarchetypeArtifactId=java-action-archetype \
  -DarchetypeVersion=1.0-SNAPSHOT \
  -DgroupId=com.example \
  -DartifactId=splitter \
  -Dversion=1.0-SNAPSHOT \
  -DinteractiveMode=false

Update the FunctionApp class with this code:

  public static JsonObject main(JsonObject args) {
    JsonObject response = new JsonObject();
    String text = null;
    if (args.has("text")) {
      text = args.getAsJsonPrimitive("text").getAsString();
    }
    String[] results = new String[] { text };
    if (text != null && text.indexOf(",") != -1) {
      results = text.split(",");
    }
    JsonArray splitStrings = new JsonArray();
    for (String var : results) {
      splitStrings.add(var);
    }
    response.add("result", splitStrings);
    return response;
}

Update the FunctionAppTest testFunction method with code:

  @Test
  public void testFunction() {
    JsonObject args = new JsonObject();
    args.addProperty("text", "apple,orange,banana");
    JsonObject response = FunctionApp.main(args);
    assertNotNull(response);
    JsonArray results = response.getAsJsonArray("result");
    assertNotNull(results);
    assertEquals(3, results.size());
    List<String> actuals = new ArrayList<>();
    results.forEach(j -> actuals.add(j.getAsString()));
    assertTrue(actuals.contains("apple"));
    assertTrue(actuals.contains("orange"));
    assertTrue(actuals.contains("banana"));
  }
Build Splitter Action
cd splitter
mvn clean package
wsk -i action update redhat-developers-demo/splitter target/splitter.jar --main com.example.FunctionApp

Create Uppercase Action

This Action will take the array of Strings from previous step (Splitter Action) and convert the strings to upper case

cd ..
mvn archetype:generate \
  -DarchetypeGroupId=org.apache.openwhisk.java \
  -DarchetypeArtifactId=java-action-archetype \
  -DarchetypeVersion=1.0-SNAPSHOT \
  -DgroupId=com.example \
  -DartifactId=uppercase \
  -Dversion=1.0-SNAPSHOT \
  -DinteractiveMode=false

Update the FunctionApp class with this code:

  public static JsonObject main(JsonObject args) {
    JsonObject response = new JsonObject();
    JsonArray upperArray = new JsonArray();
    if (args.has("result")) { // // // (1)
      args.getAsJsonArray("result").forEach(e -> upperArray.add(e.getAsString().toUpperCase()));
    }
    response.add("result", upperArray);
    return response;
  }
  1. The function expects the previous action in sequence to send the parameter with JSON attribute called result

Update the FunctionAppTest testFunction method with code:

  @Test
  public void testFunction() {
    JsonObject args = new JsonObject();
    JsonArray splitStrings = new JsonArray();
    splitStrings.add("apple");
    splitStrings.add("orange");
    splitStrings.add("banana");
    args.add("result", splitStrings);
    JsonObject response = FunctionApp.main(args);
    assertNotNull(response);
    JsonArray results = response.getAsJsonArray("result");
    assertNotNull(results);
    assertEquals(3, results.size());
    List<String> actuals = new ArrayList<>();
    results.forEach(j -> actuals.add(j.getAsString()));
    assertTrue(actuals.contains("APPLE"));
    assertTrue(actuals.contains("ORANGE"));
    assertTrue(actuals.contains("BANANA"));
  }
Build Uppercase Action
cd uppercase
mvn clean package
wsk -i action update redhat-developers-demo/uppercase target/uppercase.jar --main com.example.FunctionApp

Create Sort Action

This Action will take the array of Strings from previous step (Upppercase Action) and sort them

cd ..
mvn archetype:generate \
  -DarchetypeGroupId=org.apache.openwhisk.java \
  -DarchetypeArtifactId=java-action-archetype \
  -DarchetypeVersion=1.0-SNAPSHOT \
  -DgroupId=com.example \
  -DartifactId=sorter \
  -Dversion=1.0-SNAPSHOT \
  -DinteractiveMode=false

Update the FunctionApp class with this code:

  public static JsonObject main(JsonObject args) {
    JsonObject response = new JsonObject();
    List<String> upperStrings = new ArrayList<>();
    if (args.has("result")) {
      args.getAsJsonArray("result").forEach(e -> upperStrings.add(e.getAsString()));
    }

    JsonArray sortedArray = new JsonArray();
    upperStrings.stream().sorted(Comparator.naturalOrder()).forEach(s -> sortedArray.add(s));

    response.add("result", sortedArray);
    return response;
  }

Update the FunctionAppTest testFunction method with code:

  @Test
  public void testFunction() {
    JsonObject args = new JsonObject();
    JsonArray splitStrings = new JsonArray();
    splitStrings.add("APPLE");
    splitStrings.add("ORANGE");
    splitStrings.add("BANANA");
    args.add("result", splitStrings);
    JsonObject response = FunctionApp.main(args);
    assertNotNull(response);
    JsonArray results = response.getAsJsonArray("result");
    assertNotNull(results);
    assertEquals(3, results.size());
    List<String> actuals = new ArrayList<>();
    results.forEach(j -> actuals.add(j.getAsString()));
    assertTrue(actuals.get(0).equals("APPLE"));
    assertTrue(actuals.get(1).equals("BANANA"));
    assertTrue(actuals.get(2).equals("ORANGE"));
  }
Build Sorter Action
cd sorter
mvn clean package
wsk -i action update redhat-developers-demo/sorter target/sorter.jar --main com.example.FunctionApp

Create an Action Sequence

Having created all the three actions, lets now create OpenWhisk that calls all three function split,uppercase and sort in sequence.

cd ..
wsk -i action update redhat-developers-demo/splitUpperAndSort --sequence redhat-developers-demo/splitter,redhat-developers-demo/uppercase,redhat-developers-demo/sorter
Invoke and Verify
wsk -i action invoke redhat-developers-demo/splitUpperAndSort --param text "zebra,cat,antelope" --result

The above action invoke should result in response like:

{
    "result": [
        "ANTELOPE",
        "CAT",
        "ZEBRA"
    ]
}

Troubleshooting

Reinstall default Catalog

If you are on a low bandwidth sometimes the default catalog will not be populated, run the following commands to have them installed

#!/bin/bash

oc delete job install-catalog (1)

cat <<EOF | oc apply -f -
apiVersion: batch/v1
kind: Job
metadata:
  name: install-catalog
spec:
  activeDeadlineSeconds: 600
  template:
    metadata:
      name: install-catalog
    spec:
      containers:
      - name: catalog
        image: projectodd/whisk_catalog:openshift-latest
        env:
          - name: "WHISK_CLI_VERSION"
            valueFrom:
              configMapKeyRef:
                name: whisk.config
                key: whisk_cli_version_tag
          - name: "WHISK_AUTH"
            valueFrom:
              secretKeyRef:
                name: whisk.auth
                key: system
          - name: "WHISK_API_HOST_NAME"
            value: "http://controller:8080"
      initContainers:
      - name: wait-for-controller
        image: busybox
        command: ['sh', '-c', 'until wget -T 5 --spider http://controller:8080/ping; do echo waiting for controller; sleep 2; done;']
      restartPolicy: Never
EOF # # (2)
  1. Delete the old job

  2. Run the install-catalog job again

Now when you run wsk -i action list you should see output like:

Install Catalog

Tips and Tricks

💡
  • If you are going to use a lot of wsk then its worth aliasing wsk with alias wsk='wsk -i $@' to avoid SSL errors and skip adding -i for every command.

  • For detailed JSON output form wsk commands prefix -v. This is a great command option for troubleshooting.

  • Its safe to use wsk -i update [resource] when creating OpenWhisk resources like Actions, Packages etc., as this command will act like create for new resources and update for existing resources.

  • wsk -i [resource command ] --summary provides detailed information about a specific resource e.g. wsk -i action get foo --summary

  • wsk -i activation poll, a very useful command when we want to debug some error or see the exception stack traces during a function execution. The simple example of this could be that we star this command on one terminal and fire the action on another to see poll window showing exceptions/errors/stacktraces if any during execution.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].