Architecture complète : comment tout s'articule


Mise à jour février 2026 : Depuis la rédaction de cet article, l’architecture a évolué avec l’ajout de Velero pour les backups vers S3 Glacier, d’un système de déploiement automatique (GitHub Push → Build → Deploy avec Kaniko), et d’une stack LGTM complète (Prometheus, Loki, Tempo, Grafana) pour l’observabilité. Les articles suivants couvrent ces ajouts.

Après tous ces articles sur les composants individuels, j’ai pensé qu’il serait utile de faire un article qui montre comment tout s’articule. La vue d’ensemble.

L’architecture en couches

┌─────────────────────────────────────────────────────────────┐
│                    COUCHE 1 : HARDWARE                      │
│  Dell R430                                                  │
│  - 2x Xeon E5-2630 v3 (16 cores, 32 threads)              │
│  - 128GB RAM DDR4 ECC                                       │
│  - 4x 1TB SAS (RAID 10)                                     │
│  - iDRAC 8 Enterprise                                       │
└───────────────────────────┬─────────────────────────────────┘

┌───────────────────────────▼─────────────────────────────────┐
│                    COUCHE 2 : OS                            │
│  Talos Linux v1.6.x                                         │
│  - OS immutable                                             │
│  - Pas de SSH, pas de shell                                 │
│  - Géré via API uniquement                                  │
│  - Optimisé pour Kubernetes                                 │
└───────────────────────────┬─────────────────────────────────┘

┌───────────────────────────▼─────────────────────────────────┐
│              COUCHE 3 : ORCHESTRATION                       │
│  Kubernetes v1.29.x                                         │
│  ┌───────────────────────────────────────────────────────┐ │
│  │ Control Plane                                         │ │
│  │ - kube-apiserver                                      │ │
│  │ - etcd                                                │ │
│  │ - kube-scheduler                                       │ │
│  │ - kube-controller-manager                             │ │
│  └───────────────────────────────────────────────────────┘ │
│  ┌───────────────────────────────────────────────────────┐ │
│  │ Worker Node (même machine)                            │ │
│  │ - kubelet                                             │ │
│  │ - kube-proxy                                          │ │
│  │ - Container Runtime (containerd)                      │ │
│  └───────────────────────────────────────────────────────┘ │
└───────────────────────────┬─────────────────────────────────┘

┌───────────────────────────▼─────────────────────────────────┐
│              COUCHE 4 : INFRASTRUCTURE                       │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐    │
│  │ MetalLB      │  │ Local Path   │  │ Traefik      │    │
│  │ (LoadBalancer)│  │ (Storage)    │  │ (Ingress)    │    │
│  └──────────────┘  └──────────────┘  └──────────────┘    │
└───────────────────────────┬─────────────────────────────────┘

┌───────────────────────────▼─────────────────────────────────┐
│              COUCHE 5 : APPLICATIONS                        │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐    │
│  │ KubeVirt     │  │ Console Web  │  │ Blog         │    │
│  │ (VMs)        │  │ (Go + React) │  │ (Astro)      │    │
│  └──────────────┘  └──────────────┘  └──────────────┘    │
│  ┌──────────────┐  ┌──────────────┐                       │
│  │ NPM          │  │ ArgoCD       │                       │
│  │ (Proxy)      │  │ (GitOps)     │                       │
│  └──────────────┘  └──────────────┘                       │
└─────────────────────────────────────────────────────────────┘

Le flux réseau

Quand tu accèdes à un service depuis ton navigateur :

1. Navigateur

2. Routeur (port forwarding 80/443)

3. Nginx Proxy Manager (192.168.1.202)

4. Traefik Ingress (192.168.1.200)

5. Service Kubernetes

6. Pod (container)

Exemple concret : accéder au blog

  1. Tu tapes blog.sortium.fr dans ton navigateur
  2. Le DNS résout vers ton IP publique (port forwarding)
  3. Le routeur forward vers 192.168.1.202 (NPM)
  4. NPM vérifie le certificat SSL et route vers Traefik (via Ingress)
  5. Traefik regarde les Ingress rules et trouve le Service homelab-blog
  6. Le Service route vers les Pods du blog (2 replicas)
  7. Le Pod Astro répond avec la page HTML

Tout ça en quelques millisecondes.

Le flux de déploiement

Avec ArgoCD (GitOps)

1. Tu push du YAML dans Git

2. ArgoCD détecte le changement

3. ArgoCD applique le YAML dans Kubernetes

4. Kubernetes crée/modifie les ressources

5. Les pods se mettent à jour

Sans ArgoCD (manuel)

1. Tu écris/modifies du YAML

2. `kubectl apply -f app.yaml`

3. Kubernetes crée/modifie les ressources

4. Les pods se mettent à jour

Les namespaces

J’organise mes apps par namespace :

