Quick guide to deploying Java apps on OpenShift

Quick guide to deploying Java apps on OpenShift

In this article, I’m going to show you how to deploy your Java applications on OpenShift (Minishift), and connect them with other services running there. We will also learn how to use some other interesting deployment features provided by OpenShift. Openshift is built on top of Docker containers and the Kubernetes container cluster orchestrator. Currently, it is the most popular enterprise platform based on those two technologies, so it is definitely worth examining it in more detail.

1. Running Minishift

We use Minishift to run a single-node OpenShift cluster on the local machine. The only pre requirement before installing MiniShift is the necessity to have a virtualization tool installed. I use Oracle VirtualBox as a hypervisor, so I should set the --vm-driver parameter to virtualbox in my running command.

$  minishift start --vm-driver=virtualbox --memory=3G

2. Running Docker

It turns out that you can easily reuse the Docker daemon managed by Minishift, in order to be able to run Docker commands directly from your command line, without any additional installations. To achieve this just run the following command after starting Minishift.

@FOR /f "tokens=* delims=^L" %i IN ('minishift docker-env') DO @call %i

3. Running OpenShift CLI

The last tool that is required before starting any practical exercise with Minishift is CLI. CLI is available under command oc. To enable it on your command-line run the following commands.

$ minishift oc-env
$ SET PATH=C:\Users\minkowp\.minishift\cache\oc\v3.9.0\windows;%PATH%
$ REM @FOR /f "tokens=*" %i IN ('minishift oc-env') DO @call %i

Alternatively you can use the OpenShift web console which is available under port 8443. On my Windows machine it is by default launched under address 192.168.99.100.

4. Building Docker images of Java applications

