Extraire des infos de votre cluster Kubernetes au format CSV
Autre petit déterrage de brouillon. Je sais, quand on bosse sur en tant que Sysadmin, c’est pas spécialement le premier format de données auquel on pense. Mais quand on commence à bosser sur un cluster avec des dizaines de sites web déployés, plus des utilitaires/opérateurs autour, extraire les infos devient un léger sport, pire, quand il faut partager l’info à des gens moins techniques, c’est compliqué. Et donc, le format CSV peut aider, je vous explique.
Quand j’étais encore chez LinkByNet/Accenture, un des clusters Kube que j’avais à gérer était mutualisé entre plusieurs entités d’un client (appelés maisons), les applications étaient identifiées clairement comme telles via un label, mais on a découvert que cet élément d’inventaire, qui permettait la refacturation de la consommation dans le cluster à chaque maison (je vous passe le reste des détails), n’était pas toujours positionné correctement. Avant d’effectuer des corrections il est donc nécessaire de récupérer les informations existantes.
La configuration du label « maison » est splittée dans autant de dépôts git qu’on a de sites déployés, donc on s’évite le truc, et on part du principe qu’on peut récupérer l’information dans le cluster directement. Et pour traiter le résultat, surtout le partager au client, vu le format que ça risque de prendre (un tableau), on pense partager un tableau Excel pour ensuite se voir retourner la version corrigée.
Si vous manipulez régulièrement Kubernetes, vous savez que Excel, et même CSV, ne sont pas des formats supportés pour récupérer les informations, on a que JSON et YAML (en tout cas pour avoir les détails des déploiements/statefulsets/daemonsets, comme les labels), et éventuellement jsonPath pour pré-filtrer les infos. Ceci dit, à partir de JSON il y a potentiellement de quoi faire, en particulier avec jq. J’aime bien jq, même si je rote du sang à chaque fois que j’essaie de l’utiliser. Quelques recherches plus tard, mes intuitions étaient correctes et il s’avère que jq permet de formater les donner en CSV. Mais au fait, c’est quoi le CSV ?
Petit retour sur un format texte simple et pourtant si pratique
CSV ça veut dire Comma Separated Values, soit valeurs séparées par des virgules. Oui, c’est aussi simple que ça en a l’air, dans un fichier texte, la première ligne peut désigner éventuellement les titres des colonnes, et les lignes suivantes les valeurs. Un exemple ?
Nom,Prénom,Profession,Ville Chabrier,Pierre,Pleureur,MontCuq Lévy,Sylvain,Sniper,Saint-Denis Christ,Jesus,,Nazareth
Vous voyez, c’est assez Simple. Et en vrai on pourrait aussi procéder de la même façon avec la première colonne de description et les suivantes de données, voire les deux. Vous avez remarqué la troisième ligne ? Pour indiquer qu’on ne « remplit » pas la colonne correspondant ici à Profession, on laisse juste un vide avec deux virgules successives. Comme je l’ai dit, simple, efficace.
Reste à savoir comment on obtient ça à partir du JSON que nous retourne kubectl.
Dans le dur
Bien, comme je l’ai dit, jq va nous servir très fort, parce qu’il a justement une fonction dédiée au formatage de données en CSV. Commençons déjà par récupérer ce dont on a besoin. On cherche donc les infos des Deployments, StatefulSets, DaemonSets de tous les namespaces. Sur le papier, j’aurais pu utiliser jsonPath comme sortie pour récupérer les champs que je voulais, mais j’ai noté de déjà passer par jq pour filtrer tout ça et ne garder que ce qui m’intéresse, à savoir le type (Kind), le Namespace, le nodeselector (oui, on a eu une histoire de nodepool à gérer aussi), et le fameux label « maison ». Avec jq j’y suis arrivé plus simplement alors qu’avec jsonPath j’ai toujours buté sur un des points (mais là j’avoue, entre le premier brouillon y’a trois ans et maintenant, j’ai oublié lequel). Bref, tout ça finit dans un fichier json, ça donne quelque chose comme ça :
kubectl get deploy,sts,ds -o json -A | jq -r '[ .items[] | {Kind: .kind , Name: .metadata.name, Maison: .metadata.labels["maison"], Namespace: .metadata.namespace, selector: .spec.template.spec.nodeSelector["lbn.fr/project"] }]' > workload.json
Pas super simple à lire, mais pas non plus super compliqué à comprendre. Maintenant vient la partie qui permet de structurer notre futur fichier CSV. On commence par les entêtes. Actuellement on a ça dans le fichier json:
[{"Kind":"Deployment", "Name":"site1-com","Maison":"maison1","Namespace":"dev-site1-com","selector","web"}, {"Kind":"Deployment", "Name":"site2-com","Maison":"maison2","Namespace":"dev-site2-com","selector","web"}]
La première action va donc être d’extraire les clés. La petite triche ici c’est qu’on est certain qu’on peut se reposer sur la première « ligne » du tableau JSON parce qu’on a exactement les mêmes champs dans toutes les entrées. Et petite subtilité par contre, comme il s’agit des clés, on doit forcer jq à ne pas les trier par ordre alphabétique sinon ça fout la grouille :
cat workload.json | jq '.[0]|keys_unsorted | @csv' > workload.csv
- Le
[0]
, c’est pour forcer à se baser sur la première ligne uniquement (sinon il sortirait les clés de toutes les lignes et on aurait autant de lignes avec les noms des champs qu’on a d’entrées dans le tableau JSON) keys_unsorted
, comme je l’ai dit, permet de ne pas trier alphabétiquement- le
@csv
est la fonction magique qui formate le résultat pour nos besoins. - le chevron simple permet de s’assurer que c’est la première ligne du fichier, puisque comme ça tout contenu éventuellement existant est directement supprimé pour être remplacé par notre unique ligne.
C’est pas si compliqué hein ? Reste ensuite à extraire les valeurs, pour toutes les lignes cette fois. La logique reste grosso modo la même, à part que là, et j’ignore pourquoi, pas besoin de forcer à ne pas trier les valeurs, elles restent dans le bon ordre :
cat workload.json | jq '.[]|map(values) | @csv' >> workload.csv sed -i.bak 's/[\"\\]//g' workload.csv
À la place de [0]
on met []
pour indiquer « toutes les entrées », on mappe les valeurs sinon le filtre csv râle, et on met un double chevron pour mettre le résultat à la suite du contenu existant du fichier.
Et voilà, on peut désormais importer ce CSV dans un tableur pour le présenter de manière intelligible, avec des jolies colonnes qu’on peut filtrer/trier/annoter/corriger, à des gens qui n’ont pas l’habitude de bosser avec du JSON. Au final on aura eu quand même une petite dizaine de sites à corriger, on a donc pu se permettre de le faire manuellement. Sur un plus gros volume, on serait certainement passé par du scripting Python pour modifier les fichiers de values directement dans tous les dépôts Gitlab concernés, en laissant ensuite faire la magie du déploiement continu