Azure Functions + Quarkus HTTP Native/Graal

Leave a comment

You can take an existing Quarkus HTTP application and deploy it as a native executable (Graal build) to Azure Functions using the Custom Handler runtime provided with Azure Functions.

Prerequisites:

You’ll need to configure a root path in application.properties:

quarkus.http.root-path=/api/{functionName}

Replace {functionName} with whatever you want to call the function you’re creating for the Quarkus HTTP application.

Next, rebuild your application to create a native executable targeting linux 64bit x86 runtime.

$ mvn package -Pnative -DskipTests=true \
         -Dquarkus.native.container-build=true \
         -Dquarkus.native.builder-image=quay.io/quarkus/ubi-quarkus-mandrel-builder-image:jdk-17


After you do this create a directory in the root of your project and create function deployment descriptors. Specify anything you want for the name of the function

$ mkdir my-app
$ cd my-app
$ func new --language Custom --template HttpTrigger  \
                   --name my-func --authlevel anonymous

The func command will generate a custom handler host.json file and an http trigger function.json for you for the function named my-func.

Next copy your application’s native executable to your app directory:

$ cp ../target/code-with-quarkus-1.0.0-SNAPSHOT-runner app

Next you’ll need to edit my-func/function.json. Add a route that matches to all paths.

{
  "bindings": [
    {
      "authLevel": "Anonymous",
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "methods": [
        "get",
        "post"
      ],
      "route": "{*path}"
    },
    {
      "type": "http",
      "direction": "out",
      "name": "res"
    }
  ]
}

Without specifying a route, you will not be able to get requests to rest endpoints defined in your application.