I prepared the two sample applications that are used for the purposes of presenting the OpenShift deployment process. These are simple Java, Vert.x applications that provide HTTP API and store data in MongoDB. However, technology is not very important now. We need to build Docker images with these applications. The source code is available on GitHub (https://github.com/piomin/sample-vertx-kubernetes.git) in branch openshift (https://github.com/piomin/sample-vertx-kubernetes/tree/openshift). Here’s a sample Dockerfile for account-vertx-service.

FROM openjdk:8-jre-alpine
ENV VERTICLE_FILE account-vertx-service-1.0-SNAPSHOT.jar
ENV VERTICLE_HOME /usr/verticles
ENV DATABASE_USER mongo
ENV DATABASE_PASSWORD mongo
ENV DATABASE_NAME db
EXPOSE 8095
COPY target/$VERTICLE_FILE $VERTICLE_HOME/
WORKDIR $VERTICLE_HOME
ENTRYPOINT ["sh", "-c"]
CMD ["exec java -jar $VERTICLE_FILE"]

Go to account-vertx-service directory and run the following command to build an image from a Dockerfile visible above.

$ docker build -t piomin/account-vertx-service .

The same step should be performed for customer-vertx-service. After it you have two images built, both in the same version latest, which now can be deployed and run on Minishift.

5. OpenShift deployment descriptor

When working with OpenShift, the first step of the application’s deployment is to create a YAML configuration file. This file contains basic information about deployment like containers used for running applications (1), scaling (2), triggers that drive automated deployments in response to events (3) or a strategy of deploying your pods on the platform (4).

kind: "DeploymentConfig"
apiVersion: "v1"
metadata:
  name: "account-service"
spec:
  template:
    metadata:
      labels:
        name: "account-service"
    spec:
      containers: # (1)
      - name: "account-vertx-service"
        image: "piomin/account-vertx-service:latest"
        ports:
        - containerPort: 8095
          protocol: "TCP"
      replicas: 1 # (2)
      triggers: # (3)
      - type: "ConfigChange"
      - type: "ImageChange"
      imageChangeParams:
        automatic: true
        containerNames:
        - "account-vertx-service"
      from:
        kind: "ImageStreamTag"
        name: "account-vertx-service:latest"
      strategy: # (4)
        type: "Rolling"
        paused: false
      revisionHistoryLimit: 2

Deployment configurations can be managed with the oc command like any other resource. You can create a new configuration or update the existing one by using oc apply command.

$ oc apply -f account-deployment.yaml

You can be surprised a little, but this command does not trigger any build and does not start the pods. In fact, you have only created a resource of type deploymentConfig, which may describe the deployment process. You can start this process using some other oc commands, but first let’s take a closer look on the resources required by our application.

6. Injecting environment variables

As I have mentioned before, our sample applications use external datasource. They need to open the connection to the existing MongoDB instance in order to store their data passed using HTTP endpoints exposed by the application. Here’s MongoVerticle class, which is responsible for establishing client connection with MongoDB. It uses environment variables for setting security credentials and database names.

public class MongoVerticle extends AbstractVerticle {

   @Override
   public void start() throws Exception {
      ConfigStoreOptions envStore = new ConfigStoreOptions()
         .setType("env")
         .setConfig(new JsonObject().put("keys", new JsonArray()
            .add("DATABASE_USER")
            .add("DATABASE_PASSWORD")
            .add("DATABASE_NAME")));
      ConfigRetrieverOptions options = new ConfigRetrieverOptions().addStore(envStore);
      ConfigRetriever retriever = ConfigRetriever.create(vertx, options);
      retriever.getConfig(r -> {
         String user = r.result().getString("DATABASE_USER");
         String password = r.result().getString("DATABASE_PASSWORD");
         String db = r.result().getString("DATABASE_NAME");
         JsonObject config = new JsonObject();
         config.put("connection_string", "mongodb://" + user + ":" + password + "@mongodb/" + db);
         final MongoClient client = MongoClient.createShared(vertx, config);
         final AccountRepository service = new AccountRepositoryImpl(client);
         ProxyHelper.registerService(AccountRepository.class, vertx, service, "account-service");
      });
   }

}

MongoDB is available in OpenShift’s catalog of predefined Docker images. You can easily deploy it on your Minishift instance just by clicking “MongoDB” icon in “Catalog” tab. Username and password will be automatically generated if you do not provide them during deployment setup. All the properties are available as deployment’s environment variables and are stored as secrets/mongodb, where mongodb is the name of the deployment.

openshift-1

Environment variables can be easily injected into any other deployment using oc set command, and therefore they are injected into the pod after performing the deployment process. The following command inject all secrets assigned to mongodb deployment to the configuration of our sample application’s deployment.

$ oc set env --from=secrets/mongodb dc/account-service

7. Importing Docker images to OpenShift

A deployment configuration is ready. So, in theory we could have start deployment process. However, we have back for a moment to the deployment config defined in the Step 5. We defined there two triggers that cause a new replication controller to be created, which results in deploying a new version of pod. First of them is a configuration change trigger that fires whenever changes are detected in the pod template of the deployment configuration (ConfigChange). The second of them, image change trigger (ImageChange) fires when a new version of the Docker image is pushed to the repository. To be able to watch if an image in repository has been changed, we have to define and create image stream. Such an image stream does not contain any image data, but present a single virtual view of related images, something similar to an image repository. Inside deployment config file we referred to image stream account-vertx-service, so the same name should be provided inside image stream definition. In turn, when setting the spec.dockerImageRepository field we define the Docker pull specification for the image.

apiVersion: "v1"
kind: "ImageStream"
metadata:
name: "account-vertx-service"
spec:
dockerImageRepository: "piomin/account-vertx-service"

Finally, we can create resources on the OpenShift platform.


$ oc apply -f account-image.yaml

8. Running deployment

Once a deployment configuration has been prepared, and Docker images have been succesfully imported into a repository managed by an OpenShift instance, we may trigger the build using the following oc command.

$ oc rollout latest dc/account-service
$ oc rollout latest dc/customer-service

If everything goes fine the new pods should be started for the defined deployments. You can easily check it out using the OpenShift web console.

9. Updating image stream

We have already created two image streams related to the Docker repositories. Here’s the screen from OpenShift web console that shows the list of available image streams.

openshift-images

To be able to push a new version of an image to OpenShift internal Docker registry we should first perform docker login against this registry using user’s authentication token. To obtain the token from OpenShift use oc whoami command, and then pass it to your docker login command with -p parameter.

$ oc whoami -t
Sz9_TXJQ2nyl4fYogR6freb3b0DGlJ133DVZx7-vMFM
$ docker login -u developer -p Sz9_TXJQ2nyl4fYogR6freb3b0DGlJ133DVZx7-vMFM https://172.30.1.1:5000

Now, if you perform any change in your application and rebuild your Docker image with latest tag, you have to push that image to the image stream on OpenShift. The address of internal registry has been automatically generated by OpenShift, and you can check it out in the image stream’s details. For me, it is 172.30.1.1:5000.

$ docker tag piomin/account-vertx-service 172.30.1.1:5000/sample-deployment/account-vertx-service:latest
$ docker push 172.30.1.1:5000/sample-deployment/account-vertx-service

After pushing a new version of Docker image to the image stream, a rollout of the application is started automatically. Here’s the screen from OpenShift web console that shows the history of account-service application deployments.

openshift-2

Conclusion

I have shown you the further steps of deploying your application on the OpenShift platform. Based on a sample Java application that connects to a database, I illustrated how to inject credentials to that application’s pod entirely transparently for a developer. I also perform an update of the application’s Docker image, in order to show how to trigger a new version deployment on image change.

openshift-3

1 COMMENT

Leave a Reply