Stack
Ce qui fait tourner mon infrastructure personnelle, avec le raisonnement derrière chaque choix. Et, plus utile encore, ce que j'ai écarté : un outil retenu ne dit rien tant qu'on ignore contre quoi il a gagné.
Cette page décrit mon infrastructure personnelle, celle dont je maîtrise chaque décision. Les outils que j'exploite en entreprise — Active Directory, CrowdStrike, Fortinet, Proxmox, Prometheus/Grafana — relèvent de contraintes qui ne sont pas les miennes ; ils sont décrits dans l'étude de cas dédiée.
langage
-
Go
Les trois applications sont des binaires Go uniques, compilés avec
CGO_ENABLED=0. Un binaire statique, pas d'interpréteur, pas de gestionnaire de paquets au runtime : l'image finale contient un fichier et rien d'autre. Sur un cluster à 1,8 Go, ce n'est pas de l'esthétique, c'est du budget mémoire.Écarté : Python et Node. Excellents pour itérer, mais l'un comme l'autre imposent un runtime, un arbre de dépendances à maintenir et une surface de vulnérabilités qui vit sa propre vie. Pour du logiciel que je dois pouvoir laisser tourner des mois sans y toucher, le compromis penche nettement.
-
La bibliothèque standard, presque seule
Pas de framework web, pas d'ORM :
net/httpetdatabase/sqlsuffisent. Les seules dépendances directes du projet Frigo sont un pilote SQLite et une bibliothèque Web Push — le reste est de la stdlib.Écarté : Gin, Echo, GORM. Ils font gagner du temps au démarrage et en coûtent à chaque montée de version. Sur un projet dont je suis le seul mainteneur, moins de dépendances = moins de choses qui cassent pendant que je regarde ailleurs.
données
-
SQLite via
modernc.org/sqliteUne base par foyer, un fichier sur un volume persistant. Le pilote est une transpilation de SQLite en Go pur : pas de CGO, donc la compilation statique reste possible et l'image finale n'embarque aucune bibliothèque C.
Écarté :
mattn/go-sqlite3, plus rapide, mais qui impose CGO — donc une chaîne de compilation croisée, une image de base non vide, et l'abandon du binaire statique. Le gain de performance était sans objet à cette échelle.Conséquence assumée : un seul writer. D'où
SetMaxOpenConns(1), une réplique unique et une stratégie de déploiementRecreate— donc quelques secondes de coupure à chaque mise à jour. Pourquoi je n'ai pas « corrigé » ça : la note qui raconte le diagnostic. -
MariaDB pour Coach Adaptatif
Instance dédiée, isolée par NetworkPolicy, sauvegardée par CronJob quotidien avec checksums. Le modèle de données y est franchement relationnel et concurrent — le cas où SQLite n'est plus le bon outil.
Écarté : mutualiser une base pour les deux applications. Ça aurait économisé de la RAM, au prix d'un couplage entre deux projets sans rapport et d'une panne partagée.
plateforme
-
k3s
Kubernetes single-node sur un VPS Debian. Distribution allégée : etcd remplacé par SQLite, Traefik et le contrôleur Helm intégrés au binaire — donc 0 Mo résident pour ce dernier.
Écarté :
kubeadm, qui ne tenait pas dans l'enveloppe mémoire. Et docker-compose, que j'utilisais avant : parfaitement suffisant pour faire tourner des conteneurs, mais sans réconciliation d'état, sans rollback déclaratif, sans NetworkPolicies. -
Flux CD
GitOps en pull : le cluster tire son état depuis un dépôt Git et le réconcilie en continu. Le pipeline n'a aucun accès SSH ni kubectl sur la machine ; un rollback est un
git revert.Écarté : Argo CD, plus complet mais nettement plus gourmand, et son interface web serait une surface exposée de plus. Écarté aussi : le déploiement en push depuis la CI — ce serait donner à un tiers un chemin d'accès permanent à la production. C'est la même raison qui fait que les chiffres du cluster affichés sur l'étude de cas sont mesurés à la main.
-
SOPS + age
Les secrets sont versionnés dans Git, chiffrés. Déchiffrés uniquement dans le cluster, par une clé qui n'existe nulle part ailleurs. Un diff de secret reste lisible en structure : on voit quelle clé a changé, jamais sa valeur.
Écarté : HashiCorp Vault — dimensionné pour des organisations, pas pour un nœud. Sealed Secrets, qui ne permet pas de relire ce qu'on a chiffré. Et surtout : les secrets hors de Git, qui reviennent à ne plus savoir ce qui est réellement déployé.
-
Traefik + cert-manager
Ingress fourni par k3s, certificats Let's Encrypt renouvelés automatiquement. Le port 6443 de l'API Kubernetes n'est pas exposé : l'administration passe par tunnel SSH.
durcissement
-
Conteneurs contraints par défaut
Chaque pod tourne en
runAsNonRoot(UID 65532), système de fichiers racine en lecture seule,seccompProfile: RuntimeDefault, capabilities retirées, jeton de compte de service non monté. Les images sont épinglées par digest et un scan Trivy bloque la CI.Le principe : ces réglages ne sont pas ajoutés après un incident, ils sont le point de départ. Kubernetes, lui, laisse tout passer par défaut — y compris le trafic entre pods, d'où des NetworkPolicies sur chaque base.
-
Ce site
nginx non privilégié servant des fichiers statiques.
default-src 'none', le seul script inline autorisé par son empreinte SHA-256, HSTS avec préchargement,security.txtconforme à la RFC 9116. Zéro cookie, zéro tracker, zéro requête tierce — y compris pour les polices, qui sont celles du système.Écarté : tout générateur de site statique. Des pages HTML écrites à la main, une feuille de style, un fichier JavaScript facultatif. Ajouter Hugo ou Astro serait introduire une chaîne de build pour un problème que je n'ai pas. Écartés aussi : les analytics, un CDN, et une bascule de thème manuelle — explications ici.
ce que je n'utilise pas
-
Pas de supervision sur ce cluster
Pas de Prometheus ni de Grafana ici : la pile complète coûterait plus de RAM que les trois applications réunies. Je m'appuie sur les probes Kubernetes, les logs et une vérification manuelle. C'est un compromis, pas une bonne pratique — sur l'infrastructure d'entreprise, où la contrainte mémoire n'existe pas, la supervision est en place.
-
Pas de haute disponibilité
Un nœud, une réplique par application. Une panne matérielle, c'est une interruption. Le choix est budgétaire et assumé : mieux vaut un single-node dont je connais chaque composant qu'un semblant de HA dont je ne pourrais pas payer le vrai coût.
Les décisions racontées en détail — avec les erreurs de diagnostic — sont dans les notes techniques.