← Retour au portfolio RSS

· 21 min de lecture

Un VPS, de vieilles machines, et tout mon parc tourne dessus

Comment j'utilise un VPS comme control plane k3s et mes machines personnelles comme workers, reliés par WireGuard — avec les pièges qui m'ont coûté des soirées.

Le calcul qui m’a décidé

Un VPS correct, c’est quelques euros par mois. Deux VPS, c’est le double. Et quand on veut faire tourner un registre Docker, un Keycloak, un PostgreSQL, un portfolio et deux ou trois projets en cours, on arrive vite à la question : est-ce que je paie pour de la RAM que j’ai déjà chez moi ?

Parce que la RAM, je l’avais. Une vieille machine qui prenait la poussière, largement assez pour faire tourner des conteneurs. Ce qui lui manquait, ce n’était pas la puissance : c’était une adresse IP publique, un certificat valide, et la garantie de rester joignable.

D’où l’architecture que j’utilise aujourd’hui : un VPS comme control plane et porte d’entrée, mes machines personnelles comme muscles, le tout relié par WireGuard.

            Internet
                |  80/443 (HTTP/S)   51820/udp (WireGuard)
                v
   +-------------------------+
   | VPS - 10.10.0.1         |  k3s server, Traefik, cert-manager
   | IP publique, DNS, TLS   |
   +------------+------------+
                |  tunnel wg0 (initié depuis la maison)
   +------------+------------+
   | Maison - 10.10.0.2      |  k3s agent, charges lourdes
   | derrière box / CGNAT    |
   +-------------------------+

Ce que fait chaque machine

VPS Machine à la maison
Rôle k3s control plane worker
Ressources 8 Go RAM, 3 vCPU ce qu’elle a, et c’est déjà beaucoup
Ce qui y tourne Traefik, cert-manager, l’API k3s les charges gourmandes
Ce qu’elle apporte IP publique, disponibilité RAM et CPU gratuits
Ports ouverts sur internet 80, 443, 51820/udp aucun

Le point important : le trafic entre toujours par le VPS. C’est lui qui porte le nom de domaine, termine le TLS et route vers les services — peu importe sur quel nœud le pod s’exécute réellement. La machine à la maison, elle, n’expose rien : pas de redirection de port sur la box, pas d’IP à publier.

Pourquoi WireGuard n’est pas optionnel

Kubernetes suppose que tous les nœuds peuvent se parler directement. Or ma machine est derrière une box, sans IP publique, souvent derrière du CGNAT. Le VPS ne peut pas l’appeler.

WireGuard renverse le problème : c’est la machine à la maison qui initie la connexion vers le VPS, et le tunnel reste ouvert. Une fois établi, les deux nœuds se voient sur un réseau privé stable — 10.10.0.1 et 10.10.0.2 — qui ne change pas quand mon opérateur me réattribue une IP.

On commence par générer une paire de clés sur chaque machine :

sudo apt install wireguard
wg genkey | tee privatekey | wg pubkey > publickey
chmod 600 privatekey

Chaque machine garde sa clé privée et donne sa clé publique à l’autre. Rien d’autre ne circule.

Côté VPS (/etc/wireguard/wg0.conf) :

[Interface]
Address = 10.10.0.1/24
ListenPort = 51820
PrivateKey = <clé privée du VPS>

[Peer]
# La machine à la maison
PublicKey = <clé publique du worker>
AllowedIPs = 10.10.0.2/32

Côté machine à la maison :

[Interface]
Address = 10.10.0.2/24
PrivateKey = <clé privée du worker>

[Peer]
PublicKey = <clé publique du VPS>
Endpoint = <ip.publique.du.vps>:51820
AllowedIPs = 10.10.0.0/24
# Sans cette ligne, le tunnel meurt dès qu'il n'y a plus de trafic :
# le NAT de la box oublie la correspondance au bout de quelques minutes.
PersistentKeepalive = 25

On active le tunnel au démarrage, des deux côtés :

sudo systemctl enable --now wg-quick@wg0
sudo wg show

La ligne à regarder dans wg show, c’est latest handshake. Si elle affiche quelques secondes, le tunnel vit. Si elle est absente, les clés ou le port UDP sont en cause — pas la peine d’aller plus loin tant que ce n’est pas réglé.

PersistentKeepalive est le genre de ligne qu’on oublie et qu’on regrette. Sans elle, tout marche — jusqu’à ce que le nœud passe NotReady pendant la nuit, sans raison apparente.

