Stockage

TopoLVM

Provisioner de volumes locaux via LVM pour Kubernetes. Scheduling capacity-aware. Haute performance NVMe sans réseau. Sans réplication. Développé par Cybozu/CNCF.

TopoLVM est un CSI driver Kubernetes qui provisionne des volumes locaux en utilisant LVM (Logical Volume Manager) sur les nœuds. Contrairement aux solutions distribuées comme Longhorn ou Rook/Ceph, TopoLVM n'effectue aucune réplication réseau - les données restent sur les disques locaux du nœud, ce qui donne des performances maximales (accès NVMe direct, pas de saut réseau).

Sa fonctionnalité clé est le scheduling capacity-aware : TopoLVM expose la capacité LVM disponible sur chaque nœud au scheduler Kubernetes, qui peut alors placer les pods sur les nœuds ayant suffisamment d'espace. Cette fonctionnalité manque aux provisioners locaux standards de Kubernetes. Développé par Cybozu (Japon), il est projet CNCF Sandbox.


Informations essentielles

Origine : Cybozu (Japon) → CNCF  ·  Licence : Apache 2.0  ·  Architectures : x86_64, ARM64

Liens : Site officiel  ·  Documentation  ·  GitHub  ·  Releases

Support : Communauté GitHub. Cybozu maintient le projet activement.

Stack par défaut

