Annotation based Kubernetes and Openshift Manifests for Java Applications with ap4k
February 28th, 2019 by Micha KopsWriting our manifest files for Kubernetes / Openshift often forces us to edit xml, json and yml files by hand.
A new library, ap4k allows to specify metadata for these manifest files directly in our Java code using annotations.
In the following short example I am going to demonstrate how to generate manifest files using Maven and ap4k.
Contents
Dependencies
Using Maven we just need to add the following one dependency to our project’s pom.xml
<dependency> <groupId>io.ap4k</groupId> <artifactId>kubernetes-annotations</artifactId> <version>0.2.2</version> </dependency>
Sample Application
This is our first and simplest version as we’re just adding one annotation, @KubernetesApplication to our Java class:
package com.hascode.tutorial; import io.ap4k.kubernetes.annotation.KubernetesApplication; import java.time.Instant; @KubernetesApplication public class App { public static void main(String[] args) { System.out.printf("hello world, it's %s%n", Instant.now()); } }
Adding additional Metadata
This is not much yet .. we may use additional declarations to configure ports, volumes, JVM options and much more.
For a complete list of possible configurations, please visit the project’s documentation on GitHub.
The following example adds a port and mounts/volumes:
package com.hascode.tutorial; import io.ap4k.kubernetes.annotation.KubernetesApplication; import io.ap4k.kubernetes.annotation.Mount; import io.ap4k.kubernetes.annotation.Port; import java.time.Instant; @KubernetesApplication(ports = @Port(name = "http", containerPort = 8080), mounts = @Mount(name = "mysql-volume", path = "/var/lib/mysql") ) public class App { public static void main(String[] args) { System.out.printf("hello world, it's %s%n", Instant.now()); } }
Generating the Manifest
We may use the following command to generate the manifest files:
mvn clean install
Our project directory might look similar to this one now:
tree target
target
├── ap4k-tutorial-1.0.0.jar
├── classes
│ ├── com
│ │ └── hascode
│ │ └── tutorial
│ │ └── App.class
│ └── META-INF
│ └── ap4k
│ ├── kubernetes.json
│ └── kubernetes.yml
├── generated-sources
│ └── annotations
├── maven-archiver
│ └── pom.properties
└── maven-status
└── maven-compiler-plugin
├── compile
│ └── default-compile
│ ├── createdFiles.lst
│ └── inputFiles.lst
└── testCompile
└── default-testCompile
└── inputFiles.lst
So let’s inspect the generated manifest files
Example 1
JSON Manifest
This is the generated kubernetes.json for our first simple example:
{ "apiVersion" : "v1", "kind" : "List", "items" : [ { "apiVersion" : "apps/v1", "kind" : "Deployment", "metadata" : { "labels" : { "app" : "ap4k-tutorial", "version" : "1.0.0", "group" : "soma" }, "name" : "ap4k-tutorial" }, "spec" : { "replicas" : 1, "selector" : { "matchLabels" : { "app" : "ap4k-tutorial", "version" : "1.0.0", "group" : "soma" } }, "template" : { "metadata" : { "labels" : { "app" : "ap4k-tutorial", "version" : "1.0.0", "group" : "soma" } }, "spec" : { "containers" : [ { "env" : [ { "name" : "KUBERNETES_NAMESPACE", "valueFrom" : { "fieldRef" : { "fieldPath" : "metadata.namespace" } } } ], "image" : "soma/ap4k-tutorial:1.0.0", "imagePullPolicy" : "IfNotPresent", "name" : "ap4k-tutorial" } ] } } } } ] }
YAML Manifest
This is our generated kubernetes.yml:
--- apiVersion: "v1" kind: "List" items: - apiVersion: "apps/v1" kind: "Deployment" metadata: labels: app: "ap4k-tutorial" version: "1.0.0" group: "soma" name: "ap4k-tutorial" spec: replicas: 1 selector: matchLabels: app: "ap4k-tutorial" version: "1.0.0" group: "soma" template: metadata: labels: app: "ap4k-tutorial" version: "1.0.0" group: "soma" spec: containers: - env: - name: "KUBERNETES_NAMESPACE" valueFrom: fieldRef: fieldPath: "metadata.namespace" image: "soma/ap4k-tutorial:1.0.0" imagePullPolicy: "IfNotPresent" name: "ap4k-tutorial"
Example 2
JSON Manifest
This is our generated kubernetes.json
{ "apiVersion" : "v1", "kind" : "List", "items" : [ { "apiVersion" : "v1", "kind" : "Service", "metadata" : { "labels" : { "app" : "ap4k-tutorial", "version" : "1.0.0", "group" : "soma" }, "name" : "ap4k-tutorial" }, "spec" : { "ports" : [ { "name" : "http", "port" : 8080, "targetPort" : 8080 } ], "selector" : { "app" : "ap4k-tutorial", "version" : "1.0.0", "group" : "soma" }, "type" : "ClusterIP" } }, { "apiVersion" : "apps/v1", "kind" : "Deployment", "metadata" : { "labels" : { "app" : "ap4k-tutorial", "version" : "1.0.0", "group" : "soma" }, "name" : "ap4k-tutorial" }, "spec" : { "replicas" : 1, "selector" : { "matchLabels" : { "app" : "ap4k-tutorial", "version" : "1.0.0", "group" : "soma" } }, "template" : { "metadata" : { "labels" : { "app" : "ap4k-tutorial", "version" : "1.0.0", "group" : "soma" } }, "spec" : { "containers" : [ { "env" : [ { "name" : "KUBERNETES_NAMESPACE", "valueFrom" : { "fieldRef" : { "fieldPath" : "metadata.namespace" } } } ], "image" : "soma/ap4k-tutorial:1.0.0", "imagePullPolicy" : "IfNotPresent", "name" : "ap4k-tutorial", "ports" : [ { "containerPort" : 8080, "name" : "http", "protocol" : "TCP" } ], "volumeMounts" : [ { "mountPath" : "/var/lib/mysql", "name" : "mysql-volume", "readOnly" : false, "subPath" : "" } ] } ] } } } } ] }
YAMLManifest
This is our generated kubernetes.yml
--- apiVersion: "v1" kind: "List" items: - apiVersion: "v1" kind: "Service" metadata: labels: app: "ap4k-tutorial" version: "1.0.0" group: "soma" name: "ap4k-tutorial" spec: ports: - name: "http" port: 8080 targetPort: 8080 selector: app: "ap4k-tutorial" version: "1.0.0" group: "soma" type: "ClusterIP" - apiVersion: "apps/v1" kind: "Deployment" metadata: labels: app: "ap4k-tutorial" version: "1.0.0" group: "soma" name: "ap4k-tutorial" spec: replicas: 1 selector: matchLabels: app: "ap4k-tutorial" version: "1.0.0" group: "soma" template: metadata: labels: app: "ap4k-tutorial" version: "1.0.0" group: "soma" spec: containers: - env: - name: "KUBERNETES_NAMESPACE" valueFrom: fieldRef: fieldPath: "metadata.namespace" image: "soma/ap4k-tutorial:1.0.0" imagePullPolicy: "IfNotPresent" name: "ap4k-tutorial" ports: - containerPort: 8080 name: "http" protocol: "TCP" volumeMounts: - mountPath: "/var/lib/mysql" name: "mysql-volume" readOnly: false subPath: ""
Tutorial Sources
Please feel free to download the tutorial sources from my Bitbucket repository, fork it there or clone it using Git:
git clone https://bitbucket.org/hascode/ap4k-tutorial.git