Installer k3s sur le tunnel, pas à côté

C’est ici que se joue la réussite ou l’échec du montage. Par défaut, k3s détecte l’interface réseau principale et annonce cette adresse aux autres nœuds. Sur le VPS, ce serait l’IP publique ; sur la machine à la maison, une adresse privée de box que personne ne peut joindre.

Il faut donc lui dire explicitement de vivre dans le tunnel.

Sur le VPS :

curl -sfL https://get.k3s.io | sh -s - server \
  --node-ip 10.10.0.1 \
  --advertise-address 10.10.0.1 \
  --flannel-iface wg0 \
  --tls-san <ip.publique.du.vps>

Sur la machine à la maison :

curl -sfL https://get.k3s.io | K3S_URL=https://10.10.0.1:6443 \
  K3S_TOKEN=<le token du serveur> sh -s - agent \
  --node-ip 10.10.0.2 \
  --flannel-iface wg0

Le token se trouve sur le VPS dans /var/lib/rancher/k3s/server/node-token.

--flannel-iface wg0 est le cœur du sujet : sans lui, le réseau des pods tente de passer par l’interface publique et rien ne communique entre les nœuds. Les pods démarrent, le cluster paraît sain, et pourtant un service sur un nœud ne répond jamais depuis l’autre.

Pour vérifier que tout le monde s’annonce avec la bonne adresse :

kubectl get nodes -o wide

La colonne INTERNAL-IP doit afficher 10.10.0.1 et 10.10.0.2. Toute autre valeur signifie qu’un nœud vit en dehors du tunnel.

L’ordre de démarrage

Un piège discret : au redémarrage de la machine, rien ne garantit que wg0 existe quand k3s se lance. k3s tente alors de s’attacher à une adresse qui n’existe pas encore, et le nœud reste en échec. Un simple override systemd règle ça :

sudo systemctl edit k3s-agent   # « k3s » sur le VPS
[Unit]
After=wg-quick@wg0.service
Requires=wg-quick@wg0.service

Et la fonction WireGuard intégrée de k3s ?

k3s propose un backend --flannel-backend=wireguard-native qui chiffre le trafic entre pods tout seul. Je ne l’utilise pas ici, pour une raison simple : il chiffre le réseau des pods, mais l’agent doit d’abord pouvoir joindre l’API du serveur, et c’est précisément ce que le CGNAT empêche. Le tunnel explicite, initié depuis la maison avec un keepalive, résout les deux problèmes d’un coup et reste facile à diagnostiquer avec wg show.

Le piège du MTU

Celui-là m’a coûté une soirée, alors je le détaille.

WireGuard encapsule : il ajoute ses propres en-têtes, ce qui réduit la taille utile des paquets. Flannel encapsule à son tour par-dessus, en VXLAN. Résultat, les gros paquets dépassent la taille maximale et sont silencieusement perdus.

Le symptôme est déroutant : ping fonctionne, curl sur une petite réponse fonctionne, mais une réponse volumineuse se fige à mi-chemin. Un kubectl logs qui s’interrompt, une image qui ne se télécharge jamais complètement, un git clone bloqué à 47 %.

La règle : le MTU de flannel doit être inférieur à celui de WireGuard, lui-même inférieur à celui du lien physique.

Couche Surcoût MTU typique
Lien physique — 1500 (1492 en PPPoE)
WireGuard 60 à 80 octets 1420
Flannel VXLAN 50 octets 1370

Le problème, c’est que ces valeurs par défaut supposent un lien à 1500. Si la box est en PPPoE, ou si l’opérateur réduit la taille des paquets quelque part sur le chemin, WireGuard à 1420 est déjà trop grand.

La mesure se fait avec ping en interdisant la fragmentation. La taille passée à -s est la charge utile : il faut ajouter 28 octets d’en-têtes IP et ICMP pour obtenir le MTU réel.

# Depuis la maison, vers l'IP publique du VPS : quel est le MTU du chemin ?
ping -M do -s 1472 <ip.publique.du.vps>   # 1472 + 28 = 1500
# « message too long » ou pas de réponse ? on descend jusqu'à ce que ça passe.

Une fois la valeur connue, on fixe le MTU de WireGuard explicitement (MTU du chemin moins 80) dans [Interface] :

MTU = 1412

Flannel calcule le sien à partir de l’interface qu’on lui a désignée. On vérifie ce qu’il a retenu :

cat /run/flannel/subnet.env   # FLANNEL_MTU=...