Next you need to edit your host.json file to specify some configuration. You’ll need to enable http forwarding (enableForwardingHttpRequest), specify the executable name of your application (defaultExecutablePath), and define the http port for Quarkus to bind to by specifying a system property to pass to the application as an argument (arguments. Here’s what it should look like in the end:

{
  "version": "2.0",
  "logging": {
    "applicationInsights": {
      "samplingSettings": {
        "isEnabled": true,
        "excludedTypes": "Request"
      }
    }
  },
  "extensionBundle": {
    "id": "Microsoft.Azure.Functions.ExtensionBundle",
    "version": "[3.*, 4.0.0)"
  },
  "customHandler": {
    "enableForwardingHttpRequest": true,
    "description": {
    "defaultExecutablePath": "app",
    "workingDirectory": "",
	"arguments": [
       "-Dquarkus.http.port=${FUNCTIONS_CUSTOMHANDLER_PORT:8080}"
      ]
    }
  }
}

Test Locally

To test locally, use the func start command. Make sure you are in the my-app directory you created earlier!

$ func start


# Azure Functions Core Tools
# Core Tools Version:       4.0.5198 Commit hash: N/A  (64-bit)
#Function Runtime Version: 4.21.1.20667
#
#
# Functions:
#
#	my-func: [GET,POST] http://localhost:7071/api/my-func/{*path}
#
# For detailed output, run func with --verbose flag.
# [2023-08-10T19:08:59.190Z] __  ____  __  _____   ___  __ ____  ______ 
# [2023-08-10T19:08:59.191Z]  --/ __ \/ / / / _ | / _ \/ //_/ / / / __/ 
# [2023-08-10T19:08:59.191Z]  -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \   
# [2023-08-10T19:08:59.191Z] --\___\_\____/_/ |_/_/|_/_/|_|\____/___/   
# [2023-08-10T19:08:59.191Z] 2023-08-10 15:08:59,187 INFO  [io.quarkus] (main) code-with-quarkus-1.0.0-SNAPSHOT native (powered by Quarkus 999-SNAPSHOT) started in 0.043s. Listening on: http://0.0.0.0:33917
# [2023-08-10T19:08:59.191Z] 2023-08-10 15:08:59,188 INFO  [io.quarkus] (main) Profile prod activated. 
# [2023-08-10T19:08:59.191Z] 2023-08-10 15:08:59,188 INFO  [io.quarkus] (main) Installed features: [cdi, resteasy, smallrye-context-propagation, vertx]
# [2023-08-10T19:08:59.209Z] Worker process started and initialized.
# [2023-08-10T19:09:04.023Z] Host lock lease acquired by instance ID '000000000000000000000000747C0C10'.

To test go to http://localhost:7071/api/my-func/hello. The hello part of the path can be replaced with your REST API.

Deployment

To deploy you’ll need to create an Azure Group and Function Application.

# login
$ az login

# list subscriptions
$ az account list -o table

# set active subscription.  You do not have to do this if you only have one subscription
$ az account set --subscription <SUBSCRIPTION_ID>

# create an Azure Resource Group 
az group create -n rg-quarkus-functions \
  -l eastus

# create an Azure Storage Account (required for Azure Functions App)
az storage account create -n sargquarkusfunctions2023 \
  -g rg-quarkus-functions \
  -l eastus

# create an Azure Functions App
az functionapp create -n my-app-quarkus \
  -g rg-quarkus-functions \
  --consumption-plan-location eastus\
  --os-type Linux \
  --runtime custom \
  --functions-version 4 \
  --storage-account sargquarkusfunctions2023 

Make sure that the os-type is Linux!!! Note that your Function Application name must be unique and may collide with others.

Now you can deploy your application. Again, make sure you are in the my-app directory!

$ func azure functionapp publish my-app-quarkus

# Getting site publishing info...
# Uploading package...
# Uploading 15.32 MB 
# Upload completed successfully.
# Deployment completed successfully.
# Syncing triggers...
# Functions in my-app-quarkus:
#    my-func - [httpTrigger]
#         Invoke url: https://my-app-quarkus.azurewebsites.net/api/{*path}

A successful deployment will tell you how to access the app. Just remember the root context will always be /api/{functionName}.

REST-*.org launched

20 Comments

I’m pleased to announce the launch of REST-*.org (http://rest-star.org).  REST-*.org is dedicated to bringing the architecture of the Web to traditional middleware services.  While REST has gained huge momentum in the SOA community, there hasn’t been a lot of standardization of traditional middleware services. The REST-* community aims to introduce new REST-based standards for these traditional services where none exist and provide well-defined guidelines where protocols do exist.

Architecturally we aim to be lightweight and pragmatic with a heavy emphasis on avoiding envelope formats and leveraging the full capabilities of the HTTP protocol.  From a community point of view, we have a totally open process and open source IP.  Anybody can contribute and join the fun.

We’re starting off with two specifications: Transactions and Messaging.  We’ll be expanding it to other areas as the community permits.

Mark Little defined an initial design of RESTful transactions 8 years ago at HP of both 2pc, ACID model as well as a forward-compensation engine.  Michael Musgrove recently used RESTEasy to implement this for the JBoss Transaction Manager.  Some consider REST + Transactions an oxymoron but there are some systems that may require a two-phase-commit protocol.  Compensating Transactions though fit quite nicely in the RESTful paradigm as in the end a TM is really just a coordination service.

For messaging we’re building off of the ideas of both JBoss and others in the open source community.  While Atom has become a popular mechanism for pub/sub in the REST community, it is dependent on an envelope format and was really meant for publishing blogs.  We feel that something simpler is needed to satisify the variety of data formats exchanged between intra and inter business machine-based applications.

Both of these specifications are very raw right now.  We feel that launching with something that is incomplete will allow us to build a set of contributors that have a vested interest in the specifications.

Why are we doing this?

The thing is SOAP has failed as an interoperability protocol. It is too complex to implement both from a vendor point of view and from an application point of view.  Yet, the REST world still lacks many of the core services that enterprise developers have grown accustomed to (rightly or wrongly).  REST-*.org will attempt to fill in the gaps.

Is this vendor driven?

Yes, Red Hat is a vendor.  Yes, we want big vendors to get involved as this will help out with adoption.  But all REST-* specifications are and will be run using open source processes. This means anybody can participate.  Yes, the organization will have to have some structure.

How will we avoid the mistakes of SOAP and WS-*?

Blind idealism.  😉  Red Hat has a good track record for developing software that is community and user driven and then bringing these efforts to standards bodies (Hibernate and Seam are great examples of this).  In this case though, we will be the ones jumpstarting and founding the standards body itself.  We are battle tested in specifications efforts at the JCP and other bodies.  We’ve often been frustrated by the closed and inflexible attributes of these organizations.  We feel our open source roots and ideals make us an excellent candidate to drive and host standardization efforts.