Workloads

OpenFaaS can host multiple types of workloads from functions to microservices, but FaaS Functions have the best support.

Common properties

All workloads must:

  • serve HTTP traffic on TCP port 8080
  • assume ephemeral storage
  • be stateless

And integrate with a health-check mechanism:

On Swarm:

  • create a lock file in /tmp/.lock - removing this file signals service degradation
  • add a HEALTHCHECK instruction if using Docker Swarm

On Kubernetes:

  • or enable httpProbe in the helm chart and implement /_/health as a HTTP endpoint
  • create a lock file in /tmp/.lock - removing this file signals service degradation

Note: You can specify a custom HTTP Path for the health-check using the com.openfaas.health.http.path annotation

If running in read-only mode, then you can write files to the /tmp/ mount only. These files may be accessible upon subsequent requests but it is not guaranteed. For instance - if you have two replicas of a function then both may have different contents in their /tmp/ mount. When running without read-only mode you can write files to the user’s home directory subject to the same rules.

FaaS Functions

To build a FaaS Function simply use the OpenFaaS CLI to scaffold a new function using one of the official templates or one of your own templates. All FaaS Functions make use of the OpenFaaS classic watchdog or the next-gen of-watchdog.

  1. faas-cli template pull
  2. faas-cli new --list

Or build your own templates Git repository and then pass that as an argument to faas-cli template pull

  1. faas-cli template pull https://github.com/my-org/templates
  2. faas-cli new --list

Custom binaries can also be used as a function. Just use the dockerfile language template and replace the fprocess variable with the command you want to run per request. If you would like to pipe arguments to a CLI utility you can prefix the command with xargs.

Running an existing Docker image on OpenFaaS

Let’s take a Node.js app which listens on traffic using port 3000, and assume that we can’t make any changes to it.

You can view its Dockerfile and code at: alexellis/expressjs-k8s and the image is published to the Docker Hub at: alexellis2/service:0.3.6

Start by creating a new folder:

  1. mkdir -p node-service/

Write a custom Dockerfile ./node-service/Dockerfile:

  1. # Import the OpenFaaS of-watchdog
  2. FROM openfaas/of-watchdog:0.7.2 as watchdog
  3. # Add a FROM line from your existing image
  4. FROM alexellis2/service:0.3.6
  5. # Let's say that the image listens on port 3000 and
  6. # that we can't change that easily
  7. ENV http_port 3000
  8. # Install the watchdog from the base image
  9. COPY --from=watchdog /fwatchdog /usr/bin/
  10. # Now set the watchdog as the start-up process
  11. # Along with the HTTP mode, and an upstream URL to
  12. # where your HTTP server will be running from the original
  13. # image.
  14. ENV mode="http"
  15. ENV upstream_url="http://127.0.0.1:3000"
  16. # Set fprocess to the value you have in your base image
  17. ENV fprocess="node index.js"
  18. CMD ["fwatchdog"]

Now create a stack.yml at the root directory ./stack.yml:

  1. provider:
  2. name: openfaas
  3. functions:
  4. node-service:
  5. handler: ./node-service
  6. image: docker.io/alexellis2/node-service-watchdog:0.1.0
  7. lang: dockerfile

Now run faas-cli up

Your code will now listen on port 8080 and fulfil the serverless definition including automatic health-checks and a graceful shutdown.

You can then access the service at: http://127.0.0.1:8080/function/node-service

Custom service account

When using Kubernetes, OpenFaaS workloads can assume a ServiceAccount in the namespace in which they are deployed.

For example if a workload needed to read logs from the Kubernetes API using a ServiceAccount named function-logs-sa, you could bind it in this way:

stack.yml

  1. functions:
  2. system-logs:
  3. annotations:
  4. com.openfaas.serviceaccount: function-logs-sa

Here is an example Role that can list pods and work with Pod logs within the openfaas-fn namespace:

  1. kind: Role
  2. apiVersion: rbac.authorization.k8s.io/v1
  3. metadata:
  4. name: function-logs-role
  5. namespace: openfaas-fn
  6. rules:
  7. - apiGroups: [""]
  8. resources: ["pods", "pods/log"]
  9. verbs: ["get", "list", "create"]
  10. ---
  11. kind: RoleBinding
  12. apiVersion: rbac.authorization.k8s.io/v1
  13. metadata:
  14. name: function-logs-role-binding
  15. namespace: openfaas-fn
  16. subjects:
  17. - kind: ServiceAccount
  18. name: function-logs-sa
  19. namespace: openfaas-fn
  20. roleRef:
  21. kind: Role
  22. name: function-logs-role
  23. apiGroup: rbac.authorization.k8s.io
  24. ---
  25. apiVersion: v1
  26. kind: ServiceAccount
  27. metadata:
  28. name: function-logs-sa
  29. namespace: openfaas-fn
  30. labels:
  31. app: openfaas

Stateless microservices

A stateless microservice can be built using the dockerfile language type and the OpenFaaS CLI - or by building a custom Docker image which serves traffic on port 8080 and deploying that via the RESTful API, CLI or UI.

An example of a stateless microservice may be an Express.js application using Node.js, a Sinatra app with Ruby or an ASP.NET 2.0 Core website.

Use of the OpenFaaS next-gen of-watchdog is optional, but recommended for stateless microservices to provide a consistent experience for timeouts, logging and configuration.

On Kubernetes is possible to run any container image as an OpenFaaS function as long as your application exposes port 8080 and has a HTTP health check endpoint.

Custom HTTP health check

You can specify the HTTP path of your health check and the initial check delay duration with the following annotations:

  • com.openfaas.health.http.path
  • com.openfaas.health.http.initialDelay

Stack file example:

  1. functions:
  2. kubesec:
  3. image: docker.io/stefanprodan/kubesec:v2.1
  4. skip_build: true
  5. annotations:
  6. com.openfaas.health.http.path: "/healthz"
  7. com.openfaas.health.http.initialDelay: "30s"

Note: The initial delay value must be a valid Go duration e.g. 80s or 3m.