Ne recopiez pas un chiffre trouvé sur internet, le mien compris : mesurez le vôtre.

Décider où les choses s’exécutent

Un cluster hétérogène demande qu’on soit explicite. Le VPS ne doit pas se faire remplir de charges lourdes, et certains services doivent impérativement rester chez lui.

J’étiquette donc les nœuds selon ce qu’ils sont :

kubectl label node <vps> location=vps
kubectl label node <worker> node-role.kubernetes.io/worker=true
kubectl label node <worker> location=home

Puis je place les charges gourmandes à la maison avec un nodeSelector :

spec:
  nodeSelector:
    location: home

Garder Traefik sur le VPS

Par défaut, k3s peut lancer Traefik sur n’importe quel nœud. S’il atterrit à la maison, le trafic entre par le VPS, traverse le tunnel jusqu’à Traefik, puis revient peut-être vers un pod sur le VPS : un aller-retour inutile, et un site qui tombe avec ma box. k3s permet de surcharger la configuration de son Traefik intégré :

# /var/lib/rancher/k3s/server/manifests/traefik-config.yaml
apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
  name: traefik
  namespace: kube-system
spec:
  valuesContent: |-
    nodeSelector:
      location: vps

Même logique pour le load balancer intégré de k3s (ServiceLB) : il déploie par défaut un pod sur chaque nœud pour écouter sur 80 et 443. En étiquetant le VPS, on lui dit de n’utiliser que lui :

kubectl label node <vps> svccontroller.k3s.cattle.io/enablelb=true

Dès qu’un nœud porte ce label, ServiceLB se limite aux nœuds étiquetés.

Sécuriser la porte d’entrée

Le VPS est la seule machine exposée, c’est donc lui qu’on verrouille. Le minimum :

sudo ufw default deny incoming
sudo ufw allow 22/tcp
sudo ufw allow 80,443/tcp
sudo ufw allow 51820/udp
sudo ufw allow in on wg0      # tout ce qui vient du tunnel est de confiance
sudo ufw enable

Notez ce qui manque volontairement : le port 6443 de l’API Kubernetes n’est pas ouvert sur internet. Pour utiliser kubectl depuis mon portable, je l’ajoute comme troisième pair WireGuard (10.10.0.3) et je pointe mon kubeconfig sur https://10.10.0.1:6443. L’API n’est ainsi joignable que depuis le tunnel. Si c’est votre choix aussi, l’option --tls-san avec l’IP publique devient superflue.

Ce que cette architecture ne fait pas

Il faut être honnête sur les limites, sinon la démonstration devient un mensonge.

Il n’y a aucune haute disponibilité. Un seul control plane : s’il tombe, plus rien ne s’ordonnance. Les pods déjà lancés continuent de tourner, mais le cluster ne réagit plus. Avec un seul serveur, k3s stocke l’état du cluster dans SQLite, sous /var/lib/rancher/k3s/server/db/ : c’est ce dossier, avec le token, qu’il faut sauvegarder pour pouvoir reconstruire. (La section suivante montre comment lever cette limite.)

La machine à la maison n’est pas fiable. Coupure de courant, box redémarrée, mise à jour du système : le nœud disparaît. Kubernetes attend environ cinq minutes avant de considérer ses pods comme perdus et de tenter de les relancer ailleurs. Tout ce qui doit répondre en permanence n’a rien à y faire.

Le stockage ne suit pas. Avec le provisionneur local-path de k3s, un volume vit sur le disque du nœud qui l’a créé, et il est attaché à ce nœud. Si la machine tombe, le pod qui l’utilise ne sera pas relancé ailleurs : il restera en Pending jusqu’au retour du nœud. Et si la machine meurt, les données meurent avec. Toute donnée qui compte doit être sauvegardée ailleurs, explicitement — un pg_dump planifié vers un stockage objet, par exemple.

La latence existe. Le tunnel traverse internet. Pour des conteneurs qui bavardent beaucoup entre eux, mieux vaut les garder sur le même nœud.

Passer à l’échelle : plusieurs VPS, plusieurs machines

