Talos Linux et déploiement d'un Homelab
Introduction à Talos Linux
Talos Linux est un système d'exploitation minimaliste et immutable conçu spécifiquement pour Kubernetes. Contrairement aux distributions Linux traditionnelles, Talos n'a pas de shell, pas de SSH, et se configure entièrement via une API.
Pourquoi Talos plutôt que kubeadm ?
| Aspect | kubeadm | Talos |
|---|---|---|
| Complexité | Configuration manuelle, nombreuses étapes | Déclaratif, fichier de config unique |
| Sécurité | OS complet avec surface d'attaque large | Immutable, pas de shell, API only |
| Maintenance | Updates OS + K8s séparés | Updates atomiques OS + K8s |
| Debug | SSH disponible | Pas de SSH (API + talosctl) |
| Philosophie | "Pets" (serveurs à maintenir) | "Cattle" (jetable et reproductible) |
Caractéristiques principales
-> Immutable
Le système de fichiers racine est en lecture seule. Pas de modification possible à runtime, ce qui élimine le drift de configuration.
-> Secure by default
- Pas de SSH
- Pas de shell
- Pas de package manager
- API mutuelle TLS uniquement
-> API-driven
Toute la configuration se fait via talosctl ou des fichiers YAML. Parfait pour l'Infrastructure as Code.
-> Minimal
~80MB d'image, boot en quelques secondes, surface d'attaque réduite.
Architecture du cluster
Pour ce TP, nous allons déployer un cluster minimal sur VirtualBox :
┌─────────────────────────────────────────────────────────┐
│ Votre PC │
│ │
│ ┌─────────────────────────────────────────────────────┐│
│ │ VirtualBox ││
│ │ ││
│ │ ┌─────────────┐ ┌─────────────┐ ││
│ │ │ talos-cp-1 │ │ talos-wk-1 │ ││
│ │ │ (control) │◄──►│ (worker) │ ││
│ │ │ 2CPU/2GB │ │ 2CPU/2GB │ ││
│ │ └─────────────┘ └─────────────┘ ││
│ │ │ ││
│ │ │ API (6443) ││
│ │ ▼ ││
│ │ ┌─────────────┐ ││
│ │ │ talosctl │ ││
│ │ │ kubectl │ ││
│ │ └─────────────┘ ││
│ └─────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────┘Configuration minimale requise :
- 2 VMs (1 control plane + 1 worker)
- 2 CPU et 2 GB RAM par VM
- 20 GB de disque par VM
- Total : 4 CPU, 4 GB RAM, 40 GB disque
Installation sur VirtualBox
Prérequis
-> Sur votre machine hôte :
- VirtualBox installé (https://www.virtualbox.org/)
talosctlinstallékubectlinstallé
Étape 1 : Installer talosctl
# Linux
curl -sL https://talos.dev/install | sh
# Windows (PowerShell en admin)
iwr -useb https://talos.dev/install.ps1 | iex
# Vérifier l'installation
talosctl version --clientÉtape 2 : Télécharger l'ISO Talos
Téléchargez l'ISO depuis les releases GitHub :
# Récupérer la dernière version
curl -LO https://github.com/siderolabs/talos/releases/download/v1.6.0/talos-amd64.isoOu directement depuis : https://github.com/siderolabs/talos/releases
Étape 3 : Configurer le réseau VirtualBox
Avant de créer les VMs, configurez un réseau Host-Only :
- Ouvrez VirtualBox → Fichier → Outils → Network Manager
- Cliquez sur Créer pour ajouter un réseau Host-Only
- Configurez-le :
- Adresse IPv4 :
192.168.56.1 - Masque :
255.255.255.0 - Serveur DHCP : Désactivé (on utilisera des IPs fixes)
- Adresse IPv4 :
Étape 4 : Créer les VMs
VM Control Plane (talos-cp-1)
Nouvelle VM :
- Nom :
talos-cp-1 - Type : Linux
- Version : Other Linux (64-bit)
- Mémoire : 2048 MB
- Disque : Créer un disque virtuel, 20 GB, VDI, Dynamically allocated
- Nom :
Configuration (clic droit → Configuration) :
- Système → Processeur : 2 CPU
- Stockage → Contrôleur IDE → Ajouter l'ISO
talos-amd64.iso - Réseau → Adapter 1 :
- Attached to : Host-only Adapter
- Name : vboxnet0 (celui créé à l'étape 3)
VM Worker (talos-wk-1)
Répétez les mêmes étapes avec :
- Nom :
talos-wk-1 - Mêmes specs (2 CPU, 2 GB RAM, 20 GB disque)
- Même configuration réseau
Étape 5 : Démarrer les VMs et noter les IPs
- Démarrez les deux VMs
- Au boot, Talos affiche l'IP obtenue (ou
waiting for IPsi pas de DHCP)
Comme on n'a pas de DHCP, on va assigner les IPs via la configuration Talos. Pour l'instant, notez les MACs des VMs (visibles dans Configuration → Réseau).
Étape 6 : Générer la configuration Talos
# Créer un dossier pour le projet
mkdir ~/talos-homelab && cd ~/talos-homelab
# Générer les fichiers de configuration
# L'IP sera celle du control plane
talosctl gen config homelab https://192.168.56.10:6443
# Fichiers générés :
# - controlplane.yaml
# - worker.yaml
# - talosconfigÉtape 7 : Personnaliser les configurations
Éditez controlplane.yaml pour configurer l'IP statique :
machine:
type: controlplane
network:
hostname: talos-cp-1
interfaces:
- interface: enp0s3
addresses:
- 192.168.56.10/24
install:
disk: /dev/sda
image: ghcr.io/siderolabs/installer:v1.6.0
cluster:
controlPlane:
endpoint: https://192.168.56.10:6443
network:
cni:
name: flannelÉditez worker.yaml :
machine:
type: worker
network:
hostname: talos-wk-1
interfaces:
- interface: enp0s3
addresses:
- 192.168.56.11/24
install:
disk: /dev/sda
image: ghcr.io/siderolabs/installer:v1.6.0Étape 8 : Appliquer les configurations
Depuis votre machine hôte :
# Configurer talosctl pour utiliser notre config
export TALOSCONFIG=~/talos-homelab/talosconfig
# Appliquer la config au control plane
# --insecure car le cluster n'est pas encore bootstrappé
talosctl apply-config --insecure \
--nodes 192.168.56.10 \
--file controlplane.yaml
# Attendre que la VM redémarre et installe Talos (~2-3 minutes)
# Appliquer la config au worker
talosctl apply-config --insecure \
--nodes 192.168.56.11 \
--file worker.yamlÉtape 9 : Bootstrap le cluster
# Bootstrap Kubernetes sur le control plane
talosctl bootstrap --nodes 192.168.56.10
# Attendre ~2-3 minutes que le cluster démarre
# Récupérer le kubeconfig
talosctl kubeconfig --nodes 192.168.56.10 -f ~/.kube/config
# Vérifier le cluster
kubectl get nodes
# NAME STATUS ROLES AGE VERSION
# talos-cp-1 Ready control-plane 2m v1.29.0
# talos-wk-1 Ready <none> 1m v1.29.0Commandes talosctl essentielles
# Voir l'état des services
talosctl services --nodes 192.168.56.10
# Dashboard en temps réel (très utile !)
talosctl dashboard --nodes 192.168.56.10
# Voir les logs d'un service
talosctl logs kubelet --nodes 192.168.56.10
# Redémarrer un noeud
talosctl reboot --nodes 192.168.56.10
# Mettre à jour Talos
talosctl upgrade --nodes 192.168.56.10 \
--image ghcr.io/siderolabs/installer:v1.6.1
# Mettre à jour Kubernetes
talosctl upgrade-k8s --nodes 192.168.56.10 --to 1.29.1Stockage persistant
Kubernetes a besoin d'un système de stockage pour les données persistantes (bases de données, fichiers uploadés, etc.). Deux options s'offrent à vous selon vos ressources.
Option 1 : local-path-provisioner (léger)
Pour un cluster avec peu de ressources (notre setup VirtualBox), local-path-provisioner de Rancher est idéal :
kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/master/deploy/local-path-storage.yaml-> Avantages
- Très léger (~15 MB de RAM)
- Simple à installer
- Parfait pour le développement
-> Inconvénients
- Pas de réplication (données sur un seul nœud)
- Si le nœud tombe, les données sont perdues
- Pas de snapshots
-> Vérifier l'installation
kubectl get storageclass
# NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION
# local-path (default) rancher.io/local-path Delete WaitForFirstConsumer falseOption 2 : Longhorn (production)
Longhorn est un système de stockage distribué cloud-native développé par Rancher/SUSE. Il réplique les données sur plusieurs nœuds et offre des fonctionnalités avancées.
-> Prérequis matériels
| Ressource | Minimum | Recommandé |
|---|---|---|
| Nœuds | 3 | 3+ |
| CPU par nœud | 4 vCPU | 8 vCPU |
| RAM par nœud | 4 GB | 8 GB |
| Disque par nœud | 50 GB | 100 GB+ |
WARNING
Longhorn consomme environ 500 MB de RAM par nœud. Pour notre cluster VirtualBox minimal (2 nœuds, 2 GB RAM chacun), préférez local-path-provisioner.
-> Configuration Talos pour Longhorn
Longhorn nécessite des modules kernel et des chemins spécifiques. Modifiez vos fichiers de configuration Talos :
# controlplane.yaml et worker.yaml
machine:
kubelet:
extraMounts:
- destination: /var/lib/longhorn
type: bind
source: /var/lib/longhorn
options:
- bind
- rshared
- rw
sysctls:
vm.max_map_count: "262144"
kernel:
modules:
- name: iscsi_tcp
- name: nvme_tcpAppliquez les configurations mises à jour :
talosctl apply-config --nodes 192.168.56.10 --file controlplane.yaml
talosctl apply-config --nodes 192.168.56.11 --file worker.yaml-> Installation de Longhorn via Helm
# Ajouter le repo Helm
helm repo add longhorn https://charts.longhorn.io
helm repo update
# Installer Longhorn
helm install longhorn longhorn/longhorn \
--namespace longhorn-system \
--create-namespace \
--set defaultSettings.defaultDataPath="/var/lib/longhorn" \
--set defaultSettings.defaultReplicaCount=2 \
--set persistence.defaultClassReplicaCount=2-> Vérifier l'installation
# Attendre que tous les pods soient prêts (~2-3 minutes)
kubectl get pods -n longhorn-system -w
# Vérifier le StorageClass
kubectl get storageclass
# NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION
# longhorn (default) driver.longhorn.io Delete Immediate true-> Accéder à l'interface web Longhorn
kubectl port-forward -n longhorn-system svc/longhorn-frontend 8080:80
# Ouvrir http://localhost:8080L'interface permet de :
- Voir l'état des volumes et réplicas
- Créer des snapshots et backups
- Monitorer l'espace disque
- Configurer la réplication
Fonctionnalités Longhorn
Réplication automatique
Longhorn réplique automatiquement les données sur plusieurs nœuds :
┌─────────────────────────────────────────────────────────────┐
│ Volume PVC │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Replica 1 │ │ Replica 2 │ │ Replica 3 │ │
│ │ (node-1) │ │ (node-2) │ │ (node-3) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │ │ │ │
│ └────────────┬────┴──────────────────┘ │
│ │ │
│ ┌───────▼───────┐ │
│ │ Longhorn │ │
│ │ Engine │ │
│ └───────────────┘ │
└─────────────────────────────────────────────────────────────┘Si un nœud tombe, Longhorn reconstruit automatiquement les réplicas sur les nœuds restants.
Snapshots et backups
# Créer un snapshot via kubectl
kubectl apply -f - <<EOF
apiVersion: longhorn.io/v1beta2
kind: Snapshot
metadata:
name: my-snapshot
namespace: longhorn-system
spec:
volume: pvc-xxxx-xxxx # Nom du volume Longhorn
EOF
# Ou via l'interface web : Volume → Take SnapshotBackup vers S3
Configurez un backup target pour sauvegarder vers S3 (ou MinIO) :
# Via Helm values
helm upgrade longhorn longhorn/longhorn \
--namespace longhorn-system \
--set defaultSettings.backupTarget="s3://bucket-name@region/" \
--set defaultSettings.backupTargetCredentialSecret="longhorn-backup-secret"Créez le secret avec les credentials S3 :
apiVersion: v1
kind: Secret
metadata:
name: longhorn-backup-secret
namespace: longhorn-system
type: Opaque
stringData:
AWS_ACCESS_KEY_ID: "your-access-key"
AWS_SECRET_ACCESS_KEY: "your-secret-key"
AWS_ENDPOINTS: "https://s3.amazonaws.com" # ou URL MinIOReadWriteMany (RWX)
Longhorn supporte les volumes RWX (accès simultané depuis plusieurs pods) via NFS :
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: shared-data
spec:
accessModes:
- ReadWriteMany # RWX
storageClassName: longhorn
resources:
requests:
storage: 5GiComparaison des solutions de stockage
| Critère | local-path | Longhorn | Rook-Ceph |
|---|---|---|---|
| Complexité | Très simple | Moyenne | Complexe |
| RAM utilisée | ~15 MB | ~500 MB/nœud | ~2 GB/nœud |
| Réplication | Non | Oui | Oui |
| Snapshots | Non | Oui | Oui |
| RWX | Non | Oui | Oui |
| Cas d'usage | Dev, CI/CD | Homelab, PME | Enterprise |
Migrer de local-path vers Longhorn
Si vous avez commencé avec local-path et voulez migrer vers Longhorn :
Installer Longhorn (voir ci-dessus)
Changer le StorageClass par défaut
# Retirer le défaut de local-path
kubectl patch storageclass local-path -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}'
# Mettre longhorn par défaut
kubectl patch storageclass longhorn -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'- Pour les PVC existants, vous devrez :
- Créer un nouveau PVC avec Longhorn
- Copier les données (via un pod avec les deux PVC montés)
- Mettre à jour le Deployment pour utiliser le nouveau PVC
Déploiement du Homelab
Maintenant que le cluster et le stockage sont prêts, déployons quelques applications.
Applications du homelab
| Application | Description | Ressources |
|---|---|---|
| Glance | Dashboard avec bookmarks et widgets | Très léger (~32 MB) |
| Miniflux | Lecteur RSS minimaliste | Léger (~64 MB) |
| Vaultwarden | Gestionnaire de mots de passe | Léger (~64 MB) |
Structure des manifestes
homelab/
├── glance/
│ ├── namespace.yaml
│ ├── configmap.yaml
│ ├── deployment.yaml
│ └── service.yaml
├── miniflux/
│ ├── namespace.yaml
│ ├── postgres.yaml
│ ├── deployment.yaml
│ └── service.yaml
└── vaultwarden/
├── namespace.yaml
├── secret.yaml
├── pvc.yaml
├── deployment.yaml
└── service.yamlGlance - Dashboard
Glance est un dashboard léger et élégant pour visualiser vos services.
# glance/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: glance
---
# glance/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: glance-config
namespace: glance
data:
glance.yml: |
pages:
- name: Home
columns:
- size: small
widgets:
- type: clock
hour-format: "24h"
- type: weather
location: Paris, France
- size: full
widgets:
- type: bookmarks
groups:
- title: Homelab
links:
- title: Vaultwarden
url: http://192.168.56.10:30080
- title: Miniflux
url: http://192.168.56.10:30081
- title: Kubernetes
links:
- title: Documentation K8s
url: https://kubernetes.io/docs/
- title: Talos Docs
url: https://www.talos.dev/docs/
- type: rss
limit: 5
feeds:
- url: https://www.reddit.com/r/selfhosted/.rss
title: r/selfhosted
---
# glance/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: glance
namespace: glance
spec:
replicas: 1
selector:
matchLabels:
app: glance
template:
metadata:
labels:
app: glance
spec:
containers:
- name: glance
image: glanceapp/glance:latest
ports:
- containerPort: 8080
volumeMounts:
- name: config
mountPath: /app/glance.yml
subPath: glance.yml
resources:
requests:
memory: "32Mi"
cpu: "10m"
limits:
memory: "128Mi"
cpu: "100m"
volumes:
- name: config
configMap:
name: glance-config
---
# glance/service.yaml
apiVersion: v1
kind: Service
metadata:
name: glance
namespace: glance
spec:
type: NodePort
selector:
app: glance
ports:
- port: 8080
targetPort: 8080
nodePort: 30000-> Déployer Glance
kubectl apply -f glance/
# namespace/glance created
# configmap/glance-config created
# deployment.apps/glance created
# service/glance created
kubectl get pods -n glance
# NAME READY STATUS RESTARTS AGE
# glance-xxxxxxxxx-xxxxx 1/1 Running 0 30s-> Accéder à Glance
Ouvrez votre navigateur : http://192.168.56.10:30000
Vaultwarden - Gestionnaire de mots de passe
Vaultwarden est une implémentation légère et compatible avec Bitwarden.
# vaultwarden/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: vaultwarden
---
# vaultwarden/secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: vaultwarden-secrets
namespace: vaultwarden
type: Opaque
stringData:
admin-token: "votre-token-admin-genere" # openssl rand -hex 32
---
# vaultwarden/pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: vaultwarden-data
namespace: vaultwarden
spec:
accessModes:
- ReadWriteOnce
storageClassName: local-path
resources:
requests:
storage: 1Gi
---
# vaultwarden/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: vaultwarden
namespace: vaultwarden
spec:
replicas: 1
selector:
matchLabels:
app: vaultwarden
template:
metadata:
labels:
app: vaultwarden
spec:
containers:
- name: vaultwarden
image: vaultwarden/server:latest
env:
- name: DOMAIN
value: "http://192.168.56.10:30080"
- name: SIGNUPS_ALLOWED
value: "true" # Mettre à false après création du premier compte
- name: ADMIN_TOKEN
valueFrom:
secretKeyRef:
name: vaultwarden-secrets
key: admin-token
ports:
- containerPort: 80
volumeMounts:
- name: data
mountPath: /data
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "256Mi"
cpu: "200m"
volumes:
- name: data
persistentVolumeClaim:
claimName: vaultwarden-data
---
# vaultwarden/service.yaml
apiVersion: v1
kind: Service
metadata:
name: vaultwarden
namespace: vaultwarden
spec:
type: NodePort
selector:
app: vaultwarden
ports:
- port: 80
targetPort: 80
nodePort: 30080-> Déployer Vaultwarden
# Générer un token admin
openssl rand -hex 32
# Copiez le résultat et mettez-le dans le secret
kubectl apply -f vaultwarden/-> Accéder à Vaultwarden
- Application : http://192.168.56.10:30080
- Admin panel : http://192.168.56.10:30080/admin
Miniflux - Lecteur RSS
Miniflux nécessite PostgreSQL. On va déployer les deux ensemble.
# miniflux/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: miniflux
---
# miniflux/postgres.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-data
namespace: miniflux
spec:
accessModes:
- ReadWriteOnce
storageClassName: local-path
resources:
requests:
storage: 1Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres
namespace: miniflux
spec:
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:15-alpine
env:
- name: POSTGRES_USER
value: "miniflux"
- name: POSTGRES_PASSWORD
value: "miniflux123"
- name: POSTGRES_DB
value: "miniflux"
ports:
- containerPort: 5432
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "500m"
volumes:
- name: data
persistentVolumeClaim:
claimName: postgres-data
---
apiVersion: v1
kind: Service
metadata:
name: postgres
namespace: miniflux
spec:
selector:
app: postgres
ports:
- port: 5432
targetPort: 5432
---
# miniflux/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: miniflux
namespace: miniflux
spec:
replicas: 1
selector:
matchLabels:
app: miniflux
template:
metadata:
labels:
app: miniflux
spec:
initContainers:
- name: wait-for-postgres
image: busybox
command: ['sh', '-c', 'until nc -z postgres 5432; do echo waiting for postgres; sleep 2; done']
containers:
- name: miniflux
image: miniflux/miniflux:latest
env:
- name: DATABASE_URL
value: "postgres://miniflux:miniflux123@postgres:5432/miniflux?sslmode=disable"
- name: RUN_MIGRATIONS
value: "1"
- name: CREATE_ADMIN
value: "1"
- name: ADMIN_USERNAME
value: "admin"
- name: ADMIN_PASSWORD
value: "admin123" # Changez ce mot de passe !
ports:
- containerPort: 8080
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "256Mi"
cpu: "500m"
---
# miniflux/service.yaml
apiVersion: v1
kind: Service
metadata:
name: miniflux
namespace: miniflux
spec:
type: NodePort
selector:
app: miniflux
ports:
- port: 8080
targetPort: 8080
nodePort: 30081-> Déployer Miniflux
kubectl apply -f miniflux/
# Attendre que postgres soit prêt avant miniflux
kubectl get pods -n miniflux -w-> Accéder à Miniflux
- URL : http://192.168.56.10:30081
- Login : admin / admin123
Récapitulatif
URLs des applications
| Application | URL | Credentials |
|---|---|---|
| Glance | http://192.168.56.10:30000 | - |
| Vaultwarden | http://192.168.56.10:30080 | À créer |
| Vaultwarden Admin | http://192.168.56.10:30080/admin | Token dans le secret |
| Miniflux | http://192.168.56.10:30081 | admin / admin123 |
Vérifier l'état du cluster
# Voir tous les pods
kubectl get pods -A
# Voir l'utilisation des ressources
kubectl top nodes
kubectl top pods -A
# Dashboard Talos
talosctl dashboard --nodes 192.168.56.10Ressources consommées
Avec les 3 applications déployées, vous devriez avoir :
| Ressource | Utilisé | Disponible |
|---|---|---|
| CPU | ~300m | 4000m |
| RAM | ~700Mi | 4Gi |
Il reste de la marge pour expérimenter !
Prochaines étapes
Pour aller plus loin avec votre homelab :
- Ajouter plus de RAM aux VMs pour déployer plus d'applications
- Configurer un Ingress Controller (Traefik) pour avoir des URLs propres
- Ajouter des certificats TLS avec cert-manager
- Configurer des backups de vos données
- Ajouter du monitoring avec Prometheus et Grafana (si ressources suffisantes)
Nettoyage
Pour supprimer le homelab :
# Supprimer les applications
kubectl delete namespace glance miniflux vaultwarden
# Ou supprimer le cluster entier
talosctl reset --nodes 192.168.56.10,192.168.56.11 --graceful=falsePour supprimer les VMs VirtualBox, éteignez-les et supprimez-les depuis l'interface VirtualBox.