As a developer, I find the promise of serverless attractive because it would allow me to focus on building my applications without worrying about many of the deployment details. OpenFaaS is an open-source serverless framework that works with Docker and Kubernetes, and it makes developing and deploying functions a breeze.

In September 2018, OpenFaaS released support for stateless microservices. A stateless microservice is just a microservice that can be deployed as if it were a FaaS function. I find this to be a compelling use case for a few reasons. It allows you to develop microservices using the web technologies you are already used to using, and it allows you to streamline or completely migrate the deployment of legacy microservices to k8s.

This week, I released a Clojure template for OpenFaaS that helps you implement stateless microservices. The template provides a simple deps.edn-based project containing a ring handler. When the function is triggered, the handler will be used to process the request. It’s that simple!

Let’s explore the template by re-implementing the astronaut-finder function from the OpenFaaS Workshop. The final version is in the OpenFaaS Clojure examples repository.

Development Setup

Alex Ellis (the OpenFaaS founder) wrote an excellent post on getting up and running with OpenFaaS. It will show you how to set up OpenFaaS and install faas-cli on your computer.

After getting OpenFaaS set up, you should install Clojure. Installing Clojure will let you run your code in a REPL using the clj command and run the microservice locally. The power of developing with a REPL cannot be overstated. :smile:

The last step is installing the Clojure template for OpenFaaS. The following command needs to be executed in the folder in which you plan to create functions. faas-cli will copy the template into a template folder in your directory, creating it if necessary.

faas-cli template pull https://github.com/tessellator/openfaas-clojure-template

Creating the Function

Now that the everything is set up, we can instruct faas-cli to create a new function using the template we just installed.

faas-cli new --lang clojure astronaut-finder

This will create a file named astronaut-finder.yml and a folder named astronaut-finder in your current directory. Let’s take a peek at astronaut-finder/src/function/core.clj.

(ns function.core)

(defn handler [req]
  {:status 200
   :body "Hello, Clojure."
   :headers {}})

(def app
  (-> handler))

This should look pretty familiar if you are used to writing ring applications in Clojure. The template has only one requirement: function.core/app must be a ring handler. Otherwise, you are free to add dependencies and structure your code however you see fit.

Let’s change this function to find out who is in space.

Finding an Astronaut

We should first update the deps.edn file with any additional dependencies we will need. Add clj-http (HTTP client) and cheshire (JSON parsing). We should also explicitly state which version of Clojure we want to use. The template will use 1.10.0 by default, but you are free to specify which version you need specifically. Let’s stick with 1.10.0 for now.

After you add the dependencies, your file should look like the following:

{:paths ["src"]
 :deps {org.clojure/clojure {:mvn/version "1.10.0"}
        ring/ring-core {:mvn/version "1.7.1"}
        cheshire {:mvn/version "5.8.1"}
        clj-http {:mvn/version "3.9.1"}}
 :aliases
 {:dev {:extra-deps {ring/ring-devel {:mvn/version "1.7.1"}
                     ring/ring-jetty-adapter {:mvn/version "1.7.1"}}
        :extra-paths ["dev"]}}}

Update the code in src/function/core.clj to match the following. The resulting code defines ring handler app that will find the current astronauts, select one at random, and return a message containing his or her name.

(ns function.core
  (:require [cheshire.core :as json]
            [clj-http.client :as client]
            [ring.util.response :refer [response]]))

(def data-url "http://api.open-notify.org/astros.json")

(defn astronauts []
  (-> data-url
      client/get
      :body
      (json/parse-string true)
      :people))

(defn random-astronaut []
  (rand-nth (astronauts)))

(defn app [_]
  (response (format "%s is in space!" (:name (random-astronaut)))))

At this point, you may want to interact with the code. You can start a REPL by running clj -A:dev in the astronaut-finder folder. By running with the dev alias, you are also able to start a local Jetty server that reloads your code on every request. To start the server, enter the following at the REPL.

(require '[function.server :as server])
(server/start! {:port 4000})

Once you are satisfied with the behavior of the application, it is time to deploy your stateless microservice using OpenFaaS.

Deploying the Function

At this point, we have developed and tested a service locally and we are ready to deploy it into some other environment. Assuming you followed the setup instructions in the getting started post, you need only run the following command. (If you have a different setup, you may have to adjust some of the values in the astronaut-finder.yml file to match your scenario.)

faas-cli up -f ./astronaut-finder.yml

This will take a few minutes to complete. Behind the curtain, OpenFaaS will use the Clojure template to download all your dependencies, compile your code and package it into a jar file, and create a Docker image. It will then push your image to a Docker repository and start an instance running. While it is useful to know what is happening, the beauty is that you do not really have to know the details.

When the deployment is complete, the function should appear in your OpenFaaS Portal (http://localhost:8080/ui), and you can invoke the function from the UI. Alternatively, you can use curl against the OpenFaaS gateway:

curl http://localhost:8080/function/astronaut-finder

You should receive a text response containing a message like Anne McClain is in space! Congratulations - you have successfully created and deployed a Clojure stateless microservice using OpenFaaS!

Wrapping Up

OpenFaaS is a great open-source framework for developing and deploying serverless functions and stateless microservices. Alex is really engaged with the community, and he has designed it to be extensible by the community in many different ways, of which sharing custom templates for building functions is only one.

The OpenFaaS Clojure template is a quick way to get started writing ring-based stateless microservices with OpenFaaS. I have tried to make the template small and flexible, but please let me know if you find it lacking in any way. I would love feedback on how it can be improved.

You might want to check out the docs for the service we used to find out who is in space. There are some other neat features, like finding when the ISS will pass over your location. A big shout out to Nathan Bergey for making this public API and maintaining the data.

In another post, I will describe how I like to version my functions using annotations, the template’s manifest support, and a ring middleware.