Note (Dernière mise à jour le 24/01/2025)
Guide remis à jour pour Talos v1.12.2 et Kubernetes v1.35, ainsi que l’ensemble des charts Helm à date.
Talos devient au fil du temps la solution de choix pour déployer des clusters Kubernetes cloud ou bare metal hautement sécurisés et performants. Zéro SSH, API-REST first, à peine une douzaine de binaires embarqués, support chiffrement complet, cet OS est entièrement construit de manière conteneurisée et bootstrappé depuis le projet stagex, permettant un build entièrement reproductible et théoriquement insensible aux attaques supply-chain. Nous allons ici tenter de mettre en place un cluster kubernetes Talos sur des Cloud Service Providers européens (non GAFAM), le plus proche possible du production-grade, sans coût délirant.
Pour qui et pourquoi ? 🤔
Ce guide s’adresse aux devs à fortes inspirations DevOps/GitOps, principalement européens, adeptes du self-hosting, souhaitant s’affranchir des VPS à la papa et qui en ont plus qu’assez de la fatalité ambiante face à notre dépendance aux GAFAM, notamment :
- De leurs coûts délirants, inhérents aux modèles économiques de ces hyperscalers.
- De leur main mise sur nos données, notamment pour les entreprises non américaines, qui veulent s’extirper du Cloud Act et adhérer pleinement à la conformité RGPD.
- De leur opacité et aspect vendor-locking, du fait de l’intégration dans leur écosystème propriétaire, alors que l’écosystème Kubernetes commence à offrir de sérieuses alternatives via de puissants opérateurs.
En bref :
- Vous pensez avoir avez fait le tour de Dokploy et/ou de tout ce qui est possible de faire à partir des solutions type Docker Swarm, qui reste toujours parfaitement adapté aux besoins simples.
- Vous souhaitez monter à l’étage supérieur, basé sur des solutions Kubernetes et vous rapprochez de ce qui se fait de mieux chez les hyperscalers sans pour autant en subir le coût.
- Aller plus loin que les solutions classiques type
k3s/kubeadmmontées à l’arrache vite fait qui trainent sur des debians non maintenus dans leur coin, et monter votre infra complète entièrement contrôlée en mode GitOps. - Avoir une chaîne de déploiement CI/CD complète, du développement jusqu’au run, avec tous les outils d’observabilité nécessaires.
Note (Coûts)
Que l’on soit clair, en termes de coûts pour suivre ce guide, nous parlons ici d’un billet de ~50€/mois au minimum pour avoir quelque chose de potable, sinon partez plutôt sur du ~100€/mois pour du production-grade.
Le but ici est donc de se rapprocher raisonnablement et à notre échelle de ces hyperscalers sur le triptyque suivant :
- La sécurité, via Zero Trust, chiffrement des données, mTLS, WAF (nous voulons aussi éviter les solutions de facilité tel que Cloudflare).
- La résilience, via haute disponibilité, backups et Plan de Reprise d’Activité (PRA).
- L’observabilité, à tous les niveaux, métriques, logs, traces.
Le sacrifice en échange ?
- Des efforts et du temps…
- Une plus grande part de responsabilité technique.
Les bénéfices :
- Une maîtrise de la stack logicielle complète, infra incluse, du build jusqu’au run.
- Un juste milieu entre les VPS à la papa et les hyperscalers.
- Des coûts 100% maitrisés, aux rapports performances/prix d’un niveau tel qu’aucun CSP ne peut (et ne pourra jamais) rivaliser.
Et les solutions type OVHcloud ou Scaleway alors ? Très bien, c’est un vrai pas dans la bonne direction, et un excellent compromis entre le GAFAM et le cluster kube tout géré à la main. Si cela répond parfaitement à votre besoin alors nickel, vous pouvez déjà vous arrêter ici !
Mais je pense qu’il reste encore la problématique du coût, que ces Cloud Services Providers ont bien naturellement besoin de marger aux clients du fait du nombre toujours grandissant de services cloud proposés.
Comment ? 🛤️
Ici, ce guide se focalisera sur une seule ressource principale et essentielle : le VPS, mais cuisiné aux petits oignons. Exit les grands CSP multi-services, en dehors des S3, DNS, TEM (email transactionnel).
Sur tout ce que j’ai testé, Hetzner Cloud reste le leader incontestable du marché du VPS Cloud européen au rapport performance/prix imbattable, tout en disposant d’une interface UX et API/CLI simple et sans fioritures, uniquement centrée autour des VPS. Il ne propose pratiquement aucun autre service managé mis à part le Load Balancer indispensable (l’Object Storage n’est d’ailleurs arrivé que très récemment), et c’est à peu près tout ce qu’on lui demande ! Il inclut également un provider terraform qui fait super bien son taf sans 15000 ressources à connaître (coucou les providers AWS/AZ).
Comme il est de rigueur, nous n’allons pas mettre tous nos œufs dans le même panier, et utiliserons à ce titre 3 cloud providers différents :
Hetzner Cloud: l’infrastructure principale, VPS / volumes / Load BalancersOVHcloud: pour la partie stockage long-terme et backups S3, avec support chiffrementSSE-Cet équivalentSSE-KMSviaSSE-OMK.Scaleway: pour la partie DNS et TEM (serveur de mail transactionnel)
Proposition (Providers)
Pour faciliter le suivi de ce guide, vous devriez avoir un compte actif sur chacun de ces services. Mais à vrai dire, seul Hetzner Cloud est réellement indispensable, les 2 autres sont fonction de vos préférences, qu’il faudra adapter. Mais vous devrez à minima avoir un S3, un DNS, et un TEM prêt à l’emploi sous la main.
Les objectifs 🎯
- Monter un cluster Talos fonctionnel européen sécurisé, résiliant, et totalement monitoré.
- Tout en self-hosted ou presque, on maîtrise toute la brique infra logicielle.
- Déploiement 100% GitOps via
Terraform/OpenTofupuisFluxCD - Afin de comprendre et maîtriser chaque étape du processus, l’infra est construite en mode pas à pas, sans raccourci ni utilisation d’outil tout-en-un qui fait tout tel que terraform-hcloud-kubernetes.
Zero Trustde bout en bout, via l’utilisation deTailscale.- Cluster construit avec la notion de worker pools, comme chez tous les providers d’infra cloud proposant du kube managé.
- Utilisation d’un
Vaultwardenself-hosted avec backup pour le stockage des secrets dédiés à la partie Terraform. - Chiffrement à tous les niveaux (volumes systèmes et externes, backups, s3, réseau pod à pod, mTLS).
- Intégration de
Ciliumsur la couche réseau physique de l’infra Hetzner. - Intégration cloud (Network, Nodes et Load-Balancer) avec
Hetzner Cloud Controller Manager. - Solution de volumes distribués et backups via
Longhorn. - Du Ingress avec tout l’attirail qui va bien (
cert-manager,Traefik, etCrowdSecen tant que solution WAF incluant analyse comportementale du trafic). - Un Ingress avec endpoint public via Load Balancer et endpoint privé à travers le réseau Tailscale pour l’accès aux outils internes.
- Un cluster primary/replica
PostgreSQLdes familles viaCloudNativePG, avec backup barman cloud sur s3 chiffré. - Un cluster
Dragonfly(équivalent Redis). - Observabilité à tous les niveaux, monitoring complet via
Prometheus/Grafana, logging et tracing distribué viaLokietTempoen passant par le collecteurAlloy. - Mis en place d’une logistique CI/CD via
Gitea ActionetFlux, avec analyse de code viaSonarQube, incluant l’observabilité post-déploiement, au travers d’un exemple de projet .NET API.
Un sacré programme, be brave.
Remark (Tailscale)
Pour ceux qui tiqueraient sur la “dépendance” Tailscale :
- Oui, ce n’est pas super européen, mais je ne connais pas de réel équivalent au niveau en termes de simplicité/efficacité pour mettre en place du Zero Trust.
- Le client Tailscale est installable absolument partout, dont l’extension Talos officielle.
- Toute la partie cliente est en Open Source.
- Le réseau est chiffré de bout en bout via
Wireguard, avec communication pair-à-pair, et les clés privées ne sont pas connues de Tailscale, le site central n’étant qu’un serveur de coordination. - Possibilité d’utiliser à la place headscale pour du self-hosted, mais ce ne sera pas le sujet de ce guide.
- Pour les plus paranos, la feature Tailnet Lock permet même d’empêcher un client de rejoindre le réseau tailnet sans avoir été accepté par un autre client signataire de ce même tailnet. Tout est donc géré côté client et non plus par le serveur de coordination. À ce titre, même un serveur Tailscale compromis ne saurait donc forcer de lui-même un nouveau client malveillant au sein de notre réseau privé tailnet. Le corollaire étant de bien conserver les clés de lock au risque de perdre le contrôle du tailnet définitivement.
À ce niveau-là, j’estime que Tailscale est particulièrement irréprochable en termes de protection de données en transit sur le réseau.
Note (Versions)
À l’écriture de ce guide, tout a été testé sur la toute dernière version stable de Talos v1.11, ainsi que sur la dernière version de Kubernetes vanilla v1.34.
Suite à la suppression du catalogue public officiel de Bitnami, nous n’utiliserons aucune images ni de charts venant d’eux.
Pré-requis 🛠️
Déjà niveau prérequis ça va demander pas mal d’effort avant de commencer quoi que ce soit :
- Un compte Hetzner Cloud.
- Un compte Tailscale et être connecté sur votre tailnet de travail. Activer Tailnet Lock en option pour le maximum de sécurité, sinon activer au moins l’approbation manuelle d’un nouvel appareil.
- Un service S3 avec fonction de chiffrement SSE-C. Un équivalent SSE-KMS en plus sera idéal, car certains services de backup comme barman cloud ne supporte pas le chiffrage serveur avec clé client. On utilisera ici OVH. Activer le versioning pour être moins vulnérable aux ransomwares.
- Un nom de domaine avec un accès admin au DNS (on partira sur
ohmytalos.ioici). On réservera un sous-domaineint.ohmytalos.iopour l’accès aux outils internes. - Un compte de mail transactionnel, déjà actif et pré-paramétré sur votre nom de domaine, avec SPF/DKIM et tout le bousin. Si vous n’utiliser pas Scaleway pour la partie DNS, il faudra adapter le provider DNS-01 en fonction pour le challenge ACME.
- Un environnement
bash/zshavec une belle brochette d’outils CLI préinstallés suivants :mise,step,direnv,bitwarden-cli,hcloud,talosctl,kubectl,helm,terraform,packer,flux,sops. On n’est pas là pour déconner. L’utilisation debrewou encore mieuxmiseest vivement recommandé pour les installer et mettre à jour facilement. - Outils complémentaires optionnels pour la productivité sur kube :
cilium,cmctl,cnpg(plugin krew),k9s,ktop,kube-capacity,oh-my-zshavec pluginkubectlpour les alias.
Vaultwarden 🔑
De nos jours, plus d’infra sérieuse sans KMS centralisé. Le 1er élément critique à mettre en place sera donc Vaultwarden. Il va nous permettre de stocker les secrets de notre infra, notamment les clés d’accès au service S3, les tokens d’authentification Tailscale, Hcloud, etc. À bichonner bien comme il se doit, et à héberger de préférence uniquement en interne ou sinon à travers un VPN.
Dans le cas où le self-hosting de KMS ne vous intéresse pas, vous pouvez utiliser directement le cloud Bitwarden et sauter à la section suivante. L’avantage ici est de pouvoir tester le bon fonctionnement de toute la chaîne VPN / DNS / Email / S3 avant de démarrer les choses sérieuses avec le cluster Talos. Nous n’userons pas de pratique GitOps à ce stade, on veut juste monter un KMS tout en restant propre et sécurisé.
Initialiser un VPS quelconque (Hetzner, OVH, etc.) ou un serveur interne chez vous avec un docker préinstallé (utiliser le script curl -fsSL https://get.docker.com | sh pour l’installer rapidement, mais non recommandé en production bien sûr 😊). Une machine basique suffit amplement (même un NAS ferait l’affaire). Voici un exemple typique simple à démarrer :
services: server: image: vaultwarden/server:latest restart: unless-stopped ports: - 3000:80 environment: DOMAIN: https://vw.int.ohmytalos.io SMTP_HOST: smtp.tem.scaleway.com SMTP_FROM: vaultwarden@ohmytalos.io SMTP_PORT: 587 SMTP_USERNAME: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx SMTP_PASSWORD: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx volumes: - data:/data
backup: image: ttionya/vaultwarden-backup:latest restart: unless-stopped environment: RCLONE_REMOTE_DIR: ohmytalos/vaultwarden ZIP_PASSWORD: xxxxxxxxxxxxxxxxxxxx ZIP_TYPE: 7z BACKUP_KEEP_DAYS: 7 CRON: 5 * * * * volumes: - data:/bitwarden/data/ - ./rclone:/config/rclone
volumes: data:[BitwardenBackup]type = s3provider = OVHaccess_key_id = xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxsecret_access_key = xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxregion = graendpoint = s3.gra.io.cloud.ovh.netExplanation (Vaultwarden compose)
Rien de spécial à expliquer ici, on expose le service sur le port 3000 et l’on monte un volume docker pour la persistance des données. On précise le nom de domaine interne qu’il faut, puis l’on configure l’email pour les envois de mails transactionnels (reset de mot de passe, etc), les clés d’accès SMTP devant être générés depuis l’interface de votre provider.
J’ai rajouté aussi un petit service de backup des familles (indispensable à vrai dire, vous n’avez vraiment pas envie de perdre vos données KMS…) pour un export chiffré toutes les heures vers un bucket S3 avec rclone. Le bucket ohmytalos doit être préalablement créé.
Générer les clés d’accès S3 sur l’interface OVH via la création d’un nouvel utilisateur. Vous pouvez utiliser la politique d’accès suivante (importable via l’interface ou via CLI) pour s’assurer qu’il n’ait accès qu’au sous-répertoire vaultwarden :
{ "Statement": [ { "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListBucket", "s3:ListMultipartUploadParts", "s3:ListBucketMultipartUploads", "s3:AbortMultipartUpload", "s3:GetBucketLocation" ], "Effect": "Allow", "Resource": [ "arn:aws:s3:::ohmytalos/vaultwarden", "arn:aws:s3:::ohmytalos/vaultwarden/*" ], "Sid": "RWContainer" } ]}On démarre le tout avec un docker compose up -d et assurez-vous que tout tourne bien via docker compose logs -f. Une simple mise à jour du service se fera simplement avec docker compose pull && docker compose up -d.
Tailscale et Nginx
Pour accéder à notre Vaultwarden de manière sécurisé dans le cadre d’un VPS externe, on va devoir mettre en place un VPN. On va utiliser Tailscale pour ça. Pour faire simple et rapide lancer curl -fsSL https://tailscale.com/install.sh | sh (toujours à vos risques et périls !), ajouter le node dans votre tailnet via tailscale up, et si besoin approuvez-le. Lancer tailscale ip pour récupérer l’IP Tailscale du client, et créer un enregistrement DNS de type A (le CNAME fonctionne mal sur les clients Windows) dans votre zone DNS, par exemple vw.int.ohmytalos.io pointant vers cette IP privée.
On installe ensuite nginx avec un ufw qui va bien :
sudo apt install ufw nginx
sudo ufw allow ssh# penser à whitelister votre propre ip sur le port sshsudo ufw deny 443/tcpsudo ufw allow from 100.64.0.0/10 to any port 443 proto tcpsudo ufw enableWarning
On n’autorise que les IPs privés de Tailscale. Vous pouvez préférablement utiliser un pare-feu cloud plutôt qu’un pare-feu local tel que ufw
Challenge DNS
C’est super, mais il n’y a pas de HTTPS et on ne va pas pouvoir utiliser HTTP-01 pour générer notre script puisque tout se situe dans un réseau privé. Nous allons utiliser acme.sh pour générer notre certificat TLS via le challenge DNS-01 qui supporte des tonnes de providers DNS. Installer acme.sh via curl https://get.acme.sh | sh -s email=my@ohmytalos.io puis configurer l’accès à l’API DNS de votre provider, ici Scaleway dans notre cas :
export SCALEWAY_API_TOKEN='xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'acme.sh --issue --dns dns_scaleway -d vw.int.ohmytalos.io
mkdir -p /etc/nginx/certs
acme.sh --install-cert -d vw.int.ohmytalos.io \ --key-file /etc/nginx/certs/vw.int.ohmytalos.io.pem \ --fullchain-file /etc/nginx/certs/vw.int.ohmytalos.io.crt \ --reloadcmd "service nginx force-reload"À chaque renouvellement de certificats, on s’assure que nginx soit bien redémarré. Enfin, on peut créer la conf nginx pour le site dédié au vault :
server { listen 443 ssl http2; server_name vw.int.ohmytalos.io;
ssl_certificate /etc/nginx/certs/vw.int.ohmytalos.io.crt; ssl_certificate_key /etc/nginx/certs/vw.int.ohmytalos.io.pem;
location / { proxy_pass http://127.0.0.1:3000; }}Lancer ln -s /etc/nginx/sites-available/vaultwarden /etc/nginx/sites-enabled/ pour activer le site. Après redémarrage de nginx, vous devriez enfin accéder à votre Vaultwarden via https://vw.int.ohmytalos.io de manière sécurisé via tailscale.
Accès, backup et CLI
Connectez-vous à l’interface web pour créer un compte admin et/ou une orga dédiée. Assurez-vous que le backup fonctionne bien via docker compose exec backup bash /app/backup.sh et que le fichier zip chiffré est bien présent dans le bucket S3.
Ensuite, on installe le client Bitwarden via brew install bitwarden-cli. Puis activer le login via api-key et tester l’accès :
bw config server https://vw.int.ohmytalos.iobw login --apikeybw unlockTester avec bw list items pour voir si la liste des identifiants se fait bien. À partir de là, on a un KMS self-hosted de “pro” pour stocker les secrets de notre infra. Et l’on a pu en même temps nous assurer que tout est bon niveau VPN, DNS, envoi de mail et stockage S3, on est bien là non ?
Snapshots 💽
Deuxième prérequis nécessaire, nous allons avoir besoin d’uploader des images Talos prête à l’emploi pour la création à la volée de nos VPS. Un peu comme propose l’outil hcloud-upload-image, mais je préfère utiliser packer pour une approche plus GitOps.
La 1ère chose à faire est d’aller sur le site officiel de Talos Image Factory afin de construire vos images Talos personnalisées. Vous pouvez également créer un fichier de définition des extensions offline comme nous allons le faire ici.
Dans ce guide, nous allons avoir besoin de 2 types d’images :
- Une dédiée pour les control planes, contenant l’extension Tailscale pour accéder à nos nodes en mode zero-trust.
- Une dédiée par défaut pour les workers, sans Tailscale car l’on passera par les control planes pour y accéder, mais avec les extensions nécessaires au bon fonctionnement de Longhorn (iSCSI, util-linux-tools).
Créez-vous un nouveau répertoire projet ohmytalos-cluster puis les 2 fichiers suivants :
customization: systemExtensions: officialExtensions: - siderolabs/tailscalecustomization: systemExtensions: officialExtensions: - siderolabs/iscsi-tools - siderolabs/util-linux-toolsRécupérer les hashs d’identifiant de l’image comme suit :
curl -s -X POST --data-binary @schematic-cp.yaml https://factory.talos.dev/schematics | jq .id# 4a0d65c669d46663f377e7161e50cfd570c401f26fd9e7bda34a0216b6f1922bcurl -s -X POST --data-binary @schematic-wk.yaml https://factory.talos.dev/schematics | jq .id# 613e1592b2da41ae5e265e8789429f22e121aab91cb4deb6bc3c0b62629612452 infos importantes à retenir à partir de ce hash :
- Notre image de première installation qui va nous servir pour la création de notre snapshot https://factory.talos.dev/image/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/v1.12.2/hcloud-amd64.raw.xz
- L’URL de l’image OCI pour les opérations d’upgrade
factory.talos.dev/hcloud-installer/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx:v1.12.2
Pour chaque image, il faut prévoir une pour l’archi amd64, et une autre pour arm64, car l’on pourrait très bien imaginer des node pools sous différentes architectures. Rajouter autant de schematics que nécessaire, par exemple une contenant les drivers spécifiques pour des clusters de nodes GPU. On reflète tout cela dans le fichier packer suivant :
packer { required_plugins { hcloud = { source = "github.com/hetznercloud/hcloud" version = "~> 1" } }}
variable "hcloud_token" { type = string default = env("HCLOUD_TOKEN") sensitive = true}
variable "schematic_id" { type = string default = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba"}
variable "image_name" { type = string default = "default"}
variable "talos_version" { type = string default = "v1.12.2"}
variable "arch" { type = string}
variable "server_location" { type = string default = "nbg1"}
locals { server_types = { amd64 = "cpx22" arm64 = "cax11" } image = "https://factory.talos.dev/image/${var.schematic_id}/${var.talos_version}/hcloud-${var.arch}.raw.xz"}
source "hcloud" "talos" { rescue = "linux64" image = "debian-13" location = var.server_location server_type = local.server_types[var.arch] ssh_username = "root"
snapshot_name = "talos system disk - ${var.image_name} - ${var.arch} - ${var.talos_version}" snapshot_labels = { type = "infra", os = "talos", version = var.talos_version, arch = var.arch, name = var.image_name } token = var.hcloud_token}
build { sources = ["source.hcloud.talos"]
provisioner "shell" { inline = [ "apt-get install -y wget", "wget -O /tmp/talos.raw.xz ${local.image}", "xz -d -c /tmp/talos.raw.xz | dd of=/dev/sda && sync", ] }}Il nous est maintenant possible de construire notre image Talos personnalisée pour chaque architecture. Pour cela, nous allons avoir besoin de préparer notre token API Hcloud secrète dans notre environnement. Créer un nouveau projet dédié sur Hcloud et générez-y une clé API read/write, puis enregistrer là dans le Vaultwarden. Retenez l’identifiant itemId dans l’URL et exportez-le dans ce fichier :
export HCLOUD_TOKEN=$(bw get password xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)Ceci fait, lancer bw sync puis direnv allow pour activer les variables d’environnement spécifique au répertoire de travail courant. Ce dernier devrait ensuite vous demander votre mot de passe Vaultwarden. Une fois saisie, votre clé privée devrait être inscrite dans votre environnement, vérifier avec echo $HCLOUD_TOKEN.
Warning (bw sync)
N’oubliez pas de faire un bw sync afin de synchroniser vos derniers identifiants en local après création dans Vaultwarden, sinon ils ne seront pas disponibles dans votre shell.
Nous allons maintenant utiliser mise pour automatiser le build de nos images pour nos control planes et workers, créer le fichier suivant pour enregistrer nos tâches de build :
[tasks.install]run = "packer init ."
[tasks.amd64-cp]run = "packer build -var arch=amd64 -var image_name=cp -var schematic_id=4a0d65c669d46663f377e7161e50cfd570c401f26fd9e7bda34a0216b6f1922b ."
[tasks.arm64-cp]run = "packer build -var arch=arm64 -var image_name=cp -var schematic_id=4a0d65c669d46663f377e7161e50cfd570c401f26fd9e7bda34a0216b6f1922b ."
[tasks.build-cp]depends = ["amd64-cp", "arm64-cp"]
[tasks.amd64-wk]run = "packer build -var arch=amd64 -var image_name=wk -var schematic_id=613e1592b2da41ae5e265e8789429f22e121aab91cb4deb6bc3c0b6262961245 ."
[tasks.arm64-wk]run = "packer build -var arch=arm64 -var image_name=wk -var schematic_id=613e1592b2da41ae5e265e8789429f22e121aab91cb4deb6bc3c0b6262961245 ."
[tasks.build-wk]depends = ["amd64-wk", "arm64-wk"]
[tasks.build]depends = ["amd64-cp", "arm64-cp", "amd64-wk", "arm64-wk"]Note (schematic_id)
Les hash schematic_id sont ceux récupérés précédemment depuis le site Talos Factory depuis l’exemple ci-dessus. Pensez à les modifier si vous avez créé vos propres images personnalisées.
Plus qu’à lancer mise build et c’est parti pour la construction de tous nos snapshots images Talos en parallèle. À la fin de l’opération, vous devriez obtenir 4 snapshots d’image prêt à l’emploi, visible dans l’onglet “Servers/Snapshots” sur Hetzner Cloud ou plus directement côté CLI via hcloud image list -t snapshot. Pour l’utilisation du CLI, pensez à créer puis activer le contexte auparavant via hcloud context create ohmytalos-dev puis hcloud context use ohmytalos-dev, une clé API uniquement en lecture suffira si vous choisissez la voie 100% terraform à la section 2 de ce guide.
Architecture cloud 🏭
Comme toute architecture kube qui se respecte, et à l’image des kubes managés des hyperscalers, notre IaC devra exprimer la notion de node pools, dans lesquels on indiquera les caractéristiques (type de machine par défaut, volumes, talos config, etc.) puis la liste des hosts. Chaque pool sera dédié à un type de workload spécifique. Dans ce guide, nous allons créer 2 node pools :
- Un pool de worker : dédié aux workloads applicatifs, dont les nœuds ferons également office de reverse proxy traefik et seront rattachés à un load balancer Hcloud dédié au trafic public.
- Un pool de stockage : dédié aux workloads type base de données, ainsi que backends de métriques, logs et traces, il sera composé de nœuds avec des volumes rattachés à haute capacité.
Remark (Node pool monitoring)
Afin d’optimiser les coûts, nous mélangeons ici dans les nœuds de storage du workload de base de données et de monitoring. Bien que largement suffisant pour un besoin standard, ce n’est pas forcément idéal en termes de scalabilité.
Au niveau production, il peut rapidement être préférable de mettre en place un pool de monitoring dédié à Prometheus/Loki/Tempo (au moins 2 serveurs pour le côté HA), ce qui permet de bien meilleure flexibilité en termes de dimensionnement horizontal. Vous n’aurez qu’à adapter le guide en fonction.
Côté choix du type de VPS, l’essentiel est de partir sur 4 Go de RAM (minimum requis) pour les control planes (3 fois pour HA), puis 8 Go de RAM pour les 5 workers, répartis sur les 2 node pools. Ensuite, vous aurez principalement le choix entre 2 typologies niveau performance/prix :
| Type VPS | Coût mensuel HT pour un cluster | Avantages |
|---|---|---|
cx23/cx33 | ~47€* | Intel Skylake, option ultra-compétitive pour du kube pas trop dégueux. |
cpx22/cpx32 | ~91€* | AMD Genoa, plus recommandable pour du pro. Deux fois plus cher que la 1ère option, pour ~2.5 fois plus de performance par cœur. |
* Inclus le load balancer lb11 à 6,57€ HT
En imaginant un cluster de staging utilisant l’option 1, et un cluster de production sur l’option 2, vous arriverez très grossièrement à un coût total mensuel TTC de ~200€ en comptant tout. À ce tarif, vous pouvez être certain d’avoir bien plus de performance sous le capot tout en étant largement 10 fois moins cher qu’un équivalent anémique chez les hyperscalers. Le tout en étant maître de votre infra de A à Z, avec les avantages, mais aussi disons-le, les inconvénients que cela comporte.
Sur ce segment VPS low cost, en termes de perf CPU et I/O brute, seul OVH parvient à réellement rivaliser, mais pour une tarification 4 à 6 fois supérieure (en tenant compte du nombre de cœurs, par exemple 2 seulement pour un B3-8 à 33,48€ HT sans saving plans), ce qui commence à piquer pour monter une infra kube à haute dispo.
Architecture réseau 🌐
Avant de continuer, il est vital de bien poser l’architecture réseau de notre cluster. Il s’agit du système nerveux de Kubernetes, difficile de le changer après coup sans douleur, sinon à part le recréer. Nous allons dédier un subnet pour nos control planes et chaque node pool. Ce qui nous donne l’architecture suivante :
| Adresse IP CIDR | Description | Note |
|---|---|---|
10.0.0.0/10 | Réseau principal | Plage 10.0.0.0 → 10.63.x.x pour tout notre cluster |
10.0.0.0/24 | Sous-réseau control planes | Plage 10.0.0.x pour les control planes |
10.0.1.0/24 | Sous-réseau node pool worker | Plage 10.0.1.x pour les nœuds de workload principal |
10.0.2.0/24 | Sous-réseau node pool storage | Plage 10.0.2.x pour les nœuds de storage / DB |
10.42.0.0/16 | Plage des pods kube | Plage 10.42.x.x pour les pods kube |
10.43.0.0/16 | Plage des services kube | Plage 10.43.x.x pour les services kube |
Warning (Route Controller)
Nous activerons plus tard le route-controller intégré dans hcloud-cloud-controller-manager afin de créer automatiquement les routes “physiques” dans le réseau privé hcloud pour chaque nœud, permettant la communication entre les pods, sans overhead au niveau OS.
La plage utilisée pour chaque route découlera de la plage choisie pour les pods, donc ici nécessairement de type 10.42.x.0/24 (allocation /24 par défaut dans kubernetes/CNI). Dans cette configuration le nombre maximum de nœuds possibles dans le cluster serait de 255, tous pools confondus, avec maximum ~250 pods par nœud (selon les adresses déjà réservées par kube).
Note (Exemples)
Tout découle de l’adresse du réseau principal. On utilise ici 10.0.0.0/10 afin de libérer de la place pour d’autres usages à côté du cluster. Cela laisse la place pour d’autres clusters à côté, par exemple sur 10.64.0.0/10, 10.128.0.0/10 et/ou 10.192.0.0/10. Exemple pour 10.64.0.0/10 :
| Adresse IP CIDR | Description |
|---|---|
10.64.0.0/10 | Réseau principal |
10.64.0.0/24 | Sous-réseau control planes |
10.64.1.0/24 | Sous-réseau node pool worker |
10.64.2.0/24 | Sous-réseau node pool storage |
10.106.0.0/16 | Plage des pods kube |
10.107.0.0/16 | Plage des services kube |
Si vous êtes certain de n’avoir besoin que d’un seul cluster par projet Hcloud, vous pouvez directement partir sur 10.0.0.0/8. Cela donnerai :
| Adresse IP CIDR | Description |
|---|---|
10.0.0.0/8 | Réseau principal |
10.0.0.0/24 | Sous-réseau control planes |
10.0.1.0/24 | Sous-réseau node pool worker |
10.0.2.0/24 | Sous-réseau node pool storage |
10.244.0.0/16 | Plage des pods kube (valeur par défaut) |
10.96.0.0/12 | Plage des services kube (valeur par défaut) |
Côté configuration Talos, il faudra bien préciser la plage des pods et services dans la section cluster.network :
cluster: network: cni: name: none podSubnets: - 10.42.0.0/16 serviceSubnets: - 10.43.0.0/16 proxy: disabled: true externalCloudProvider: enabled: true* Ne faite rien pour le moment, il s’agit juste d’un élément à garder en tête pour la prochaine section du guide.
Warning (CNI)
On désactivera le CNI natif flannel de Talos en indiquant cni.name à none, de même pour le proxy natif de kube. Le tout sera remplacé par Cilium, permettant de bénéficier nativement de eBPF.
On précisera cluster.externalCloudProvider.enabled à true puisque l’on utilisera le CCM externe Hetzner Cloud Controller Manager.
Architecture storage 🗄️
Chiffrement des disques 🔐
Le chiffrement sera effectué à tous les niveaux, non seulement côté Longhorn mais aussi côté système grâce au support de chiffrement natif de Talos via LUKS. Nous utiliserons la config indiquée dans la doc officielle. Il existe trois types de volumes à chiffrer, les 2 volumes système STATE et EPHEMERAL, valable sur tous les nœuds, ainsi que le volume utilisateur longhorn spécifique aux volumes Hetzner que l’on rattachera aux nœuds du pool de stockage. Nous aurons donc besoin de déclarer jusqu’à 3 configurations de volumes dans notre fichier de configuration Talos :
apiVersion: v1alpha1kind: VolumeConfigname: STATEencryption: keys: - nodeID: {} slot: 0---apiVersion: v1alpha1kind: VolumeConfigname: EPHEMERALencryption: keys: - static: passphrase: supersecret slot: 0 lockToSTATE: true---apiVersion: v1alpha1kind: UserVolumeConfigname: longhornencryption: keys: - static: passphrase: supersecret slot: 0 lockToSTATE: true* De même cette partie sera traitée dans la prochaine section.
Nous utilisons des clés statiques en dehors du volume STATE afin de permettre la rotation des clés. Nouveauté de Talos 1.11, nous activons également lockToSTATE pour mixer la clé de chiffrement du volume EPHEMERAL et Longhorn avec un salt propre au volume STATE (utilisation d’une fonction de dérivation au niveau de la clé de déchiffrement). L’intérêt est que ces volumes soient indéchiffrables en cas de perte du volume STATE.
C’est le mode le plus sécurisé possible (en tout cas sur Hetzner Cloud à date), mais le moindre problème ou changement matériel entraînant une éventuelle modification de nodeID peut vous faire perdre l’ensemble des données du nœud. Toutefois l’utilisation de replicas Longhorn limite l’impact de la perte d’un nœud. Comme toujours un vrai backup externe sera toujours d’autant plus indispensable (naturellement chiffré). N’indiquez pas lockToSTATE (false par défaut) si vous préférez garder la possibilité de récupérer vos volumes en cas de perte du volume STATE.
Warning (Chiffrement des disques)
Les VPS disponibles par Hcloud ne supportent ni Secure Boot ni TPM à date. On se limite ici à une pass-phrase statique sur les volumes EPHEMERAL et Longhorn. Cette pass-phrase sera stocké dans le volume STATE lui-même chiffré via une clé unique spécifique au nœud.
Ne pas utiliser de pass-phrase statique sur les volumes STATE, cela rendrait tout le chiffrement inutile. En effet, la partition META, contenant notamment la config de la partition STATE dont la configuration du chiffrement, est forcément en clair.
Paramétrage Longhorn 🛠️
Pour automatiser au mieux la configuration des volumes pour Longhorn lors de son installation plus tard, le mieux est de préconfigurer les nodes en amont, via des labels et annotations spécifiques. Ils seront logiquement différents selon que ce soit un nœud de workload ou un nœud de stockage avec disque externe rattaché. Voici ce qu’on est en droit d’attendre comme labels et annotations pour chaque node pool selon notre schéma d’archi :
machine: nodeLabels: node.longhorn.io/create-default-disk: config nodeAnnotations: node.longhorn.io/default-disks-config: '[{"allowScheduling":true,"name":"system","path":"/var/lib/longhorn","tags":["local"]}]' node.longhorn.io/default-node-tags: '["worker"]'machine: nodeLabels: node.kubernetes.io/exclude-from-external-load-balancers: "true" node.kubernetes.io/role: storage node.longhorn.io/create-default-disk: config nodeAnnotations: node.longhorn.io/default-disks-config: '[{"allowScheduling":true,"name":"system","path":"/var/lib/longhorn","tags":["local"]},{"allowScheduling":true,"name":"volume","path":"/var/mnt/longhorn","tags":["volume"]}]' node.longhorn.io/default-node-tags: '["storage"]'On s’assure que seuls les nœuds workers soient rattachés aux loads balancers via le label node.kubernetes.io/exclude-from-external-load-balancers sur les autres pools.
Le label node.longhorn.io/create-default-disk: config permet de personnaliser la configuration par défaut des disques Longhorn via l’annotation node.longhorn.io/default-disks-config. On indique ici au format json que l’on souhaite un disque system local sur chaque nœud et un disque volume supplémentaire sur les nœuds de stockage. Ce dernier devra être monté au préalable sur /var/mnt/longhorn côté Hcloud.
On précise le tag local vs volume pour différencier les types de disques dans Longhorn, notamment pour l’aspect performance. On veut s’assurer en effet que les workloads à forts besoins en IOPS (base de données, etc) soient bien placés sur les disques locaux, plus performant que les disques externe.
On rajoute enfin node.longhorn.io/default-node-tags pour faciliter le placement des futurs volumes.
Conclusion
Voilà pour la partie préparatif, il est temps de commencer pour de vrai à la prochaine section.