K8S suite: Migration CloudNative
AAAaaaah, un nouvel article ! Bon alors pas mal occupé et la migration de mon serveur Bare Metal a pris son temps. Content de pouvoir réécrire donc car la migration est terminée ! J'aurais pu faire plein d'articles pour chaque étape, mais booon. Tunnel vision toussa.
Bon j'ai menti, le DHCP qui est géré par DHCPD sur mon vieux serveur n'a pas migré mais ce sera migré sur le DHCP de la box ou quelque chose comme ça bientôt. Ce n'est quand même pas le plus exaltant. Qui plus est le faire tourner sur un Kubernetes n'est à mon avis pas une bonne solution. Seul intérêt serait que ce serait l'occasion d'utiliser une Gateway API plutôt que les ingress que j'utilise encore pour l'instant. On a connu mieux comme prétexte au POC.
Alors au menu de cette migration et puis quel intérêt ? Problème de performance majoritairement, au bout d'un moment ton kernel même tweaker il fait pas de miracle quand il tourne sur un trop vieux CPU, et aussi côté consommation électrique. De ce point de vue pas évident que j'y gagne, ça consomme moins mais j'ai multiplié les serveurs avec 2 NUCs et 1 NAS, j'arrive au moins à égaliser la précédente consommation et j'ai dans l'espoir de la réduire ensuite en optimisant les apps et le cluster. J'avais deux-trois trucs qui tournaient sur ce vieux serveur Asus C60M1-I (2012...) quand même, long billet en vue:
- Service Web:
- Service réseau:
- BDD:
Alors, j'ai tout n'a pas été migré ! Au rang des laisser pour compte:
- TMNF: si j'ai une raison de remonter les serveurs, je le ferais ^^
- Subsonic: remplacé par Jellyfin mais je recommande ce soft car il m'aura bien servi et très bien fait.
- ShellInABox: no more need of SSH
- CUPS: j'ai plus d'imprimante ni scanner problème résolu !
- NFS: j'ai gardé Samba pour le côté windows fileshare ça suffira bien.
- DHCP
Hardware et OS: Immutable OS pour Kube = Talos
Le cluster Kube aura bien évolué, on sera passé d'un control plane sur un NUC Intel tournant sur du CoreOS, et le vieux serveur Asus de 2012 sous Fedora en temps que worker à, toujours le NUC Intel et en plus un nœud TBao T8 plus, tous deux désormais sous Talos ! Et avec tout ça, un petit NAS perso tournant sous TrueNAS Scale. Cet édition Scale tournant sous Linux au lieu de BSD et embarque un k3s pour faire tourner des applications conteneurisées.
Alors qu'est ce que j'y gagne ? je ne n'occupe plus de l'OS, c'est devenu un composant de base au même titre que le kernel Linux. Le besoin de pouvoir tweak l'OS est trop peu intéressant pour ce que ça apporte face à un OS immutable pensé et optimisé pour Kubernetes. Je n'ai aucun besoin spécifique et toutes mes bidouilles autour du kernel étaient faites pour palier sa vieillesse... Au bout d'un moment faut débrancher. Alors, on pourra toujours composer l'OS avec Talos cela dit, c'est à dire choisir quels services systèmes on veut ajouter dedans, comme l'iSCSI par exemple, ou des drivers GPU. Au même titre que le kernel était très peu tweaker au final et ce plus dans un but de curiosité que de réels besoins.
Merci à ce serveur Asus de 2012 lui même successeur d'un serveur de 2006 dont j'ai oublié les références. On sera quand même passé d'une Fedora Core 6 à Fedora 35 en upgrade de paquets classique sans réinstalle. Qui a dit que Fedora n'était pas une rolling release !?
Bref ça marche tout seul, on suit la doc, on configure un YAML et on a un nœud avec une API Talos permettant d'y accéder et de le configurer par la suite avec la CLI talosctl
. Elle vous permettra notamment de mettre à jour l'OS et/ou la version de Kubernetes aussi facilement qu'un apt-get upgrade
, pacman -Suy
ou dnf update
.
Pourquoi avoir migré de CoreOS à Talos ? Simplement car CoreOS se retrouvait être une Fedora déguisée avec un gestionnaire de paquets à base de rpm-ostree et qu'au final, ça perdait un peu du sens de l'OS immutable. C'est dû notamment au rachat par RedHat de CoreOS qui a forké à l'époque et renommé flatcar. Bref, flatcar était un peu chiant à géré dans le sens où il fallait build son image avec encore des outils pour qui ne faisait pas grand chose à part du templating comme butane. Le support était pas ouf à l'époque, y'a 2 ans, et quand j'ai trouvé Talos, j'ai bien plus aimé l'approche. Le but étant de ne plus s'occuper de l'OS, il a gagné. Il peut venir en addition avec Sidero afin de provisionner le bare metal en automatique avec le classique PXE+TFTP. Je l'ai testé un peu, c'est très facilement utilisable et se base sur ClusterAPI pour provisionner Kube. Propre. Je n'ai pas l'utilité d'un tel logiciel cela dit, donc je provisionne mes nœuds à la main, et utilise la cli pour update. Cela me va bien.
Pour la mise à jour avec Talos, cela se passe par là: https://factory.talos.dev pour composer son image selon le matériel. Ensuite, on prend le sha de l'image et lance l'upgrade. Exemple avec une image x86_64, avec iSCSI et driver i915 (pour l’accélération matérielle de Jellyfin):
$ talosctl upgrade -i factory.talos.dev/installer/ebdfa27a8d6272acf806ac6a5c968c3c284a47ce880273cecb19442c11bf0474:v1.7.1 --preserve
Le flags --preserve
étant là pour préserver les data si votre nœud est un control plane et qu'il est le seul du cluster, sinon, scoop, vous perdez votre cluster :D.
Sans faire un manuel de la cli et vu que ce n'est pas un post pour Talos, voici juste les commandes disponibles pour se donner une idée:
A CLI for out-of-band management of Kubernetes nodes created by Talos
Usage:
talosctl [command]
Available Commands:
apply-config Apply a new configuration to a node
bootstrap Bootstrap the etcd cluster on the specified node.
cluster A collection of commands for managing local docker-based or QEMU-based clusters
completion Output shell completion code for the specified shell (bash, fish or zsh)
config Manage the client configuration file (talosconfig)
conformance Run conformance tests
containers List containers
copy Copy data out from the node
dashboard Cluster dashboard with node overview, logs and real-time metrics
disks Get the list of disks from /sys/block on the machine
dmesg Retrieve kernel logs
edit Edit a resource from the default editor.
etcd Manage etcd
events Stream runtime events
gen Generate CAs, certificates, and private keys
get Get a specific resource or list of resources (use 'talosctl get rd' to see all available resource types).
health Check cluster health
help Help about any command
image Manage CRI containter images
inject Inject Talos API resources into Kubernetes manifests
inspect Inspect internals of Talos
kubeconfig Download the admin kubeconfig from the node
list Retrieve a directory listing
logs Retrieve logs for a service
machineconfig Machine config related commands
memory Show memory usage
meta Write and delete keys in the META partition
mounts List mounts
netstat Show network connections and sockets
patch Update field(s) of a resource using a JSON patch.
pcap Capture the network packets from the node.
processes List running processes
read Read a file on the machine
reboot Reboot a node
reset Reset a node
restart Restart a process
rollback Rollback a node to the previous installation
service Retrieve the state of a service (or all services), control service state
shutdown Shutdown a node
stats Get container stats
support Dump debug information about the cluster
time Gets current server time
upgrade Upgrade Talos on the target node
upgrade-k8s Upgrade Kubernetes control plane in the Talos cluster.
usage Retrieve a disk usage
validate Validate config
version Prints the version
La suite, la stack, les choix, le parcours
Pourquoi cette migration puisque après tout ça marchait bien sur un vieux matos. Plutôt écolo donc. Et ce fût le plus "dur", se séparer d'un truc qui marche. Plus lentement, mais suffisant pour mes besoins. Le problème étant que ce matériel n'était absolument pas fait pour faire tourner un Kubernetes dessus. Vanilla je précise, un k3s aurait pu le faire mais je voulais du vanilla. Le but étant de se faire la main, autant utiliser le mainstream. Je vous renvoi à mon précédent billet sur le sujet de la transition à l'époque.
Alors depuis ces premiers pas, quelques choix ont évolué:
- Interne:
- CSI: SMB, NFS et Longhorn --> SMB et Democratic CSI. Rationalisation en supprimant le NFS qui était une plaie à gérer avec le mapping UID/GID, et parfait pour se faire la main mais n'est plus adapté. SMB est conservé par commodité de partage de fichier réseau plus "traditionnel". Le changement Longhorn vers Democratic CSI est uniquement lié au NAS TrueNAS Scale.
- CNI: Flannel --> Cilium, ici simplement le plus évident avec le CNI le plus populaire en ce moment avec de très bonnes performances, fonctionnalités et support documentaire.
- Ingress Controller: Traefik --> Cilium, question curiosité et rationalisation des briques logiciels à "maintenir".
- Gateway API: Cilium
- Middleware:
- ArgoCD pour enfin faire du GitOps. Les manifests étaient déjà stockés dans un dépôt Git mais il manquait un outil pour les appliquer sur le cluster.
- CoreDNS: une seconde instance à celle interne à Kube pour géré les noms sur le réseau local et puis ça fait facilement du DNS over TCP ou HTTPS.
- LLDAP qui succède à l'ancien OpenLDAP. Ici je me suis basé sur le dépôt conseillé par l'upstream, adapté pour y inclure le script bootstrap.sh et importer mes quelques utilisateurs avec le job kube fourni. Pif paf c'est fait, n'ayant pas besoin de plus, j'ai reconfiguré mes instances pour pointer sur ce nouveau serveur et fait quelques arrangement pour remapper des anciens uuid avec les nouveaux pour ne pas perdre de profils utilisateurs (coucou Nextcloud). Je ferais sans doute un article rapide sur le sujet car ça peut servir.
- Cert-Manager avec un cluster-issuer let's encrypt.
- Kyverno pour configurer des trucs non prévu par les charts Helm
- mysql-operator pour provisionner du MySQL
- cloudnativepg pareil pour Postgres
- node-feature-discovery pour l'application des labels sur les nodes selon leur fonctionnalité hardware.
- inteldeviceplugins-system, gestion des drivers GPU pour Intel, ça peut proposer plus de choses mais je n'utilise que celle-ci.
- Apps:
- Dashy: Start page configurable et dynamique
- Nextcloud: Stockage Web distant
- Blog
- Jellyfin: streaming multimedia
- Jackett: Download de torrent
- Home Assistant: Domotique
- Hedgedoc: Prise de notes collaborative
- Grafana: monitoring, observabilité webUI
- Excalidraw: plateforme pour diagramme et schéma collaborative
- Matomo: pour le suivi d'audience du blog
- Psono: gestionnaire de mots de passes et secrets
- OpenWebUI: ollama et sa webUI pour la maison.
- J'adore les bulletpoints vous saviez ?
Le DNS externe: CoreDNS
Passage de Bind9 à CoreDNS pour la gestion du DNS local. Ici, je ne me suis pas cassé la tête plus que nécessaire, j'ai porté mes fichiers bind9 sous CoreDNS en utilisant son plugin file. Alors pas très flexible cela dit, mais je n'ai pas d'évolution si dynamique de mon "parc". Je continue donc avec les fichiers pour le moment. Ce qui donne quelque chose comme ceci:
Il est prévu d'activer le DNS over HTTPs et TLS bientôt, c'était tout l'intérêt de CoreDNS. Pouvoir le faire, simplement et rapidement.
ArgoCD: enfin du GitOps, petit bonheur
Aaaah, bon Git c'est bien, le GitOps c'est mieux ! Désormais gestion du cluster depuis ArgoCD. Il nous faut pour être complet la gestion des secrets afin de pouvoir les provisionner dans le cluster. J'ai préféré Sops à Vault simplement par facilité pour la migration. D'abord on migre après, on verra pour mettre un Vault 😄. Du coup, on utilise un plugin kustomizations KSOPS, qui utilise SOPS derrière pour les capacités de chiffrement, avec une intégration à ArgoCD. Alors je trouve ça pas forcément très "pratique" ou "straigthforward" mais une fois en place, ça s'oublie et go.
J'utilise KSOPS avec Age, car je n'utilise pas de KMS externe et comme j'ai pas mis de Vault y'en aura pas non plus en interne. Peut-être plus tard, chaque chose en son temps. La kustomization finale utilise les manifestes de base d'argocd avec les patchs suivant:
Wala, wala, le argocd-repo-server-patch.yaml
justifie le fait que ne soit pas fan de la soluce mais bon. J'ai choisi de mettre la clé SOPS dans un secret qui fait partie d'une autre kustomizations appliquer à la main dans un premier temps, œuf-poule oblige, puis en tant que partie de la post-install d'ArgoCD ensuite.
Voilà après ça, on a un ArgoCD qui fonctionne. Plus qu'à créer pleins d'applications ArgoCD à déployer ! Globalement j'ai suivi le pattern avec des Apps d'Apps pour bootstrapper le reste du cluster. Une grosse apps qui définit toutes les autres Apps.
Le statefulset, l'operator et la DB
Dans mon précédent billet, je relayais les problèmes de Kubernetes avec le stockage et surtout les problèmes hébergés une DB type SQL. En effet, une base de données SQL fonctionne en standalone de base et ne scale pas trop horizontalement "simplement". Elles ont généralement toute en revanche des mécanismes de réplication et HA ad hoc pour se mettre en cluster avec de la configuration et huile de coude. Souvent à base de journaux d'opérations SQL à rejouer et écriture plus intensives sur les disques etc. Cela existe depuis un sacré moment et les options sont pléthores, cela ne rends pas la chose très accessible pour un néophyte.
Du côté Kubernetes, il faudra attendre les statefulsets stable pour avoir un accès au stockage stable. Après le boulot de réplication n'étant pas son boulot, il nous manquera les operator Kube qui vont nous permettre de configurer la réplication d'instance SQL simplement sans être DBA ou manger de la docs sur X BDD. Du moins pour des besoins modestes de homelab, ça fait le taf facile !
Donc, j'avais mariaDB et MySQL, j'ai décidé de ne garder que MySQL à mon grand désarroi juste parce que la plateforme de blog que j'utilise, ghost, n'est plus compatible MariaDB... Et donc, il me reste 2 operators pour gérer cela:
- mysql-operator pour provisionner du MySQL
- cloudnativepg pour PostGres
Globalement, un cluster simple de MySQL ressemble à ça:
Hop, suffit de modifier le nombre d'instance et paf, ça réplique. 😎 Pas trop fan de cet operator, notamment à cause de la documentation vraiment limite. J'aurais bien aimé des capacités d'import un peu plus évolués pour cloner une base en fonctionnement.
Par contre l'operator cloudnativepg, lui le fait et est très bien documenté. Petit exemple:
Configuration de Cilium
Pas grand chose à dire ici, j'avais quelques prérequis pour m'éviter de multiplier les middlewares, je voulais qu'il puisse être à la fois provider d'IP pour les services LoadBalancer, et m'éviter de déployer un MetalLB ou assimiler, Ingress controller pour remiser mon Traefik, supporter le Gateway API pour une future migration. Ce que fait Cilium !
Pour le côté LB, j'utilise la fonctionnalité de L2 announcements afin d'exposer les IPs sur le réseau local en dehors de Kube. Une alternative au setup BGP cilium, restons pragmatique j'ai pas trop envie de mettre du matos pour géré du BGP ou faire un setup pour ça.
Les values helm que j'utilise sont celles-ci:
On y voit notamment le port 7445 pour joindre l'api kube en local qui est une fonctionnalité apporté par Talos avec KubePrism, tldr ça permet de toujours pouvoir joindre l'API tant qu'ya un control plane dispo sans pour autant setup d'adresse IP de service.
Côté l2 announcement:
apiVersion: cilium.io/v2alpha1
kind: CiliumL2AnnouncementPolicy
metadata:
name: l2announce
spec:
externalIPs: true
interfaces:
- ^enp
- ^eno
loadBalancerIPs: true
serviceSelector:
matchLabels:
announced: "true"
Et mon pool d'IPs pour l'IPAM:
apiVersion: "cilium.io/v2alpha1"
kind: CiliumLoadBalancerIPPool
metadata:
name: main-pool
spec:
cidrs:
- start: "192.168.1.128"
stop: "192.168.1.253"
Democratic CSI et chiffrement de valeurs de Chart Helm pour ArgoCD
Democratic CSI peut s'installer avec une Helm Chart. C'est encore le plus simple, un ensemble d'exemples ont été fourni par le CSI pour plusieurs type. Ici, ce sera l'exemple de freenas-iscsi que j'utiliserais pour utiliser à mon TrueNAS en tant que fournisseur de volumes.
Et en gros, voici les valeurs que j'utilise:
Et cela a son importance car j'ai pas mal galéré pour pouvoir avoir ArgoCD qui déchiffre le fichier de valeurs car autant dans une kustomization KSOPS fait le job pour les kustomizations mais autant pour un fichier de values Helm c'est pas prévu ! J'ai eu à modifier un chouille la syntaxe pour le faire fonctionner dans mon cas car il me fallait récup le secret depuis un endroit externe ce qui n'est pas supporté par le projet helm-secrets.
On ArgoCD 2.6.x, helm-secrets isn't supported in Multi-Source application, because the source reference, e.g.:$ref
needs to be at the beginning of a string. This is in conflict with helm-secrets, since the string needs to begin withsecrets://
. On top, ArgoCD do not resolve references in URLs.
On remarquera la syntaxe digne des meilleures regex pointant vers le fichier de values donc... Alors ça ne pointe sur $ref comme précisé car pas au début la ligne... Alors on contourne salement. On lui indique le fichier de valeurs est un secrets SOPS à déchiffrer, ensuite on indique qu'il est stocké dans une git accessible par https, puis et c'est la où le bât blesse, y'a un token gitlab qui traine dans l'url https vers le fichier. Bon le token n'a que des droits en lecture mais clairement pas fou ! C'est le problème. Enfin à la fin de la ligne on y met la branche qu'on veut.
Clairement si vous utilisez un KMS c'est mieux... Next step donc.
Bon bah on a fait le tour, je crois 😄.