Annotation based Kubernetes and Openshift Manifests for Java Applications with ap4k

February 28th, 2019 by

Writing 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.

ap4k Tutorial

ap4k Tutorial

 

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

Resources

Tags: , , , , , , , , , , , , , ,

Leave a Reply

Please note, that no personal information like your IP address is stored and you're not required to enter you real name.

Comments must be approved before they are published, so please be patient after having posted a comment - it might take a short while.

Please leave these two fields as-is:
Search
Categories