Serverless on Azure Function with Quarkus
This article will teach you how to create and run serverless apps on Azure Function using the Quarkus Funqy extension. You can compare it to the Spring Boot and Spring Cloud support for Azure functions described in my previous article. There are also several other articles about Quarkus on my blog. If you are interested in the Kubernetes native solutions you can read more about serverless functions on OpenShift here.
Source Code
If you would like to try it by yourself, you may always take a look at my source code. In order to do that you need to clone my GitHub repository. The Quarkus app used in the article is located in the account-function
directory. After you go to that directory you should just follow my further instructions.
Prerequisites
There are some prerequisites before you start the exercise. You need to install JDK17+ and Maven on your local machine. You also need to have an account on Azure and az
CLI to interact with that account. Once you install the az
CLI and log in to Azure you can execute the following command for verification:
$ az account show
If you would like to test Azure Functions locally, you need to install Azure Functions Core Tools. You can find detailed installation instructions in Microsoft Docs here. For macOS, there are three required commands to run:
$ brew tap azure/functions
$ brew install azure-functions-core-tools@4
$ brew link --overwrite azure-functions-core-tools@4
Create Resources on Azure
Before proceeding with the source code, we must create several required resources on the Azure cloud. In the first step, we will prepare a resource group for all the required objects. The name of the group is quarkus-serverless
. The location depends on your preferences. For me it is eastus
.
$ az group create -l eastus -n quarkus-serverless
In the next step, we need to create a storage account. The Azure Function service requires it, but we will also use that account during the local development with Azure Functions Core Tools.
$ az storage account create -n pminkowsserverless \
-g quarkus-serverless \
-l eastus \
--sku Standard_LRS
In order to run serverless apps with the Quarkus Azure extension, we need to create the Azure Function App instances. Of course, we use the previously created resource group and storage account. The name of my Function App instance is pminkows-account-function
. We can also set a default OS type (Linux), functions version (4
), and a runtime stack (Java) for each Function App.
$ az functionapp create -n pminkows-account-function \
-c eastus \
--os-type Linux \
--functions-version 4 \
-g quarkus-serverless \
--runtime java \
--runtime-version 17.0 \
-s pminkowsserverless
Now, let’s switch to the Azure Portal. Then, find the quarkus-serverless
resource group. You should have the same list of resources inside this group as shown below. It means that our environment is ready and we can proceed to the app implementation.
Building Serverless Apps with Quarkus Funqy HTTP
In this article, we will consider the simplest option for building and running Quarkus apps on Azure Functions. Therefore, we include the Quarkus Funqy HTTP extension. It provides a simple way to expose services as HTTP endpoints, but shouldn’t be treated as a replacement for REST over HTTP. In case you need the full REST functionality you can use, e.g. the Quarkus RESTEasy module with Azure Function Java library. In order to deploy the app on the Azure Function service, we need to include the quarkus-azure-functions-http
extension. Our function will also store data in the H2 in-memory database through the Panache module integration. Here’s a list of required Maven dependencies:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-funqy-http</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-azure-functions-http</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-orm-panache</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-h2</artifactId>
</dependency>
With the quarkus-azure-function
s extension we don’t need to include and configure any Maven plugin to deploy an app on Azure. That extension will do the whole deployment work for us. By default, Quarkus uses the Azure CLI in the background to authenticate and deploy to Azure. We just need to provide several configuration properties with the quarkus.azure-functions
prefix inside the Quarkus application.properties
file. In the configuration section, we have to set the name of the Azure Function App instance (pminkows-account-function
), the target resource group (quarkus-serverless
), the region (eastus
), the service plan (EastUSLinuxDynamicPlan
). We will also add several properties responsible for database connection and setting the root API context path (/api
).
quarkus.azure-functions.app-name = pminkows-account-function
quarkus.azure-functions.app-service-plan-name = EastUSLinuxDynamicPlan
quarkus.azure-functions.resource-group = quarkus-serverless
quarkus.azure-functions.region = eastus
quarkus.azure-functions.runtime.java-version = 17
quarkus.datasource.db-kind = h2
quarkus.datasource.username = sa
quarkus.datasource.password = password
quarkus.datasource.jdbc.url = jdbc:h2:mem:testdb
quarkus.hibernate-orm.database.generation = drop-and-create
quarkus.http.root-path = /api
Here’s our @Entity
class. We take advantage of the Quarkus Panache active record pattern.
@Entity
public class Account extends PanacheEntity {
public String number;
public int balance;
public Long customerId;
}
Let’s take a look at the implementation of our Quarkus HTTP functions. By default, with the Quarkus Funqy extension, the URL path to execute a function is the function name. We just need to annotate the target method with @Funq
. In case we want to override a default path, we put the request name as the annotation value
field. There are two methods. The addAccount
method is responsible for adding new accounts and is exposed under the add-account
path. On the other hand, the findByNumber
method allows us to find the account by its number. We can access it under the by-number
path. This approach allows us to deploy multiple Funqy functions on a single Azure Function.
public class AccountFunctionResource {
@Inject
Logger log;
@Funq("add-account")
@Transactional
public Account addAccount(Account account) {
log.infof("Add: %s", account);
Account.persist(account);
return account;
}
@Funq("by-number")
public Account findByNumber(Account account) {
log.infof("Find: %s", account.number);
return Account
.find("number", account.number)
.singleResult();
}
}
Running Azure Functions Locally with Quarkus
Before we deploy our functions on Azure, we can run and test them locally. I assume you have already the Azure Functions Core Tools according to the “Prerequisites” section. Firstly, we need to build the app with the following Maven command:
$ mvn clean package
Then, we can take advantage of Quarkus Azure Extension and use the following Maven command to run the app in Azure Functions local environment:
$ mvn quarkus:run
Here’s the output after running the command visible above. As you see, there is just a single Azure function QuarkusHttp
, although we have two methods annotated with @Funq
. Quarkus allows us to invoke multiple Funqy functions using a single, wildcarded route http://localhost:8081/api/{*path}
.
All the required Azure Function configuration files like host.json
, local.settings.json
and function.json
are autogenerated by Quarkus during the build. You can find them in the target/azure-functions
directory.
Here’s the auto-generated function.json
with our Azure Function definition:
{
"scriptFile" : "../account-function-1.0.jar",
"entryPoint" : "io.quarkus.azure.functions.resteasy.runtime.Function.run",
"bindings" : [ {
"type" : "httpTrigger",
"direction" : "in",
"name" : "req",
"route" : "{*path}",
"methods" : [ "GET", "HEAD", "POST", "PUT", "OPTIONS" ],
"dataType" : "binary",
"authLevel" : "ANONYMOUS"
}, {
"type" : "http",
"direction" : "out",
"name" : "$return"
} ]
}
Let’s call our local function. In the first step, we will add a new account by calling the addAccount
function:
$ curl http://localhost:8081/api/add-account \
-d "{\"number\":\"124\",\"customerId\":1, \"balance\":1000}" \
-H "Content-Type: application/json"
Then, we can find the account by its number. For GET requests, the Funqy HTTP Binding allows to use of a query parameter mapping for function input parameters. The query parameter names are mapped to properties on the bean class.
$ curl http://localhost:8081/api/by-number?number=124
Deploy Quarkus Serverless on Azure Functions
Finally, we can deploy our sample Quarkus serverless app on Azure. As you probably remember, we already have all the required settings in the application.properties
file. So now, we just need to run the following Maven command:
$ mvn quarkus:deploy
Here’s the output of the command. As you see, there is still one Azure Function with a wildcard in the path.
Let’s switch to the Azure Portal. Here’s a page with the pminkows-account-function
details:
We can call a similar query several times with different input data to test the service:
$ curl https://pminkows-account-function.azurewebsites.net/api/add-account \
-d "{\"number\":\"127\",\"customerId\":4, \"balance\":1000}" \
-H "Content-Type: application/json"
Here’s the invocation history visible in the Azure Monitor for our QuarkusHttp
function.
Final Thoughts
In this article, I’m showing you a simplified scenario of running a Quarkus serverless app on Azure Function. You don’t need to know much about Azure Function to run such a service, since Quarkus handles all the required things around for you.
2 COMMENTS