From 0bdfe49d75dc47ce6174bb30db3689c6b97f93d1 Mon Sep 17 00:00:00 2001 From: alkatrazz Date: Tue, 12 May 2026 22:51:03 +0200 Subject: [PATCH] docs: guide d'installation complet cluster Talos/k8s MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Couvre de A à Z : outils, SOPS/age, talconfig, installation des 3 nœuds, bootstrap etcd, MetalLB, Traefik, DNS wildcard. Section pièges documentant toutes les erreurs rencontrées lors de l'installation réelle. Co-Authored-By: Claude Sonnet 4.6 --- admin/installe.md | 935 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 935 insertions(+) create mode 100644 admin/installe.md diff --git a/admin/installe.md b/admin/installe.md new file mode 100644 index 0000000..8f49ba2 --- /dev/null +++ b/admin/installe.md @@ -0,0 +1,935 @@ +# Installation complète du cluster Kubernetes Funk + +Guide d'installation de A à Z — écrit après avoir effectué l'installation réelle sur les 3 ThinkCentre. Toutes les erreurs rencontrées sont documentées dans la section [Pièges et erreurs](#pièges-et-erreurs-rencontrés) en bas. + +--- + +## Sommaire + +1. [Contexte et architecture](#1-contexte-et-architecture) +2. [Prérequis réseau — Ansible (dnsmasq + nftables)](#2-prérequis-réseau--ansible-dnsmasq--nftables) +3. [Installer les outils sur le poste perso](#3-installer-les-outils-sur-le-poste-perso) +4. [Installer les outils sur storage-01](#4-installer-les-outils-sur-storage-01) +5. [Configurer SOPS + age (chiffrement des secrets)](#5-configurer-sops--age-chiffrement-des-secrets) +6. [Écrire la configuration Talos](#6-écrire-la-configuration-talos) +7. [Générer les secrets et les configs nœuds](#7-générer-les-secrets-et-les-configs-nœuds) +8. [Préparer la clé USB Talos](#8-préparer-la-clé-usb-talos) +9. [Installer chaque nœud](#9-installer-chaque-nœud) +10. [Bootstrap etcd](#10-bootstrap-etcd) +11. [Récupérer le kubeconfig](#11-récupérer-le-kubeconfig) +12. [Vérifier que les nœuds sont Ready](#12-vérifier-que-les-nœuds-sont-ready) +13. [Installer MetalLB](#13-installer-metalLB) +14. [Installer Traefik](#14-installer-traefik) +15. [Configurer le DNS wildcard *.lab.local](#15-configurer-le-dns-wildcard-lablocal) +16. [Vérification finale](#16-vérification-finale) +17. [Administration courante](#17-administration-courante) +18. [Pièges et erreurs rencontrés](#18-pièges-et-erreurs-rencontrés) + +--- + +## 1. Contexte et architecture + +### Matériel + +Trois Lenovo ThinkCentre M715q (AMD A10-9700E, 2.4 GHz, 4 cœurs) récupérés et réutilisés en cluster. + +| Machine | IP fixe | Rôle | Disque | RAM | +|---|---|---|---|---| +| compute-01 | 192.168.10.11 | control-plane | NVMe 256 GB | 16 GB | +| compute-02 | 192.168.10.12 | worker | NVMe 256 GB | 8 GB | +| compute-03 | 192.168.10.13 | worker | NVMe 256 GB | 8 GB | + +Les machines sont dans un LAN dédié `192.168.10.0/24`, isolé du LAN domestique (`192.168.1.0/24`) par storage-01 qui fait passerelle + NAT. + +### Stack logicielle + +| Composant | Version | Rôle | +|---|---|---| +| Talos Linux | v1.13.0 | OS immuable pour k8s — géré entièrement via API | +| Kubernetes | v1.33.1 | Orchestration des workloads | +| talhelper | latest | Génère les configs Talos depuis `talconfig.yaml` | +| SOPS + age | v3.9.4 / latest | Chiffrement des secrets dans git | +| Flannel | intégré Talos | CNI (réseau pods) — CIDR `10.42.0.0/16` | +| MetalLB | latest (Helm) | LoadBalancer bare-metal — pool `192.168.10.200-230` | +| Traefik | v3.x (Helm) | Ingress controller — IP fixe `192.168.10.200` | +| Helm | v3.x | Gestionnaire de charts k8s | + +### Pourquoi Talos Linux ? + +Talos est un OS immuable conçu exclusivement pour faire tourner Kubernetes. Il n'a pas de shell, pas de SSH, pas de package manager. Tout se fait via son API (`talosctl`). Ça force des bonnes pratiques : configuration déclarative, secrets chiffrés dans git, zéro dérive manuelle possible. + +### Schéma réseau + +``` +Internet + │ +Freebox (192.168.1.254) + │ + ├── LAN domestique 192.168.1.0/24 + │ └── Poste perso (192.168.1.10) + │ + └── storage-01 WAN (192.168.1.200) + │ + NAT + routage + │ + storage-01 LAN (192.168.10.1) + │ + LAN cluster 192.168.10.0/24 + ├── compute-01 192.168.10.11 + ├── compute-02 192.168.10.12 + ├── compute-03 192.168.10.13 + └── gpu-01 192.168.10.20 +``` + +--- + +## 2. Prérequis réseau — Ansible (dnsmasq + nftables) + +Avant de toucher aux nœuds Talos, storage-01 doit être opérationnel avec : +- **dnsmasq** : sert le DHCP sur le LAN cluster (les nœuds en maintenance mode se prennent une IP DHCP) + DNS +- **nftables** : le port 67 (DHCP) doit être ouvert côté LAN + +Ces deux rôles sont gérés par Ansible. Si ce n'est pas encore fait : + +```bash +cd ansible/ +ansible-playbook -i inventory.yml playbooks/site.yml --tags dnsmasq,gateway +``` + +### Ce que dnsmasq doit avoir dans sa config + +Fichier `ansible/roles/dnsmasq/defaults/main.yml` : +```yaml +dhcp_range_start: 192.168.10.50 +dhcp_range_end: 192.168.10.99 +dhcp_lease_time: 12h + +dhcp_static_hosts: + - mac: "6c:4b:90:82:8e:47" + name: compute-01 + ip: 192.168.10.11 + - mac: "6c:4b:90:cf:7f:c5" + name: compute-02 + ip: 192.168.10.12 + - mac: "6c:4b:90:b6:49:20" + name: compute-03 + ip: 192.168.10.13 +``` + +> Si les baux statiques ne sont pas encore configurés au moment de l'installation, les nœuds prendront une IP dynamique dans `192.168.10.50-99`. C'est normal — noter l'IP affiché sur l'écran du ThinkCentre ou dans les logs dnsmasq. + +### Ce que nftables doit avoir + +Le port 67 (DHCP) doit être ouvert : +``` +udp dport 67 iif {{ lan_interface }} accept +``` + +> Sans cette règle, les requêtes DHCP des nœuds sont silencieusement droppées — les nœuds restent sans IP et `talosctl apply-config` ne peut pas les atteindre. + +--- + +## 3. Installer les outils sur le poste perso + +Tout cela s'installe dans `/usr/local/bin`. + +```bash +# talhelper — génère les configs Talos depuis talconfig.yaml +curl -sL https://github.com/budimanjojo/talhelper/releases/latest/download/talhelper_linux_amd64.tar.gz \ + | sudo tar xz -C /usr/local/bin talhelper + +# age + age-keygen — chiffrement asymétrique des secrets +AGE_VERSION=$(curl -s https://api.github.com/repos/FiloSottile/age/releases/latest | grep tag_name | cut -d'"' -f4) +curl -sL "https://github.com/FiloSottile/age/releases/download/${AGE_VERSION}/age-${AGE_VERSION}-linux-amd64.tar.gz" \ + | sudo tar xz -C /tmp +sudo mv /tmp/age/age /usr/local/bin/ +sudo mv /tmp/age/age-keygen /usr/local/bin/ + +# sops — orchestrateur de chiffrement (utilise age en backend) +sudo curl -sL "https://github.com/getsops/sops/releases/download/v3.9.4/sops-v3.9.4.linux.amd64" \ + -o /usr/local/bin/sops +sudo chmod +x /usr/local/bin/sops + +# talosctl — CLI pour administrer les nœuds Talos +curl -sL https://talos.dev/install | sh + +# kubectl — CLI Kubernetes standard +curl -sL "https://dl.k8s.io/release/$(curl -sL https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" \ + -o /tmp/kubectl +sudo install /tmp/kubectl /usr/local/bin/kubectl + +# helm — gestionnaire de charts k8s +curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash +``` + +Vérifier : +```bash +talhelper --version +age-keygen --version +sops --version +talosctl version --client +kubectl version --client +helm version +``` + +--- + +## 4. Installer les outils sur storage-01 + +Depuis le poste perso, se connecter en SSH puis rejouer les mêmes commandes. storage-01 doit pouvoir administrer le cluster indépendamment (backup etcd, urgences...). + +```bash +ssh storage-01 + +# talhelper +curl -sL https://github.com/budimanjojo/talhelper/releases/latest/download/talhelper_linux_amd64.tar.gz \ + | sudo tar xz -C /usr/local/bin talhelper + +# age +AGE_VERSION=$(curl -s https://api.github.com/repos/FiloSottile/age/releases/latest | grep tag_name | cut -d'"' -f4) +curl -sL "https://github.com/FiloSottile/age/releases/download/${AGE_VERSION}/age-${AGE_VERSION}-linux-amd64.tar.gz" \ + | sudo tar xz -C /tmp +sudo mv /tmp/age/age /usr/local/bin/ +sudo mv /tmp/age/age-keygen /usr/local/bin/ + +# sops +sudo curl -sL "https://github.com/getsops/sops/releases/download/v3.9.4/sops-v3.9.4.linux.amd64" \ + -o /usr/local/bin/sops +sudo chmod +x /usr/local/bin/sops + +# talosctl +curl -sL https://talos.dev/install | sh + +# kubectl +curl -sL "https://dl.k8s.io/release/$(curl -sL https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" \ + -o /tmp/kubectl +sudo install /tmp/kubectl /usr/local/bin/kubectl + +# helm +curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash +``` + +--- + +## 5. Configurer SOPS + age (chiffrement des secrets) + +Les secrets Talos (clés PKI du cluster) doivent être dans git mais ne peuvent pas être en clair. SOPS les chiffre avec age. + +### Générer la clé age (une fois par poste d'administration) + +**Sur le poste perso :** +```bash +mkdir -p ~/.config/sops/age +age-keygen -o ~/.config/sops/age/keys.txt +``` + +La sortie affiche la clé publique, exemple : +``` +Public key: age16rg9hnvnu89rll5schg8d2q5sccxlp7cxms9jmflguxapmtjp3lq0j4393 +``` + +> La clé privée est dans `~/.config/sops/age/keys.txt`. Ne jamais la committer. Elle est dans `.gitignore`. + +**Sur storage-01 (si administration depuis storage-01 souhaitée) :** + +Option A — copier la même clé privée (plus simple) : +```bash +scp ~/.config/sops/age/keys.txt storage-01:~/.config/sops/age/keys.txt +``` + +Option B — générer une clé séparée et l'ajouter dans `.sops.yaml` (plus sécurisé, mais nécessite de recréer `talsecret.sops.yaml`). + +### Créer le fichier `.sops.yaml` à la racine du repo + +```yaml +creation_rules: + - path_regex: \.sops\.yaml$ + age: age16rg9hnvnu89rll5schg8d2q5sccxlp7cxms9jmflguxapmtjp3lq0j4393 +``` + +> La `path_regex` doit matcher le fichier `talsecret.sops.yaml`. Une regex trop restrictive (ex: `talos/.*\.sops\.yaml$`) peut ne pas matcher si SOPS calcule le chemin relatif différemment. La regex `\.sops\.yaml$` est plus robuste. + +--- + +## 6. Écrire la configuration Talos + +### `talos/talconfig.yaml` — source de vérité du cluster + +```yaml +clusterName: funk +talosVersion: v1.13.0 +kubernetesVersion: v1.33.1 +endpoint: https://192.168.10.11:6443 +domain: cluster.local +allowSchedulingOnControlPlanes: false + +clusterPodNets: + - 10.42.0.0/16 +clusterSvcNets: + - 10.43.0.0/16 + +cniConfig: + name: flannel + +patches: + - "@./patches/all.yaml" + +nodes: + - hostname: compute-01 + ipAddress: 192.168.10.11 + installDisk: /dev/nvme0n1 + controlPlane: true + networkInterfaces: + - deviceSelector: + driver: r8169 + dhcp: false + addresses: + - 192.168.10.11/24 + routes: + - network: 0.0.0.0/0 + gateway: 192.168.10.1 + + - hostname: compute-02 + ipAddress: 192.168.10.12 + installDisk: /dev/nvme0n1 + controlPlane: false + patches: + - "@./patches/workers.yaml" + networkInterfaces: + - deviceSelector: + driver: r8169 + dhcp: false + addresses: + - 192.168.10.12/24 + routes: + - network: 0.0.0.0/0 + gateway: 192.168.10.1 + + - hostname: compute-03 + ipAddress: 192.168.10.13 + installDisk: /dev/nvme0n1 + controlPlane: false + patches: + - "@./patches/workers.yaml" + networkInterfaces: + - deviceSelector: + driver: r8169 + dhcp: false + addresses: + - 192.168.10.13/24 + routes: + - network: 0.0.0.0/0 + gateway: 192.168.10.1 +``` + +Points critiques : +- `installDisk: /dev/nvme0n1` — les ThinkCentre ont un NVMe, **pas** `/dev/sda` (qui est la clé USB) +- `endpoint: https://192.168.10.11:6443` — IP directe du control-plane, pas de VIP +- Pas de VIP configuré — un VIP = même IP que le nœud = casse etcd au bootstrap +- `cniConfig: name: flannel` — Talos gère Flannel nativement, ne pas appliquer de manifest externe +- `deviceSelector: driver: r8169` — carte réseau des ThinkCentre M715q + +### `talos/patches/all.yaml` — appliqué à tous les nœuds + +```yaml +machine: + time: + servers: + - 192.168.10.1 + - time.cloudflare.com + network: + nameservers: + - 192.168.10.1 +``` + +NTP et DNS pointent vers storage-01 (`192.168.10.1`) qui est la passerelle + dnsmasq du LAN cluster. + +### `talos/patches/workers.yaml` — appliqué aux workers seulement + +```yaml +machine: + kubelet: + extraArgs: + system-reserved: cpu=500m,memory=1Gi + kube-reserved: cpu=500m,memory=1Gi +``` + +compute-02 et compute-03 n'ont que 8 GB de RAM. Ces réservations protègent l'OS et kubelet de l'OOM killer quand les workloads chargent. + +--- + +## 7. Générer les secrets et les configs nœuds + +```bash +cd talos/ + +# Générer les secrets du cluster (une seule fois — contient les PKI, tokens, etc.) +talhelper gensecret | sops --filename-override talsecret.sops.yaml --encrypt /dev/stdin > talsecret.sops.yaml + +# Vérifier que le fichier est bien chiffré (doit commencer par "sops:") +head -5 talsecret.sops.yaml + +# Générer les configs par nœud + talosconfig dans clusterconfig/ +talhelper genconfig +``` + +Le dossier `talos/clusterconfig/` est dans `.gitignore` — il est généré localement à partir des secrets déchiffrés. Ne pas committer. + +```bash +ls talos/clusterconfig/ +# funk-compute-01.yaml +# funk-compute-02.yaml +# funk-compute-03.yaml +# talosconfig +``` + +Committer les fichiers sources (pas le `clusterconfig/`) : +```bash +git add talos/talconfig.yaml talos/talsecret.sops.yaml talos/patches/ .sops.yaml +git commit -m "feat: talos cluster config funk" +``` + +--- + +## 8. Préparer la clé USB Talos + +Télécharger l'ISO Talos v1.13.0 (metal AMD64) depuis `https://github.com/siderolabs/talos/releases`. + +```bash +# Identifier le device de la clé USB +lsblk + +# Flasher — remplacer /dev/sdX par le bon device +sudo dd if=/chemin/vers/metal-amd64.iso of=/dev/sdX bs=4M status=progress conv=fsync +sync +``` + +> **Utiliser `dd` direct, pas Ventoy.** Talos doit être le seul système bootable sur la clé. Ventoy ajoute ses propres entrées EFI qui peuvent interférer. + +La même clé peut être utilisée pour les 3 nœuds successivement. + +--- + +## 9. Installer chaque nœud + +Le processus est identique pour les 3 machines. Les étapes pour chaque nœud : + +### Étape A — Démarrer en maintenance mode + +1. Brancher la clé USB dans le ThinkCentre +2. Allumer ou redémarrer +3. Si le BIOS démarre sur un autre OS : appuyer sur **F12** au boot pour choisir la clé USB +4. Talos démarre en "maintenance mode" (pas encore d'OS installé) +5. Le nœud obtient une IP DHCP de dnsmasq dans `192.168.10.50-99` + +### Étape B — Identifier l'IP DHCP attribuée + +```bash +# Option 1 — regarder les logs dnsmasq sur storage-01 +ssh storage-01 "journalctl -u dnsmasq -n 20 | grep DHCP" + +# Option 2 — scanner le réseau +nmap -sn 192.168.10.50-99 +``` + +### Étape C — Vérifier le disque cible (IMPORTANT) + +Avant d'appliquer la config, confirmer que `nvme0n1` existe bien : + +```bash +export TALOSCONFIG=talos/clusterconfig/talosconfig +talosctl get disks --insecure --nodes +``` + +Exemple de sortie attendue : +``` +NODE NAMESPACE TYPE ID SIZE TRANSPORT ROTATIONAL +192.168.10.78 runtime Disk nvme0n1 238 GB nvme false +192.168.10.78 runtime Disk sda 16 GB usb false +``` + +> `nvme0n1` = le SSD NVMe interne → cible de l'installation. +> `sda` = la clé USB → **ne PAS mettre ça dans `installDisk`**. + +### Étape D — Appliquer la config et déclencher l'installation + +```bash +# compute-01 (remplacer l'IP DHCP réelle) +talosctl apply-config --insecure --nodes 192.168.10.78 \ + --file talos/clusterconfig/funk-compute-01.yaml + +# compute-02 +talosctl apply-config --insecure --nodes \ + --file talos/clusterconfig/funk-compute-02.yaml + +# compute-03 +talosctl apply-config --insecure --nodes \ + --file talos/clusterconfig/funk-compute-03.yaml +``` + +Après l'application de la config : +1. Talos installe automatiquement sur `/dev/nvme0n1` +2. Il efface les entrées EFI existantes sur le NVMe +3. Il reboot +4. Après reboot, le nœud démarre sur son IP fixe configurée dans `talconfig.yaml` + +Vérifier que le nœud est disponible sur son IP fixe : +```bash +talosctl --nodes 192.168.10.11 get members +# ou juste un ping +ping 192.168.10.11 +``` + +La clé USB n'est plus nécessaire après la première installation réussie. + +### Si le nœud reboot sur un ancien OS + +Si le ThinkCentre avait déjà un OS (Windows, Linux...), ses entrées EFI peuvent prendre la priorité. + +**Solution A — Retirer la clé USB pendant que Talos est en train de s'installer.** Le reboot se fait directement sur le NVMe fraîchement installé. + +**Solution B — Via BIOS** : entrer dans le BIOS (F1 sur ThinkCentre), aller dans Boot Order, mettre le NVMe en premier. + +**Solution C — Force via `talosctl upgrade`** (si Talos démarre mais depuis la clé, pas le NVMe) : +```bash +talosctl upgrade --image ghcr.io/siderolabs/installer:v1.13.0 --nodes +``` + +--- + +## 10. Bootstrap etcd + +Une fois les 3 nœuds sur leurs IPs fixes, initialiser etcd sur le control-plane. + +```bash +talosctl bootstrap --nodes 192.168.10.11 +``` + +> **Cette commande ne se lance qu'une seule fois, jamais sur un cluster existant.** Relancer bootstrap casse etcd irrémédiablement — il faudrait tout réinstaller. + +Attendre environ 2 minutes que etcd démarre et que Kubernetes s'initialise : +```bash +talosctl --nodes 192.168.10.11 service etcd +# Attendre que l'état passe à "Running" +``` + +--- + +## 11. Récupérer le kubeconfig + +```bash +# Sur le poste perso +talosctl kubeconfig --nodes 192.168.10.11 ~/.kube/config + +# Sur storage-01 (depuis storage-01 après avoir cloné le repo et régénéré clusterconfig/) +talosctl kubeconfig --nodes 192.168.10.11 ~/.kube/config +``` + +> storage-01 doit avoir son propre `clusterconfig/` généré localement (après avoir copié la clé age privée et `talsecret.sops.yaml`). Il faut faire `talhelper genconfig` sur storage-01 également. + +--- + +## 12. Vérifier que les nœuds sont Ready + +```bash +kubectl get nodes -o wide +``` + +Sortie attendue : +``` +NAME STATUS ROLES AGE VERSION INTERNAL-IP +compute-01 Ready control-plane 5m v1.33.1 192.168.10.11 +compute-02 Ready 4m v1.33.1 192.168.10.12 +compute-03 Ready 4m v1.33.1 192.168.10.13 +``` + +Si les nœuds sont `NotReady`, c'est normal dans les premières minutes — Flannel intégré à Talos démarre automatiquement. Patienter 1-2 minutes. + +> **Ne pas appliquer le manifest Flannel externe** (`kubectl apply -f kube-flannel.yml`). Talos v1.13 inclut Flannel nativement dans `kube-system` avec le bon CIDR `10.42.0.0/16`. Appliquer le manifest externe crée deux DaemonSets Flannel en conflit. + +--- + +## 13. Installer MetalLB + +MetalLB permet d'assigner des IPs réelles (du LAN cluster) aux services Kubernetes de type `LoadBalancer`. Sans MetalLB, les services LoadBalancer restent en `` indéfiniment. + +### Installation via Helm + +```bash +helm repo add metallb https://metallb.github.io/metallb +helm repo update + +kubectl create namespace metallb-system + +helm install metallb metallb/metallb --namespace metallb-system + +# Attendre que tous les pods soient Running (controller + 3 speakers) +kubectl get pods -n metallb-system -w +``` + +Sortie attendue : +``` +NAME READY STATUS RESTARTS AGE +controller-7dd9f6d5f9-xxxxx 1/1 Running 0 2m +speaker-xxxxx 1/1 Running 0 2m +speaker-xxxxx 1/1 Running 0 2m +speaker-xxxxx 1/1 Running 0 2m +``` + +### Configuration du pool d'IPs + +```bash +cat <<'EOF' | kubectl apply -f - +apiVersion: metallb.io/v1beta1 +kind: IPAddressPool +metadata: + name: funk-pool + namespace: metallb-system +spec: + addresses: + - 192.168.10.200-192.168.10.230 +--- +apiVersion: metallb.io/v1beta1 +kind: L2Advertisement +metadata: + name: funk-l2 + namespace: metallb-system +spec: + ipAddressPools: + - funk-pool +EOF +``` + +> Si l'application échoue avec une erreur de webhook (`connection refused` vers `metallb-webhook-service.metallb-system.svc:443`), c'est un signe que le réseau pod est instable. Vérifier d'abord que tous les pods Flannel sont `1/1 Running`. + +--- + +## 14. Installer Traefik + +Traefik est l'ingress controller — il reçoit tout le trafic HTTP/HTTPS entrant sur `192.168.10.200` et le route vers les bons services en fonction des IngressRoute. + +```bash +helm repo add traefik https://traefik.github.io/charts +helm repo update + +kubectl create namespace infra + +helm install traefik traefik/traefik \ + --namespace infra \ + --set service.type=LoadBalancer \ + --set service.loadBalancerIP=192.168.10.200 \ + --set ports.web.port=80 \ + --set ports.websecure.port=443 \ + --set ingressRoute.dashboard.enabled=true \ + --set logs.general.level=INFO +``` + +Vérifier que MetalLB a bien assigné l'IP : +```bash +kubectl get svc -n infra traefik +``` + +Sortie attendue : +``` +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) +traefik LoadBalancer 10.43.192.29 192.168.10.200 80:30507/TCP,443:30480/TCP +``` + +### Accéder au dashboard Traefik + +Le dashboard est disponible sur le port 9000 via un IngressRoute interne : +```bash +kubectl port-forward -n infra svc/traefik 9000:9000 +# Ouvrir http://localhost:9000/dashboard/ +``` + +--- + +## 15. Configurer le DNS wildcard *.lab.local + +Le rôle Ansible `dnsmasq` gère ça via une ligne dans la config dnsmasq : + +``` +address=/.lab.local/192.168.10.200 +``` + +Elle est générée automatiquement si la variable `traefik_ip` est définie dans `ansible/roles/dnsmasq/defaults/main.yml` : + +```yaml +traefik_ip: 192.168.10.200 +``` + +Et dans le template `dnsmasq.conf.j2` : +```jinja2 +address=/.{{ dns_domain }}/{{ traefik_ip }} +``` + +Appliquer : +```bash +ansible-playbook -i ansible/inventory.yml ansible/playbooks/site.yml --tags dnsmasq +``` + +Tester depuis le poste perso ou storage-01 : +```bash +dig @192.168.10.1 monservice.lab.local +short +# → 192.168.10.200 +``` + +Avec ça, n'importe quel `.lab.local` sera résolu vers Traefik, qui se charge du routage vers le bon service k8s. + +--- + +## 16. Vérification finale + +```bash +# État des nœuds +kubectl get nodes -o wide + +# Tous les pods (rien ne doit être en erreur) +kubectl get pods -A + +# MetalLB +kubectl get pods -n metallb-system + +# Traefik avec son IP +kubectl get svc -n infra traefik + +# Flannel (doit être 3/3 Running) +kubectl get pods -n kube-system | grep flannel + +# Test DNS wildcard depuis storage-01 +ssh storage-01 "dig @192.168.10.1 test.lab.local +short" +``` + +Sortie finale attendue : +``` +NAME STATUS ROLES AGE VERSION +compute-01 Ready control-plane 1h v1.33.1 +compute-02 Ready 1h v1.33.1 +compute-03 Ready 1h v1.33.1 +``` + +--- + +## 17. Administration courante + +### Variables d'environnement (à mettre dans ~/.bashrc) + +```bash +export TALOSCONFIG=~/Projets/lab/talos/clusterconfig/talosconfig +export KUBECONFIG=~/.kube/config +``` + +### Commandes du quotidien + +```bash +# État global +kubectl get nodes -o wide +kubectl get pods -A +kubectl get events -A --sort-by='.lastTimestamp' + +# État Talos (services internes) +talosctl --nodes 192.168.10.11,192.168.10.12,192.168.10.13 service + +# Logs d'un service Talos +talosctl --nodes 192.168.10.11 dmesg | tail -30 +talosctl --nodes 192.168.10.11 service etcd + +# Logs d'un pod k8s +kubectl logs -n +kubectl logs -n -f # follow + +# Reboot propre d'un nœud +talosctl --nodes 192.168.10.12 reboot + +# Shutdown d'un nœud +talosctl --nodes 192.168.10.12 shutdown + +# Tous les workers d'un coup +talosctl --nodes 192.168.10.12,192.168.10.13 reboot +``` + +### Mise à jour de la config d'un nœud + +```bash +cd talos/ + +# 1. Modifier talconfig.yaml +vim talconfig.yaml + +# 2. Régénérer les configs +talhelper genconfig + +# 3. Appliquer sur le nœud (sans --insecure cette fois, on connaît déjà le nœud) +talosctl --nodes 192.168.10.11 apply-config \ + --file clusterconfig/funk-compute-01.yaml +``` + +### Backup etcd (critique — single control-plane) + +```bash +talosctl --nodes 192.168.10.11 etcd snapshot /tmp/etcd-backup-$(date +%Y%m%d).db +scp /tmp/etcd-backup-*.db storage-01:/srv/data/backups/etcd/ +``` + +> À planifier en cron hebdomadaire. Sur un single control-plane, la perte de etcd sans backup = reinstallation complète du cluster. + +### Régénérer les configs depuis un nouveau poste + +Si tu changes de poste ou récupères le repo sur storage-01 : + +```bash +# 1. Copier la clé age privée +mkdir -p ~/.config/sops/age +# Copier keys.txt depuis l'ancien poste + +# 2. Cloner le repo +git clone git@github.com:Alkatrazz24/Funk-lab.git + +# 3. Régénérer clusterconfig/ (déchiffre talsecret.sops.yaml avec la clé age) +cd Funk-lab/talos +talhelper genconfig + +# 4. Récupérer le kubeconfig +talosctl kubeconfig --nodes 192.168.10.11 ~/.kube/config +``` + +--- + +## 18. Pièges et erreurs rencontrés + +Tout ce qui a planté lors de l'installation réelle, pour ne pas y retomber. + +--- + +### ❌ installDisk = /dev/sda → Talos installe sur la clé USB + +**Symptôme** : Le nœud reboot mais reste en maintenance mode ou redémarre en boucle. + +**Cause** : `installDisk: /dev/sda` dans `talconfig.yaml`. Sur les ThinkCentre M715q, `/dev/sda` est la clé USB. Le NVMe interne est `/dev/nvme0n1`. + +**Vérification avant d'appliquer** : +```bash +talosctl get disks --insecure --nodes +# Chercher le disk avec TRANSPORT=nvme +``` + +**Fix** : Corriger `talconfig.yaml`, régénérer avec `talhelper genconfig`, reflasher la clé USB avec le bon ISO, redémarrer le nœud en maintenance mode, réappliquer. + +--- + +### ❌ VIP = même IP que le nœud → etcd bloque sur "Waiting for etcd spec" + +**Symptôme** : Après `talosctl bootstrap`, etcd ne démarre jamais. `talosctl service etcd` montre `Waiting for etcd spec`. + +**Cause** : Une VIP (Virtual IP) avait été configurée dans `talconfig.yaml` avec la même adresse que le nœud (`192.168.10.11`). Talos attend que la VIP soit différente du nœud pour certaines opérations réseau internes, et ça crée un deadlock. + +**Fix** : Supprimer complètement la section VIP dans `talconfig.yaml`. On n'en a pas besoin — sur un single control-plane, l'endpoint est l'IP directe du nœud. + +--- + +### ❌ Nœud reboot sur l'ancien OS (entrées EFI résiduelles) + +**Symptôme** : Après `talosctl apply-config`, le nœud reboot mais démarre sur Windows ou un Linux précédent. + +**Cause** : L'ancien OS avait des entrées dans le firmware EFI (NVRAM) avec une priorité plus haute que le nouveau bootloader Talos. + +**Fix A** : Retirer la clé USB pendant que Talos est en train d'installer sur le NVMe — le reboot suivant n'a plus que l'entrée NVMe disponible. + +**Fix B** : Via BIOS (F1) → Boot Priority → mettre le NVMe en premier. + +--- + +### ❌ DHCP ne fonctionne pas — nœuds sans IP + +**Symptôme** : Le nœud démarre en maintenance mode mais reste sans IP. `talosctl apply-config --insecure --nodes ` ne trouve rien. + +**Cause** : nftables sur storage-01 ne laissait pas passer le port 67 (DHCP). Les broadcast DHCP des nœuds étaient silencieusement droppés. + +**Fix** : Ajouter la règle dans `ansible/roles/gateway/templates/nftables.conf.j2` : +``` +udp dport 67 iif {{ lan_interface }} accept +``` + +Puis rejouer le playbook `gateway` : +```bash +ansible-playbook -i inventory.yml playbooks/site.yml --tags gateway +``` + +--- + +### ❌ SOPS "no matching creation rules found" + +**Symptôme** : `talhelper gensecret | sops --filename-override talsecret.sops.yaml --encrypt /dev/stdin` échoue. + +**Cause** : La `path_regex` dans `.sops.yaml` ne matchait pas le chemin relatif calculé par SOPS. Exemple : `talos/.*\.sops\.yaml$` ne matche pas si SOPS calcule le chemin depuis la racine du repo. + +**Fix** : Utiliser une regex plus simple et robuste : +```yaml +creation_rules: + - path_regex: \.sops\.yaml$ + age: +``` + +--- + +### ❌ Flannel CrashLoopBackOff sur 2 pods sur 3 + +**Symptôme** : +``` +kube-flannel-frw6t 0/1 CrashLoopBackOff +kube-flannel-zl6ds 0/1 CrashLoopBackOff +``` + +Logs : +``` +Failed to create SubnetManager: error retrieving pod spec for 'kube-system/kube-flannel-frw6t': +pods "kube-flannel-frw6t" is forbidden: User "system:serviceaccount:kube-system:flannel" +cannot get resource "pods" in API group "" in the namespace "kube-system" +``` + +**Cause** : Le manifest Flannel externe avait été appliqué (`kubectl apply -f kube-flannel.yml`), créant une `ClusterRoleBinding` qui pointait vers le service account `flannel` dans le namespace `kube-flannel`. Quand on a supprimé ce namespace (conflit avec le Flannel intégré de Talos), la `ClusterRoleBinding` a perdu sa cible mais est restée — sauf qu'elle pointait toujours vers `kube-flannel`, pas vers `kube-system` où tournent les pods Flannel natifs de Talos. + +**Fix** : +```bash +# 1. Corriger le namespace dans la ClusterRoleBinding +kubectl patch clusterrolebinding flannel --type='json' \ + -p='[{"op": "replace", "path": "/subjects/0/namespace", "value": "kube-system"}]' + +# 2. Supprimer les pods en erreur pour forcer leur redémarrage +kubectl delete pod -n kube-system kube-flannel-frw6t kube-flannel-zl6ds +``` + +**Leçon** : Avec Talos v1.13+, ne jamais appliquer le manifest Flannel externe. Flannel est déjà là, dans `kube-system`, avec le bon CIDR. + +--- + +### ❌ MetalLB webhook timeout + +**Symptôme** : `kubectl apply` de l'`IPAddressPool` échoue avec : +``` +Internal error occurred: failed calling webhook "ipaddresspoolvalidationwebhook.metallb.io": +Post "https://metallb-webhook-service.metallb-system.svc:443/validate-metallb-io-v1beta1-ipaddresspool" +dial tcp 10.42.2.2:9443: connect: connection refused +``` + +**Cause** : Le réseau pod était instable à cause des deux pods Flannel en CrashLoopBackOff. L'appel webhook du kube-apiserver vers le pod MetalLB ne passait pas. + +**Fix** : Résoudre d'abord le problème Flannel (voir ci-dessus), attendre que les 3 pods Flannel soient `1/1 Running`, puis réappliquer la config MetalLB. + +--- + +### Résumé des points d'attention + +| Sujet | Règle | +|---|---| +| `installDisk` | Toujours `/dev/nvme0n1` sur les ThinkCentre M715q | +| USB ISO | `dd` direct, **pas Ventoy** | +| Bootstrap etcd | **Une seule fois** sur compute-01, jamais relancer | +| VIP | Ne pas configurer si VIP = IP du nœud | +| Flannel externe | Ne **pas** appliquer `kube-flannel.yml` — Talos le gère nativement | +| MetalLB webhook | Vérifier que Flannel est 100% healthy avant d'appliquer IPAddressPool | +| SOPS path_regex | Utiliser `\.sops\.yaml$` (simple) plutôt qu'un chemin relatif complet | +| etcd backup | Critique sur single control-plane — cron hebdomadaire minimum | +| RAM workers | compute-02/03 = 8 GB — toujours mettre `resources.requests/limits` | +| clusterconfig/ | Généré par talhelper — **ne pas committer**, dans `.gitignore` |