Spring Cloud Functions are yet another interesting option for Java developers when building serverless applications. You have already seen how to build and run applications for Red Hat OpenShift Serverless using Quarkus, but in this article, we talk about how to use Spring Cloud Functions and walk you through those steps. These steps are similar to running any other Spring Boot application with OpenShift Serverless. One of the benefits of building an open hybrid serverless platform is giving developers a choice of programming languages, tools, frameworks, and portability across any environment to run serverless applications. Beyond that, you want to ensure that the developer experience and overall workflow is intuitive and practical, which is what you will learn here.
If you are interested in just watching the steps performed in this article you can watch the recording on YouTube.
https://www.youtube.com/watch?v=yXlTs0On3Ys
Requirements
- Gradle
- Java Development Tool Kit (JDK 8+)
- OpenShift 4.3+
- OpenShift Serverless 1.7+
curl
kn
(Knative Client)
Generate the Spring Cloud Functions project
One of the easiest ways to generate a Spring project is using curl
to access start.spring.io
and that's exactly how we will start our project:
https://gist.github.com/markito/ab6184e4f18bca33f89df78721731411
This will generate and download a project inside the my-function-project
folder.
Implement your first function
We will implement a quick and dirty Google Translator wrapper. In order to create functions using Spring Cloud Functions, you need a method with the @Bean
annotation that can follow any of the functional interfaces from java.util.function
such as Consumer
, Supplier
, or Function
. For more details about how Spring Cloud Functions work please read the documentation, but for this example just copy and paste the following method to your DemoApplication.java file:
https://gist.github.com/markito/e976443fd61a6d7d2278f2acfa7c18d3
Test your function locally
Since Spring Cloud Functions are just Spring Boot apps, you can implement unit tests just like any other Java application using JUnit, Mockito, or whatever you would like. You can also run the application locally, using gradle bootRun
or mvn spring-boot:run
, which is useful for validating the application iteratively before running inside a container. Make sure you have fixed all Java imports and then start the application locally using:
https://gist.github.com/markito/cf771a9941e7b8e3e187aaaa1eee55e7
Every function will be mapped to an endpoint that can be accessed as follows:
http://localhost:8080/<functionName>
Concretely, for our translate function, you can access the endpoint using the following:
https://gist.github.com/markito/5340d90f3288507d7c522733e0dd6338
Using curl
we post a word or a phrase and it gets translated to Spanish. You can parse the output and format it properly, but to keep this short I'll leave that as an exercise for the reader. May Jackson and JSON be your friends.
Building a container using Jib
So far we have built a Spring application and executed it locally but it's time to containerize the application. There are many ways to execute this step but I've decided to stick to well-known tools used by the Java community, so I'll use Jib
and add it as a plugin to my Gradle project.
Edit the build.gradle
file and append this line id 'com.google.cloud.tools.jib' version "2.4.0"
to your plugins section. It should look like this:
https://gist.github.com/markito/f6ee63153a25351d7fcccbb37885598b
With Jib as part of your project, Gradle can build your container and push it to your container registry of choice. Quay or DockerHub are well-known choices.
After you have the jib plugin on your Gradle project, build a container for the project using the following command: gradle build jib --image=<your_container_registry>/demo-app:v1
On my machine this is what it looks like:
https://gist.github.com/markito/9bf7aca34c8c1aabdd17bce7f8abde50
This generated a container for your project and automatically pushed it to the container registry. If you are new to Linux containers and container registries please read this post to get a practical introduction to the topic.
Deploy to OpenShift Serverless
With the container built you can now deploy the application using the Knative CLI, kn
:
https://gist.github.com/markito/7f40db940f0edd2bd8e3898d71543f0a
OpenShift Serverless will do the heavy lifting of creating a Kubernetes deployment, a route, SSL configuration (using your cluster's configuration), and auto-scaling configuration based on the number of requests. For more details about OpenShift Serverless, please check out the product documentation.
The above code will auto-scale the application down (to zero) after six seconds without a new request.
Try it again using the URL from the service running in OpenShift:
https://gist.github.com/markito/b1673b96043b3670c24f9a23a3666925
Adding a new function that can handle a JSON document
When creating REST APIs it is very common to send and receive JSON documents and you can very easily add a function that takes a JSON message instead of a String.
The data model
Create a new UserReview.java
file with the following content:
https://gist.github.com/markito/df869ca8245f59ffde0175b0f679c476
We will use this class to marshal and unmarshal the JSON objects being sent or received by our API. Create a review.json
file with the content below. This will be the input of our new translateReview
function.
https://gist.github.com/markito/31a7e912be19d7babf9b1aa719632b94
The function code
This will be very similar to the previous function we created, it's just another method with the @Bean
annotation and using our POJO class as input and output.
https://gist.github.com/markito/6cf0c421f3936ffb741ca61948666a98
Posting a JSON file
We will continue using curl
as our client here and specify a path to the review.json
file created in the previous step. Start the application again using gradle bootRun
then post a json file to the API:
https://gist.github.com/markito/f35a880180b46258b5050db381b20c6f
As you can see, the function now accepts a JSON document and translates the user comment to Spanish. You can experiment with a declarative function composition way to implement this if that's your cup of tea, but I'm keeping this simple for now.
Building your new container
Same steps as before but using a v2
tag, build a new version of the container:
$ gradle jib --image=<your_container_registry>/demo-app:v2
Updating the deployed application to include the new function
Now we are going to use another interesting feature of OpenShift Serverless, we will deploy a new version of the application but with a different URL, that way current clients of this API won't even know about this new functionality until we decide to send traffic to it:
https://gist.github.com/markito/5e71653d3bda5f13b36151526a392cdc
This can be achieved by using tags. In this example, I'm tagging a particular revision @latest
with a preview
value that will be appended to the service URL. Also note, that I'm setting 100% of traffic to the previous revision translator-v1
, which means no traffic will be sent to the new version being deployed. This is also called a "dark launch," where a new version of my application is available in a production environment but not necessarily receiving any requests unless someone knows which URL to use.
After validation is complete you can decide to gradually send traffic using canary or blue/green deployment models. There is a step-by-step lab about how to implement those models on https://developers.redhat.com/node/269571
Testing
Now you can execute the same curl command as before but this time adding the preview
prefix:
https://gist.github.com/markito/efec228f47b7515d03d5475d8deb6851
Conclusion
You can easily build and deploy a Spring Cloud Functions application using OpenShift Serverless. The workflow feels natural for a Java developer and you can even build the container using a Gradle (or Maven) plugin such as Jib.
Using OpenShift Serverless you can also deploy multiple versions of the application and perform a dark launch, blue/green, or canary deployment with no sweat. The OpenShift developer console makes it even easier to visualize traffic route information and the overall topology of your application.
For more details, check the OpenShift Serverless page.