Deploy your JBang script to Kubernetes/OpenShift

Tadayoshi Sato
4 min readJun 24, 2021

--

Normally, if you want to run a Java app on Kubernetes/OpenShift, you would have to generate a project with start.spring.io or code.quarkus.io and then deploy it with a Maven command. It’s still blazing fast, but you can be even faster with JBang.

Here is how you can make a JBang script into a Pod and run it directly on Kubernetes/OpenShift.

What is JBang?

Before jumping into the actual steps, JBang is a dev tool/execution environment for Java that makes it easy to run Java programs like a script.

It takes a little time to set up a Java project from Maven or IDE, adding dependencies to pom.xml, and so on, before you are ready to execute the first “Hello World!” in Java. However, with JBang, you can immediately jump into writing code and execute it just like a scripting language.

The primary purpose of JBang would be for small scripting and experimentation with Java code, but JBang also has support for IDE and native compilation using GraalVM, so it has a full potential of becoming a Next Java development and execution environment.

As a Java engineer, once you get used to JBang, it will become a tool you can’t live without.

How to use JBang

Here’s how to use it. First, generate a code template with jbang CLI:

$ jbang init hello.java
[jbang] File initialized. You can now run it with 'jbang hello.java' or edit it using 'jbang edit --open=[editor] hello.java' where [editor] is your editor or IDE, e.g. 'eclipse'

It will generate a Java class like the following:

///usr/bin/env jbang “$0” “$@” ; exit $?
// //DEPS <dependency1> <dependency2>
import static java.lang.System.*;public class hello {
public static void main(String… args) {
out.println(“Hello World”);
}
}

You can directly execute the code with jbang like this:

$ jbang hello.java 
[jbang] Building jar...
Hello World

Deploy JBang script to Kubernetes

Here is the subject of the article.

The steps to deploy JBang script to Kubernetes are as follows:

  • Make a ConfigMap of the script to be executed (script.java)
  • Create a Pod from the jbang-action container image with mounting the ConfigMap as a volume
  • Run the script on k8s

Write code

First, generate script.java and write some code that you want to run.

$ jbang init script.java
[jbang] File initialized. You can now run it with 'jbang script.java' or edit it using 'jbang edit --open=[editor] script.java' where [editor] is your editor or IDE, e.g. 'netbeans'

# Edit the code with the editor of your choice
$ code `jbang edit script.java`

Create ConfigMap

Then, create ConfigMap jbang-script from script.java.

$ kubectl create configmap jbang-script --from-file=script.java

You should get a ConfigMap like this:

$ kubectl get configmaps jbang-script -oyaml
apiVersion: v1
data:
script.java: |
///usr/bin/env jbang "$0" "$@" ; exit $?
// //DEPS <dependency1> <dependency2>
import static java.lang.System.*; public class script { public static void main(String... args) {
out.println("Java running on k8s...");
}
}
kind: ConfigMap
metadata:
creationTimestamp: "2021-06-23T07:26:10Z"
name: jbang-script
namespace: test
resourceVersion: "2122129"
selfLink: /api/v1/namespaces/test/configmaps/jbang-script
uid: 65f43430-3e3d-4cdc-87ce-aa7d6974cb33

Run the Pod

Create a Pod with the script script.java in ConfigMap mounted under /scripts and the path to the script /scripts/script.java set to the environment variable INPUT_SCRIPT, so that the jbangdev/jbang-action image can execute it.

You can copy and paste the following command to do the job:

$ kubectl run jbang-script \
--image=jbangdev/jbang-action \
--restart=Never \
--overrides='
{
"apiVersion": "v1",
"spec": {
"containers": [
{
"name": "jbang-script",
"image": "jbangdev/jbang-action",
"env": [
{
"name": "INPUT_SCRIPT",
"value": "/scripts/script.java"
}
],
"volumeMounts": [
{
"name": "scripts",
"mountPath": "/scripts"
}
]
}
],
"volumes": [
{
"name": "scripts",
"configMap": {
"name": "jbang-script"
}
}
]
}
}
'

Alternatively, you can create the following YAML and kubectl apply -f it.

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: jbang-script
labels:
app: jbang-script
spec:
containers:
- name: jbang-script
image: jbangdev/jbang-action
env:
- name: INPUT_SCRIPT
value: /scripts/script.java
volumeMounts:
- name: scripts
mountPath: /scripts
volumes:
- name: scripts
configMap:
name: jbang-script
restartPolicy: Never
EOF

Execution result

The output log would look like this:

$ kubectl logs -f jbang-script 
jbang /scripts/script.java
[jbang] Building jar...
Java running on k8s...

Run some more serious code

The power of JBang is that you can specify and download dependency JARs in Maven GAV format in the header part of a script. It even supports BOM, although there is a limitation that you can only specify one.

Let’s try running some integration code using an Apache Camel route with JBang.

///usr/bin/env jbang "$0" "$@" ; exit $?
//DEPS org.apache.camel:camel-bom:3.10.0@pom
//DEPS org.apache.camel:camel-core
//DEPS org.apache.camel:camel-main
//DEPS org.apache.camel:camel-stream
//DEPS org.slf4j:slf4j-nop:1.7.30
import org.apache.camel.*;
import org.apache.camel.builder.*;
import org.apache.camel.main.*;
import org.apache.camel.spi.*;
import static java.lang.System.*;public class script {
public static void main(String... args) throws Exception {
out.println("Running Camel route...");
Main main = new Main();
main.configure().addRoutesBuilder(new RouteBuilder() {
public void configure() throws Exception {
from("timer:hello?period=3000")
.setBody().constant("Hello Camel!")
.to("stream:out");
}
});
main.run();
}
}

After following the same steps as above, you’ll get the ouput log like this:

$ kubectl logs -f jbang-script 
jbang /scripts/script.java
[jbang] Resolving dependencies...
[jbang] Loading MavenCoordinate [org.apache.camel:camel-bom:pom:3.10.0]
[jbang] Resolving org.apache.camel:camel-core...Done
[jbang] Resolving org.apache.camel:camel-main...Done
[jbang] Resolving org.apache.camel:camel-stream...Done
[jbang] Resolving org.slf4j:slf4j-nop:1.7.30...Done
[jbang] Dependencies resolved
[jbang] Building jar...
Running Camel route...
Hello Camel!
Hello Camel!
Hello Camel!
...

JBang can even load multiple source files, so there are no limits in what you can run with it!

Wrap up

Once you’ve created the script file script.java, ConfigMap jbang-script, and the YAML definition of Pod jbang-script, you won’t need to create another set again just for the purpose of running some Java code on Kubernetes/OpenShift. You can just modify script.java and run another experiment.

The sample code used in this article is also found here:

I recommend this method if you want to verify something or run a brief task using Java on a Kubernetes/OpenShift cluster.

--

--

Tadayoshi Sato
Tadayoshi Sato

Written by Tadayoshi Sato

Software engineer at Red Hat, working on Red Hat build of Apache Camel and the open-source projects: Apache Camel and Hawtio.

No responses yet