Monitoring de raspberry, la suite.
Maintenant que l’on a instrumenté et analysé son infra, il est temps d’afficher tout ça dans un dashboard.
Il y a plusieurs façon d’e faire de la dataviz avec Warp 10 .
Coder sois-même son interface
Utiliser un outil tout fait comme Grafana
Utiliser WarpView pour se construire son dashboard
Utiliser Discovery pour un dashboard as code
Nous utiliserons ici Discovery. Un dashboard as code a de multiples intérêts :
versionner son dashboard
le rendre dynamique en fonction de certains paramètres
afficher certaines info quand on dépasse un seuil par exemple
passer un graphique en rouge en fonction d’un KPI
…
le réutiliser simplement pour d’autres usages
le modulariser
Mode d’emploi Discovery est très simple à prendre en main. Il s’agit de passer une structure de donnée (que je vais détailler) en WarpScript entre 2 balises HTML.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <html> <head> <title>Raspi-1 dashboard</title> </head> <body> <discovery-dashboard url="https://your warp 10/api/v0/exec"> // Le WarpScript va ici </discovery-dashboard> <!-- Les imports --> <script nomodule src="https://unpkg.com/@senx/discovery-widgets/dist/discovery/discovery.js"></script> <script type="module" src="https://unpkg.com/@senx/discovery-widgets/dist/discovery/discovery.esm.js"></script> </body> </html>
Le WarpScript doit produire la structure suivante :
// dashboard
title : Titre du dashboard (optionnel)
description : Description du dashboard (optionnel)
options : les options communes pour le dashboard (ce sont les mêmes que pour WarpView et ici aussi ) (optionnel)
autoRefresh : période en seconde entre 2 refresh des données, -1 pour désactiver (par défaut) (optionnel)
tiles : Liste des “tuiles” à afficher
// tuile
title : Titre de la tuile (optionnel)
x, y : position x et y sur la grille (commencent à 0 et la grille fait 12 cases de large)
w, h : hauteur et largeur en nombre de cases sur la grille (la largeur max est de 12)
type : type de graphique (‘line’, ‘area’, ‘scatter’, ‘spline-area’, ‘spline’, ‘step’, ‘step-after’, ‘step-before’, ‘annotation’, ‘bar’, ‘display’, ‘image’, ‘map’, ‘gauge’, ‘circle’, ‘pie’, ‘plot’, ‘doughnut’, ‘rose’, ‘tabular’, ‘button’)
unit : unité à afficher (optionnel)
options : les options communes pour le dashboard (ce sont les mêmes que pour WarpView et ici aussi ) (optionnel)
autoRefresh : période en seconde entre 2 refresh des données, -1 pour désactiver (par défaut) (optionnel et ne concerne que ‘macro’)
data / macro
data : le code sera exécuté au moment de l’exécution de la structure du dashboard
macro : le code sera exécuté par la tuile une fois affichée
Bon, un exemple sera plus parlant :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 { 'title' 'Test' 'options' { 'autoRefresh' 10 } // l'ensemble du dashboard se rafraîchira toutes les 10 secondes 'tiles' [ { 'title' 'Utilisation de "macro"' 'x' 0 'y' 0 'w' 6 'h' 1 'options' { 'autoRefresh' 2 } // cette tuile se rafraîchira toutes les 2 secondes 'type' 'display' 'macro' <% // C'est une macro WarpScript RAND 100.0 * ROUND %> } { 'title' 'Utilisation de "data"' 'x' 6 'y' 0 'w' 6 'h' 1 'type' 'display' 'data' RAND 100.0 * ROUND } ] }
regardez, ça bouge oO
Notre dashboard En utilisant ‘data’ on peut calculer au moment de la génération (ou du refresh) du dashboard, avec ‘macro’ c’est en ajax depuis la tuile.
Bon, cool, passons à nos métriques réalisés la dernière fois . On va utiliser ‘data’ car, ça nous permet d’utiliser des variables (genre, le token ou le host name) et on va rafraîchir le dashboard périodiquement.
Commençons par les variables :
1 2 3 4 // Variables 'your read token' 'token' STORE '2592000000000' 'duration' STORE // 1 mois 'rasp1-1' 'hname' STORE
Puis le dashboad (sans les tuiles) :
1 2 3 4 5 6 7 { 'title' 'Raspi-1' 'options' { 'autoRefresh' 30 } // auto refresh de 30 secondes 'tiles' [ // On va ajouter les tuiles là ! ] }
Tuile d’affichage de l’espace disque :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 { 'title' 'Disks' 'x' 0 'y' 0 'w' 3 'h' 1 'type' 'gauge' 'unit' 'Gb' 'data' [ $token '~linux.df.bytes.(capacityfree)' { 'hname' $hname 'device' '~/dev/.*' } NOW -1 ] FETCH 'gts' STORE [ $gts bucketizer.last NOW 0 1 ] BUCKETIZE [ 'device' 'mountpoint' ] PARTITION 'gts' STORE [] 'data' STORE [] 'params' STORE $gts KEYLIST <% 'k' STORE { $gts $k GET <% 'g' STORE $g NAME 'linux.df.bytes.' '' REPLACE $g VALUES REVERSE 0 GET %> FOREACH } 'vals' STORE $data { 'key' $k 'device' GET ' (' + $k 'mountpoint' GET + ')' + 'value' $vals 'capacity' GET $vals 'free' GET - 1024 / 1024 / 1024.0 / 100 * ROUND 100.0 / } +! DROP $params { 'maxValue' $vals 'capacity' GET 1024 / 1024 / 1024 / } +! DROP %> ASREGS FOREACH { 'data' $data 'params' $params } }
La tuile pour le réseau :
1 2 3 4 5 6 7 8 9 10 11 { 'title' 'Network' 'x' 0 'y' 1 'w' 3 'h' 1 'type' 'area' 'data' [ $token '~linux.proc.net.dev.(receivetransmit).bytes' { 'hname' $hname 'iface' 'eth0' } NOW $duration TOLONG ] FETCH false RESETS 'gts' STORE [ [ $gts [] 'linux.proc.net.dev.receive.bytes' filter.byclass ] FILTER [ $gts [] 'linux.proc.net.dev.transmit.bytes' filter.byclass ] FILTER ] FLATTEN [ SWAP bucketizer.mean NOW 1 h 0 ] BUCKETIZE 'gts' STORE [ $gts mapper.delta 1 0 0 ] MAP }
La tuile pour la charge CPU :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 { 'title' 'Load' 'x' 3 'y' 1 'w' 9 'h' 1 'type' 'area' 'data' <% [ $token '~linux.proc.stat.userhz.(usernicesystemidleiowait)' { 'hname' $hname 'cpu' 'cpu' } NOW $duration TOLONG ] FETCH 'gts' STORE [ $gts bucketizer.mean NOW 1 h 0 ] BUCKETIZE 'gts' STORE [ $gts [] '~linux.proc.stat.userhz.(usernicesystemidleiowait)' filter.byclass ] FILTER [ SWAP [] reducer.sum ] REDUCE 0 GET 'cpuGTS' STORE [ $gts [] 'linux.proc.stat.userhz.idle' filter.byclass ] FILTER 0 GET 'idleGTS' STORE [ $gts [] '~linux.proc.stat.userhz.(iowaitidle)' filter.byclass ] FILTER [ SWAP [] reducer.sum ] REDUCE 0 GET 'iowaitGTS' STORE [ $gts [] '~linux.proc.stat.userhz.(systemidle)' filter.byclass ] FILTER [ SWAP [] reducer.sum ] REDUCE 0 GET 'systemGTS' STORE [ [ [ $cpuGTS $idleGTS ] [] <% 'd' STORE $d 7 GET 0 GET 'cpu' STORE $d 7 GET 1 GET 'idle' STORE 1000.0 $cpu $idle - * $cpu 5 + / 10 / 'v' STORE [ $d 0 GET NaN NaN NaN $v ] %> ASREGS MACROREDUCER ] REDUCE 0 GET 'CPU' RENAME [ [ $iowaitGTS $idleGTS ] [] <% 'd' STORE $d 7 GET 0 GET 'cpu' STORE $d 7 GET 1 GET 'idle' STORE 1000.0 $cpu $idle - * $cpu 5 + / 10 / 'v' STORE [ $d 0 GET NaN NaN NaN $v ] %> ASREGS MACROREDUCER ] REDUCE 0 GET 'Disk IO' RENAME [ [ $systemGTS $idleGTS ] [] <% 'd' STORE $d 7 GET 0 GET 'cpu' STORE $d 7 GET 1 GET 'idle' STORE 1000.0 $cpu $idle - * $cpu 5 + / 10 / 'v' STORE [ $d 0 GET NaN NaN NaN $v ] %> ASREGS MACROREDUCER ] REDUCE 0 GET 'System' RENAME ] %> <% [] %> <% %> TRY }
Et enfin, la tuile pour la RAM/Swap :
1 2 3 4 5 6 7 { 'title' 'RAM/Swap' 'x' 3 'y' 0 'w' 9 'h' 1 'type' 'area' 'data' [ $token '~linux.proc.meminfo.(MemFreeSwapFree)' { 'hname' $hname } NOW $duration TOLONG ] FETCH 'gts' STORE [ $gts bucketizer.mean NOW 1 h 0 ] BUCKETIZE 'gts' STORE [ $gts 1.0 1024.0 1024.0 * 1024.0 * / mapper.mul 0 0 0 ] MAP }
Vous trouverez l’ensemble dans ce Gist . Et voici le résultat :
c’est beau
Pimp my dashboard Bon, les goûts et les couleurs… On va skiner un peut tout ça.
D’abord, on peut utiliser un des thèmes de couleurs proposé par la lib, il y en a quelques uns. Choisissons ‘CHARTANA’ au pif.
1 2 3 4 5 { 'title' 'Raspi-1' 'options' { 'scheme' 'CHARTANA' 'autoRefresh' 30 } 'tiles' [ ... ] }
Ensuite, on peut utiliser quelques variables CSS :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 <head> <title>Raspi-1 dashboard</title> <style> * { box-sizing: border-box; } :root { --wc-split-gutter-color : #404040; --warp-view-pagination-bg-color : #343a40 !important; --warp-view-pagination-border-color: #6c757d; --warp-view-datagrid-odd-bg-color : rgba(255, 255, 255, .05); --warp-view-datagrid-odd-color : #FFFFFF; --warp-view-datagrid-even-bg-color : #212529; --warp-view-datagrid-even-color : #FFFFFF; --warp-view-font-color : #FFFFFF; --warp-view-chart-label-color : #FFFFFF; --gts-stack-font-color : #FFFFFF; --warp-view-resize-handle-color : #111111; --warp-view-chart-legend-bg : #000; --gts-labelvalue-font-color : #ccc; --gts-separator-font-color : #FFFFFF; --gts-labelname-font-color : rgb(105, 223, 184); --gts-classname-font-color : rgb(126, 189, 245); --warp-view-chart-legend-color : #FFFFFF; --wc-tab-header-color : #FFFFFF; --wc-tab-header-selected-color : #404040; --warp-view-tile-background : #3A3C46; } body { font-size : 12px; line-height : 1.52; background-color: #333540; color : #FFFFFF; padding : 1rem; height : calc(100vh - 2rem); } discovery-dashboard { color: transparent; } </style> </head>
Et voilà :
C’est beau
On peut aller beaucoup plus loin, on peut par exemple changer la couleur de fond d’une tuile en fonction d’une valeur :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 RAND 100.0 * ROUND 'value' STORE // ce qu'on surveille, ici du random entre 0 et 100 { 'type' 'display' 'unit' '%25' // % 'w' 1 'h' 1 'x' 0 'y' 1 'data' { 'data' $value 'globalParams' { 'bgColor' <% $value 33 < %> <% '#77BE69' %> // value < 33 ? -> vert <% $value 66 < %> <% '#FF9830' %> // value < 66 ? -> orange <% '#F24865' %> 2 SWITCH // sinon rouge 'fontColor' 'white' // affichage de 'value' en blanc } }
Bref, faites vous plaisir :)