ComposantValeur
Type de stockageLocal (LVM Logical Volumes)
RéplicationAucune - données locales uniquement
Thin provisioningOui (LVM thin pools)
SchedulingCapacity-aware (informe le scheduler K8s de l'espace disponible)
Access modesReadWriteOnce uniquement
Extension de volumeOui (online resize via LVM)
Kubernetes minimum1.20+

Composants

ComposantRôleDéploiement
topolvm-controllerProvisionne les LV, gère les PVCDeployment
topolvm-nodeGère les LV sur chaque nœud, expose la capacitéDaemonSet
topolvm-schedulerExtender du scheduler K8s - utilise la capacité LVMDeployment ou config kube-scheduler

Prérequis

RessourceValeur
Kubernetes1.20+
OSLinux avec LVM2 installé
LVMVolume Group(s) créé(s) sur chaque nœud de stockage
Droitscluster-admin

Préparer LVM sur chaque nœud de stockage

# Installer LVM2
sudo apt-get install -y lvm2     # Ubuntu/Debian
sudo dnf install -y lvm2         # Rocky/AlmaLinux

# Créer un Physical Volume sur le disque dédié
sudo pvcreate /dev/sdb

# Créer un Volume Group (nommé par convention "myvg" ou selon le paramètre storageClass)
sudo vgcreate myvg /dev/sdb

# Vérifier
sudo vgs
sudo pvs

TopoLVM créera des Logical Volumes dans ce VG à la demande. Ne pas créer de LV manuellement dans le VG utilisé par TopoLVM.


Installation

Via Helm (recommandé)

helm repo add topolvm https://topolvm.github.io/topolvm
helm repo update

# Installer TopoLVM
helm install topolvm topolvm/topolvm \
  --namespace topolvm-system \
  --create-namespace \
  --set scheduler.enabled=true

kubectl get pods -n topolvm-system

Options clés

# topolvm-values.yaml
controller:
  storageCapacityTracking:
    enabled: true          # Capacity-aware scheduling

node:
  volumeGroupName: myvg    # Nom du VG LVM à utiliser sur les nœuds

scheduler:
  enabled: true            # Activer l'extender du scheduler
helm install topolvm topolvm/topolvm \
  -n topolvm-system --create-namespace \
  -f topolvm-values.yaml

Configuration

Annoter les nœuds avec le VG LVM

TopoLVM doit savoir quel VG utiliser sur chaque nœud :

# Annoter un nœud avec le nom du VG
kubectl annotate node node1 topolvm.io/capacity-key-prefix=topolvm.io
kubectl label node node1 topolvm.io/node=node1

Ou via la configuration du DaemonSet (voir values.yamlnode.devices).

StorageClass

# thin provisioning (recommandé - meilleur usage de l'espace)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: topolvm-provisioner-thin
provisioner: topolvm.io
parameters:
  "topolvm.io/device-class": thin    # Utiliser un thin pool LVM
volumeBindingMode: WaitForFirstConsumer   # OBLIGATOIRE pour local storage
allowVolumeExpansion: true
reclaimPolicy: Delete
---
# thick provisioning (alloué immédiatement)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: topolvm-provisioner-thick
provisioner: topolvm.io
parameters:
  "topolvm.io/device-class": ""      # Thick LV par défaut
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
reclaimPolicy: Delete

WaitForFirstConsumer est obligatoire pour le stockage local : cela garantit que le PV est créé sur le même nœud que le pod.

Vérification de l'installation

# 1. Pods TopoLVM
kubectl get pods -n topolvm-system
# topolvm-controller, topolvm-node (DaemonSet, 1 par nœud), topolvm-scheduler

# 2. Vérifier que la capacité LVM est bien détectée et remontée au scheduler
kubectl get nodes -o custom-columns=\
  "NODE:.metadata.name,CAPACITY:.status.allocatable.topolvm\.io/capacity"
# Chaque nœud doit afficher sa capacité LVM disponible (en octets)
# "0" ou "<none>" = le VG n'est pas détecté (voir pièges ci-dessous)

# 3. Vérifier les StorageClasses
kubectl get sc | grep topolvm

# 4. Vérifier les logs du node daemon sur un nœud spécifique
kubectl logs -n topolvm-system \
  $(kubectl get pod -n topolvm-system -l app.kubernetes.io/component=node \
    -o jsonpath='{.items[0].metadata.name}') --tail=30

Pièges courants à l'installation

SymptômeCauseCorrection
Capacité = 0 sur tous les nœudsLe nom du VG dans values.yaml ne correspond pas au VG crééVérifier sudo vgs sur le nœud et corriger node.volumeGroupName
PVC reste PendingWaitForFirstConsumer attend un podNormal - créer le pod qui utilise le PVC pour déclencher le binding
PVC reste Pending après création du podNœud schedulé sur un nœud sans espace LVMVérifier la capacité par nœud, ajouter de l'espace au VG
LVM non installévgs introuvable sur les nœudsapt-get install -y lvm2 + pvcreate + vgcreate sur chaque nœud

Premier test de bout en bout

# test-topolvm.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: test-topolvm-pvc
  namespace: default
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: topolvm-provisioner-thin
  resources:
    requests:
      storage: 1Gi
---
apiVersion: v1
kind: Pod
metadata:
  name: test-topolvm
  namespace: default
spec:
  containers:
  - name: writer
    image: busybox
    command: ["/bin/sh", "-c"]
    args:
    - |
      echo "TopoLVM OK - nœud: $NODE_NAME" > /data/test.txt
      cat /data/test.txt
      df -h /data
      sleep 3600
    env:
    - name: NODE_NAME
      valueFrom:
        fieldRef:
          fieldPath: spec.nodeName
    volumeMounts:
    - name: data
      mountPath: /data
  volumes:
  - name: data
    persistentVolumeClaim:
      claimName: test-topolvm-pvc
kubectl apply -f test-topolvm.yaml

# Le PVC se bind uniquement quand le pod est schedulé (WaitForFirstConsumer)
kubectl get pvc test-topolvm-pvc -w
# Pending → Bound (en quelques secondes après scheduling du pod)

kubectl logs test-topolvm
# Doit afficher le message + le nœud choisi + df -h avec ~1G disponible

# Vérifier le LV créé sur le nœud (se connecter sur le nœud)
kubectl get pv $(kubectl get pvc test-topolvm-pvc -o jsonpath='{.spec.volumeName}') \
  -o jsonpath='{.spec.nodeAffinity.required.nodeSelectorTerms[*]}'
# Affiche le nœud sur lequel le LV a été créé
# Sur ce nœud : sudo lvs | grep <pv-name>

# Nettoyage
kubectl delete -f test-topolvm.yaml

Utilisation

# PVC TopoLVM
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: local-data
  namespace: production
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: topolvm-provisioner-thin
  resources:
    requests:
      storage: 20Gi

Le scheduler Kubernetes, aidé par l'extender TopoLVM, choisit automatiquement un nœud ayant 20+ Gi disponibles dans son VG LVM.

Vérifier l'espace disponible par nœud

# Capacité LVM reportée à Kubernetes
kubectl get nodes -o custom-columns=\
  "NAME:.metadata.name,\
  CAPACITY:.status.allocatable.topolvm\.io/capacity"

# Ou via les annotations
kubectl describe node node1 | grep topolvm

Extension de volume

# Éditer le PVC pour augmenter la taille
kubectl patch pvc local-data -n production \
  -p '{"spec":{"resources":{"requests":{"storage":"40Gi"}}}}'

# Le LV est étendu en ligne par TopoLVM (le pod n'a pas besoin d'être arrêté)
kubectl get pvc local-data -n production

Thin provisioning (LVM thin pools)

Pour utiliser le thin provisioning, créer un thin pool dans le VG avant l'installation :

# Créer un thin pool "thin" dans le VG "myvg" (utilise 100% de l'espace VG)
sudo lvcreate -l 100%FREE --thinpool thin myvg

# Vérifier
sudo lvs

Puis configurer la StorageClass avec "topolvm.io/device-class": "thin".


Mise à jour

helm repo update

helm upgrade topolvm topolvm/topolvm \
  -n topolvm-system \
  --reuse-values

kubectl rollout status deploy/topolvm-controller -n topolvm-system
kubectl rollout status ds/topolvm-node -n topolvm-system

Troubleshooting

# État des pods TopoLVM
kubectl get pods -n topolvm-system

# Logs du controller
kubectl logs -n topolvm-system -l app.kubernetes.io/component=controller --tail=100

# Logs du node daemon
kubectl logs -n topolvm-system -l app.kubernetes.io/component=node --tail=100

# Vérifier les LV créés sur un nœud
sudo lvs | grep -v LVM2

PVC bloqué en Pending

kubectl describe pvc <nom> -n <namespace>
# Events : souvent "no nodes are available that match all of the following predicates: Insufficient topolvm.io/capacity"

# Vérifier l'espace LVM disponible par nœud
kubectl get nodes -o yaml | grep topolvm

# Sur le nœud lui-même
sudo vgs                   # Espace total / libre dans le VG
sudo lvs                   # LVs existants

Extension de volume échouée

# Vérifier que le thin pool n'est pas plein
sudo lvs -a | grep thin    # data% et meta% < 100%

# Si plein, étendre le thin pool
sudo lvextend -l +100%FREE myvg/thin

Commandes utiles

# K8s
kubectl get pvc -n <namespace> | grep topolvm
kubectl get pv | grep topolvm

# Sur les nœuds de stockage
sudo vgs                               # Volume Groups et espace
sudo pvs                               # Physical Volumes
sudo lvs                               # Logical Volumes (un par PVC)
sudo lvs -a                            # Inclure thin pools et metadata LVs
sudo vgdisplay myvg                    # Détail d'un VG

Ressources

Newsletter · 2 000+ abonnés

Reste au courant de ce qui bouge en prod

RudeOps veille devops hebdo, droit au but.

Gratuit · Sans spam · Désinscription en un clic