Je construis ma propre console


Après l’échec KubeSphere, j’ai décidé de construire ma propre console. Pas une usine à gaz, juste ce dont j’avais besoin.

Le stack

  • Backend : Go avec client-go (le client Kubernetes officiel)
  • Frontend : React + TypeScript + Vite + Tailwind

Pourquoi Go ? Parce que client-go est en Go, et je voulais apprendre.

Pourquoi React ? Parce que je connais, et c’est efficace.

L’architecture

┌─────────────────────┐       ┌─────────────────────┐
│    Frontend         │       │     Backend         │
│    React + TS       │◀─────▶│    Go + client-go   │
│    Port 80          │  API  │    Port 8080        │
└─────────────────────┘       └──────────┬──────────┘


                              ┌─────────────────────┐
                              │   Kubernetes API    │
                              └─────────────────────┘

Simple. Le frontend fait des appels API au backend, qui parle à Kubernetes.

Le backend Go

Le truc cool avec Go et client-go, c’est que t’as accès à toute l’API Kubernetes nativement.

import (
    "k8s.io/client-go/kubernetes"
    "k8s.io/client-go/rest"
)

func main() {
    // Config in-cluster (quand on tourne dans K8s)
    config, _ := rest.InClusterConfig()
    
    // Client Kubernetes
    clientset, _ := kubernetes.NewForConfig(config)
    
    // Lister les pods
    pods, _ := clientset.CoreV1().Pods("").List(ctx, metav1.ListOptions{})
}

Pour KubeVirt, j’utilise le dynamic client parce que les VirtualMachines c’est des CRDs :

import "k8s.io/client-go/dynamic"

var vmGVR = schema.GroupVersionResource{
    Group:    "kubevirt.io",
    Version:  "v1",
    Resource: "virtualmachines",
}

vms, _ := dynamicClient.Resource(vmGVR).
    Namespace("").
    List(ctx, metav1.ListOptions{})

Le frontend React

Classique : des pages, des composants, des hooks pour fetch les données.

// hooks/usePods.ts
export function usePods(namespace: string) {
  return useQuery({
    queryKey: ['pods', namespace],
    queryFn: () => fetch(`/api/v1/pods?ns=${namespace}`).then(r => r.json()),
    refetchInterval: 5000,
  })
}

J’utilise React Query pour le cache et le refresh automatique.

Les features

Ce que ma console fait :

  1. Dashboard : Vue d’ensemble du cluster (nodes, pods, mémoire)
  2. Pods : Liste, logs, delete, exec
  3. VMs : Liste, start/stop, accès console
  4. Deployments : Scale, restart
  5. Chat IA : Mais ça c’est venu après

Le déploiement

Deux containers dans le même namespace :

# Backend
apiVersion: apps/v1
kind: Deployment
metadata:
  name: console-api
spec:
  template:
    spec:
      serviceAccountName: console-api  # Important pour les permissions
      containers:
        - name: api
          image: mon-registry/console-api:latest
          ports:
            - containerPort: 8080

# Frontend
apiVersion: apps/v1
kind: Deployment
metadata:
  name: console-ui
spec:
  template:
    spec:
      containers:
        - name: ui
          image: mon-registry/console-ui:latest
          ports:
            - containerPort: 80

Le backend a besoin d’un ServiceAccount avec les bonnes permissions (ClusterRole) pour accéder à l’API Kubernetes.

Le RBAC

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: console-api
rules:
  - apiGroups: ["", "apps", "kubevirt.io"]
    resources: ["*"]
    verbs: ["*"]

Oui, c’est permissif. C’est un homelab, pas une banque.

Ce que j’ai appris

  1. Go c’est cool. Typage statique, compilation rapide, binaires standalone.

  2. client-go est bien documenté. Les exemples dans le repo GitHub sont utiles.

  3. Le dynamic client c’est la solution pour les CRDs.

  4. Vite c’est rapide. Build en quelques secondes.

  5. Faire simple marche. Ma console fait 10% de KubeSphere mais c’est 100% de ce que j’utilise.

L’évolution

Au début c’était juste un dashboard. Puis j’ai ajouté la gestion des VMs. Puis le déploiement d’apps.

Et puis j’ai eu une idée : et si je pouvais juste parler au cluster ?


Prochain article : Et si je parlais à mon cluster ? - L’idée de l’agent IA.