OpenFaaS Asynchronous Functions and Function Chaining on OpenShift for IBM Power ppc64le
Introduction
In Part 1 and Part 2, we saw the use of synchronous functions to compute Pi and Euler’s number where the caller process was blocked till the function completed. The most common way to trigger an OpenFaaS function is using a HTTP request. OpenFaaS utilizes NATS Streaming in order to invoke functions. A proxy server decouples HTTP messaging between the caller and the function. OpenFaaS produces asynchronous and synchronous versions for every function. Asynchronous function invocations can be used for long running tasks where a response is returned to a callback URL. The initial call to the function does not need to hold on to the connection while waiting for the response. We can also chain functions together either on the client side or call one function from another. In this Part 3, we cover asynchronous function execution and see the use of function chaining with OpenFaaS on RedHat OpenShift for ppc64le.
Asynchronous Function Execution
Synchronous execution works well if your function can complete within a few seconds on every invocation. For example, the following request from the previous Part 2 shows a response within a few milliseconds. A request with 0 or invalid numeric value returns 40 digits accuracy.
time(printf "40\n\n" | curl http://gateway-external-openfaas.apps.test-cluster.priv/function/pi-ppc64le --data-binary @- -H "Content-Type: text/plain")
40 3.141592653589793238462643383279502884197
real 0m0.236s
user 0m0.005s
sys 0m0.007s
However, the following request to compute Pi to 50 digits takes more than a few minutes using half a core (500m) that might be set as the limit for the function pod:
time(printf "5000\n\n" | curl http://gateway-external-openfaas.apps.test-cluster.priv/function/pi-ppc64le --data-binary @- -H "Content-Type: text/plain")
5000 3.141592653… truncated
real 2m56.130s
user 0m0.008s
sys 0m0.017s
When requests take on the order of 10s of seconds or more, you might need a better solution. OpenFaaS enables long-running tasks or function invocations to run in the background. The HTTP request is serialized to NATS Streaming through the gateway. The queue-worker deserializes the HTTP request and uses it to invoke the function directly. The asynchronous workflow can have a longer, separate timeout compared with synchronous timeout on the gateway. Any function can be invoked asynchronously by changing the route on the gateway from /function/<name>
to /async-function/<name>
. A 202 Accepted
message will be issued in response to asynchronous calls. Asynchronous invocations are queued and deferred. For this reason, they are submitted with a HTTP POST
request. Normally, we would like to receive a value from an asynchronous call. This requires passing a HTTP header with the URL to be used for the call-back. In this blog, we will see the working of the callback to public URLs for requestbin.com and hookb.in and later to our own callback handler.
If your infrastructure requires outbound proxy to connect to make external web requests, you will need to update the environment variables HTTP_PROXY, HTTPS_PROXY for the deployment queue-worker with the required proxy for the outbound requests. You will also need to set the NO_PROXY to the addresses that are directly accessible without the proxy. Using CIDR for IP addressing is not supported by NO_PROXY. You must add individual IP addresses for values, such as, the registry. OpenShift does not accept *
as a wildcard attached to a domain suffix. For example, the “.local” is allowed, but “*.local” is not. The only wildcard NO_PROXY accepts is a single * character, which matches all hosts, and effectively disables the proxy. Each name in this list is matched as either a domain which contains the host name as a suffix, or the host name itself. The queue-worker deployment can be edited as follows:
oc edit deployment queue-worker -n openfaas
Add the variable in env:
containers:
- env:
- name: HTTP_PROXY
value: http://10.3.0.3:3128
- name: HTTPS_PROXY
value: http://10.3.0.3:3128
- name: NO_PROXY
value: localhost,127.0.0.1,.test-cluster.priv,10.0.0.0/8,.local,172.0.0.0/8
Go to https://requestbin.com/ and click on “Create a public bin instead.”. On the top right, you will see the endpoint you can use for the callback. For example, https://enskyb3isyhi.x.pipedream.net
Let’s invoke 20 simultaneous requests to compute Pi for accuracy from 1 digit up to 20 digits with a X-Callback-Url retrieved from requestbin.com. We have appended a path to this URL to indicate the accuracy of Pi.
for i in {1..20}; do printf "$i\n\n" | curl http://gateway-external-openfaas.apps.test-cluster.priv/async-function/pi-ppc64le --data-binary @- -H "Content-Type: text/plain" -H "X-Callback-Url: https://enskyb3isyhi.x.pipedream.net/$i"& done
printf "0\n\n" | curl http://gateway-external-openfaas.apps.test-cluster.priv/async-function/pi-ppc64le --data-binary @- -H "Content-Type: text/plain" -H "X-Callback-Url: https://enskyb3isyhi.x.pipedream.net/0
We can also alternatively click on “CREATE NEW ENDPOINT” at https://hookbin.com/ This will show “Your Hookbin Endpoint” on top. For example, https://hookb.in/zrrn2Bkl3RUol3MMlwYk
The hookbin endpoint does not accept a path, so we send a query-string to indicate accuracy of Pi. Let’s invoke simultaneous requests to compute Pi for accuracy from 1 digit up to 20 digits with a X-Callback-Url retrieved from requestbin.com. For each request, you will see the HTTP Headers, Query String and finally the Body containing the value of Pi at the bottom of the request.
for i in {1..20}; do printf "$i\n\n" | curl http://gateway-external-openfaas.apps.test-cluster.priv/async-function/pi-ppc64le --data-binary @- -H "Content-Type: text/plain" -H "X-Callback-Url: https://hookb.in/zrrn2Bkl3RUol3MMlwYk?accuracy=$i"& done
When an initial connection is formed to the gateway, the user's request is serialized to a queue via the queue-worker and NATS. At a later time, the queue-worker then dequeues the request, deserializes it and makes it to the function using a synchronous call. We can look at the queue-worker logs to see that the queue-worker invokes the function. After the function returns, it posts a call back to the X-Callback_Url and shows the status 200.
oc logs deployment/queue-worker -f -n openfaas
[#104] Received on [faas-request]: 'sequence:106 subject:"faas-request" data:"{\"Header\":{\"Accept\":[\"*/*\"],\"Content-Length\":[\"4\"],\"Content-Type\":[\"text/plain\"],\"Forwarded\":[\"for=10.3.158.61;host=gateway-external-openfaas.apps.test-cluster.priv;proto=http\"],\"User-Agent\":[\"curl/7.64.1\"],\"X-Call-Id\":[\"4e500216-7d63-4596-a846-5895e5065637\"],\"X-Callback-Url\":[\"https://hookb.in/kxxQKNWm1qhBjzggjbZq?accuracy=20\"],\"X-Forwarded-For\":[\"10.3.158.61\"],\"X-Forwarded-Host\":[\"gateway-external-openfaas.apps.test-cluster.priv\"],\"X-Forwarded-Port\":[\"80\"],\"X-Forwarded-Proto\":[\"http\"],\"X-Start-Time\":[\"1625942123265788049\"]},\"Host\":\"gateway-external-openfaas.apps.test-cluster.priv\",\"Body\":\"MjAKCg==\",\"Method\":\"POST\",\"Path\":\"\",\"QueryString\":\"\",\"Function\":\"pi-ppc64le\",\"QueueName\":\"\",\"CallbackUrl\":{\"Scheme\":\"https\",\"Opaque\":\"\",\"User\":null,\"Host\":\"hookb.in\",\"Path\":\"/kxxQKNWm1qhBjzggjbZq\",\"RawPath\":\"\",\"ForceQuery\":false,\"RawQuery\":\"accuracy=20\",\"Fragment\":\"\",\"RawFragment\":\"\"}}" timestamp:1625942123265920554 '
[#104] Invoking: pi-ppc64le with 4 bytes, via: http://gateway.openfaas.svc.cluster.local:8080/function/pi-ppc64le/
[#104] pi-ppc64le returned 25 bytes
[#104] Invoked: pi-ppc64le [200] in 0.170517s
[#104] Callback to: https://hookb.in/kxxQKNWm1qhBjzggjbZq?accuracy=20
[#104] Posted result for pi-ppc64le to callback-url: https://hookb.in/kxxQKNWm1qhBjzggjbZq?accuracy=20, status: 200
We can increase the parallelism by scaling up the queue-worker replicas or by setting the queue worker's "max_inflight" option to a value greater than one. Concurrency for certain functions can be restricted by separating the functions into multiple queues. Configuration & Limits, Parallelism, Multiple Queues, Verbosity, Callback request headers and other details for asynchronous execution are available at the OpenFaaS Reference.
Function to manipulate a binary image
In this section we will create functions to manipulate an image: draft, thumbnail, transpose by 180 degrees, black and white. Image manipulation can often be time consuming. We will start by creating a function that takes a color JPEG image and return a draft of the image. We will clone this function to create additional functions. Finally, we will see how to chain the functions asynchronously.
By default, most of the OpenFaaS templates accept a string input, but that can be overridden to a raw binary body. We set the following in the stack.yml file for the function:
environment:
RAW_BODY: True
We use a template with Debian instead of Alpine Linux because Pillow needs to be compiled and requires a build toolchain. It would be cumbersome to add to Alpine Linux. We use the template python3-flask-debian and created the python3-flask-debian-ppc64le with minor changes in the Dockerfile to use the ppc64le base image and copied the of-watchdog we compiled in Part 1 for ppc64le. The req value in the handler will automatically be input as raw binary bytes, rather than as a string in the index.py.
Function Watchdog
The Function Watchdog is an embedded HTTP server that supports concurrent requests and is responsible for handling function timeouts and pod healthchecks in the OpenFaaS environment. Every OpenFaaS function can use a watchdog as an entrypoint which is the init process for the container running the function. Watchdogs come in two flavors: "classic" and the newer "of-watchdog". We had compiled both flavors in Part 1.
The classic watchdog approach forks one process per request. After the process is forked, the watchdog passes a request to the function via stdin and reads the corresponding response via stdout. This approach with a single process forking has multiple advantages such as process isolation, portability and simplicity.
The of-watchdog is suitable in streaming use cases or in resource intensive operations, such as serving data from databases or serving machine learning models. The of-watchdog is based on "http mode" where processes can be reused. It forks your process only once, at start-up, and then communicates with it over HTTP until the function is killed or scaled down. The of-watchdog keeps a function process running between invocations. Any machine learning models or datasets that you need to fetch can be performed once, at start-up, instead of on every invocation. This reduces latency and bandwidth costs. The of-watchdog templates often reference an HTTP server framework such as Flask (Python) or Express (Node.js).
Testing the ofwatchdog-ppc64le
Check that the watchdog binary compiled for the ppc64le works outside of the container.
fprocess=ls port=8083 template/python3-flask-debian-ppc64le/ofwatchdog-ppc64le&
curl localhost:8083
# This will show the output of current directory
Function to return a draft of the image
We create a new function pil-draft-ppc64le from the python3-flask-debian-ppc64le template
faas-cli new --lang python3-flask-debian-ppc64le pil-draft-ppc64le
We update the handler.py using the following pil-draft function handler that shows how to manipulate an image using the Python Imaging Library (PIL) to convert it to draft mode. It also shows how to enable debug logs.
import io
import sys
import os
import logging
import time
from PIL import Image
logging.basicConfig(level=logging.DEBUG)
def handle(req):
#return b"Hello", 200, {"Content-type": "application/octet-stream"}
logging.debug("> handle")
buf = io.BytesIO()
with Image.open(io.BytesIO(req)) as im:
# Do not use comma to separate the parameters in logging.debug
# It will cause "TypeError: not all arguments converted during string formatting"
logging.debug(" original "+str(im.mode)+" "+str(im.size))
# manipulate the image while reading it from a file
im.draft("L", (100, 100))
logging.debug(" draft "+str(im.mode)+" "+str(im.size))
try:
im.save(buf, format='JPEG')
except OSError:
logging.debug("< handle")
return "cannot process input file", 500, {"Content-type": "text/plain"}
byte_im = buf.getvalue()
logging.debug("< handle")
# Return a binary response
return byte_im, 200, {"Content-type": "application/octet-stream"}
Build and Deploy Function pil-draft-ppc64le
The following script helps with building and deploying of the function using the internal OpenShift Registry as follows.
pil-draft-ppc64le-up.sh
faas-cli delete -f ./pil-draft-ppc64le.yml
PROXY_URL="//10.3.0.3:3128";export http_proxy="http:$PROXY_URL";export https_proxy="http:$PROXY_URL";export no_proxy=.test-cluster.priv,localhost,127.0.0.1
faas-cli build --build-arg 'TEST_ENABLED=false' -f ./pil-draft-ppc64le.yml
if [ $? -eq 0 ]; then
docker tag image-registry.openshift-image-registry.svc:5000/openfaas-fn/pil-draft-ppc64le:latest default-route-openshift-image-registry.apps.test-cluster.priv/openfaas-fn/pil-draft-ppc64le:latest
unset http_proxy;unset https_proxy
docker push default-route-openshift-image-registry.apps.test-cluster.priv/openfaas-fn/pil-draft-ppc64le:latest --tls-verify=false
faas-cli deploy -f pil-draft-ppc64le.yml
oc get pods -w
# faas-cli logs pil-draft-ppc64le
else
echo Build failed
fi
When deployed, look at the output of the logs
faas-cli logs pil-draft-ppc64le
or
oc logs deployment/pil-draft-ppc64le -f
If you see the function pod in Error or CrashLoopBackOff state and the log shows error “Forked function has terminated: exec: not started”, you will need to delete the pod (may be more than once). A new pod will be started.
Synchronous invocation of the Function pil-draft-ppc64le
Download a sample image (of the political world map)
curl -sLS https://www.freeworldmaps.net/download/maps/political-world-map.jpg -o /tmp/political-world-map.jpg
or
curl -sLS https://www.nationsonline.org/maps/Political-World-Map-3360.jpg -o /tmp/political-world-map.jpg
Send the data as a binary input into our function pil-draft-ppc64le and have it saved to a local file political-world-map-draft.jpg that we can open later.
curl --data-binary @/tmp/political-world-map.jpg $OPENFAAS_URL/function/pil-draft-ppc64le -o /tmp/political-world-map-draft.jpg
The draft picture looks as follows:
Delete the function
faas-cli delete pil-draft-ppc64le
We can also deploy the function using a Function Custom Resource pil-draft-ppc64le-function.yaml
oc apply -f pil-draft-ppc64le-function.yaml
We can invoke the function as before.
Asynchronous invocation of the Function pil-draft-ppc64le
We want to invoke this function so that it manipulates the image asynchronously. Just as we had used the public requestbin and hookbin in the previous section, this time we create our own container to receive the draft image asynchronously. The pil-draft-client shows sample Python flask code to receive the callback from the asynchronous call and save the images. You can change this to save the images on a volume. Deploy the container using the runme.sh on docker or as a pod in Podman such that it is accessible from the queue-worker.
docker build -t karve/pil-draft-client .
docker run --rm --publish 5000:5000 karve/pil-draft-client
The receiverip is the ipaddress of the pod or service running the pil-draft-client that can be accessed from the queue-worker pod in the openfaas namespace.
# Set the receiverip to the address when the container is running
curl --data-binary @/tmp/political-world-map.jpg -H "X-Callback-Url: http://$receiverip:5000/messages/political-world-map-draft.jpg" http://gateway-external-openfaas.apps.test-cluster.priv/async-function/pil-draft-ppc64le
The image will be saved in the container. The container log shows a message when it receives an image.
[11/Jul/2021 14:31:05] "POST /messages/political-world-map-draft.jpg HTTP/1.1" 200 -
We can also run the pil-draft-client as a deployment in OpenShift. We can run it in the openfaas namespace.
# Tag the image
docker tag karve/pil-draft-client default-route-openshift-image-registry.apps.test-cluster.priv/openfaas/pil-draft-client-ppc64le:latest
oc whoami -t > oc_token
docker login --tls-verify=false -u kubeadmin default-route-openshift-image-registry.apps.test-cluster.priv -p `cat oc_token`
docker push default-route-openshift-image-registry.apps.test-cluster.priv/openfaas/pil-draft-client-ppc64le:latest --tls-verify=false
oc project openfaas
oc apply -f pil-draft-client-ppc64le.yaml
The pil-draft-client-ppc64le.yaml contains the Service and the Deployment.
apiVersion: v1
kind: Service
metadata:
name: pil-draft-client-service
spec:
selector:
app: pil-draft-client
ports:
- protocol: TCP
port: 5000
targetPort: 5000
name: web
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: pil-draft-client
name: pil-draft-client
spec:
replicas: 1
selector:
matchLabels:
app: pil-draft-client
template:
metadata:
labels:
app: pil-draft-client
spec:
containers:
- name: pil-draft-client
image: image-registry.openshift-image-registry.svc:5000/openfaas/pil-draft-client-ppc64le:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 5000
name: http
protocol: TCP
We can request the callback url to point to the “service ip” or “pod ip” or the dns name pil-draft-client-service.openfaas.svc.cluster.local as long as it is specified in the no_proxy environment variable for the queue-worker.
curl --data-binary @/tmp/political-world-map.jpg -H "X-Callback-Url: http://pil-draft-client-service.openfaas.svc.cluster.local:5000/messages/political-world-map-inverted-bw-thumbnail.jpg" http://gateway-external-openfaas.apps.test-cluster.priv/async-function/chaining-ppc64le
Function Chaining
We can easily clone this draft function yml and folders to create the additional functions that we will use to illustrate chaining. We could have used a single function with a query-string to differentiate between the above conversions since the changes are small. However, the query-string headers only seems to work with the classic watchdog, not with the of-watchdog. i.e.
query = os.environ['Http_Query']
http_path=os.getenv("Http_Path")
Both the above work fine with “classic watchdog” but return None for the “of-watchdog”. So, we separate the functions. There is some discussion in faas issues about path handling.
To clone the draft function to thumbnail function for example, we do the following:
cp -r pil-draft-ppc64le pil-thumbnail-ppc64le
cp pil-draft-ppc64le.yml pil-thumbnail-ppc64le.yml
cp pil-draft-ppc64le-up.sh pil-thumbnail-ppc64le-up.sh
Replace the string “draft” with “thumbnail” in the yml and sh files. Update the handler.py to produce a thumbnail instead of draft. Similarly update the handler for the black and white and transpose (rotate 180 degrees) processing.
The final functions are pil-draft-ppc64le, pil-bw-ppc64le, pil-thumbnail-ppc64le, pil-transpose-ppc64le. We can execute the pil-draft-ppc64le-up.sh, pil-bw-ppc64le-up.sh, pil-thumbnail-ppc64le-up.sh, pil-transpose-ppc64le-up.sh to deploy the functions. We will see the four functions are deployed below.
faas-cli list
Function Invocations Replicas
pil-bw-ppc64le 0 1
pil-draft-ppc64le 0 1
pil-thumbnail-ppc64le 0 1
pil-transpose-ppc64le 0 1
Function Chaining with Client-side piping
The following command illustrates client-side piping through three synchronous function invocations where we first transpose the image, then convert it to black and white and finally create a thumbnail thus finally producing an inverted black and white thumbnail of the original image. Each curl command pipes the output to the next stage for further conversion.
curl -s --data-binary @/tmp/political-world-map.jpg http://gateway-external-openfaas.apps.test-cluster.priv/function/pil-transpose-ppc64le | curl -s --data-binary @- http://gateway-external-openfaas.apps.test-cluster.priv/function/pil-bw-ppc64le | curl -s --data-binary @- http://gateway-external-openfaas.apps.test-cluster.priv/function/pil-thumbnail-ppc64le -o /tmp/inverted-bw-thumbnail.jpg
The image looks as follows:
Function Chaining with a Workflow Function
Another mechanism is to use a workflow template function. This is a rudimentary YAML workflow runner for OpenFaaS. The modified workflow template for ppc64le contains few modifications to the Dockerfile for the ppc64le base image and the ppc64le watchdog. We create a new chaining-ppc64le function from the workflow template.
faas-cli new --lang workflow-ppc64le chaining-ppc64le
Update the workflow.yml in the chaining-ppc64le folder for the chaining of the three functions:
workflow:
name: transpose-bw-thumbnail
gateway_url: http://gateway-external-openfaas.apps.test-cluster.priv
steps:
- name: transpose
function: pil-transpose-ppc64le
method: POST
- name: bw
function: pil-bw-ppc64le
method: POST
- name: thumbnail
function: pil-thumbnail-ppc64le
method: POST
Build and deploy the chaining-ppc64le function with chaining-ppc64le-up.sh. This script uses the local OpenShift Registry, so does not use the “faas-cli up” command. Instead, it separately invokes the “faas-cli build”, then pushes the image using the podman or docker command and then does a “faas-cli deploy” because the image used by the pod points to image-registry.openshift-image-registry.svc:5000 whereas the image is pushed to default-route-openshift-image-registry.apps.test-cluster.priv. The list of functions is shown below.
faas-cli list
Function Invocations Replicas
chaining-ppc64le 6 1
pi-ppc64le 15429 4
pil-bw-ppc64le 14 1
pil-draft-ppc64le 9 1
pil-thumbnail-ppc64le 10 1
pil-transpose-ppc64le 16 1
or
oc get function -n openfaas-fn
NAME AGE
chaining-ppc64le 10h
pi-ppc64le 4d2h
pil-bw-ppc64le 30h
pil-draft-ppc64le 30h
pil-thumbnail-ppc64le 30h
pil-transpose-ppc64le 30h
We can invoke the chaining-ppc64le function with the input image and get the inverted-bw-thumbnail.jpg as before with this command.
curl --data-binary @/tmp/political-world-map.jpg http://gateway-external-openfaas.apps.test-cluster.priv/function/chaining-ppc64le > /tmp/inverted-bw-thumbnail.jpg
This method avoids retrieving and sending the intermediate images via the client. The processing happens within the workflow function on the server. The logs for the chaining-ppc64le function show the transpose, bw and the thumbnail functions being called along with the time for the invocations.
oc logs deployment/chaining-ppc64le -n openfaas-fn
2021/07/11 21:27:15 Version: 0.8.4-1-g989ac5f-dirty-1623851326 SHA: 989ac5f0d2b4560d7b1d9f18d0231449527cc47c
2021/07/11 21:27:15 Watchdog mode: streaming
2021/07/11 21:27:15 Timeouts: read: 10s, write: 10s hard: 10s.
2021/07/11 21:27:15 Listening on port: 8080
2021/07/11 21:27:15 Writing lock-file to: /tmp/.lock
2021/07/11 21:27:15 Metrics listening on port: 8081
2021/07/11 21:29:20 Running ./handler
2021/07/11 21:29:20 Started logging: stderr from function.
2021/07/11 21:29:26 stderr: 2021/07/11 21:29:26 [0] transpose 208856 byte(s) HTTP: 200 - 1.105000s
2021/07/11 21:29:26 stderr: 2021/07/11 21:29:26 [1] bw 176135 byte(s) HTTP: 200 - 0.047934s
2021/07/11 21:29:26 stderr: 2021/07/11 21:29:26 [2] thumbnail 1620 byte(s) HTTP: 200 - 0.015484s
2021/07/11 21:29:26 Took 6.708752 secs
The chaining-ppc64le function can also be invoked asynchronously with the image being sent to the callback url specified with X-Callback-Url.
# Set the receiverip to the address when the container is running
curl --data-binary @/tmp/political-world-map.jpg -H "X-Callback-Url: http://$receiverip:5000/messages/political-world-map-draft.jpg" http://gateway-external-openfaas.apps.test-cluster.priv/async-function/chaining-ppc64le
The queue-worker log will show that the request was invoked asynchronously by the chaining-ppc64le workflow function and result was posted to the callback-url.
[#119] Received on [faas-request]: 'sequence:121 subject:"faas-request" data:"{\"Header\":{\"Accept\":[\"*/*\"],\"Content-Length\":[\"153196\"],\"Content-Type\":[\"application/x-www-form-urlencoded\"],\"Expect\":[\"100-continue\"],\"Forwarded\":[\"for=10.3.158.61;host=gateway-external-openfaas.apps.test-cluster.priv;proto=http\"],\"User-Agent\":[\"curl/7.64.1\"],\"X-Call-Id\":[\"854188b8-ffd0-4f22-97d2-2a5465d85638\"],\"X-Callback-Url\":[\"http://10.3.158.61:5000/messages/political-world-map-inverted-bw-thumbnail.jpg\"],\"X-Forwarded-For\":[\"10.3.158.61\"],\"X-Forwarded-Host\":[\"gateway-external-openfaas.apps.test-cluster.priv\"],\"X-Forwarded-Port\":[\"80\"],\"X-Forwarded-Proto\":[\"http\"],\"X-Start-Time\":[\"1626040618946622776\"]},\"Host\":\"gateway-external-openfaas.apps.test-cluster.priv\",\"Body\":\"<truncated> \",\"Method\":\"POST\",\"Path\":\"\",\"QueryString\":\"\",\"Function\":\"chaining-ppc64le\",\"QueueName\":\"\",\"CallbackUrl\":{\"Scheme\":\"http\",\"Opaque\":\"\",\"User\":null,\"Host\":\"10.3.158.61:5000\",\"Path\":\"/messages/political-world-map-inverted-bw-thumbnail.jpg\",\"RawPath\":\"\",\"ForceQuery\":false,\"RawQuery\":\"\",\"Fragment\":\"\",\"RawFragment\":\"\"}}" timestamp:1626040624817970813 '
[#119] Invoking: chaining-ppc64le with 153196 bytes, via: http://gateway.openfaas.svc.cluster.local:8080/function/chaining-ppc64le/
[#119] chaining-ppc64le returned 1620 bytes
[#119] Invoked: chaining-ppc64le [200] in 1.416328s
[#119] Callback to: http://10.3.158.61:5000/messages/political-world-map-inverted-bw-thumbnail.jpg
[#119] Posted result for chaining-ppc64le to callback-url: http://10.3.158.61:5000/messages/political-world-map-inverted-bw-thumbnail.jpg, status: 200
The image will be saved in the container. The container shows a log message when it receives an image.
10.3.158.67 - - [11/Jul/2021 21:57:07] "POST /messages/political-world-map-inverted-bw-thumbnail.jpg HTTP/1.1" 200 -
OpenFaaS does not allow nested callback URLs where you can create a chain of asynchronous callbacks. Also, an asynchronous pipeline to pass around very large binary data is not recommended. You can only send one X-Callback-Url header. So, you have to essentially write a wrapper function to call the multiple functions in the chain. The return from this wrapper function can be asynchronous.
Another option is the Faas-flow tool that allows you to realize OpenFaaS function composition. It provides the backbone for building complex solutions and promote automation. By supplying a number of pipeline operators, the complex composition can be achieved.
Conclusion
Function chaining can be useful in many image processing applications on the cloud and the edge. Image processing may be used for detecting patterns within an image for plant disease, malignant tumors, cancer, galaxy detection etc. Once the image is captured from digital media, preprocessing is done to the captured image to improve the resolution, noise, and color in the picture. Segmentation is done on the enhanced image. Segmented images are compared with reference images to identify the patterns using machine learning. Identified segments may be used for further analysis. Each of these tasks can be separated into functions and chained together in multiple ways as illustrated in the simple example in this blog. We write a function focused on solving one problem without having to worry about the next. It makes the function loosely coupled from the business logic promoting reusability. We can write the stateless function and use it across multiple applications. It can be really easy to start developing serverless applications on OpenShift using OpenFaaS for ppc64le with source code available for reference. In Part 4, we will work on OpenFaaS functions for Model Asset eXchange (MAX) models.
Hope you have enjoyed the article. Share your thoughts in the comments or engage in the conversation with me on Twitter @aakarve. I look forward to hearing about how you use OpenFaaS with asynchronous function chaining and if you would like to see something covered in more detail.
References