default/
  └─ (rien, c'est le namespace par défaut)

kube-system/
  ├─ coredns
  ├─ kube-proxy
  ├─ flannel
  └─ (composants Kubernetes)

kubevirt/
  ├─ virt-controller
  ├─ virt-handler
  └─ virt-operator

metallb-system/
  ├─ controller
  └─ speaker

traefik/
  └─ traefik

console/
  ├─ console-api (backend Go)
  └─ console-ui (frontend React)

blog/
  └─ blog (Astro)

argocd/
  ├─ argocd-server
  ├─ argocd-repo-server
  └─ argocd-application-controller

Chaque namespace isole les ressources. Tu peux avoir des policies différentes par namespace.

Le stockage

Avec Local Path Provisioner, tous les volumes sont stockés dans /opt/local-path-provisioner sur le node.

/opt/local-path-provisioner/
├── pvc-abc123/          # Volume pour PostgreSQL
│   └── data/
├── pvc-def456/          # Volume pour une VM
│   └── disk.img
└── pvc-ghi789/          # Volume pour autre chose
    └── files/

C’est simple, mais ça marche. Pour un homelab single-node, c’est suffisant.

Les IPs

J’ai réservé une plage d’IPs pour MetalLB :

IPServicePort
192.168.1.200Traefik80, 443
192.168.1.201Console Web80, 443
192.168.1.202Nginx Proxy Manager80, 443
192.168.1.203ArgoCD80, 443
192.168.1.204+Apps déployéesVariable

Le R430 lui-même est sur 192.168.1.100.

Les VMs avec KubeVirt

Quand tu crées une VM, KubeVirt crée un Pod spécial :

Pod: virt-launcher-ubuntu-vm-xxxxx
  └─ Container: virt-launcher
      └─ QEMU/KVM
          └─ VM Ubuntu

Le Pod apparaît dans kubectl get pods comme n’importe quel autre pod. Mais dedans, c’est une vraie VM qui tourne.

La console web

Ma console custom (Go + React) est déployée dans le namespace console :

console/
├─ console-api (Deployment, 1 replica)
│  └─ Pod: console-api-xxxxx
│     └─ Container: api (Go)
│        └─ Port: 8080
│        └─ ServiceAccount: console-api (avec ClusterRole)

└─ console-ui (Deployment, 1 replica)
   └─ Pod: console-ui-xxxxx
      └─ Container: ui (React + Nginx)
         └─ Port: 80

Le frontend fait des appels API au backend, qui utilise client-go pour parler à Kubernetes. Le backend a les permissions nécessaires via un ServiceAccount avec ClusterRole.

Les backups

Pour l’instant, c’est manuel :

  1. Config Kubernetes : Tout est en YAML dans Git, donc c’est déjà sauvegardé
  2. Volumes : Script qui tar les dossiers de Local Path et les envoie sur un NAS
  3. VMs : Export manuel des PVCs si besoin

À terme, j’aimerais automatiser avec Velero.

Les logs

Les logs de tous les pods sont accessibles via :

  • kubectl logs <pod-name>
  • La console web (qui affiche les logs)
  • Les fichiers sur le node (dans /var/log/ pour Talos)

Pour un vrai système centralisé, faudrait ajouter Loki ou ELK. Mais pour l’instant, kubectl logs suffit.

Le monitoring

J’utilise pas encore de monitoring avancé. Mais je peux voir :

  • L’état des pods : kubectl get pods -A
  • L’utilisation CPU/RAM : kubectl top nodes
  • Les événements : kubectl get events

À terme, j’aimerais ajouter Prometheus + Grafana. Mais c’est pas prioritaire.

Les points de défaillance

Sur un single-node, tout est un point de défaillance :

  • Si le serveur plante, tout est down
  • Si le disque meurt, les données sont perdues (sauf backups)
  • Si le réseau saute, plus d’accès

C’est le compromis d’un homelab. Pour de la vraie HA, faudrait plusieurs nodes.

Ce que j’aurais aimé savoir avant

  1. Tout est lié : Si MetalLB plante, plus de LoadBalancer. Si Traefik plante, plus d’Ingress.

  2. L’ordre d’installation compte : Il faut installer MetalLB avant de créer des Services LoadBalancer. Il faut installer le storage avant de créer des PVCs.

  3. Les dépendances : Certains composants dépendent d’autres. Par exemple, ArgoCD a besoin d’un Ingress pour être accessible.

  4. Le debugging : Quand ça marche pas, faut vérifier dans l’ordre : Pods → Services → Ingress → DNS.

  5. La documentation : Chaque composant a sa doc. Mais comprendre comment ils s’articulent, c’est autre chose.

L’évolution

Cette architecture a évolué au fil du temps :

  1. Début : Juste Kubernetes + MetalLB
  2. + Storage : Local Path Provisioner
  3. + VMs : KubeVirt
  4. + Console : Ma console custom
  5. + GitOps : ArgoCD
  6. + Proxy : NPM

Et ça continue d’évoluer. Chaque ajout apporte de nouvelles possibilités.

Pourquoi cette architecture ?

  • Simple : Pas de sur-ingénierie
  • Modulaire : Chaque composant a un rôle précis
  • Évolutive : Facile d’ajouter des trucs
  • Apprenante : Chaque composant m’a appris quelque chose

C’est pas parfait, mais c’est fonctionnel. Et c’est le mien.


Prochain article : Métriques et coûts - combien ça consomme, combien ça coûte, et est-ce que ça vaut le coup.