Bibliothèque Java Fabric8 pour Kubernetes : principes et utilisation pratique

Fabric8 Client pour Kubernetes en Java

La bibliothèque Fabric8 constitue une surcouche Java complète pour interagir avec l'API REST de Kubernetes. Elle se distingue du client officiel client-java par une ergonomie supérieure, des abstractions de haut niveau et une intégration naturelle avec l'écosystème Java.

Architecture interne

Le fonctionnement reposera sur trois composants fondamentaux :

  • Couche d'abstraction métier : fournit des interfaces typées pour manipuler les ressources Kubernetes (Pods, Deployments, Services, etc.) avec un style d'API fluent et chaîné ;
  • Moteur de communication : gère l'authentification (fichier kubeconfig, jeton OAuth, certificats, ServiceAccount), la sérialisation JSON/YAML via Jackson, et les échanges HTTP grâce à OkHttp ;
  • Appels REST : les opérations aboutissent à des requêtes vers les endpoints de l'API Kubernetes (/api/v1/pods, /apis/apps/v1/deployments, etc.).

Caractéristiques techniques principales

  • Modélisation des ressources : chaque type de ressource Kubernetes correspond à une classe Java (POJO), dont les attributs reflètent fidèlement la structure YAML de Kubernetes ;
  • Mécanisme Watch : s'appuie sur l'API Watch de Kubernetes pour détecter en temps réel les modifications de ressources, exposé via l'interface Watcher avec prise en charge asynchrone ;
  • Découverte automatique : récupère automatiquement les identifiants d'authentification depuis ~/.kube/config en développement local, ou depuis le Secret de ServiceAccount dans un environnement conteneurisé ;
  • Résilience : intègre des mécanismes de relance automatique et de gestion des dépassements de délai.

Comparaison avec le client officiel

Critère Fabric8 client-java officiel
Style d'API Fluent, chaîné, concis Verbose, proche du REST brut
Courbe d'apprentissage Progressive, accessible Plus raide, destiné aux frameworks
Fonctionnalités intégrées Mises à jour progressives, rollbacks, etc. Opérations CRUD basiques uniquement
Maintenance Communauté Fabric8 Équipe officielle Kubernetes

Mise en pratique

Prérequis

Environnement requis : JDK 8 ou supérieur, un outil de build Maven ou Gradle, et un cluster Kubernetes accessible (Minikube, Docker Desktop ou distant).

Ajout de la dépendance Maven dans le fichier pom.xml :

<dependency>
    <groupId>io.fabric8</groupId>
    <artifactId>kubernetes-client</artifactId>
    <version>6.12.0</version>
</dependency>

<!-- Facultatif : pour la gestion des Custom Resource Definitions -->
<dependency>
    <groupId>io.fabric8</groupId>
    <artifactId>crd-generator-apt</artifactId>
    <version>6.12.0</version>
    <scope>provided</scope>
</dependency>

Instanciation du client

Trois approches sont disponibles selon le contexte de déploiement :

import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.ConfigBuilder;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.DefaultKubernetesClient;
import java.io.File;

public class ConnexionCluster {

    public static void main(String[] args) {

        // Approche 1 : configuration automatique (recommandée)
        try (KubernetesClient session = new DefaultKubernetesClient()) {
            System.out.println("Connexion établie : " + session.getConfiguration().getMasterUrl());
        }

        // Approche 2 : paramétrage manuel pour un accès distant
        Config parametres = new ConfigBuilder()
                .withMasterUrl("https://10.0.0.50:6443")
                .withOauthToken("eyJhbGciOiJSUzI1NiIsInR5cCI6...")
                .withTrustCerts(true) // uniquement en environnement de test
                .build();
        try (KubernetesClient session = new DefaultKubernetesClient(parametres)) {
            System.out.println("Connexion manuelle réussie");
        }

        // Approche 3 : chargement d'un fichier kubeconfig personnalisé
        Config depuisFichier = ConfigBuilder.parseConfig(
                new File("/etc/k8s/cluster-config.yaml"));
        try (KubernetesClient session = new DefaultKubernetesClient(depuisFichier)) {
            System.out.println("Connexion via kubeconfig personnalisé réussie");
        }
    }
}

L'instruction try-with-resources garantit la libération automatique des connexions réseau. En production, évitez withTrustCerts(true) et configurez un certificat CA valide.

Opérations CRUD sur les Deployments

L'exemple suivant illustre les quatre opérations fondamentales sur une ressource Deployment :

import io.fabric8.kubernetes.api.model.*;
import io.fabric8.kubernetes.api.model.apps.Deployment;
import io.fabric8.kubernetes.api.model.apps.DeploymentBuilder;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.DefaultKubernetesClient;
import java.util.Map;

public class GestionDeployments {

