Release of WildFly Operator 0.4.1

The WildFly Operator helps deploy and manage WildFly applications on Kubernetes and OpenShift.

With the recently released 0.4.1 version, the WildFly Operator is now able to provide seamless upgrades of your application on OpenShift.

Note
This feature is specific to OpenShift as it relies on resources such as ImageStreams that are not provided by vanilla Kubernetes.

The WildFly operator was using the applicationImage field to identify and deploy your application on the container platform. This field accepts different types:

  • the name of the image: quay.io/jmesnil/wildfly-demo-app:step-1

  • a tag: quay.io/jmesnil/wildfly-demo-app:step-1

  • a digest: quay.io/wildfly-quickstarts/wildfly-operator-quickstart@sha256:0af38bc38be93116b6a1d86a9c78bd14cd527121970899d719baf78e5dc7bfd2

In all these cases, the container platform would pull the image when the application is deployed and uses it for the whole application lifecycle. If you wanted to deploy a new image, you had to edit the applicationImage manually and specify the checksum of the new image to deploy.

Now, with the 0.4.1 release, the WildFly Operator also accepts an ImageStreamTag for its applicationImage field, for example wildfly-demo-app:latest. This imagestream tag must belong to an ImageStream in the same namespace than the application. With that simple setting, any changes to the imagestream tag will trigger a new deployment of the application without having to modify any resources.

Note

This article does not provide an extensive explanation on how Images are managed by OpenShift with ImageStreams and ImageStreamTags. Please refer to the OpenShift documentation for a complete description.

Create a Demo application

To illustrate this feature, I wrote a very simple MicroProfile application that return a JSONified version of "Hello, World!": https://github.com/jmesnil/wildfly-operator-demo-app

AppEndpoint.java
@Path("/")
@ApplicationScoped
public class AppEndpoint {

    @Inject
    @ConfigProperty(name = "greetings", defaultValue = "Hello")
    String greetings;

    @GET
    @Produces({ "application/json" })
    public String getText() {
        String text = "{\"text\":\"" + greetings + ", World!\"}";
        return text;
    }
}

As the application has access to the full MicroProfile APIs (including MicroProfile Config), it is possible to configure the greetings returned by the application by setting the greetings config property (which defaults to "Hello"). It can be configured using an environment variable named GREETINGS.

I have built a Docker image of this application and pushed it to quay.io/jmesnil/wildfly-demo-app:step-1.

You can run it directly from Docker:

$ docker run -p 8080:8080 -e GREETINGS=Bonjour quay.io/jmesnil/wildfly-demo-app:step-1
...
$ curl http://localhost:8080
{"text":"Bonjour, World!"}
Note

I created this Docker image from the step-1 tag of the Git repository. It uses the WildFly 21 S2I image and the microprofile-platform layer so that the image contains only what is needed to run a MicroProfile application. The command I used to create the application image is:

$ s2i build https://github.com/jmesnil/wildfly-operator-demo-app.git \
    --ref=step-1                                                     \
    quay.io/wildfly/wildfly-centos7:21.0                             \
    -e GALLEON_PROVISION_LAYERS=microprofile-platform                \
    wildfly-demo-app:step-1

To show how we can seamlessly upgrade to a new version of this application, I created a second image of the application corresponding to the step-2 tag where the text is now returned in uppercase:

public String getText() {
    String text = "{\"text\":\"" + (greetings + ", World!").toUpperCase() + "\"}";
    return text;
}

This second image is pushed to quay.io/jmesnil/wildfly-demo-app:step-2 and can also be run directly from Docker:

$ docker run -p 8080:8080 -e GREETINGS=Ahoj quay.io/jmesnil/wildfly-demo-app:step-2
...
$ curl http://localhost:8080
{"text":"AHOJ, WORLD!"}

Install the WildFly Operator on OpenShift

We will first install the WildFly Operator on OpenShift in a new project named wildfly-demo

First, we create the new project (and the corresponding namespace):

$ oc new-project wildfly-demo

Then we install a CatalogSource to get access to the WildFly Operator that is in the operatorhub.io catalog:

operatorhub-io-catalogsource.yaml
apiVersion: operators.coreos.com/v1alpha1
kind: CatalogSource
metadata:
  name: operatorhub-io
spec:
  displayName: Operators from operatorhub.io
  image: quay.io/operatorhubio/catalog:latest
  sourceType: grpc

We install it by running:

$ oc apply -f operatorhub-io-catalogsource.yaml
catalogsource.operators.coreos.com/operatorhub-io created

Then we create a subscription for the current version (0.4.1) of the WildFly Operator:

wildfly.yaml
apiVersion: operators.coreos.com/v1alpha1
kind: Subscription
metadata:
  name: wildfly
  labels:
    operators.coreos.com/wildfly.wildfly-demo: ''
spec:
  channel: alpha
  installPlanApproval: Automatic
  name: wildfly
  source: operatorhub-io
  sourceNamespace: wildfly-demo
  startingCSV: wildfly-operator.v0.4.1

Again, we install it on OpenShift by running:

$ oc apply -f wildfly.yaml
subscription.operators.coreos.com/wildfly created

We will then wait until the WildFly Operator is installed by monitoring its installation:

$ oc get csv -w
NAME                      DISPLAY   VERSION   REPLACES   PHASE
wildfly-operator.v0.4.1   WildFly   0.4.1
wildfly-operator.v0.4.1   WildFly   0.4.1                Pending
...
wildfly-operator.v0.4.1   WildFly   0.4.1                InstallReady
...
wildfly-operator.v0.4.1   WildFly   0.4.1                Installing
...
wildfly-operator.v0.4.1   WildFly   0.4.1                Succeeded
Note

