We as Java developers are often busy working on our applications by optimizing application memory, speed, etc. In recent years, encapsulating our applications into lightweight, independent units called containers has become quite a trend, and almost every enterprise is trying to shift its infrastructure onto container technologies like Docker and Kubernetes.
Kubernetes is an open source system for automating deployment, scaling, and management of containerized applications, but it has a steep learning curve, and an application developer with no background in DevOps can find this system a bit overwhelming. In this article, I will talk about tools that can help when deploying your Maven applications to Kubernetes/Red Hat OpenShift.
Background: Eclipse JKube
This project was not built from scratch. It’s just a refactored and rebranded version of the Fabric8 Maven plugin, which was a Maven plugin used in the Fabric8 ecosystem. Although the Fabric8 project was liked and appreciated by many people in the open source community, due to unfortunate reasons it could not become successful, and the idea of Fabric8 as an integrated development platform on top of Kubernetes died. Although the main project is archived, there are still active repositories used by the community, such as the Fabric8 Docker Maven plugin, the Fabric8 Kubernetes client, and of course the Fabric8 Maven plugin.
As maintainers of the Fabric8 Maven plugin, we started decoupling the Fabric8 ecosystem related pieces from the plugin to make a general-purpose Kubernetes/OpenShift plugin. We also felt there was a need for rebranding because most people were confused about whether this plugin had something to do with Fabric8. Hence, we decided to rebrand it, and fortunately, someone from the Eclipse foundation approached us to take in our project. Now, the project is being renamed to Eclipse JKube and can be found in the Eclipse Foundation repos on GitHub.
Eclipse JKube can be seen as a reincarnation of the Fabric8 Maven plugin. It contains the good parts of this plugin and offers a clean and smooth workflow with the tooling it provides. We refactored this plugin into three components:
The JKube Kit contains the core logic for building Docker images, generating Kubernetes/OpenShift manifests, and applying them onto Kubernetes/OpenShift clusters. Plugins consume this library for their operations. In the future, we also plan to add support for Gradle plugins.
Example
Now, let’s have a look at Eclipse JKube in action. For the demo, I will deploy a simple Spring Boot project onto Kubernetes using the Eclipse Kubernetes Maven plugin. Let's walk through this process:
- Add the Kubernetes Maven plugin as a dependency in your
pom.xml
file, as shown here:
<plugin> <groupId>org.eclipse.jkube</groupId> <artifactId>kubernetes-maven-plugin</artifactId> <version>${jkube.version}</version> </plugin>
- Build your Docker images. The Eclipse JKube Kubernetes Maven plugin offers a zero-config mode, in which it builds your Docker image with opinionated defaults. Right now I'm just customizing the name of the image created with the
jkube.generator.name
property to include my Docker Hub username. However, you can also customize it by providing an image configuration in the plugin configuration.Here is an example with my DockerHub username:
<jkube.generator.name>docker.io/rohankanojia/random-generator:${project.version}</jkube.generator.name>
All right, you're now set to containerize your application using Eclipse JKube. In order to build a Docker image, you just need to run the following:
eclipse-jkube-demo-project : $ mvn k8s:build [INFO] Scanning for projects... [INFO] [INFO] ----------------------< meetup:random-generator >----------------------- [INFO] Building random-generator 0.0.1 [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- kubernetes-maven-plugin:1.0.1:build (default-cli) @ random-generator --- [INFO] k8s: Running in Kubernetes mode [INFO] k8s: Building Docker image in Kubernetes mode [INFO] k8s: Running generator spring-boot [INFO] k8s: spring-boot: Using Docker image quay.io/jkube/jkube-java-binary-s2i:0.0.8 as base / builder [INFO] k8s: Pulling from jkube/jkube-java-binary-s2i 0fd3b5213a9b: Pulling fs layer aebb8c556853: Pulling fs layer 0fd3b5213a9b: Downloading [> ] 539.5kB/54.37MB 595bd04e186b: Downloading [> ] 2.158MB/133.7MB 0fd3b5213a9b: Downloading [> ] 1.08MB/54.37MB 595bd04e186b: Downloading [=> ] 5.349MB/133.7MB 0fd3b5213a9b: Downloading [=> ] 1.621MB/54.37MB 595bd04e186b: Downloading [===> ] 8.572MB/133.7MB 0fd3b5213a9b: Downloading [=> ] 2.162MB/54.37MB 0fd3b5213a9b: Downloading [==> ] 2.702MB/54.37MB 0fd3b5213a9b: Downloading [==> ] 3.243MB/54.37MB 0fd3b5213a9b: Downloading [===> ] 3.784MB/54.37MB 595bd04e186b: Downloading [=====> ] 14.43MB/133.7MB 0fd3b5213a9b: Downloading [===> ] 4.324MB/54.37MB 0fd3b5213a9b: Downloading [====> ] 5.406MB/54.37MB 0fd3b5213a9b: Downloading [=====> ] 6.487MB/54.37MB 0fd3b5213a9b: Downloading [======> ] 7.568MB/54.37MB 595bd04e186b: Downloading [=======> ] 19.79MB/133.7MB 0fd3b5213a9b: Downloading [=======> ] 8.65MB/54.37MB 0fd3b5213a9b: Downloading [========> ] 9.731MB/54.37MB 0fd3b5213a9b: Downloading [=========> ] 10.27MB/54.37MB 0fd3b5213a9b: Downloading [=========> ] 10.81MB/54.37MB 0fd3b5213a9b: Pull complete aebb8c556853: Pull complete 595bd04e186b: Pull complete [INFO] k8s: Digest: sha256:69cacf4092e7ac1765395798d42c16efe2bf88e71472eaffbd2764d0ae95a8fe [INFO] k8s: Status: Downloaded newer image for quay.io/jkube/jkube-java-binary-s2i:0.0.8 [INFO] k8s: Pulled quay.io/jkube/jkube-java-binary-s2i:0.0.8 in 27 seconds [INFO] k8s: [rohankanojia/random-generator:0.0.1] "spring-boot": Created docker-build.tar in 141 milliseconds [INFO] k8s: [rohankanojia/random-generator:0.0.1] "spring-boot": Built image sha256:a3c06 [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 33.047 s [INFO] Finished at: 2020-10-13T23:52:24+05:30 [INFO] ------------------------------------------------------------------------ eclipse-jkube-demo-project : $ docker images | grep random-generator rohankanojia/random-generator 0.0.1 a3c06251eed7 9 seconds ago 528 MB
- Generate your Kubernetes resource manifests. Eclipse JKube plugins have a powerful and configurable resource generation mechanism allowing them to generate Kubernetes resources in zero-config mode. This feature can also be configured using XML configuration or by placing customized resource fragments in the
src/main/jkube
directory. The results are merged with the final generated resource fragments.In order to generate resources, run the following:
eclipse-jkube-demo-project : $ mvn k8s:resource [INFO] Scanning for projects... [INFO] [INFO] ----------------------< meetup:random-generator >----------------------- [INFO] Building random-generator 0.0.1 [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- kubernetes-maven-plugin:1.0.1:resource (default-cli) @ random-generator --- [INFO] k8s: Running generator spring-boot [INFO] k8s: spring-boot: Using Docker image quay.io/jkube/jkube-java-binary-s2i:0.0.8 as base / builder [INFO] k8s: Using resource templates from /home/rohaan/work/repos/eclipse-jkube-demo-project/src/main/jkube [INFO] k8s: jkube-controller: Adding a default Deployment [INFO] k8s: jkube-service: Adding a default service 'random-generator' with ports [8080] [INFO] k8s: jkube-healthcheck-spring-boot: Adding readiness probe on port 8080, path='/actuator/health', scheme='HTTP', with initial delay 10 seconds [INFO] k8s: jkube-healthcheck-spring-boot: Adding liveness probe on port 8080, path='/actuator/health', scheme='HTTP', with initial delay 180 seconds [INFO] k8s: jkube-revision-history: Adding revision history limit to 2 [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 2.940 s [INFO] Finished at: 2020-10-13T23:53:39+05:30 [INFO] ------------------------------------------------------------------------ eclipse-jkube-demo-project : $ ls target/classes/META-INF/jkube/ kubernetes kubernetes.yml eclipse-jkube-demo-project : $ ls target/classes/META-INF/jkube/kubernetes random-generator-deployment.yml random-generator-service.yml eclipse-jkube-demo-project : $
- Apply generated Kubernetes resources onto the Kubernetes cluster. In order to apply resources onto this cluster, run one of the following commands (results shown in the listing below):
$ mvn k8s:apply
or:
$ mvn k8s:deploy
eclipse-jkube-demo-project : $ mvn k8s:apply [INFO] Scanning for projects... [INFO] [INFO] ----------------------< meetup:random-generator >----------------------- [INFO] Building random-generator 0.0.1 [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- kubernetes-maven-plugin:1.0.1:apply (default-cli) @ random-generator --- [INFO] k8s: Using Kubernetes at https://192.168.39.129:8443/ in namespace default with manifest /home/rohaan/work/repos/eclipse-jkube-demo-project/target/classes/META-INF/jkube/kubernetes.yml [INFO] k8s: Using namespace: default [INFO] k8s: Creating a Service from kubernetes.yml namespace default name random-generator [INFO] k8s: Created Service: target/jkube/applyJson/default/service-random-generator.json [INFO] k8s: Creating a Deployment from kubernetes.yml namespace default name random-generator [INFO] k8s: Created Deployment: target/jkube/applyJson/default/deployment-random-generator.json [INFO] k8s: HINT: Use the command `kubectl get pods -w` to watch your pods start up [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 8.282 s [INFO] Finished at: 2020-10-13T23:55:10+05:30 [INFO] ------------------------------------------------------------------------ eclipse-jkube-demo-project : $ kubectl get pods NAME READY STATUS RESTARTS AGE random-generator-8697c5d7d6-k9s7d 0/1 Running 0 9s eclipse-jkube-demo-project : $ kubectl get all NAME READY STATUS RESTARTS AGE pod/random-generator-8697c5d7d6-k9s7d 0/1 Running 0 12s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4h3m service/random-generator NodePort 10.106.53.34 <none> 8080:32404/TCP 12s NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/random-generator 0/1 1 0 12s NAME DESIRED CURRENT READY AGE replicaset.apps/random-generator-8697c5d7d6 1 1 0 12s eclipse-jkube-demo-project : $ kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4h4m random-generator NodePort 10.106.53.34 <none> 8080:32404/TCP 36s eclipse-jkube-demo-project : $ curl `minikube ip`:32404/random | jq . % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 45 0 45 0 0 2500 0 --:--:-- --:--:-- --:--:-- 2500 { "id": "fb4f1cdb-cdd2-4604-ada3-35c27ebd733b" }
- Undeploy your Maven application from Kubernetes. We have a cleanup goal, too, which just deletes all resources created during the deploy phase. To use this feature, run the following (results shown in the listing below):
$ mvn k8s:undeploy
eclipse-jkube-demo-project : $ kubectl get all NAME READY STATUS RESTARTS AGE pod/random-generator-8697c5d7d6-mxv7g 1/1 Running 0 45s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4h7m service/random-generator NodePort 10.107.3.32 <none> 8080:32287/TCP 45s NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/random-generator 1/1 1 1 45s NAME DESIRED CURRENT READY AGE replicaset.apps/random-generator-8697c5d7d6 1 1 1 45s eclipse-jkube-demo-project : $ mvn k8s:undeploy [INFO] Scanning for projects... [INFO] [INFO] ----------------------< meetup:random-generator >----------------------- [INFO] Building random-generator 0.0.1 [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- kubernetes-maven-plugin:1.0.1:undeploy (default-cli) @ random-generator --- [INFO] k8s: Deleting resource Deployment default/random-generator [INFO] k8s: Deleting resource Service default/random-generator [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 2.662 s [INFO] Finished at: 2020-10-13T23:58:39+05:30 [INFO] ------------------------------------------------------------------------ eclipse-jkube-demo-project : $ kubectl get all NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4h7m eclipse-jkube-demo-project : $
- Debug your Java application inside Kubernetes. Apart from these goals, we also have a goal for remote debugging. Suppose that you see a bug inside your application that's running inside Kubernetes and you want to debug its behavior. You can simply run our debug goal, which does port forwarding for debugging:
eclipse-jkube-demo-project : $ mvn k8s:debug [INFO] Scanning for projects... [INFO] [INFO] ----------------------< meetup:random-generator >----------------------- [INFO] Building random-generator 0.0.1 [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- kubernetes-maven-plugin:1.0.1:debug (default-cli) @ random-generator --- [INFO] k8s: Using Kubernetes at https://192.168.39.129:8443/ in namespace default with manifest /home/rohaan/work/repos/eclipse-jkube-demo-project/target/classes/META-INF/jkube/kubernetes.yml [INFO] k8s: Using namespace: default [INFO] k8s: Updating Service from kubernetes.yml [INFO] k8s: Updated Service: target/jkube/applyJson/default/service-random-generator-5.json [INFO] k8s: Enabling debug on Deployment random-generator [INFO] k8s: Waiting for debug pod with selector LabelSelector(matchExpressions=[], matchLabels={app=random-generator, provider=jkube, group=meetup}, additionalProperties={}) and environment variables {JAVA_DEBUG_SUSPEND=false, JAVA_ENABLE_DEBUG=true} [INFO] k8s: Port forwarding to port 5005 on pod random-generator-69f9947655-ws4pk using command /home/rohaan/.local/bin/kubectl
- Configure your IDE in order to connect to this open port for debugging, as shown in Figure 1:
- Set a breakpoint in the application code and hit the application endpoint. We can see the breakpoint being hit in IDE as shown in Figure 2:
Deploying to Red Hat OpenShift:
You can deploy the same application to Red Hat OpenShift using Eclipse JKube's OpenShift Maven Plugin. Here is how you would do it:
- Add OpenShift Maven Plugin in
<plugins>
section of yourpom.xml
:
<plugin> <groupId>org.eclipse.jkube</groupId> <artifactId>openshift-maven-plugin</artifactId> <version>${jkube.version}</version> </plugin>
- Log into your OpenShift Cluster:
$ oc login https://api.example.openshift.com --token=some-token Logged into "https://api.example.openshift.com:443" as "rohanKanojia" using the token provided. You have one project on this server: "rokumar" Using project "rokumar".
-
- Build your application and run the usual goals as Kubernetes Maven Plugin but with the
oc
prefix. The image is built inside Build Pod and pushed to OpenShift internal registry. Since we won't need to push the image to some other registry, we can delete thejkube.generator.name
property that we configured for pushing our image to docker hub:
- Build your application and run the usual goals as Kubernetes Maven Plugin but with the
- <jkube.generator.name>docker.io/rohankanojia/random-generator:${project.version}</jkube.generator.name>
Once the property is deleted, you can proceed with the usual flow for deploying to Red Hat OpenShift:
eclipse-jkube-demo-project : $ mvn oc:build oc:resource oc:apply [INFO] Scanning for projects... [INFO] [INFO] ----------------------< meetup:random-generator >----------------------- [INFO] Building random-generator 0.0.1 [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- openshift-maven-plugin:1.0.1:build (default-cli) @ random-generator --- [INFO] oc: Using OpenShift build with strategy S2I [INFO] oc: Running in OpenShift mode [INFO] oc: Running generator spring-boot [INFO] oc: spring-boot: Using Docker image quay.io/jkube/jkube-java-binary-s2i:0.0.8 as base / builder [INFO] oc: [random-generator:0.0.1] "spring-boot": Created docker source tar /home/rohaan/work/repos/eclipse-jkube-demo-project/target/docker/random-generator/0.0.1/tmp/docker-build.tar [INFO] oc: Adding to Secret pullsecret-jkube [INFO] oc: Using Secret pullsecret-jkube [INFO] oc: Creating BuildServiceConfig random-generator-s2i for Source build [INFO] oc: Creating ImageStream random-generator [INFO] oc: Starting Build random-generator-s2i [INFO] oc: Waiting for build random-generator-s2i-1 to complete... [INFO] oc: Using quay.io/jkube/jkube-java-binary-s2i:0.0.8 as the s2i builder image [INFO] oc: INFO S2I source build with plain binaries detected [INFO] oc: INFO S2I binary build from fabric8-maven-plugin detected [INFO] oc: INFO Copying binaries from /tmp/src/deployments to /deployments ... [INFO] oc: random-generator-0.0.1.jar [INFO] oc: INFO Copying deployments from deployments to /deployments... [INFO] oc: '/tmp/src/deployments/random-generator-0.0.1.jar' -> '/deployments/random-generator-0.0.1.jar' [INFO] oc: INFO Cleaning up source directory (/tmp/src) [INFO] oc: [INFO] oc: Pushing image 172.30.39.149:5000/rokumar/random-generator:0.0.1 ... [INFO] oc: Pushed 3/4 layers, 95% complete [INFO] oc: Pushed 4/4 layers, 100% complete [INFO] oc: Push successful [INFO] oc: Build random-generator-s2i-1 in status Complete [INFO] oc: Found tag on ImageStream random-generator tag: sha256:4c71b89b5345db80ab4802e127085ac45bd1410e6e91439fad956fe12200b93c [INFO] oc: ImageStream random-generator written to /home/rohaan/work/repos/eclipse-jkube-demo-project/target/random-generator-is.yml [INFO] [INFO] --- openshift-maven-plugin:1.0.1:resource (default-cli) @ random-generator --- [INFO] oc: Using docker image name of namespace: rokumar [INFO] oc: Running generator spring-boot [INFO] oc: spring-boot: Using Docker image quay.io/jkube/jkube-java-binary-s2i:0.0.8 as base / builder [INFO] oc: Using resource templates from /home/rohaan/work/repos/eclipse-jkube-demo-project/src/main/jkube [INFO] oc: jkube-controller: Adding a default DeploymentConfig [INFO] oc: jkube-service: Adding a default service 'random-generator' with ports [8080] [INFO] oc: jkube-healthcheck-spring-boot: Adding readiness probe on port 8080, path='/actuator/health', scheme='HTTP', with initial delay 10 seconds [INFO] oc: jkube-healthcheck-spring-boot: Adding liveness probe on port 8080, path='/actuator/health', scheme='HTTP', with initial delay 180 seconds [INFO] oc: jkube-revision-history: Adding revision history limit to 2 [INFO] [INFO] --- openshift-maven-plugin:1.0.1:apply (default-cli) @ random-generator --- [INFO] oc: Using OpenShift at https://api.rh-idev.openshift.com:443/ in namespace rokumar with manifest /home/rohaan/work/repos/eclipse-jkube-demo-project/target/classes/META-INF/jkube/openshift.yml [INFO] oc: OpenShift platform detected [INFO] oc: Using project: rokumar [INFO] oc: Creating a Service from openshift.yml namespace rokumar name random-generator [INFO] oc: Created Service: target/jkube/applyJson/rokumar/service-random-generator.json [INFO] oc: Creating a DeploymentConfig from openshift.yml namespace rokumar name random-generator [INFO] oc: Created DeploymentConfig: target/jkube/applyJson/rokumar/deploymentconfig-random-generator.json [INFO] oc: Creating Route rokumar:random-generator host: null [INFO] oc: HINT: Use the command `oc get pods -w` to watch your pods start up [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 48.578 s [INFO] Finished at: 2020-10-14T00:57:20+05:30 [INFO] ------------------------------------------------------------------------ eclipse-jkube-demo-project : $ oc get pods NAME READY STATUS RESTARTS AGE random-generator-1-vtdzs 1/1 Running 0 4m random-generator-s2i-1-build 0/1 Completed 0 5m eclipse-jkube-demo-project : $ oc get routes NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD random-generator random-generator-rokumar.b6ff.rh-idev.openshiftapps.com random-generator 8080 None eclipse-jkube-demo-project : $ curl random-generator-rokumar.b6ff.rh-idev.openshiftapps.com/random | jq . % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 45 0 45 0 0 62 0 --:--:-- --:--:-- --:--:-- 62 { "id": "c656151e-c48a-4331-b265-859a16209b14" }
With this result, I wrap up this article. We do have more in our pipeline, so stay tuned for new updates.
Conclusion:
Whether you are already using Eclipse JKube or just curious about it, don’t be shy about joining our welcoming community:
-
- Provide feedback on GitHub.
- Craft code and push a pull request to the Eclipse JKube repository.
- Interact with the Eclipse JKube team on Gitter and the JKube mailing list .
- Ask questions and get answers on Stack Overflow .