    public static void main(String[] args) {
        try (KubernetesClient api = new DefaultKubernetesClient()) {
            String espace = "default";

            // === Création ===
            Map<String, String> etiquettes = Map.of("composant", "web", "environnement", "dev");

            Deployment spec = new DeploymentBuilder()
                    .withNewMetadata()
                        .withName("serveur-web")
                        .withLabels(etiquettes)
                    .endMetadata()
                    .withNewSpec()
                        .withReplicas(2)
                        .withNewSelector()
                            .withMatchLabels(etiquettes)
                        .endSelector()
                        .withNewTemplate()
                            .withNewMetadata()
                                .withLabels(etiquettes)
                            .endMetadata()
                            .withNewSpec()
                                .addNewContainer()
                                    .withName("httpd")
                                    .withImage("httpd:2.4-alpine")
                                    .addNewPort()
                                        .withContainerPort(80)
                                    .endPort()
                                .endContainer()
                            .endSpec()
                        .endTemplate()
                    .endSpec()
                    .build();

            Deployment cree = api.apps().deployments()
                    .inNamespace(espace).create(spec);
            System.out.println("Déploiement créé : " + cree.getMetadata().getName());

            // === Lecture d'une instance ===
            Deployment recupere = api.apps().deployments()
                    .inNamespace(espace)
                    .withName("serveur-web")
                    .get();
            System.out.println("Nombre de réplicas : " + recupere.getSpec().getReplicas());

            // === Lecture filtrée par étiquettes ===
            api.apps().deployments()
                    .inNamespace(espace)
                    .withLabel("composant", "web")
                    .list()
                    .getItems()
                    .forEach(d -> System.out.println("Trouvé : " + d.getMetadata().getName()));

            // === Modification (réplicas → 4) ===
            Deployment modifie = api.apps().deployments()
                    .inNamespace(espace)
                    .withName("serveur-web")
                    .edit(dep -> new DeploymentBuilder(dep)
                            .editSpec()
                                .withReplicas(4)
                            .endSpec()
                            .build());
            System.out.println("Réplicas mis à jour : " + modifie.getSpec().getReplicas());

            // === Suppression ===
            boolean supprime = api.apps().deployments()
                    .inNamespace(espace)
                    .withName("serveur-web")
                    .delete();
            System.out.println(supprime ? "Suppression effectuée" : "Échec de la suppression");
        }
    }
}

Surveillance en temps réel avec le mécanisme Watch

Le système Watch permet d'écouter les événements de cycle de vie des ressources Kubernetes de manière asynchrone :

import io.fabric8.kubernetes.api.model.Pod;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.DefaultKubernetesClient;
import io.fabric8.kubernetes.client.Watcher;
import io.fabric8.kubernetes.client.WatcherException;

public class ObservateurPods {

    public static void main(String[] args) throws InterruptedException {
        try (KubernetesClient api = new DefaultKubernetesClient()) {

            api.pods().inNamespace("default").watch(new Watcher<Pod>() {

                @Override
                public void eventReceived(Action action, Pod pod) {
                    String nomPod = pod.getMetadata().getName();
                    String phase = pod.getStatus().getPhase();
                    System.out.printf("[WATCH] %s → %s (état: %s)%n",
                            action.name(), nomPod, phase);
                }

                @Override
                public void onClose(WatcherException erreur) {
                    if (erreur != null) {
                        System.err.println("Fermeture anormale : " + erreur.getMessage());
                    } else {
                        System.out.println("Observateur arrêté proprement");
                    }
                }
            });

            // Maintenir le processus actif pendant 60 secondes
            Thread.sleep(60_000);
        }
    }
}

Pour tester, exécutez kubectl run demo-pod --image=nginx -n default dans un terminal : l'événement ADDED apparaîtra dans la console. La suppression du Pod déclenchera l'événement DELETED.

Exécution de commandes à l'intérieur d'un Pod

Fabric8 permet d'exécuter des commandes dans un conteneur, équivalent à kubectl exec :

import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.DefaultKubernetesClient;
import io.fabric8.kubernetes.client.dsl.ExecWatch;
import java.io.ByteArrayOutputStream;

public class ExecDansPod {

    public static void main(String[] args) {
        try (KubernetesClient api = new DefaultKubernetesClient()) {

            String ciblePod = "serveur-web-7f8d9c6b4-abc12"; // à adapter
            String cibleConteneur = "httpd";

            ByteArrayOutputStream resultat = new ByteArrayOutputStream();

            ExecWatch session = api.pods()
                    .inNamespace("default")
                    .withName(ciblePod)
                    .inContainer(cibleConteneur)
                    .writingOutput(resultat)
                    .exec("cat", "/usr/local/apache2/conf/httpd.conf");

            session.close();
            System.out.println("Sortie de la commande :\n" + resultat.toString());
        }
    }
}

Bonnes pratiques pour la production

  1. Réutilisation de l'instance client : instanciez KubernetesClient une seule fois et partagez-la (singleton ou bean Spring) ; évitez les créations répétées ;
  2. Gestion des exceptions : interceptez KubernetesClientException et différenciez les cas d'erreur (ressource introuvable, droits insuffisants, délai dépassé) ;
  3. Principe du moindre privilège : attribuez un ServiceAccount avec les permissions minimales requises plutôt que cluster-admin ;
  4. Externalisation de la configuration : stockez l'adresse du cluster et les jetons d'authentification dans des fichiers de configuration ou des secrets, jamais en dur dans le code source ;
  5. Fermeture des observateurs : arrêtez explicitement les instances Watch à l'arrêt de l'application pour prévenir les fuites mémoire.

Étiquettes: fabric8 kubernetes-client Java watch-api deployment

Publié le 6 juillet à 23h55