Neuigkeiten von trion.
Immer gut informiert.

Kubernetes API mit Java anbinden

Kubernetes

Kubernetes bietet mit seiner erweiterbaren API eine sehr einfache Möglichkeit zusätzliches Verhalten mit Kubernetes zu integrieren. Neben der API-Spezifikation als OpenAPI (ehemals Swagger) stellt das Kubernetes-Projekt auch fertige Clients für verschiedene Programmiersprachen an. Eine besondere Rolle nimmt der Kubernetes Go-Client ein, da dieser auch innerhalb von Kubernetes selbst eingesetzt wird.

Auf Basis der OpenAPI-Spezifikation stehen generierte Clients für Java, Python, C# und JavaScript. Zu den offiziellen Kubernetes-Clients kommen noch durch die Community erstellte und gepflegte Libraries für diverse Sprachen hinzu.

Im Folgenden wird demonstriert, wie die Kubernets-API mit Java verwendet werden kann. Wie der dazu benötigte lesende API-Zugriff eingerichtet werden kann, wurde bereits in Kubernetes Readonly API Zugriff erklärt.

Die Java-Implementierung für die Kubernetes-API findet sich bei GitHub: https://github.com/kubernetes-client/java/

Mit Maven wird der Kubernetes-Client als Dependency deklariert und kann dann verwendet werden. Als HTTP-Client kommt OkHttp zum Einsatz, was auch unter Android funktioniert.

Maven Dependency für Kubernetes Java Client
<dependency>
    <groupId>io.kubernetes</groupId>
    <artifactId>client-java</artifactId>
    <version>4.0.0</version>
    <scope>compile</scope>
</dependency>

Der Java-Client bietet verschiedene Konfigurationsvarianten, um auf die Kubernetes-API zuzugreifen. Relevant sind dabei vor allem die Komponenten API Endpunkt und Authentifizierung: Aus diesen beiden Parametern ergibt sich dann, wie der technische Zugriff umgesetzt wird.

Praktischerweise bietet die Klasse io.kubernetes.client.util.Config eine statische Factory-Methode defaultClient, die verschiedene Varianten zur Konfiguration durchprobiert:

  • Ist die Umgebungsvariable $KUBECONFIG gesetzt, wird die dort spezifizierte Konfiguration verwendet

  • Existiert $HOME/.kube/config, wird diese Konfiguration verwendet

  • Es wird versucht, einen Kubernetes Serviceaccount zum Zugriff auf den kubernetes Service innerhalb eines Clusters zu verwenden

  • Als letzte Variante wird der Zugriff auf http://localhost:8080 versucht - dies entspricht einem kubectl proxy --port 8080

Somit ist der Zugriff mit relativ wenig Aufwand umzusetzen. Zur Demonstration sollen die zu einem Cluster gehörigen Nodes, ihre Architektur und das verwendete Betriebssystem abgefragt werden.

Kubernetes-API Client-Beispiel in Java
public class Client
{
    public static void main(String[] args) throws IOException, ApiException
    {
        ApiClient client = Config.defaultClient();
        Configuration.setDefaultApiClient(client);

        CoreV1Api api = new CoreV1Api();

        V1NodeList listNode = api.listNode(true, null, null, null, null, null, null, 10, false);

        for (V1Node node : listNode.getItems())
        {
            V1NodeSystemInfo nodeInfo = node.getStatus().getNodeInfo();
            String addr = node.getStatus().getAddresses().size() > 0 ? node.getStatus().getAddresses().get(0).getAddress() : "???";
            String name = node.getMetadata().getName();

            System.out.format("%s (%s) - %s: %s (%s) %n", name, addr, nodeInfo.getArchitecture(), nodeInfo.getOsImage(), nodeInfo.getOperatingSystem());
        }
    }
}

Etwas gewöhnungsbedürftig ist die Angabe von Filter- und Ausgabeparametern für die Objektabfrage, da hier viele null-Werte verwendet werden. Die Signatur von listNode sieht wie folgt aus:

public V1NodeList listNode(
  Boolean includeUninitialized,
  String pretty,
  String _continue,
  String fieldSelector,
  String labelSelector,
  Integer limit,
  String resourceVersion,
  Integer timeoutSeconds,
  Boolean watch
) throws ApiException

Da alle Parameter optional sind, werden null-Werte verwendet, wenn der Parameter nicht gesetzt wird.
Zudem fällt direkt auf, dass für includeUninitialized ein boolean-Parameter verwendet wurde, für die pretty-Ausgabe jedoch ein String, der den Wert "true" erhalten kann. Auch wenn ein Pretty-Printing der Ausgabe für eine maschinelle Verarbeitung selten sinnvoll sein wird, wäre eine bessere Konsistenz wünschenswert.

Kubernetes Watch-API

Neben der Request-Response API zur Abfrage von Objekten bietet Kubernetes auch eine Watch-API. Diese liefert kontinuierliche Updates bei Änderungen des Zustands und erlaubt damit Reaktionen in Echtzeit.
Die Umsetzung ist bei Kubernetes mit einer regulären HTTP-Verbindung gelöst, die jedoch lange bestehen bleibt und zeilenweise JSON-Objekte liefert. In den Objekten ist der jeweilige Event-Typ (ADDED, MODIFIED, DELETED) in einem Attribut vermerkt.

Die Java API dazu weicht deutlich von der Variante einer einzelnen Abfrage ab.

Kubernetes Watch-API Client-Beispiel in Java
ApiClient client = Config.defaultClient();
client.getHttpClient().setReadTimeout(0, TimeUnit.SECONDS); // (1)

Configuration.setDefaultApiClient(client);

CoreV1Api api = new CoreV1Api();

Watch<V1Pod> watch = Watch.createWatch(   // (2)
    client,
    api.listPodForAllNamespacesCall(null, null, null, null, null, null, null, null, true, null, null), // (3)
    new TypeToken<Watch.Response<V1Pod>>(){}.getType()
);

for (Watch.Response<V1Pod> item : watch)  // (4)
{
    switch (item.type)
    {
        case "ADDED":
        {
            String name = item.object.getMetadata().getName();
            String image = item.object.getSpec().getContainers().get(0).getImage();

            System.out.format("ADDED: %s (%s) %s %n", name, image, item.object.getStatus().getPhase());
            break;
        }
    }
}
  1. Für die Watch-API wird eine dauerhaft lesende HTTP-Verbindung eingesetzt, daher muss der Read-Timeout entsprechend auf unendlich konfiguriert werden

  2. Statt eines blockierenden Aufrufs wird ein Watch erzeugt, vergleichbar einem Java Future

  3. Der API-Aufruf selbst muss entsprechend konfiguriert werden (watch=true)

  4. Der Iterator blockiert und liefert neue Ergebnisse, wenn ein neues Element von der API übermittelt wird

Auf die Ergebnisse kann dann entsprechend reagiert werden, zum Beispiel auch wiederum durch Aufrufe der Kubernetes API, um Änderungen der Clusterkonfiguration auszulösen.
Ein anderes Anwendungsbeispiel ist die Watch-API für Servicediscovery von Kubernetes-Service-Objekten zu nutzen, um eine Anwendung daraufhin dynamisch zu konfigurieren.




Zu den Themen Kubernetes, Docker und Cloud Architektur bieten wir sowohl Beratung, Entwicklungsunterstützung als auch passende Schulungen an:

Auch für Ihren individuellen Bedarf können wir Workshops und Schulungen anbieten. Sprechen Sie uns gerne an.

Feedback oder Fragen zu einem Artikel - per Twitter @triondevelop oder E-Mail freuen wir uns auf eine Kontaktaufnahme!

Los geht's!

Bitte teilen Sie uns mit, wie wir Sie am besten erreichen können.