Tout ce qui précède tient sur deux nœuds, mais rien n’y est limité à deux. Le même principe — un réseau privé WireGuard, et k3s qui vit dedans — s’étend à un vrai cluster : plusieurs control planes sur des VPS pour la haute disponibilité, et autant de machines personnelles que vous en avez sous la main pour la puissance.

                           Internet
                               |
           +-------------------+-------------------+
           v                   v                   v
     +------------+      +------------+      +------------+
     |   VPS 1    |<---->|   VPS 2    |<---->|   VPS 3    |   control planes + etcd
     | 10.10.0.1  |      | 10.10.0.2  |      | 10.10.0.3  |   Traefik sur chacun
     +-----+------+      +-----+------+      +-----+------+   (VPS 1 <-> VPS 3 aussi)
           |                   |                   |
   ========+===================+===================+===   chaque maison -> chaque VPS
           |                   |                   |
     +-----+------+      +-----+------+      +-----+------+
     |  Maison A  |      |  Maison B  |      |  Maison C  |   workers
     | 10.10.0.11 |      | 10.10.0.12 |      | 10.10.0.13 |
     +------------+      +------------+      +------------+

Planifier les adresses dès le départ

Un plan d’adressage simple évite de tout renuméroter plus tard :

Plage Usage
10.10.0.1 – 10.10.0.9 VPS (control planes)
10.10.0.10 – 10.10.0.99 machines personnelles (workers)
10.10.0.100 et plus postes d’administration (portable, etc.)

Trois control planes, pas deux

k3s sait faire tourner plusieurs serveurs avec une base etcd embarquée. etcd fonctionne par quorum : le cluster reste disponible tant qu’une majorité de serveurs répond. Avec deux serveurs, la majorité est… deux : en perdre un bloque tout, exactement comme avec un seul. C’est pour ça qu’on passe directement à trois — on peut alors en perdre un sans interruption.

La règle se formalise simplement. Pour un cluster de n n serveurs, le quorum est :

q = ⌊ n 2 ⌋ + 1 q = \left\lfloor \frac{n}{2} \right\rfloor + 1

Et le nombre de pannes que le cluster tolère sans s’arrêter est :

f = n − q = ⌊ n − 1 2 ⌋ ⟺ n = 2 f + 1 f = n - q = \left\lfloor \frac{n - 1}{2} \right\rfloor \qquad\Longleftrightarrow\qquad n = 2f + 1

Serveurs ( n n ) Quorum ( q q ) Pannes tolérées ( f f )
1 1 0
2 2 0
3 2 1
4 3 1
5 3 2

Le tableau montre deux choses. Passer de 1 à 2 serveurs n’apporte aucune tolérance : on paie un VPS de plus pour rien. Et un nombre pair n’est jamais utile : 4 serveurs tolèrent autant de pannes que 3, tout en ajoutant un membre de plus dont etcd doit attendre l’accord. On reste donc sur des nombres impairs — 3 suffit largement ici, 5 si l’on veut survivre à deux pannes simultanées.

Le premier serveur initialise le cluster :

curl -sfL https://get.k3s.io | K3S_TOKEN=<un secret partagé> sh -s - server \
  --cluster-init \
  --node-ip 10.10.0.1 \
  --advertise-address 10.10.0.1 \
  --flannel-iface wg0

Les suivants le rejoignent :

curl -sfL https://get.k3s.io | K3S_TOKEN=<le même secret> sh -s - server \
  --server https://10.10.0.1:6443 \
  --node-ip 10.10.0.2 \
  --advertise-address 10.10.0.2 \
  --flannel-iface wg0

Les workers, eux, n’ont besoin d’une adresse de serveur que pour leur première connexion. Une fois inscrits, l’agent k3s connaît la liste de tous les serveurs et répartit lui-même ses connexions : si 10.10.0.1 tombe, il bascule sur les autres sans intervention.

La règle d’or : etcd ne va pas à la maison. etcd est très sensible à la latence et à la stabilité du réseau. Des control planes derrière une box, reliés par un tunnel à travers internet, c’est la recette d’un cluster qui perd son quorum à chaque coupure. Les control planes vont sur des VPS, idéalement chez le même hébergeur et dans la même région pour garder une latence faible entre eux. La maison ne fournit que des workers.

Si trois VPS dépassent le budget, k3s accepte aussi une base externe (--datastore-endpoint vers un PostgreSQL) avec seulement deux serveurs. Mais la haute disponibilité se déplace alors sur cette base : si elle est unique, on n’a fait que déplacer le point de défaillance.

La topologie WireGuard

Avec deux nœuds, le tunnel était évident. Avec six, la tentation est de tout relier à tout : un maillage complet. C’est la bonne idée, avec une limite physique.

Ce qui est impossible : relier directement deux maisons. WireGuard ne fait pas de traversée de NAT. Pour qu’un tunnel s’établisse, au moins un des deux pairs doit être joignable depuis l’extérieur. Deux machines derrière du CGNAT, dans deux lieux différents, ne peuvent donc pas se parler sans intermédiaire.

