first commit

This commit is contained in:
VC
2025-02-27 12:51:50 +01:00
commit f5e914e533
234 changed files with 11519 additions and 0 deletions

View File

@@ -0,0 +1,260 @@
+++
title = "Grapher les seaux Garage dans Munin"
date = 2023-05-07
[taxonomies]
tags = [ "système", "garage", "munin", "graphe", "auto-hébergement" ]
+++
> Tu vois, cest là quelle a triché la fille…
*Christophe, regardant ses magnifiques graphes Munin*
<!-- more -->
Munin, cest loin dêtre parfait, mais ça a un avantage non négligeable: cest ultra léger. Si tu as besoin de sortir des métriques rapidement pour un système auto-hébergé, ça évite de sortir la très grosse artillerie tout produisant des données utilisables et utiles, pour une complexité relativement réduite.
Il y a quelques jours, je me suis demandé comment je pourrais produire des graphes pour Garage (parce que `garage`, cest bien, mangez-en) histoire davoir des infos que je considère comme essentielles: quantité de données par *bucket*, nombre dobjets et éventuellement téléversements foireux.
Toutes ces données sont accessibles via la commande `garage bucket info <bucket>`, mais je cherchais a pouvoir grapher lensemble de manière un peu plus efficace.
# Métriques dans le code de Garage
Première piste suivie: regarder ce qui est existe déjà dans `garage`. Étant un jeune logiciel, il intègre par défaut tout un tas de protocoles et de standards modernes pour interagir avec dautres éléments assez communs dans les Systèmes dInformation modernes: `consul`, `kubernetes`, etc… Ce qui nous intéresse ici est la partie métrique qui est disponible au format [OpenTelemetry](https://opentelemetry.io/).
Pour lactiver, rien de plus simple, dans `/etc/garage.toml`, il suffit dajouter deux lignes pour activer lAPI `admin`:
```toml
[admin]
api_bind_addr = "[::1]:3903"
```
On redémarre `garage` et on peut alors accéder directement aux métriques via lURL [http://[::1]:3903/metrics](http://[::1]:3903/metrics).
Alors, cest cool, on peut y trouver pas mal de métriques internes à `garage`, mais malheureusement, absolument rien qui concerne les *buckets* eux-mêmes. Dans un contexte de supervision, [la documentation officielle](https://garagehq.deuxfleurs.fr/documentation/reference-manual/monitoring/) donne même les indicateurs quil faut suivre en priorité, et les éventuels soucis quils peuvent représenter.
Sauf que cest pas du tout ce quon veut, on veut simplement grapher lusage.
# Le code source de Garage?
`garage` est écrit en Rust, jai donc jeté un petit coup dœil dans le code. Autant le dire tout de suite: cest bien au-dessus de mes compétences et si jai réussi, à force dessais et derreurs, à introduire des données dans les métriques déjà supportées, en ajouter de nouvelles est une autre paire de manches!
Sans aller jusquà dire que cest impossible (encore une fois, je ne connais que très peu le code et il est dune complexité relativement élevée pour moi), je dirais que ce nest pas forcément la bonne approche.
Après avoir fait le tour de ça, je suis donc revenu à la base: quand on tape `garage bucket info <bucket>`, on obtient bien les données que lon cherche, elles doivent donc bien être disponibles quelque part…
Évidemment la première option, ce serait de parser le résultat de cette commande, mais elle nest visiblement pas vraiment faite pour être parsée (en tout cas, pas facilement), petit exemple:
```
# garage bucket info tarace
Bucket: cdfe[…]28f0
Size: 43.6 GiB (46.8 GB)
Objects: 374678
Unfinished multipart uploads: 1497
Website access: true
Global aliases:
tarace
ta.mere.lol
Key-specific aliases:
Authorized keys:
RW GKXXXX tarace-key
```
Il y a des espaces partout et il est a priori impossible dobtenir les unités brutes pour le stockage (elles sont systématiquement calculées en MiB/GiB/TiB avant dêtre affichées). On peut évidemment, et jai vérifié, bricoler le code source pour lui faire afficher ce que lon souhaite, mais une modif juste pour ça me paraît très largement au-dessus de la nécessité.
Et cest là que je me suis souvenu que `garage` avait aussi une API dadministration sur laquelle on pouvait trouver pas mal de trucs…
# API admin, quand tu nous tiens
Activer lAPI admin est relativement simple, il suffit dajouter un *token* dans `/etc/garage.toml`:
```toml
[admin]
api_bind_addr = "[::1]:3903"
admin_token = "tamerelol"
```
À partir de ce moment, on peut interroger lAPI avec un petit `curl` des familles, tout simplement (lentête doit juste contenir `Bearer <admin_token>` dans un champs `Authorization` et le tour est joué):
```bash
> curl -s -H "Authorization: Bearer tamerelol" "http://[::1]:3903/v0/status"
{
"node": "153d5ffd1f2be62c1eec4f318ba862e4aefcb28be6bc778422d22ada69cef904",
"garageVersion": "git:v0.8.1-209-g1ecd88c-modified",
"garageFeatures": [
"k2v",
"sled",
"metrics",
"bundled-libs"
],
[]
}
```
Et donc, il suffit dutiliser le *endpoint* `bucket` pour récupérer les infos dont on a besoin:
```bash
> curl -s -H "Authorization: Bearer tamerelol" "http://[::1]:3903/v0/bucket"
[
{
"id": "dd8113bace4a0f65721f88f5b386b50dff3ac62f3e7ac4bc002570d4c4c98fe2",
"globalAliases": [
"tamerelol"
],
"localAliases": []
}
]
> curl -s -H "Authorization: Bearer tamerelol" "http://[::1]:3903/v0/bucket?id=dd8113bace4a0f65721f88f5b386b50dff3ac62f3e7ac4bc002570d4c4c98fe2"
{
"id": "dd8113bace4a0f65721f88f5b386b50dff3ac62f3e7ac4bc002570d4c4c98fe2",
"globalAliases": [
"tamerelol"
],
"websiteAccess": false,
"websiteConfig": null,
"keys": [
{
"accessKeyId": "GK94ecde83f07248fb33b81d34",
"name": "Unnamed key",
"permissions": {
"read": true,
"write": true,
"owner": true
},
"bucketLocalAliases": []
}
],
"objects": 24,
"bytes": 19420604,
"unfinishedUploads": 12,
"quotas": {
"maxSize": null,
"maxObjects": null
}
}
```
À partir de ce moment-là, cest essentiellement une question de rédiger un script correct pour Munin.
# Chargez les Munin-tions (oui, cest très mauvais)
Le prérequis pour faire un script Munin est assez minimaliste en réalité. Il faut:
* avoir un morceau de script capable de renvoyer les caractéristiques des graphes si on lui ajout lentrée `config`;
* être capable de sortir un chiffre par ligne dans un format assez basique.
Cest donc simplement une affaire de récupérer les bonnes données, les mettre en forme et merci kiki.
On va ici simplement sattaquer à la donnée qui concerne les objets dans les *buckets*. Les autres graphes auront exactement le même script et la même forme, il sagit simplement de changer quelques données dedans à chaque fois (jexpliquerai un peu plus loin quelques subtilités).
Histoire davoir un truc présentable, on va aussi essayer de faire afficher à Munin les noms des *buckets* plutôt que leur identifiant. À noter quavec lAPI admin de `garage`, il est impossible dappeler le *endpoint* `bucket` avec un nom. Seuls les identifiants sont acceptés. On va donc utiliser du `bash` avec un tableau associatif (histoire de pouvoir la translation identifiant/nom plus facilement).
Première chose à faire donc, stocker en « sécurité » notre `admin_token` pour quil soit accessible depuis tous nos scripts Munin. Pour cela, on peut aller lajouter comme une variable denvironnement pour les scripts commençant par `garage*` dans `/etc/munin/plugin-conf.d/garage`:
```
[garage*]
env.BEARER tamerelol
```
La variable shell `${BEARER}` contiendra systématiquement ce *token* quand on lexécute dans lenvironnement de Munin. Voilà le résultat après une grosse heure de script:
```bash
#!/bin/bash
HEADER="Authorization: Bearer ${BEARER}" # on forme lentête un peu en avance,
# on va sen resservir souvent
declare -A BUCKETS=() # on déclare le tableau associatif
# on va aller récupérer lensemble des buckets via lAPI.
# On utilise jq pour interpréter les résultats et les stocker sous forme:
# <id>,<name>
# un par ligne
API_BUCKETS_JSON=$(curl -s -H "${HEADER}" "http://[::1]:3903/v0/bucket" | jq -r '.[] | .id + "," + .globalAliases[0]')
# on va peupler notre tabeau associatif en interprétant bêtement chaque ligne
for bucket in ${API_BUCKETS_JSON}
do
BUCKETS+=([$(echo ${bucket} | cut -d ',' -f 1)]="$(echo ${bucket} | cut -d ',' -f 2)")
done
# Ceci sert uniquement à la partie configuration:
# on va y poser les labels, le type de graphe, ainsi que le nom de chaque
# courbe. Ce nom sera donc automatiquement le nom du bucket S3.
case $1 in
config)
cat << 'EOM'
graph_title Objects by Bucket
graph_vlabel Number of objects
graph_args --base 1000 -l 0
graph_category garage
EOM
for i in "${!BUCKETS[@]}"
do
echo "${BUCKETS[${i}]}.label ${BUCKETS[${i}]}"
done
exit 0;;
esac
# ceci est un exécution normale du script
# on y refait un appel à lAPI mais avec lidentifiant du bucket cette fois-ci
# comme ça, on peut récupérer la donnée qui nous intéresse, toujours via jq
# et lafficher proprement!
for i in "${!BUCKETS[@]}"
do
OBJECTS=$(curl -s -H "${HEADER}" "http://[::1]:3903/v0/bucket?id=${i}" | jq -r '.objects')
echo "${BUCKETS[${i}]}.value ${OBJECTS}"
done
```
Dans le cas dune exécution avec configuration, on obtient donc ceci:
```bash
> ./garage_bucket_objects config
graph_title Objects by Bucket
graph_vlabel Number of objects
graph_args --base 1000 -l 0
graph_category garage
tamerelol.label tamerelol
```
Et avec une exécution standard:
```bash
> ./garage_bucket_objects
tamerelol.value 24
```
Si lon ajoute un nouveau *bucket*, `youpi`, il est automatiquement pris en compte:
```bash
> ./garage_bucket_objects
youpi.value 0
tamerelol.value 24
```
On peut alors tester le script en condition réelle sur le serveur `garage`, en le plaçant dans `/etc/munin/plugins`, via:
```bash
munin-run garage_bucket_objects
```
Si tout se passe bien, il suffit alors de redémarrer le service `munin-node` et le tour est joué!
# Et pour… le reste?
Si tu souhaite grapher dautres données, cest à peu près aussi simple: lors de second appel, sans rien modifier dautres, tu peux simplement demander le champs `unfinishedUploads` au lieu du champs `objects` pour obtenir les téléversements non terminés.
Si tu veux obtenir la taille de chaque *bucket*, cest globalement la même chose à un petit détail près: non seulement il faut effectivement demander le champs `bytes` lors du second appel à lAPI, mais il faut aussi demander au graphe de passer dune base 1000 à une base 1024 histoire dafficher des données cohérentes.
Mais voilà, cest à peu près tout, ça fait des jolis graphes et tout le monde est content \o/
# Bonus: grapher le total
Petit bonus de dernière minute: pour grapher un total (somme de toutes les courbes), il suffit dajouter ce qui suit à la `config`.
```
graph_total Total
```
Pour du disque, ça peut être pertinent…