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
Figure 1. ap4k Tutorial

Dependencies

Using Maven we just need to add the following one dependency to our project’s pom.xml

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:

App.java
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:

App.java
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:

kubernetes.json
{
  "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:

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

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

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 GitHub repository, fork it there or clone it using Git:

git clone https://github.com/hascode/ap4k-tutorial.git

Resources