For the purpose of this example, the WildFly Operator is only be installed in the current wildfly-demo namespace. All resources that are created must also be installed in that same namespace.

Create an ImageStream

Before we can deploy our application on OpenShift using the WildFly Operator, we will first create an ImageStream named wildfly-demo-app that will contain a stream of all our application images

$ oc import-image wildfly-demo-app:step-1          \
    --from quay.io/jmesnil/wildfly-demo-app:step-1 \
    --confirm

We have added the wildfly-demo-app:step-1 imagestream tag to this imagestream by importing the Docker image from quay.io/jmesnil/wildfly-demo-app:step-1.

Then we tag it with the latest tag that will be referenced from our deployments.

$ oc tag wildfly-demo-app:step-1  wildfly-demo-app:latest

At this point, we have an ImageStreamTag wildfly-demo-app:latest that we can use to deploy our application and upgrade it later seamlessly.

We have finally the image inside OpenShift and are ready to deploy our application.

Deploy the Application

To deploy the application, we create a WildFlyServer resource with the applicationImage set to wildfly-demo-app:latest. This will let OpenShift pull the image from the latest imagestream tag in the wildfly-demo-app imagestream.

wildfly-app.yaml
apiVersion: wildfly.org/v1alpha1
kind: WildFlyServer
metadata:
  name: wildfly-app
spec:
  applicationImage: 'wildfly-demo-app:latest'
  env:
    - name: GREETINGS
      value: Guten Tag
  replicas: 2
$ oc apply -f wildfly-app.yaml
wildflyserver.wildfly.org/wildfly-app created

The WildFly Operator will then configure and deploy the application on OpenShift. It will also automatically create a Route to access it outside of the cluster.

The application is thoroughly described by the oc describe command:

Name:         wildfly-app
Namespace:    wildfly-demo
Kind:         WildFlyServer
Metadata:
  ...
Spec:
  Application Image:  wildfly-demo-app:latest
  Env:
    Name:    GREETINGS
    Value:   Guten Tag
  Replicas:  2
Status:
  Hosts:
    wildfly-app-route-wildfly-demo.apps.jmesnil-80cs.eapqe.psi.redhat.com
  Pods:
    Name:            wildfly-app-0
    Pod IP:          10.128.2.199
    State:           ACTIVE
    Name:            wildfly-app-1
    Pod IP:          10.128.2.200
    State:           ACTIVE
  Replicas:          2
  Scalingdown Pods:  0
Events:              <none>

The only information we need is the hosts field that contains the public URL of our application. If we access it, we can get our text message:

$ curl http://$(oc get wfly/wildlfy-app -o jsonpath="{.status.hosts[0]}")
{"text":"Guten Tag, World!"}

Seamless Upgrade to a New Version of the Application Image

We now want to deploy the second version of our application without disrupting our services. We can take advantage of seamless upgrades to do it.

First, we will import the quay.io/jmesnil/wildfly-demo-app:step-2 image in the wildfly-demo-app imagestream with the step-2 tag:

$ oc import-image wildfly-demo-app:step-2          \
    --from quay.io/jmesnil/wildfly-demo-app:step-2 \
    --confirm

At this point, nothing has changed, the image is available in OpenShift but the WildFly Operator will not use it as it only references the wildfly-demo-app:latest imagestream tag. Let’s now change this latest tag to point to the wildfly-demo-app:step-2 tag.

$ oc tag wildfly-demo-app:step-2 wildfly-demo-app:latest
Tag wildfly-demo-app:latest set to wildfly-demo-app@sha256:a9970ab8cebad210d7248e090ea88d6af87e8f910c7a087a3aac03c951cd764e.

Once this is done, OpenShift will observe that the latest tag has changed (it corresponds to a new image) and will notify the WildFly Operator to trigger a new deployment of the application.

If you continue to access the public route of the application, you will see that it will eventually return the upper case version of the text:

$ curl http://$(oc get wfly/wildlfy-demo-app -o jsonpath="{.status.hosts[0]}")
{"text":"GUTEN TAG, WORLD!"}

It can take some time as OpenShift will terminate and redeploy all the Pods that runs the application.

Image Build Pipeline

This short demo illustrates that it is now possible to seamlessly upgrade an application maintained by the WildFly Operator by using an imagestream tag to refer to the application image.

To illustrate this, we did a step-by-step demo to understand how and when the upgrade is triggered. However, in normal use, most of these steps are automated and the upgrade becomes really seamless (and do not require user intervention).

There is a lot more that can be achieved by taking full advantage of the OpenShift ecosystem to build and deploy images.

I have built the application image using S2I outside of OpenShift but it is possible to use BuildConfig resources to build the image inside OpenShift and automatically tag them with the latest imagestream tag. Using BuildConfig to build the images has two main benefits:

  • You can specify hooks to trigger building new images when the code from a remote Git repository is updated.

  • You can also trigger building new images when WildFly S2I images are upgraded. In that case, seamless upgrades apply not only to the application image but also to the WildFly S2I images that are used to build the application image.

In a more realistic example, we could have a staging cluster with a BuildConfig that would trigger new application images when the code is updated (or when a new Git tag is pushed to a remote repository) or when new WildFly S2I images are released. This would automatically trigger a test pipeline to test and verify the new application image.

Once this new application image has been validated (automatically or manually), we can then push this new application image in our production cluster and tag it with latest to trigger an upgrade of the application in production.

Summary

When it is running on OpenShift, the WildFly Operator can leverage its ecosystem around Images to provide seamless upgrades of applications to trigger new deployments when anything in the build pipeline (application code or WildFly images) changes. This simplifies application maintenance and reduces security risks by automating the upgrades and making sure that the application is always built on top of the latest application code and WildFly images.