mirror of
https://github.com/Alkatrazz24/Funk-lab.git
synced 2026-07-08 21:04:43 +02:00
Ajout section 16 (NFS provisioner) dans le guide d'installation k8s : - Architecture du provisionnement dynamique (StorageClass → NFS RAID5) - Prérequis storage-01, fichiers de config, pattern App-of-Apps - Utilisation dans les pods (PVC, Deployment, Grafana, Prometheus) - Section admin : diagnostics, PVC manuels, cleanup, re-sync ArgoCD Ajout dans section admin courante : commandes de gestion NFS, tableau diagnostics PVC Pending, procédure test PVC manuel. Ajout dans pièges : StorageClass not found, SharedResourceWarning (boucle nom ArgoCD), StatefulSet volumeClaimTemplate immutable, PodSecurity baseline. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1336 lines
43 KiB
Markdown
1336 lines
43 KiB
Markdown
# 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. [Stockage persistant — NFS provisioner](#16-stockage-persistant--nfs-provisioner)
|
|
17. [Vérification finale](#17-vérification-finale)
|
|
18. [Administration courante](#18-administration-courante)
|
|
19. [Pièges et erreurs rencontrés](#19-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 <IP-DHCP>
|
|
```
|
|
|
|
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 <IP-DHCP-compute-02> \
|
|
--file talos/clusterconfig/funk-compute-02.yaml
|
|
|
|
# compute-03
|
|
talosctl apply-config --insecure --nodes <IP-DHCP-compute-03> \
|
|
--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 <IP>
|
|
```
|
|
|
|
---
|
|
|
|
## 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 <none> 4m v1.33.1 192.168.10.12
|
|
compute-03 Ready <none> 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 `<pending>` 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 `<service>.lab.local` sera résolu vers Traefik, qui se charge du routage vers le bon service k8s.
|
|
|
|
---
|
|
|
|
## 16. Stockage persistant — NFS provisioner
|
|
|
|
Sans stockage persistant, les pods comme Grafana ou Prometheus perdent toutes leurs données à chaque redémarrage. Le cluster Funk utilise le RAID5 de storage-01 comme backend via NFS.
|
|
|
|
### Architecture
|
|
|
|
```
|
|
Pod (Grafana, Prometheus, etc.)
|
|
└── PersistentVolumeClaim (PVC)
|
|
└── StorageClass "nfs"
|
|
└── nfs-subdir-external-provisioner (Deployment, namespace nfs-provisioner)
|
|
└── NFS export → /srv/data/nfs/k8s sur storage-01 (RAID5)
|
|
```
|
|
|
|
Quand un pod demande du stockage (`PersistentVolumeClaim`), le provisioner :
|
|
1. Reçoit la demande via l'API Kubernetes
|
|
2. Crée automatiquement un sous-répertoire dans `/srv/data/nfs/k8s/` sur storage-01
|
|
3. Crée un `PersistentVolume` pointant vers ce sous-répertoire
|
|
4. Bind le PV au PVC — le pod peut monter son volume
|
|
|
|
### Prérequis sur storage-01
|
|
|
|
Le répertoire NFS doit exister et être exporté **avant** de déployer le provisioner :
|
|
|
|
```bash
|
|
# Sur storage-01 — vérifier l'export
|
|
sudo exportfs -v | grep k8s
|
|
# → /srv/data/nfs/k8s 192.168.10.0/24(rw,sync,no_subtree_check,no_root_squash)
|
|
|
|
# Si le répertoire n'existe pas encore
|
|
sudo mkdir -p /srv/data/nfs/k8s
|
|
sudo chown nobody:nobody /srv/data/nfs/k8s
|
|
sudo chmod 0777 /srv/data/nfs/k8s
|
|
|
|
# Recharger les exports
|
|
sudo exportfs -ra
|
|
sudo systemctl status nfs-server
|
|
```
|
|
|
|
> `no_root_squash` est requis : le provisioner tourne en root dans le conteneur et doit pouvoir créer des sous-répertoires sur le partage.
|
|
|
|
### Fichiers de configuration
|
|
|
|
**`k8s/apps-of-apps/apps/nfs-provisioner.yaml`** — Application ArgoCD parente :
|
|
```yaml
|
|
spec:
|
|
source:
|
|
repoURL: git@github.com:Alkatrazz24/Funk-lab.git
|
|
path: k8s/infra/nfs-provisioner
|
|
```
|
|
|
|
**`k8s/infra/nfs-provisioner/helmrelease.yaml`** — Application ArgoCD enfant (multi-source) :
|
|
```yaml
|
|
spec:
|
|
sources:
|
|
- repoURL: https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/
|
|
chart: nfs-subdir-external-provisioner
|
|
targetRevision: 4.0.18
|
|
helm:
|
|
valueFiles:
|
|
- $values/k8s/infra/nfs-provisioner/values.yaml
|
|
- repoURL: git@github.com:Alkatrazz24/Funk-lab.git
|
|
targetRevision: main
|
|
ref: values
|
|
```
|
|
|
|
**`k8s/infra/nfs-provisioner/values.yaml`** — paramètres Helm :
|
|
```yaml
|
|
nfs:
|
|
server: 192.168.10.1 # IP de storage-01
|
|
path: /srv/data/nfs/k8s # Répertoire exporté
|
|
|
|
storageClass:
|
|
name: nfs
|
|
defaultClass: true # Classe par défaut du cluster
|
|
reclaimPolicy: Retain # Conserver les données si le PVC est supprimé
|
|
archiveOnDelete: false # Ne pas archiver à la suppression
|
|
```
|
|
|
|
> `reclaimPolicy: Retain` signifie que si tu supprimes un PVC, le sous-répertoire NFS reste. Les données sont protégées. Il faut supprimer manuellement le répertoire si souhaité.
|
|
|
|
### Pattern App-of-Apps pour ce déploiement
|
|
|
|
```
|
|
root Application (watches apps-of-apps/apps/)
|
|
└── nfs-provisioner Application (watches k8s/infra/nfs-provisioner/)
|
|
└── nfs-subdir-external-provisioner Application (multi-source Helm)
|
|
├── Deployment nfs-subdir-external-provisioner (namespace nfs-provisioner)
|
|
└── StorageClass "nfs"
|
|
```
|
|
|
|
> **Important** : le `helmrelease.yaml` dans `k8s/infra/nfs-provisioner/` doit avoir un nom différent de son Application parente. Ici `nfs-subdir-external-provisioner` ≠ `nfs-provisioner`. Un même nom créerait une boucle de gestion ArgoCD (SharedResourceWarning).
|
|
|
|
### Déploiement initial (déjà fait via ArgoCD)
|
|
|
|
```bash
|
|
# ArgoCD déploie automatiquement via Git. Pour forcer manuellement :
|
|
kubectl -n argocd annotate application nfs-provisioner \
|
|
argocd.argoproj.io/refresh=hard --overwrite
|
|
|
|
# Vérifier que tout est Synced
|
|
kubectl get applications -n argocd | grep nfs
|
|
# nfs-provisioner Synced Healthy
|
|
# nfs-subdir-external-provisioner Synced Healthy
|
|
```
|
|
|
|
### Utiliser le stockage NFS dans un pod
|
|
|
|
#### Créer un PVC
|
|
|
|
```yaml
|
|
apiVersion: v1
|
|
kind: PersistentVolumeClaim
|
|
metadata:
|
|
name: mon-app-data
|
|
namespace: mon-namespace
|
|
spec:
|
|
storageClassName: nfs # Doit correspondre à la StorageClass
|
|
accessModes:
|
|
- ReadWriteOnce
|
|
resources:
|
|
requests:
|
|
storage: 5Gi
|
|
```
|
|
|
|
> `ReadWriteOnce` (RWO) : monté par un seul pod à la fois. Suffisant pour des bases de données, Prometheus, Grafana.
|
|
> `ReadWriteMany` (RWX) : monté par plusieurs pods simultanément. NFS le supporte — utile pour du contenu statique partagé.
|
|
|
|
#### Monter le PVC dans un Deployment
|
|
|
|
```yaml
|
|
spec:
|
|
template:
|
|
spec:
|
|
containers:
|
|
- name: mon-app
|
|
image: mon-image
|
|
volumeMounts:
|
|
- name: data
|
|
mountPath: /data # Chemin dans le conteneur
|
|
volumes:
|
|
- name: data
|
|
persistentVolumeClaim:
|
|
claimName: mon-app-data # Nom du PVC ci-dessus
|
|
```
|
|
|
|
#### Comment Grafana utilise le NFS
|
|
|
|
Grafana stocke dans son volume NFS :
|
|
- Sa base de données SQLite (`grafana.db`) — dashboards, utilisateurs, alertes, préférences
|
|
- Le cache des images PNG/PDF exportées depuis les dashboards
|
|
- Les plugins installés à chaud
|
|
|
|
Dans `k8s/infra/monitoring/values.yaml` :
|
|
```yaml
|
|
grafana:
|
|
persistence:
|
|
enabled: true
|
|
storageClassName: nfs
|
|
size: 2Gi
|
|
```
|
|
|
|
Le chart Grafana crée automatiquement un PVC nommé `kube-prometheus-stack-grafana`. Le provisioner NFS provisionne un répertoire dans `/srv/data/nfs/k8s/monitoring-kube-prometheus-stack-grafana-pvc-<uid>/`.
|
|
|
|
#### Comment Prometheus utilise le NFS
|
|
|
|
Prometheus utilise un `StatefulSet` avec un `volumeClaimTemplate` dans le CRD `Prometheus` :
|
|
|
|
```yaml
|
|
prometheus:
|
|
prometheusSpec:
|
|
storageSpec:
|
|
volumeClaimTemplate:
|
|
spec:
|
|
storageClassName: nfs
|
|
accessModes: ["ReadWriteOnce"]
|
|
resources:
|
|
requests:
|
|
storage: 20Gi
|
|
```
|
|
|
|
Le PVC créé s'appelle `prometheus-kube-prometheus-stack-prometheus-db-prometheus-kube-prometheus-stack-prometheus-0` et est lié à un répertoire dans `/srv/data/nfs/k8s/`.
|
|
|
|
> **Attention** : Les `volumeClaimTemplate` de StatefulSet ne peuvent **pas** être mis à jour une fois créés. Si tu changes la taille ou la storageClass, tu dois :
|
|
> 1. Patcher le CRD Prometheus pour retirer `storageSpec`
|
|
> 2. Supprimer le StatefulSet (sans `--cascade=foreground` pour ne pas tuer les pods)
|
|
> 3. Supprimer le PVC
|
|
> 4. Remettre `storageSpec` dans values.yaml
|
|
> 5. Git push → ArgoCD recrée tout
|
|
|
|
---
|
|
|
|
## 17. 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 <none> 1h v1.33.1
|
|
compute-03 Ready <none> 1h v1.33.1
|
|
```
|
|
|
|
---
|
|
|
|
## 18. 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 <namespace> <pod>
|
|
kubectl logs -n <namespace> <pod> -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
|
|
```
|
|
|
|
### Administrer le stockage NFS
|
|
|
|
```bash
|
|
# État du provisioner
|
|
kubectl get pods -n nfs-provisioner
|
|
kubectl get deployment -n nfs-provisioner
|
|
|
|
# État de la StorageClass
|
|
kubectl get storageclass
|
|
|
|
# Tous les PVCs du cluster (toutes namespaces)
|
|
kubectl get pvc -A
|
|
|
|
# Tous les PVs (volumes physiques côté cluster)
|
|
kubectl get pv
|
|
|
|
# Détail d'un PVC (binding, capacité, storageClass)
|
|
kubectl describe pvc <nom-pvc> -n <namespace>
|
|
|
|
# Vérifier l'espace utilisé côté NFS (storage-01)
|
|
ssh storage-01 "du -sh /srv/data/nfs/k8s/*"
|
|
ssh storage-01 "df -h /srv/data"
|
|
|
|
# Logs du provisioner (utile si un PVC reste en Pending)
|
|
kubectl logs -n nfs-provisioner deployment/nfs-subdir-external-provisioner --tail=30
|
|
```
|
|
|
|
#### Pourquoi un PVC reste-t-il en Pending ?
|
|
|
|
Un PVC reste `Pending` quand le provisioner ne peut pas créer le volume. Causes courantes :
|
|
|
|
| Cause | Diagnostic | Fix |
|
|
|---|---|---|
|
|
| StorageClass `nfs` inexistante | `kubectl get sc` → vide | Forcer sync ArgoCD `nfs-provisioner` |
|
|
| Provisioner pod en erreur | `kubectl get pods -n nfs-provisioner` | Lire les logs, redéployer |
|
|
| storage-01 inaccessible | `ping 192.168.10.1` depuis un pod | Vérifier réseau/NFS |
|
|
| Répertoire NFS non exporté | `ssh storage-01 "exportfs -v"` | Relancer le playbook Ansible |
|
|
| Permissions insuffisantes | Logs provisioner : `permission denied` | Vérifier `no_root_squash` dans `/etc/exports` |
|
|
|
|
```bash
|
|
# Diagnostic complet pour un PVC bloqué
|
|
kubectl describe pvc <pvc-name> -n <namespace>
|
|
# → Section "Events" montre la raison du blocage
|
|
|
|
# Test de connectivité NFS depuis un node k8s
|
|
kubectl run nfs-test --image=busybox --rm -it --restart=Never -- \
|
|
mount -t nfs 192.168.10.1:/srv/data/nfs/k8s /mnt
|
|
```
|
|
|
|
#### Créer un PVC manuellement (hors ArgoCD)
|
|
|
|
```bash
|
|
kubectl apply -f - <<EOF
|
|
apiVersion: v1
|
|
kind: PersistentVolumeClaim
|
|
metadata:
|
|
name: test-nfs
|
|
namespace: default
|
|
spec:
|
|
storageClassName: nfs
|
|
accessModes: [ReadWriteOnce]
|
|
resources:
|
|
requests:
|
|
storage: 1Gi
|
|
EOF
|
|
|
|
# Vérifier que le PVC passe en Bound
|
|
kubectl get pvc test-nfs -w
|
|
|
|
# Vérifier le répertoire créé sur storage-01
|
|
ssh storage-01 "ls /srv/data/nfs/k8s/ | grep test-nfs"
|
|
|
|
# Nettoyage
|
|
kubectl delete pvc test-nfs
|
|
# → Le répertoire NFS reste (reclaimPolicy: Retain) — supprimer manuellement si souhaité
|
|
```
|
|
|
|
#### Supprimer un PVC et ses données
|
|
|
|
```bash
|
|
# 1. Supprimer le PVC
|
|
kubectl delete pvc <pvc-name> -n <namespace>
|
|
|
|
# 2. Le PV passe en "Released" (pas automatiquement supprimé car reclaimPolicy=Retain)
|
|
kubectl get pv # chercher le PV en Released
|
|
|
|
# 3. Si tu veux libérer l'espace, supprimer aussi le PV et le répertoire NFS
|
|
kubectl delete pv <pv-name>
|
|
ssh storage-01 "sudo rm -rf /srv/data/nfs/k8s/<répertoire-correspondant>"
|
|
```
|
|
|
|
#### Forcer la re-synchronisation ArgoCD du provisioner
|
|
|
|
Le provisioner peut être purgé par ArgoCD si l'Application parente est OutOfSync. Pattern de récupération :
|
|
|
|
```bash
|
|
# 1. Forcer refresh root pour recréer les applications enfants
|
|
kubectl -n argocd annotate application root \
|
|
argocd.argoproj.io/refresh=hard --overwrite
|
|
|
|
# 2. Forcer sync nfs-provisioner spécifiquement
|
|
kubectl -n argocd annotate application nfs-provisioner \
|
|
argocd.argoproj.io/refresh=hard --overwrite
|
|
|
|
# 3. Attendre que les deux apps soient Synced/Healthy
|
|
kubectl get applications -n argocd | grep nfs
|
|
# nfs-provisioner Synced Healthy
|
|
# nfs-subdir-external-provisioner Synced Healthy
|
|
|
|
# 4. Vérifier la StorageClass et le pod
|
|
kubectl get sc && kubectl get pods -n nfs-provisioner
|
|
```
|
|
|
|
> Si des PVCs étaient en Pending pendant l'absence du provisioner, elles se resolveront automatiquement dès que la StorageClass et le provisioner sont de nouveau présents.
|
|
|
|
---
|
|
|
|
## 19. 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 <IP-DHCP>
|
|
# 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 <IP>` 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: <clé-publique>
|
|
```
|
|
|
|
---
|
|
|
|
### ❌ 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.
|
|
|
|
---
|
|
|
|
---
|
|
|
|
### ❌ PVC reste en Pending — StorageClass "nfs" not found
|
|
|
|
**Symptôme** :
|
|
```
|
|
Warning ProvisioningFailed persistentvolume-controller storageclass.storage.k8s.io "nfs" not found
|
|
```
|
|
|
|
**Cause** : L'Application ArgoCD `nfs-provisioner` (ou `nfs-subdir-external-provisioner`) a été purgée ou est OutOfSync. La StorageClass disparaît du cluster.
|
|
|
|
**Fix** :
|
|
```bash
|
|
kubectl -n argocd annotate application root \
|
|
argocd.argoproj.io/refresh=hard --overwrite
|
|
kubectl -n argocd annotate application nfs-provisioner \
|
|
argocd.argoproj.io/refresh=hard --overwrite
|
|
# Attendre ~60s, puis vérifier :
|
|
kubectl get sc && kubectl get pods -n nfs-provisioner
|
|
```
|
|
|
|
Les PVCs en Pending se resolvent automatiquement dès que la StorageClass est recréée.
|
|
|
|
---
|
|
|
|
### ❌ helmrelease.yaml — même nom que l'Application parente (SharedResourceWarning)
|
|
|
|
**Symptôme** :
|
|
```
|
|
Application/nfs-provisioner is part of applications argocd/nfs-provisioner and root
|
|
ComparisonError: Failed to load target state: permission denied
|
|
```
|
|
|
|
**Cause** : Le `helmrelease.yaml` dans `k8s/infra/nfs-provisioner/` était une Application ArgoCD nommée `nfs-provisioner` — même nom que l'Application parente qui le contient. ArgoCD essayait de gérer une ressource qui portait son propre nom → boucle.
|
|
|
|
**Fix** : Renommer l'Application dans `helmrelease.yaml` avec un nom différent (`nfs-subdir-external-provisioner`). Toujours suivre le pattern monitoring : l'Application enfant doit avoir un nom distinct de son parent.
|
|
|
|
---
|
|
|
|
### ❌ Prometheus StatefulSet bloqué — impossible de mettre à jour volumeClaimTemplate
|
|
|
|
**Symptôme** : ArgoCD signale l'Application `kube-prometheus-stack` en erreur après modification de `storageSpec` dans `values.yaml` :
|
|
```
|
|
cannot patch "prometheus-kube-prometheus-stack-prometheus" with kind StatefulSet:
|
|
StatefulSet.apps "..." is invalid: spec.volumeClaimTemplates: Forbidden: updates to statefulset
|
|
```
|
|
|
|
**Cause** : Kubernetes n'autorise pas la modification des `volumeClaimTemplates` d'un StatefulSet existant.
|
|
|
|
**Fix** :
|
|
```bash
|
|
# 1. Retirer storageSpec du CRD Prometheus pour que l'operator le détache
|
|
kubectl patch prometheus kube-prometheus-stack-prometheus -n monitoring \
|
|
--type=merge -p '{"spec":{"storage":null}}'
|
|
|
|
# 2. Supprimer le StatefulSet sans cascade (les pods continuent)
|
|
kubectl delete statefulset prometheus-kube-prometheus-stack-prometheus-0 \
|
|
-n monitoring --cascade=orphan
|
|
|
|
# 3. Supprimer le PVC
|
|
kubectl delete pvc <pvc-name> -n monitoring
|
|
|
|
# 4. Remettre storageSpec dans values.yaml → git push → ArgoCD recrée tout
|
|
```
|
|
|
|
---
|
|
|
|
### ❌ node-exporter bloqué par PodSecurity (baseline → privileged requis)
|
|
|
|
**Symptôme** : Les pods `node-exporter` restent en `Pending` ou sont rejetés :
|
|
```
|
|
pods "kube-prometheus-stack-node-exporter-xxx" is forbidden:
|
|
violates PodSecurity "baseline:latest": host namespaces (hostNetwork=true, hostPID=true),
|
|
hostPath volumes
|
|
```
|
|
|
|
**Cause** : Talos applique par défaut la politique `baseline:latest` sur les namespaces. `node-exporter` a besoin de `hostNetwork`, `hostPID`, et de montages `hostPath` pour lire les métriques système — ce qui est refusé par `baseline`.
|
|
|
|
**Fix** : Labeler le namespace `monitoring` en `privileged` dans `k8s/infra/monitoring/namespace.yaml` :
|
|
```yaml
|
|
metadata:
|
|
name: monitoring
|
|
labels:
|
|
pod-security.kubernetes.io/enforce: privileged
|
|
pod-security.kubernetes.io/audit: privileged
|
|
pod-security.kubernetes.io/warn: privileged
|
|
```
|
|
|
|
---
|
|
|
|
### 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` |
|
|
| NFS helmrelease | Nom de l'Application enfant ≠ nom du parent ArgoCD |
|
|
| StatefulSet storage | `volumeClaimTemplate` immutable — delete StatefulSet + PVC pour changer |
|
|
| PodSecurity monitoring | Namespace `monitoring` doit être `privileged` pour node-exporter |
|