Your First OpenFaaS Function on RaspberryPi
Serverless with Functions as a Service!
This post is deprecated, look at The Official OpenFaas Github Repo
Detailed Tour through OpenFaaS:
Go on tour with Alex Ellis, where he takes you through a tour on OpenFaaS and how to setup the framework, and more information on the project:
This is also a great resource OpenFaaS TestDrive
Developing a Random Name and Surname Generator Function:
Using the faas-cli is one amazing cli client, to build, push and deploy your functions, but I wanted to post on how to write a minimal function for Python on the ARM Architecture:
Setting up the OpenFaaS Framework:
I am using my RaspberryPi Swarm Cluster, so I will be using the ARMHF compose file to deploy my stack:
$ git clone https://github.com/openfaas/faas
$ cd faas
$ git checkout 0.6.5
$ ./deploy_stack.armhf.sh
After the deployment is done, you will find the following when listing your services:
$ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
4jcqosqvsnp3 func_wordcount replicated 1/1 functions/alpine:latest-armhf
4wvoqci6sm44 func_nodeinfo replicated 2/2 functions/nodeinfo:latest-armhf
j7l8pbs1cbux func_gateway replicated 1/1 functions/gateway-armhf:0.6.0 *:8080->8080/tcp
k7wdp7rx4q8j func_markdown replicated 1/1 functions/markdownrender:latest-armhf
k8zjdgfu9hvq func_prometheus replicated 1/1 alexellis2/prometheus-armhf:1.5.2 *:9090->9090/tcp
uk4k207ttay4 func_alertmanager replicated 1/1 alexellis2/alertmanager-armhf:0.5.1 *:9093->9093/tcp
ymneye82ktpc func_echoit replicated 1/1 functions/alpine:latest-armhf
When you head over to http://docker-swarm-address:8080/ui/
you will be able to access the awesome FaaS Gateway Web Interface, where you will be able to execute and add functions.
Port: 9090
is for Prometheus and Port: 9093
is for the AlertManager.
Lets Build a Python Function:
Like mentioning before the faas-cli
makes it really convenient to build / deploy your functions, but we will go through them section at a time. All the content is from Alex's Github Repo: OpenFaaS
Create the Working Directory:
$ mkdir openfaas-myfunctions
$ cd openfaas-myfunctions
Create the Dockerfile
:
$ cat Dockerfile
FROM armhf/python:2.7-alpine
RUN apk --no-cache add curl \
&& curl -sSL https://github.com/openfaas/faas/releases/download/0.6.1/fwatchdog-armhf > /usr/bin/fwatchdog \
&& chmod +x /usr/bin/fwatchdog \
&& apk del curl --no-cache
WORKDIR /root/
COPY index.py .
# we wont be installing external packages, so leaving out the following:
# COPY requirements.txt .
# RUN pip install -r requirements.txt
COPY function function
RUN touch ./function/__init__.py
WORKDIR /root/function/
# we wont be installing external packages, so leaving out the following:
#COPY function/requirements.txt .
#RUN pip install -r requirements.txt
WORKDIR /root/
ENV fprocess="python index.py"
HEALTHCHECK --interval=1s CMD [ -e /tmp/.lock ] || exit 1
CMD ["fwatchdog"]
The fwatchdog process provides a interface between the internet and your serverless function. The fwatchdog process is written in Golang and the best of all, its really small in size, but yet powerful!
From the docs:
Every function needs to embed this binary and use it as its ENTRYPOINT or CMD, in effect it is the init process for your container. Once your process is forked the watchdog passses in the HTTP request via stdin and reads a HTTP response via stdout. This means your process does not need to know anything about the web or HTTP.
The main function that will be set to the fprocess
environment variable for fwatchdog
:
$ cat index.py
# Copyright (c) Alex Ellis 2017. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
import sys
from function import handler
def get_stdin():
buf = ""
for line in sys.stdin:
buf = buf + line
return buf
if(__name__ == "__main__"):
st = get_stdin()
handler.handle(st)
Our handler function, where we will develop a small function that uses the random library to randomly select a value from an index number using random.choice
, and then using a if statement to add some logic between name and surname.
So when we do a POST
request, and our body states name, we will then print a name from our names
list, that we defined. And so for surnames
as well:
$ cat function/handler.py
import random
names = ["ruan", "stefan", "james", "peter", "frank", "george"]
surnames = ["franklin", "james", "john", "peters", "bester", "adams"]
def handle(st):
if st == 'name':
print(random.choice(names))
elif st == 'surname':
print(random.choice(surnames))
else:
print(st + ' is not supported')
So we should have this directory structure:
$ tree
.
├── Dockerfile
├── function
│ └── handler.py
└── index.py
1 directory, 3 files
Now to build and push our image to Docker Hub:
$ docker build -t rbekker87/functions:names-amrhf .
$ docker push rbekker87/functions:names-amrhf
You can use the FaaS Web UI to Deploy your Function, but I will use the REST API to deploy my function:
$ curl http://docker-swarm-address:8080/system/functions -d '{"service": "random", "image": "rbekker87/functions:names-amrhf", "envProcess": "python index.py", "network": "func_functions"}'
Testing out our Serverless Function:
To get a name, pass the name
as the data of our POST
request:
$ curl -XPOST http://docker-swarm-address:8080/function/random/ -d "name"
peter
The same, with a surname:
$ curl -XPOST http://docker-swarm-address:8080/function/random/ -d "surname"
bester
Next, I would like to use Consul as a Key/Value Pair to PUT and GET data from teh K/V store using OpenFaaS.
Have a look at what the community is doing with OpenFaaS, its becoming (well its already) popular!!