Ce qui est possible : relier chaque maison à chaque VPS. C’est toujours la maison qui initie la connexion, et chaque VPS est joignable. On obtient ainsi un maillage complet partout où le réseau le permet :

Lien Direct ? Comment
VPS ↔ VPS oui chaque VPS déclare les autres avec un Endpoint
Maison ↔ VPS oui chaque maison initie vers chaque VPS, avec keepalive
Maison ↔ maison, même LAN oui Endpoint en adresse locale
Maison ↔ maison, lieux différents non relayé par un VPS

C’est ce qui rend la haute disponibilité réelle. Une maison rattachée à un seul VPS perd tout le cluster quand ce VPS tombe. Reliée aux trois, l’agent k3s peut basculer sur un autre serveur sans que le tunnel soit en cause.

Chaque VPS qui relaie doit avoir l’IP forwarding activé :

echo 'net.ipv4.ip_forward=1' | sudo tee /etc/sysctl.d/99-wg.conf
sudo sysctl --system

Côté maison A, un pair par VPS. Une contrainte guide tout le reste : dans WireGuard, une adresse ne peut appartenir qu’à un seul pair. Chaque VPS porte donc sa propre adresse, et on choisit un VPS relais (ici le VPS 1) pour les adresses des autres maisons :

# Maison A — 10.10.0.11
[Peer]  # VPS 1, qui relaie aussi vers les autres maisons
PublicKey = <clé publique du VPS 1>
Endpoint = <ip.publique.du.vps1>:51820
AllowedIPs = 10.10.0.1/32, 10.10.0.12/32, 10.10.0.13/32
PersistentKeepalive = 25

[Peer]  # VPS 2
PublicKey = <clé publique du VPS 2>
Endpoint = <ip.publique.du.vps2>:51820
AllowedIPs = 10.10.0.2/32
PersistentKeepalive = 25

[Peer]  # VPS 3
PublicKey = <clé publique du VPS 3>
Endpoint = <ip.publique.du.vps3>:51820
AllowedIPs = 10.10.0.3/32
PersistentKeepalive = 25

Côté VPS, c’est plus simple : chaque pair ne porte que sa propre adresse, puisque toutes les machines sont directement connectées à chaque VPS :

# VPS 1 — 10.10.0.1 (les VPS 2 et 3 ont la même structure)
[Peer]  # VPS 2
PublicKey = <clé publique du VPS 2>
Endpoint = <ip.publique.du.vps2>:51820
AllowedIPs = 10.10.0.2/32

[Peer]  # VPS 3
PublicKey = <clé publique du VPS 3>
Endpoint = <ip.publique.du.vps3>:51820
AllowedIPs = 10.10.0.3/32

[Peer]  # Maison A (pas d'Endpoint : c'est elle qui appelle)
PublicKey = <clé publique de la maison A>
AllowedIPs = 10.10.0.11/32

# … un bloc identique pour les maisons B et C

Le trafic de la maison A vers la maison B part donc vers le VPS 1, qui le renvoie dans le tunnel de la maison B. Si le VPS 1 tombe, seul ce trafic entre maisons est coupé : chaque maison reste reliée aux deux autres VPS, donc au cluster. Pour limiter l’impact, on garde les services qui dialoguent beaucoup sur une même maison.

Les machines d’une même maison se parlent directement. Si deux machines sont sur le même réseau local, on les déclare mutuellement comme pairs avec leur adresse locale en Endpoint (192.168.1.x:51820), et on retire leur adresse de la liste du VPS relais. Le trafic entre elles reste sur le LAN, à pleine vitesse.

Le coût, c’est le nombre de liens. Avec v v VPS et h h machines à la maison, il faut maintenir :

L = v ( v − 1 ) 2 + v ⋅ h L = \frac{v(v-1)}{2} + v \cdot h

Avec 3 VPS et 3 maisons, cela fait 3 + 9 = 12 tunnels, et chaque nouvelle machine touche la config de tous les VPS. Une seule erreur d’AllowedIPs suffit à isoler un nœud.

Au-delà de cinq ou six nœuds, arrêtez de le faire à la main. Headscale (la version auto-hébergée de Tailscale) ou Netmaker gèrent le maillage et la distribution des clés à votre place. Headscale va même plus loin que WireGuard seul : il tente une traversée de NAT (hole punching) pour relier directement deux maisons, et ne passe par un relais que si elle échoue. k3s propose une intégration Tailscale expérimentale via l’option --vpn-auth. Le principe reste identique : k3s vit sur l’interface du réseau privé.

Décrire la topologie à Kubernetes

Avec plusieurs nœuds de chaque type, les labels deviennent plus précis. Kubernetes a une convention pour ça :

kubectl label node vps-1 topology.kubernetes.io/zone=vps location=vps
kubectl label node maison-a topology.kubernetes.io/zone=home-a location=home
kubectl label node maison-b topology.kubernetes.io/zone=home-b location=home

On peut alors demander à Kubernetes de répartir les réplicas d’un service sur des machines différentes, pour qu’une seule coupure de courant ne les emporte pas toutes :

spec:
  replicas: 3
  template:
    spec:
      nodeSelector:
        location: home
      topologySpreadConstraints:
        - maxSkew: 1
          topologyKey: topology.kubernetes.io/zone
          whenUnsatisfiable: ScheduleAnyway
          labelSelector:
            matchLabels:
              app: mon-service

Plusieurs portes d’entrée

Avec trois VPS, autant que les trois reçoivent du trafic. On garde la surcharge Traefik vue plus haut (nodeSelector: location: vps), on étiquette chaque VPS pour ServiceLB, et on lance un Traefik par VPS :

spec:
  valuesContent: |-
    deployment:
      kind: DaemonSet
    nodeSelector:
      location: vps

Reste à répartir les visiteurs. La solution la plus simple est de déclarer les trois IP publiques dans le DNS (plusieurs enregistrements A pour le même nom). Elle a une limite : le DNS ne vérifie pas la santé des serveurs, et un VPS en panne continuera de recevoir une partie des visiteurs jusqu’à ce qu’on le retire. Pour une vraie bascule automatique, il faut un load balancer de l’hébergeur, une IP flottante, ou un DNS avec contrôles de santé.

Le stockage, enfin partageable

Avec plusieurs machines à la maison, le problème du stockage local change de nature : on peut répliquer. Longhorn, par exemple, maintient plusieurs copies de chaque volume sur des nœuds différents, et un pod peut redémarrer ailleurs en retrouvant ses données. Mais la réplication est synchrone et bavarde : on la limite aux machines d’un même réseau local, jamais à travers le tunnel internet.

Ce que ça change, en résumé

1 VPS + 1 machine 3 VPS + N machines
Panne d’un VPS cluster figé aucun impact visible
Panne d’une machine ses pods s’arrêtent réplicas relancés ailleurs
Stockage local, non répliqué répliqué entre machines d’un même LAN
WireGuard un tunnel maillage VPS + chaque maison vers chaque VPS, puis outil dédié
Coût un VPS trois VPS

La haute disponibilité a un prix — deux VPS de plus. Tout l’intérêt de cette architecture est de pouvoir choisir le moment où on le paie : on commence à deux nœuds, et on ajoute des control planes le jour où une coupure devient vraiment gênante.

Quand ça ne marche pas

Symptôme Cause probable Vérification
Nœud NotReady après une nuit calme tunnel expiré dans le NAT wg show → latest handshake
Pods qui ne se joignent pas entre nœuds flannel hors du tunnel kubectl get nodes -o wide
Grosses réponses figées à mi-chemin MTU trop grand ping -M do -s ...
Agent en échec après un redémarrage k3s lancé avant wg0 journalctl -u k3s-agent
Certificat jamais délivré Traefik hors du VPS, ou port 80 fermé kubectl get pods -n kube-system -o wide

Ce que ça m’apporte

Un seul VPS à payer. Des dizaines de gigaoctets de RAM disponibles pour mes projets. Et un cluster qui ressemble, dans son fonctionnement, à ce que je rencontre en production — mêmes manifestes, même ingress, mêmes certificats, mêmes erreurs à diagnostiquer.

Ce site tourne dessus. La section « en direct », plus bas sur la page d’accueil, affiche le commit réellement déployé et depuis combien de temps le pod tourne. Ce n’est pas une capture d’écran : c’est le serveur qui répond. Le commit est injecté au build, et Kubernetes peut même dire au pod sur quel nœud il vit :

env:
  - name: GIT_SHA
    value: "<injecté par la CI>"
  - name: NODE_NAME
    valueFrom:
      fieldRef:
        fieldPath: spec.nodeName

Si vous avez une vieille machine dans un placard, elle vaut mieux que la ligne de facture d’un second serveur.