CérénIT

Le blog tech de Nicolas Steinmetz (Time Series, IoT, Web, Ops, Data)

Warp 10 - Interactions avec une instance InfluxDB - FLoWS edition

warp10timeseriesinfluxdbwarpscriptwarpfleetflows

Maintenant que FLoWS est officiellement disponible, je vous propose de revisiter l'article de la semaine dernière Warp 10 - Interactions avec une instance InfluxDB en utliisant FLoWS à la place de WarpScript pour se faire une idée.

Pré-requis: warpfleet

Installons déjà warpfleet, le gestionnaire de package conçu pour Warp 10.

# Installation de npm
sudo dnf install -y npm

# installation de warpfleet
sudo npm install -g @senx/warpfleet

# Vérification de la bonne installation de warpfleet
wf version
      ___       __                     _______________         _____
      __ |     / /_____ __________________  ____/__  /___________  /_
      __ | /| / /_  __ `/_  ___/__  __ \_  /_   __  /_  _ \  _ \  __/
      __ |/ |/ / / /_/ /_  /   __  /_/ /  __/   _  / /  __/  __/ /_
      ____/|__/  \__,_/ /_/    _  .___//_/      /_/  \___/\___/\__/
                               /_/version: 1.0.31
1.0.31

Installation de l'extension warp10-ext-influxdb

Sans trop rentrer dans les détails de warpfleet, il utilise un système de namespace appelés "Groups" pour ces packages et qui permettent de définir ses propres dépots. Pour l'extension warp10-ext-influxdb, le "group" est io.warp10.

Ce qui pour l"installation donne la commande suivante :

# Si votre utilisateur n'a pas accès à /path/to/warp10, il vous faudra utiliser sudo
(sudo) wf g -w /path/to/warp10 io.warp10 warp10-ext-influxdb

warpfleet va vous demander quelle version de l'extension vous souhaitez puis va procéder à son téléchargement et son installation.

Cela donne :

sudo wf g -w /opt/warp10 io.warp10 warp10-ext-influxdb
       ___       __                     _______________         _____
       __ |     / /_____ __________________  ____/__  /___________  /_
       __ | /| / /_  __ `/_  ___/__  __ \_  /_   __  /_  _ \  _ \  __/
       __ |/ |/ / / /_/ /_  /   __  /_/ /  __/   _  / /  __/  __/ /_
       ____/|__/  \__,_/ /_/    _  .___//_/      /_/  \___/\___/\__/
                                /_/                                        ™
 version: 1.0.31
? Which revision do you want to retrieve? latest
✔ ext io.warp10:warp10-ext-influxdb#1.0.1-uberjar retrieved
✔ Download successful: gradle-wrapper.jar
✔ Download successful: gradle-wrapper.properties
✔ Download successful: gradlew
✔ Download successful: gradlew.bat
✔ Dependency warp10-ext-influxdb-1.0.1-uberjar.jar successfully deployed
✔ Done

Note: Pour éviter un bug dans la fonction INFLUXDB.UPDATE identifié lors de la rédaction de ce billet, assurez-vous d'avoir une version >= 1.0.1

Ensuite, il faut créer le fichier /path/to/warp10/etc/conf.d/90--influxdb-extension.conf et y ajouter la ligne suivante:

warpscript.extension.influxdb = io.warp10.script.ext.influxdb.InfluxDBWarpScriptExtension

Je préfère créer un fichier plutôt que d'éditer un fichier existant pour le suivi des mises à jour et j'ai utilisé le prefix 90 car il n'est pas utilisé par les fichiers de Warp10.

Relancer ensuite Warp 10 pour que le plugin soit chargé au démarrage de l'instance :

(sudo) /path/to/warp10/bin/warp10-standalone.init restart

Dans /path/to/warp10/logs/warp10.log, vous devriez voir apparaitre :

2020-09-17T10:59:23,742 main INFO  script.WarpScriptLib - LOADED extension 'io.warp10.script.ext.influxdb.InfluxDBWarpScriptExtension'

Installation de FLoWS

Assurez-vous d'avoir préalablement une version >= 2.7.1 de Warp 10

sudo wf g -w /opt/warp10 io.warp10 warp10-ext-flows
       ___       __                     _______________         _____
       __ |     / /_____ __________________  ____/__  /___________  /_
       __ | /| / /_  __ `/_  ___/__  __ \_  /_   __  /_  _ \  _ \  __/
       __ |/ |/ / / /_/ /_  /   __  /_/ /  __/   _  / /  __/  __/ /_
       ____/|__/  \__,_/ /_/    _  .___//_/      /_/  \___/\___/\__/
                                /_/                                        ™
 version: 1.0.31
? Which revision do you want to retrieve? latest
✔ ext io.warp10:warp10-ext-flows#0.1.0-uberjar retrieved
✔ Download successful: gradle-wrapper.jar
✔ Download successful: gradle-wrapper.properties
✔ Download successful: gradlew
✔ Download successful: gradlew.bat
✔ Dependency warp10-ext-flows-0.1.0-uberjar.jar successfully deployed
warpscript.extension.flows = io.warp10.ext.flows.FLoWSWarpScriptExtension
? Choose what you want to add (Press <space> to select, <a> to toggle all, <i> to invert selection)warpscript.extension.flows = io.warp10.ext.flows.FLoWSWarpScriptExtension
✔ warpscript.extension.flows = io.warp10.ext.flows.FLoWSWarpScriptExtension added to /opt/warp10/etc/conf.d/io.warp10-warp10-ext-flows.conf
✔ Done

Relancer ensuite Warp 10 pour que le plugin soit chargé au démarrage de l'instance :

(sudo) /path/to/warp10/bin/warp10-standalone.init restart

Dans /path/to/warp10/logs/warp10.log, vous devriez voir apparaitre :

2020-10-08T10:59:51,288 main INFO  script.WarpScriptLib - LOADED extension 'io.warp10.ext.flows.FLoWSWarpScriptExtension'

Préalable sur FLoWS

FLoWS ne dispose pas encore d'un endpoint /flows ; nous continuons donc à utiliser les "endpoints historiques warpscript compatibles".

Ce qui nous amène à réaliser une seule action en RPN/NPI (Notation Polonaise Inversée) lorsque l'on soumet du code FLoWS à Warp 10 :

# Multiligne WarpScript qui permet de mettre son code FLoWS
<'
... Code FLoWS ...
'>
# Applique la fonction FLOWS au code ci-dessus
FLOWS

Pour les types, les notations, etc - je voue renvoie au billet de blog introduisant FLoWS.

Requêtage d'une instance InfluxDB 1.x

La semaine dernière nous écrivions en WarpScript :

# Requête INFLUXQL et informations de connection à InfluxDB 1.X
{ 'influxql' "select * from cpu where host=%27myHost%27 and time > now() - 1h" 'db' "myDatabase" 'password' "myPassword" 'user' "myUser" 'url' "http://url.to.influxdb:8086" }
INFLUXDB.FETCH
# On récupère une liste de liste de séries GTS. Il n'y a qu'un seul élément dans cette liste. Nous le prenons pour n'avoir plus qu'une liste de séries GTS.
0 GET
# Sauvegarde de la liste dans une variable cpu
'cpu' STORE
# Affichage de la liste de GTS
$cpu

Cette semaine, nous écrivons en FLoWS :

<'
# Récupération de la liste de GTS
cpu_fetch = INFLUXDB.FETCH({'influxql':'select * from cpu where host=%27myHost%27 and time > now() - 1h', 'db':'myDatabase', 'password':'myPassword', 'user':'myUser', 'url':'http://url.to.influxdb:8086'})

# Récupération de la GTS en prenant l'index 0 de la liste
cpu = GET(cpu_fetch, 0)

# Affichage de la liste de GTS
return cpu
'>
FLOWS

On peut déjà noter :

  • la lecture du code correspond plus à nos habitudes dans les autres lanagage
  • les fonctions ont les mêmes noms, leur signature ne change pas non plus
  • le mot clé return est nouveau ici et permet d'afficher la GTS dans le studio par ex.
  • les tableaux (MAP) sont plus lisibles (arguments séparés par des virugules, la clé et la valeur sont séparés par deux points) et moins sources d'erreurs (gestion des espaces plus sensibles en WarpScript)

Le résultat est identique :

warp10 - influxdb 1

Pour illustrer cette liste de liste de GTS, si on veut récupérer la GTS du cpu idle, on voit dans le graphique que c'est la 5ème courbe, donc un indice 4.

En WarpScript :

{ 'influxql' "select * from cpu where host=%27myHost%27 and time > now() - 1h" 'db' "myDatabase" 'password' "myPassword" 'user' "myUser" 'url' "http://url.to.influxdb:8086" }
INFLUXDB.FETCH
0 GET
'cpu' STORE
# Récupération de la 5ème liste (indice 4)
$cpu 4 GET

En FLoWS :

<'
cpu_fetch = INFLUXDB.FETCH({'influxql':'select * from cpu where host=%27myHost%27 and time > now() - 1h', 'db':'myDatabase', 'password':'myPassword', 'user':'myUser', 'url':'http://url.to.influxdb:8086'})
cpu = GET(cpu_fetch, 0)
# Récupération de la 5ème liste (indice 4)
# on pourrait écrire : return GET(cpu, 4) mais on peut aussi écrire de façon plus concise :
return cpu[4]
'>
FLOWS

warp10 - influxdb 2

Requêtage d'une instance InfluxDB 2.x

La semaine dernière, nous écrivions en WarpScript :

# Utilisation du string multi-ligne pour améliorer la lisibilité de la requête FLUX et sauvegarde dans une variable fluxquery.
<'
from(bucket: "crntbackup")
  |> range(start: -1h, stop: now())
  |> filter(fn: (r) => r["_measurement"] == "cpu")
  |> filter(fn: (r) => r["cpu"] == "cpu-total")
  |> aggregateWindow(every: 1s, fn: mean, createEmpty: false)
  |> yield(name: "mean")
'>
'fluxquery' STORE

# Paramètres de la fonction INFLUX.FLUX avec la requête flux (la variable fluxquery) et les informations de connection à InfluxDB 2.x
{ 'flux' $fluxquery 'org' "myOrganisation" 'token' "myToken" 'url' "http://url.to.influxdb2:9999" }
INFLUXDB.FLUX
# Sauvegarde de la liste dans une variable cpu
'cpu' STORE
# Affichage de la liste de GTS
$cpu

En FLoWS :

# Utilisation du string multi-ligne pour améliorer la lisibilité de la requête FLUX et sauvegarde dans une variable fluxquery.
<'
# Utilisation du string multi-ligne pour améliorer la lisibilité de la requête FLUX et sauvegarde dans une variable fluxquery.
fluxquery = 'from(bucket: "crntbackup")
  |> range(start: -1h, stop: now())
  |> filter(fn: (r) => r["_measurement"] == "cpu")
  |> filter(fn: (r) => r["cpu"] == "cpu-total")
  |> aggregateWindow(every: 1s, fn: mean, createEmpty: false)
  |> yield(name: "mean")'

# Paramètres de la fonction INFLUX.FLUX avec la requête flux (la variable fluxquery) et les informations de connection à InfluxDB 2.x
params = {'flux':fluxquery, 'org':'myOrganisation', 'token':'myToken', 'url':'http://url.to.influxdb2:9999' }
# Sauvegarde de la liste dans une variable cpu
cpu = INFLUXDB.FLUX(params)
# Affichage de la liste de GTS
return cpu
'>
FLOWS

On obtient :

warp10 - influxdb 3

Si on veut comme précédemment avec InfluxQL afficher la courbe du CPU idle.

En WarpScript :

# Utilisation du string multi-ligne pour améliorer la lisibilité de la requête FLUX et sauvegarde dans une variable fluxquery.
<'
from(bucket: "crntbackup")
  |> range(start: -1h, stop: now())
  |> filter(fn: (r) => r["_measurement"] == "cpu")
  |> filter(fn: (r) => r["cpu"] == "cpu-total")
  |> aggregateWindow(every: 1s, fn: mean, createEmpty: false)
  |> yield(name: "mean")
'>
'fluxquery' STORE

# Paramètres de la fonction INFLUX.FLUX avec la requête flux (la variable fluxquery) et les informations de connection à InfluxDB 2.x
{ 'flux' $fluxquery 'org' "myOrganisation" 'token' "myToken" 'url' "http://url.to.influxdb2:9999" }
INFLUXDB.FLUX
# Sauvegarde de la liste dans une variable cpu
'cpu' STORE

# Affiche la 7eme liste (incide 6)
$cpu 6 GET

En FLoWS :

# Utilisation du string multi-ligne pour améliorer la lisibilité de la requête FLUX et sauvegarde dans une variable fluxquery.
<'
# Utilisation du string multi-ligne pour améliorer la lisibilité de la requête FLUX et sauvegarde dans une variable fluxquery.
fluxquery = 'from(bucket: "crntbackup")
  |> range(start: -1h, stop: now())
  |> filter(fn: (r) => r["_measurement"] == "cpu")
  |> filter(fn: (r) => r["cpu"] == "cpu-total")
  |> aggregateWindow(every: 1s, fn: mean, createEmpty: false)
  |> yield(name: "mean")'

# Paramètres de la fonction INFLUX.FLUX avec la requête flux (la variable fluxquery) et les informations de connection à InfluxDB 2.x
params = {'flux':fluxquery, 'org':'myOrganisation', 'token':'myToken', 'url':'http://url.to.influxdb2:9999' }
# Sauvegarde de la liste dans une variable cpu
cpu = INFLUXDB.FLUX(params)
# Affiche la 7eme liste (incide 6)
return cpu[6]
'>
FLOWS

warp10 - influxdb 4

Sauvegarder des données dans InfluxDB

La semaine dernière, nous écrivions en WarpScript :

'<read_token>' 'readToken' STORE
'<write_token>' 'writeToken' STORE

# Récupération des dépenses sous la forme d'une série  (GTS)
[ $readToken 'expense' { 'company' '=cerenit' } '2016-12-01T00:00:00Z' '2020-06-01T00:00:00Z' ] FETCH
0 GET
'exp' STORE

# Récupération du chiffre d'affaires mensuel sous la forme d'une série  (GTS)
[ $readToken 'revenue' { 'company' '=cerenit' } '2016-12-01T00:00:00Z' '2020-06-01T00:00:00Z' ] FETCH
0 GET
'revenue' STORE

# Calcul du résulat mensuel
$revenue $exp -
# Stockage de la série obtenue dans une série appelée "result"
"result" RENAME
{ "company" "cerenit" } RELABEL
$writeToken UPDATE

# Récupération du résultat mensuel sous la forme d'une série (GTS)
[ $readToken 'result' { 'company' '=cerenit' } '2016-12-01T00:00:00Z' '2020-06-01T00:00:00Z' ] FETCH
0 GET
'result' STORE

# Version 1.x
# Création d'une MAP 'params' avec les informations de connection à l'instance InfluxDB
{ 'v1' true 'url' "http://url.to.influxdb:8086" 'measurement' "result" 'db' "crntcompta" 'password' "myPassword" 'user' "myUser" }
'params' STORE
# Utilisatoin de la fonction INFLUXDB.UPDATE qui prend la variable 'params' pour les paramètres de connection et une GTS ou liste de GTS pour les données à sauvegarder
$result $params INFLUXDB.UPDATE

# Version 2.x
# Création d'une MAP 'params' avec les informations de connection à l'instance InfluxDB
{ 'v1' false 'url' "http://url.to.influxdb:9999" 'measurement' "result" 'bucket' "crntcompta" 'token' "myToken" 'org' "myOrganisation" }
'params' STORE
$result $params  INFLUXDB.UPDATE

En FLoWS, cela donne :

<'
# Gestion des tokens de lecture et écriture
readToken = '<read_token>'
writeToken = '<write_token>'

# Récupération de la liste de GTS dans Warp 10
expense_fetch = FETCH([readToken, 'expense', {'company':'cerenit'}, '2016-12-01T00:00:00Z', '2020-06-01T00:00:00Z'])
# On n'a toujours qu'une liste à 1 élement, on récupère donc la GTS en prenant l'index 0 de la liste
expense = expense_fetch[0]

# Même logique pour le chiffre d'affaires
revenue_fetch = FETCH([readToken, 'revenue', {'company':'cerenit'}, '2016-12-01T00:00:00Z', '2020-06-01T00:00:00Z'])
revenue = revenue_fetch[0]

# On calcule la résultat en faisant la différence entre les 2 GTS revenue et expense
r = revenue - expense
# la GTS obtenue n'ayant pas de nom, on lui en fournit un
RENAME(r, 'result')
# On ajoute les labels manquants
RELABEL(r, {"company":"cerenit"})
# On persiste la GTS dans Warp 10
UPDATE(r, writeToken)

# On récupère la contenu de la GTS result sur le même modèle que revenue et expense précédemment
result_fetch = FETCH([readToken, 'result', {'company':'cerenit'}, '2016-12-01T00:00:00Z', '2020-06-01T00:00:00Z'])
result = result_fetch[0]

# InfluxDB 1.X
# Définition des paramètres de connection
params = {'v1':true, 'url':"http://url.to.influxdb:8086", 'measurement':"result", 'db':"crntcompta", 'password':"myPassword", 'user':"myUser"}
# Persistance des données dans InfluxDB 1.x
INFLUXDB.UPDATE(result, params)

# InfluxDB 2.X
# Définition des paramètres de connection
params = {'v1':false, 'url':"http://url.to.influxdb:9999", 'measurement':"result", 'bucket':"crntcompta", 'token':"myToken", 'org':"myOrganisation" }
# Persistance des données dans InfluxDB 2.x
INFLUXDB.UPDATE(result, params)
'>
# Fin du code FLoWS
# Application de la fonction FLOWS à notre ensemble de code
FLOWS

Coté InfluxDB, on retrouve bien nos données :

warp10 - influxdb 5

Si au contraire, je veux regrouper plusieurs valeurs dans un même measurement InfluxDB, il faut passer une liste de GTS à INFLUXDB.UPDATE.

La semaine dernière, nous écrivions en WarpScript :

# Version 1.x
# Création d'une MAP 'params' avec les informations de connection à l'instance InfluxDB
{ 'v1' true 'url' "http://url.to.influxdb:8086" 'measurement' "accountancy" 'db' "crntcompta" 'password' "myPassword" 'user' "myUser" }
'params' STORE
# Passage d'une liste de GTS plutôt qu'une seule série de l'expemple précédent
[ $result $revenue $exp ] $params INFLUXDB.UPDATE

# Version 2.x
# Création d'une MAP 'params' avec les informations de connection à l'instance InfluxDB
{ 'v1' false 'url' "http://url.to.influxdb:9999" 'measurement' "accountancy" 'bucket' "crntcompta" 'token' "myToken" 'org' "myOrganisation" }
'params' STORE
# Passage d'une liste de GTS plutôt qu'une seule série de l'expemple précédent
[ $result $revenue $exp ] $params INFLUXDB.UPDATE

Cette semaine en FLoWS :

# InfluxDB 1.X
# Définition des paramètres de connection
params = {'v1':true, 'url':'http://url.to.influxdb:8086', 'measurement':'result', 'db':'crntcompta', 'password':'myPassword', 'user':'myUser'}
# Persistance des données dans InfluxDB 1.x en passant une liste de GTS
INFLUXDB.UPDATE([result, revenue, expense], params)

# InfluxDB 2.X
# Définition des paramètres de connection
params = {'v1':false, 'url':'http://url.to.influxdb:9999', 'measurement':'result', 'bucket':'crntcompta', 'token':'myToken', 'org':'myOrganisation' }
# Persistance des données dans InfluxDB 2.x en passant une liste de GTS
INFLUXDB.UPDATE([result, revenue, expense], params)
'>
FLOWS

Coté InfluxDB, on retrouve bien nos données :

warp10 - influxdb 6

Conclusion

Personnellement, au travers de cette exercice, je trouve FLoWS plutôt agréable à utiliser et il remplit bien son objectif de faciliter la prise en main de Warp 10 :

  • Même si je commençais à m'habituer à la NPI/RPN, avoir une approche plus traditionnelle évite de devoir se concentrer sur l'ordre,
  • La séparation par des virgules et/ou par des deux points (:) dans les listes et tableaux (MAP) facilitent leur lisiblité
  • La migration d'un code en Warpscript vers FLoWS peut se faire de façon très progressive puisque les deux peuvent cohabiter ; c'est d'ailleurs comme ça que j'ai procédé pour ce billet et valider ainsi que je ne perdais rien au fur et à mesure. Il suffit à la fin d'enlever tous les blocs intermédiaires pour n'avoir plus qu'un seul bloc de code en FLoWS. C'est plutôt malin ! Et si une extension ou un code ne serait pas encore disponible en FLoWS, il est possible de débrancher sur du Warpscript.
  • Il manque la complétion FLOWS dans le studio (et VSCode je présume) mais c'est pour bientôt !
  • Le code gagne en lisiblité car on identifie les blocs qui vont ensemble.

Pour illustrer ce dernier point : quand je repense au code du tutoriel sur les cyclone "Number per year", nous avons ce code à un moment :

$fetch_wind $end $end $start - TIMECLIP NONEMPTY SIZE

La fonction TIMECLIP prend trois arguments sauf que là, j'en vois potentiellement cinq. Il faut alors comprendre que le troisième argument est en fait le résulat de l'opération $end $start -.

En FLoWS, on aura alors :

TIMECLIP(fetch_wind, end, end - start)

et au global (non testé) :

SIZE(NONEMPTY(TIMECLIP(fetch_wind, end, end - start)))

Ce qui me semble nettement plus clair/lisible.

Le pari de FLoWS était de rendre Warp 10 plus accessible et de rendre les développeurs productifs plus rapidement, il semblerait bien que les objectifs soient remplis.

Warp 10 - Interactions avec une instance InfluxDB

warp10timeseriesinfluxdbwarpscriptwarpfleet

Après les premiers pas avec Warp10 et en attendant que l'extension FLoWS soit disponible pour la version 2.7.0, j'ai mis à jour mon instance Warp 10 en 2.7.0 et j'ai voulu jouer avec l'extension warp10-ext-influxdb. Cette extension permet de requêter une instance InfluxDB 1.x ou 2.x avec du WarpScript.

Attention à ne pas confondre le plugin natif InfluxDB qui permet d'envoyer des métriques au format Line Protocol d'InfluxDB dans Warp10 et l'extension InfluxDB qui permet d'interagir avce une base InfluxDB en WarpScript.

Pré-requis: warpfleet

Installons déjà warpfleet, le gestionnaire de package conçu pour Warp 10.

# Installation de npm
sudo dnf install -y npm

# installation de warpfleet
sudo npm install -g @senx/warpfleet

# Vérification de la bonne installation de warpfleet
wf version
      ___       __                     _______________         _____
      __ |     / /_____ __________________  ____/__  /___________  /_
      __ | /| / /_  __ `/_  ___/__  __ \_  /_   __  /_  _ \  _ \  __/
      __ |/ |/ / / /_/ /_  /   __  /_/ /  __/   _  / /  __/  __/ /_
      ____/|__/  \__,_/ /_/    _  .___//_/      /_/  \___/\___/\__/
                               /_/version: 1.0.31
1.0.31

Installation de l'extension warp10-ext-influxdb

Sans trop rentrer dans les détails de warpfleet, il utilise un système de namespace appelés "Groups" pour ces packages et qui permettent de définir ses propres dépots. Pour l'extension warp10-ext-influxdb, le "group" est io.warp10.

Ce qui pour l"installation donne la commande suivante :

# Si votre utilisateur n'a pas accès à /path/to/warp10, il vous faudra utiliser sudo
(sudo) wf g -w /path/to/warp10 io.warp10 warp10-ext-influxdb

warpfleet va vous demander quelle version de l'extension vous souhaitez puis va procéder à son téléchargement et son installation.

Cela donne :

sudo wf g -w /opt/warp10 io.warp10 warp10-ext-influxdb
       ___       __                     _______________         _____
       __ |     / /_____ __________________  ____/__  /___________  /_
       __ | /| / /_  __ `/_  ___/__  __ \_  /_   __  /_  _ \  _ \  __/
       __ |/ |/ / / /_/ /_  /   __  /_/ /  __/   _  / /  __/  __/ /_
       ____/|__/  \__,_/ /_/    _  .___//_/      /_/  \___/\___/\__/
                                /_/                                        ™
 version: 1.0.31
? Which revision do you want to retrieve? latest
✔ ext io.warp10:warp10-ext-influxdb#1.0.1-uberjar retrieved
✔ Download successful: gradle-wrapper.jar
✔ Download successful: gradle-wrapper.properties
✔ Download successful: gradlew
✔ Download successful: gradlew.bat
✔ Dependency warp10-ext-influxdb-1.0.1-uberjar.jar successfully deployed
✔ Done

Note: Pour éviter un bug dans la fonction INFLUXDB.UPDATE identifié lors de la rédaction de ce billet, assurez-vous d'avoir une version >= 1.0.1

Ensuite, il faut créer le fichier /path/to/warp10/etc/conf.d/90--influxdb-extension.conf et y ajouter la ligne suivante:

warpscript.extension.influxdb = io.warp10.script.ext.influxdb.InfluxDBWarpScriptExtension

Je préfère créer un fichier plutôt que d'éditer un fichier existant pour le suivi des mises à jour et j'ai utilisé le prefix 90 car il n'est pas utilisé par les fichiers de Warp10.

Relancer ensuite Warp 10 pour que le plugin soit chargé au démarrage de l'instance :

(sudo) /path/to/warp10/bin/warp10-standalone.init restart

Dans /path/to/warp10/logs/warp10.log, vous devriez voir apparaitre :

2020-09-17T10:59:23,742 main INFO  script.WarpScriptLib - LOADED extension 'io.warp10.script.ext.influxdb.InfluxDBWarpScriptExtension'

Requêtage d'une instance InfluxDB 1.x

Note: La librairie influxdb-java ne semble pas supporter un reverse proxy http/2 devant l'instance InfluxDB. Il faut donc accéder à InfluxDB en direct via le port 8086.

# Requête INFLUXQL et informations de connection à InfluxDB 1.X
{ 'influxql' "select * from cpu where host=%27myHost%27 and time > now() - 1h" 'db' "myDatabase" 'password' "myPassword" 'user' "myUser" 'url' "http://url.to.influxdb:8086" }
INFLUXDB.FETCH
# On récupère une liste de liste de séries GTS. Il n'y a qu'un seul élément dans cette liste. Nous le prenons pour n'avoir plus qu'une liste de séries GTS.
0 GET
# Sauvegarde de la liste dans une variable cpu
'cpu' STORE
# Affichage de la liste de GTS
$cpu

Dans ce qu'il faut noter ici:

  • INFLUXDB.FETCH prend ses paramètres dans une MAP ayant pour paramètres: influxql, db, password, user et url.
  • Pour la directive where, il faut encoder les apostrophes via %27 : en InfluxQL, on écrirait ... where host='myHost' ; là, il faut écrire where host=%27myHost%27
  • Si dans InfluxDB, on a un measurements (cpu) avec n items (usage_guest, usage_idle, etc), on a une conversion en une liste de n GTS avec une valeur chacune.
  • influxql prend une ou plusieurs requêtes séparées par des points virgules. Cela donnera en sortie plusieurs listes de listes de GTS (vu que comme dit au dessus, pour une requête influxql sur un measurement on a 1 à n GTS ; donc pour y requêtes, on aura y listes de n listes de GTS)

warp10 - influxdb 1

Pour illustrer cette liste de liste de GTS, si on veut récupérer la GTS du cpu idle, on voit dans le graphique que c'est la 5ème courbe, donc un indice 4.

{ 'influxql' "select * from cpu where host=%27myHost%27 and time > now() - 1h" 'db' "myDatabase" 'password' "myPassword" 'user' "myUser" 'url' "http://url.to.influxdb:8086" }
INFLUXDB.FETCH
0 GET
'cpu' STORE
# Récupération de la 5ème liste (indice 4)
$cpu 4 GET

warp10 - influxdb 2

Requêtage d'une instance InfluxDB 2.x

Note: La librairie influxdb-client-java ne semble pas plus supporter un reverse proxy http/2 devant l'instance InfluxDB. Il faut donc accéder à InfluxDB en direct via le port 9999.

Si on fait une requête similaire en flux :

# Requête FLUX et informations de connection à InfluxDB 2.x
{ 'flux' "from(bucket: %22myBucket%22) |> range(start: -1h, stop: now()) |> filter(fn: (r) => r[%22_measurement%22] == %22cpu%22) |> filter(fn: (r) => r[%22cpu%22] == %22cpu-total%22) |> aggregateWindow(every: 1s, fn: mean, createEmpty: false) |> yield(name: %22mean%22)" 'org' "myOrganisation" 'token' "myToken" 'url' "http://url.to.influxdb2:9999" }
INFLUXDB.FLUX
# On récupère une liste de séries GTS
# Sauvegarde de la liste dans une variable cpu
'cpu' STORE
# Affichage de la liste de GTS
$cpu

warp10 - influxdb 2

On note que la requête flux n'est pas très lisible de par l'encodage des guillemets et de son coté monoligne. On peut améliorer ça avec une variable STRING en multi-ligne :

# Utilisation du string multi-ligne pour améliorer la lisibilité de la requête FLUX et sauvegarde dans une variable fluxquery.
<'
from(bucket: "crntbackup")
  |> range(start: -1h, stop: now())
  |> filter(fn: (r) => r["_measurement"] == "cpu")
  |> filter(fn: (r) => r["cpu"] == "cpu-total")
  |> aggregateWindow(every: 1s, fn: mean, createEmpty: false)
  |> yield(name: "mean")
'>
'fluxquery' STORE

# Paramètres de la fonction INFLUX.FLUX avec la requête flux (la variable fluxquery) et les informations de connection à InfluxDB 2.x
{ 'flux' $fluxquery 'org' "myOrganisation" 'token' "myToken" 'url' "http://url.to.influxdb2:9999" }
INFLUXDB.FLUX
# Sauvegarde de la liste dans une variable cpu
'cpu' STORE
# Affichage de la liste de GTS
$cpu

On y gagne en lisibilité et pas besoin d'encoder les guillements !

Dans les deux cas, on obtient :

warp10 - influxdb 3

On note d'ailleurs que les méta-données (nom du measurement, nom du champ et les tags sont repris sous la forme de labels)

Contraiement à la requête en InfluxQL, on ne peut passer qu'une requête à la fois mais ce qui permet d'avoir directment une liste de GTS puis la GTS. On n'a plus une liste de liste de GTS.

Si on veut comme précédemment avec InfluxQL afficher la courbe du CPU idle:

# Utilisation du string multi-ligne pour améliorer la lisibilité de la requête FLUX et sauvegarde dans une variable fluxquery.
<'
from(bucket: "crntbackup")
  |> range(start: -1h, stop: now())
  |> filter(fn: (r) => r["_measurement"] == "cpu")
  |> filter(fn: (r) => r["cpu"] == "cpu-total")
  |> aggregateWindow(every: 1s, fn: mean, createEmpty: false)
  |> yield(name: "mean")
'>
'fluxquery' STORE

# Paramètres de la fonction INFLUX.FLUX avec la requête flux (la variable fluxquery) et les informations de connection à InfluxDB 2.x
{ 'flux' $fluxquery 'org' "myOrganisation" 'token' "myToken" 'url' "http://url.to.influxdb2:9999" }
INFLUXDB.FLUX
# Sauvegarde de la liste dans une variable cpu
'cpu' STORE

# Affiche la 7eme liste (incide 6)
$cpu 6 GET

warp10 - influxdb 4

Je ne vais pas pousser l'exemple plus loin, il ne tient qu'à vous de poursuivre la manipulation de vos données en WarpScript. On pourrait se demander où mettre la limite entre la requête en Flux/InfluxQL et les manipulations à faire en WarpScript ensuite. Tout dépendra de votre cas d'usage.

Sauvegarder des données dans InfluxDB

Pour le moment, nous avons requêté des données stockées dans InfluxDB 1.x ou 2.x ; mais nous pouvons très bien imaginer un cas où les données sont issues d'une autre source de données ou bien ont été générées avec WarpScript mais qu'on veuille les persister dans InfluxDB 1.x ou 2.x

Reprenons mon exercice de compatbilité et de prédictions et sauvegardons tout ça dans InfluxDB.

Pour rappel, nous avons fait ceci :

'<read_token>' 'readToken' STORE
'<wrtie_token>' 'writeToken' STORE

# Récupération des dépenses sous la forme d'une série  (GTS)
[ $readToken 'expense' { 'company' '=cerenit' } '2016-12-01T00:00:00Z' '2020-06-01T00:00:00Z' ] FETCH
0 GET
'exp' STORE

# Récupération du chiffre d'affaires mensuel sous la forme d'une série  (GTS)
[ $readToken 'revenue' { 'company' '=cerenit' } '2016-12-01T00:00:00Z' '2020-06-01T00:00:00Z' ] FETCH
0 GET
'revenue' STORE

# Calcul du résulat mensuel
$revenue $exp -
# Stockage de la série obtenue dans une série appelée "result"
"result" RENAME
{ "company" "cerenit" } RELABEL
$writeToken UPDATE

# Récupération du résultat mensuel sous la forme d'une série (GTS)
[ $readToken 'result' { 'company' '=cerenit' } '2016-12-01T00:00:00Z' '2020-06-01T00:00:00Z' ] FETCH
0 GET
'result' STORE

Si je veux sauvegarder une série dans un measurement influxdb :

# Version 1.x
# Création d'une MAP 'params' avec les informations de connection à l'instance InfluxDB
{ 'v1' true 'url' "http://url.to.influxdb:8086" 'measurement' "result" 'db' "crntcompta" 'password' "myPassword" 'user' "myUser" }
'params' STORE
# Utilisatoin de la fonction INFLUXDB.UPDATE qui prend la variable 'params' pour les paramètres de connection et une GTS ou liste de GTS pour les données à sauvegarder
$result $params INFLUXDB.UPDATE

# Version 2.x
# Création d'une MAP 'params' avec les informations de connection à l'instance InfluxDB
{ 'v1' false 'url' "http://url.to.influxdb:9999" 'measurement' "result" 'bucket' "crntcompta" 'token' "myToken" 'org' "myOrganisation" }
'params' STORE
$result $params  INFLUXDB.UPDATE

Coté InfluxDB, on retrouve bien nos données :

warp10 - influxdb 5

Si au contraire, je veux regrouper plusieurs valeurs dans un même measurement InfluxDB, il faut passer une liste de GTS à INFLUXDB.UPDATE.

# Version 1.x
# Création d'une MAP 'params' avec les informations de connection à l'instance InfluxDB
{ 'v1' true 'url' "http://url.to.influxdb:8086" 'measurement' "accountancy" 'db' "crntcompta" 'password' "myPassword" 'user' "myUser" }
'params' STORE
# Passage d'une liste de GTS plutôt qu'une seule série de l'expemple précédent
[ $result $revenue $exp ] $params INFLUXDB.UPDATE

# Version 2.x
# Création d'une MAP 'params' avec les informations de connection à l'instance InfluxDB
{ 'v1' false 'url' "http://url.to.influxdb:9999" 'measurement' "accountancy" 'bucket' "crntcompta" 'token' "myToken" 'org' "myOrganisation" }
'params' STORE
# Passage d'une liste de GTS plutôt qu'une seule série de l'expemple précédent
[ $result $revenue $exp ] $params INFLUXDB.UPDATE

Coté InfluxDB, on retrouve bien nos données :

warp10 - influxdb 6

On arrive au bout de ce billet, nous avons vu que nous pouvons :

  • En WarpScript, requêter des données stockées dans InfluxDB 1.x et 2.x
  • En WarpScript, manipuler des données issues de Warp 10 puis les stocker dans InfluxDB 1.x et 2.x

Nous pourrions aller plus loin avec :

  • des scénarios d'enrichissement de données en s'interfaçant par ex avec des données de sources SQL (référentiels, etc)
  • des scénarios d'analytics en croisant des données issues de Warp10, InfluxDB ou d'autres sources de données (bases SQL, etc)
  • des scénarios de projection en appliquant par exemple les algorythmes de machine learning sur des données issues d'InfluxDB
  • ...

WarpScript semble ainsi permettre d'avoir un langage de manipulation de séries temporelles multi-sources et d'offrir une expérience unifiée de manipulatoin de ces données. Dans les prochains billets, nous explorerons d'avantage la partie visualisation et alerting.

Premiers pas avec Warp 10 : comptabilité et prévisions de fin d'année

warp10timeseriesforecastdashboardwarpstudioarima

Suite de notre épopée :

Cherchant à me familiariser avec la base de données orientée série temporelles Warp 10 d'une part et à améliorer mes tableaux de bord comptables pour me faire des projections à fin d'année (parce que bon, faire juste la moyenne des mois précédents comme valeur pour les mois à venir, c'est un peu trop facile), je me suis dit que c'était un exercice qui pouvait répondre aux deux besoins après avoir lu Time series forecasts in WarpScript.

Pour ceux qui ne connaissent pas encore Warp 10 , c'est une solution de geo-timeseries (séries spatio temporelles) open source, éditée par SenX, société française basée à Brest. Pour en savoir plus sur Warp 10 , vous pouvez regarder l'éditions 1 et l'édition 5 du Paris Time Series Meetup.

Pour prendre en main Warp 10 et appréhender le langage de programmation Warpscript, je vous invite à suivre le tutoriel sur les cyclones en utilisant la Sandbox Warp10 mise à disposition par Senx.

Le jeu de données

Pour le jeu de données, j'ai donc récupéré de mes tableaux de bords mon chiffre d'affaires et mes dépenses mensuels sur la période Janvier 2017 à Mai 2020.

Nous allons donc créer 2 séries (appelées aussi GTS)

Soit crnt-revenue.gts:

# 2017
1483225200000000// revenue{company=cerenit} 0
1485903600000000// revenue{company=cerenit} 13800
1488322800000000// revenue{company=cerenit} 11325
1490997600000000// revenue{company=cerenit} 300
1493589600000000// revenue{company=cerenit} 6825
1496268000000000// revenue{company=cerenit} 8450
1498860000000000// revenue{company=cerenit} 5425
1501538400000000// revenue{company=cerenit} 10650
1504216800000000// revenue{company=cerenit} 13650
1506808800000000// revenue{company=cerenit} 0
1509490800000000// revenue{company=cerenit} 11200
1512082800000000// revenue{company=cerenit} 19225
# 2018
1514761200000000// revenue{company=cerenit} 8300
1517439600000000// revenue{company=cerenit} 8850
1519858800000000// revenue{company=cerenit} 10285
1522533600000000// revenue{company=cerenit} 8850
1525125600000000// revenue{company=cerenit} 8850
1527804000000000// revenue{company=cerenit} 9450
1530396000000000// revenue{company=cerenit} 12000
1533074400000000// revenue{company=cerenit} 11250
1535752800000000// revenue{company=cerenit} 15013
1538344800000000// revenue{company=cerenit} 15750
1541026800000000// revenue{company=cerenit} 13750
1543618800000000// revenue{company=cerenit} 10125
# 2019
1546297200000000// revenue{company=cerenit} 15375
1548975600000000// revenue{company=cerenit} 14750
1551394800000000// revenue{company=cerenit} 11600
1554069600000000// revenue{company=cerenit} 20622
1556661600000000// revenue{company=cerenit} 6376
1559340000000000// revenue{company=cerenit} 13350
1561932000000000// revenue{company=cerenit} 11250
1564610400000000// revenue{company=cerenit} 7050
1567288800000000// revenue{company=cerenit} 14750
1569880800000000// revenue{company=cerenit} 12326
1572562800000000// revenue{company=cerenit} 12513
1575154800000000// revenue{company=cerenit} 9082
# 2020
1577833200000000// revenue{company=cerenit} 13000
1580511600000000// revenue{company=cerenit} 12375
1583017200000000// revenue{company=cerenit} 15500
1585692000000000// revenue{company=cerenit} 5525
1588284000000000// revenue{company=cerenit} 15750

et crnt-expenses.gts:

# 2017
1483225200000000// expense{company=cerenit} 219
1485903600000000// expense{company=cerenit} 5471
1488322800000000// expense{company=cerenit} 7441
1490997600000000// expense{company=cerenit} 6217
1493589600000000// expense{company=cerenit} 5676
1496268000000000// expense{company=cerenit} 5719
1498860000000000// expense{company=cerenit} 5617
1501538400000000// expense{company=cerenit} 5690
1504216800000000// expense{company=cerenit} 5831
1506808800000000// expense{company=cerenit} 9015
1509490800000000// expense{company=cerenit} 8903
1512082800000000// expense{company=cerenit} 11181
# 2018
1514761200000000// expense{company=cerenit} 9352
1517439600000000// expense{company=cerenit} 9297
1519858800000000// expense{company=cerenit} 8506
1522533600000000// expense{company=cerenit} 8677
1525125600000000// expense{company=cerenit} 10136
1527804000000000// expense{company=cerenit} 10949
1530396000000000// expense{company=cerenit} 8971
1533074400000000// expense{company=cerenit} 9062
1535752800000000// expense{company=cerenit} 9910
1538344800000000// expense{company=cerenit} 10190
1541026800000000// expense{company=cerenit} 10913
1543618800000000// expense{company=cerenit} 13569
# 2019
1546297200000000// expense{company=cerenit} 11553
1548975600000000// expense{company=cerenit} 11401
1551394800000000// expense{company=cerenit} 10072
1554069600000000// expense{company=cerenit} 10904
1556661600000000// expense{company=cerenit} 9983
1559340000000000// expense{company=cerenit} 11541
1561932000000000// expense{company=cerenit} 11065
1564610400000000// expense{company=cerenit} 10359
1567288800000000// expense{company=cerenit} 10450
1569880800000000// expense{company=cerenit} 9893
1572562800000000// expense{company=cerenit} 10014
1575154800000000// expense{company=cerenit} 15354
# 2020
1577833200000000// expense{company=cerenit} 9673
1580511600000000// expense{company=cerenit} 9933
1583017200000000// expense{company=cerenit} 9815
1585692000000000// expense{company=cerenit} 9400
1588284000000000// expense{company=cerenit} 9381

Pour chaque fichier:

  • le premier champ est un timestamp correspondant au 1er jour de chaque mois à 00h00.
  • la partie // indique qu'il n'y a pas de position spatiale (longitude, lattitude, élévation)
  • expense et revenue sont les noms des classes qui vont stocker mes informations
  • company est un label que je positionne sur mes données avec le nom de mon entreprise
  • le dernier champ est la valeur de mon chiffre d'affaires ou de mes dépenses mensuels.

Pour plus d'information sur la modélisation, cf GTS Input Format.

Insertion des données

Lorsque vous utilisez la Sandbox, 3 tokens vous sont donnés :

  • un token pour lire les données ; j'y ferai référence via <readToken> par la suite
  • un token pour écrire les données ; j'y ferai référence via <writeToken> par la suite
  • un token pour supprimer les données ; j'y ferai référence via <deleteToken> par la suite
#!/usr/bin/env bash

for file in crnt-expenses crnt-revenue ; do
	curl -v -H 'Transfer-Encoding: chunked' -H 'X-Warp10-Token: <writeToken>' -T ${file}.gts 'https://sandbox.senx.io/api/v0/update'
done

Premières requêtes

Pour ce faire, nous allons utiliser le Warp Studio ; pour la datasource, il conviendra de veiller à ce que la SenX Sandbox soit bien sélectionnée.

L'équivalent de "SELECT * FROM *" peut se faire de la façon suivante :

# Authentification auprès de l'instance en lecture
'<readToken> 'readToken' STORE
# FETCH permet de récupérer une liste de GTS, ici on demande toutes les classes via ~.* et tous les labels en prenanr les 1000 dernières valeurs ; on récupère donc toutes les séries.
[ $readToken '~.*' {} NOW -1000 ] FETCH

Si vous cliquez sur l'onglet "Dataviz", vous avez alors immédiatement une représentation graphique de vos points.

warp10 - fetch

Maintenant que nos données sont bien présentes, on va vouloir aller un peu plus loin dans nos manipulations.

Premières manipulations

Ce que nous voulons faire :

  • Sélectionner chaque série et la stocker dans une variable,
  • Calculer le résultat mensuel et le persister dans une troisième série temporelle
  • Afficher les trois séries.

Pour sélectionner chaque série et la stocker dans une variable:

# Authentification auprès de l'instance en lecture
'<readToken>' 'readToken' STORE

# FETCH : permet de récupérer une liste de série, ici on filtre sur la classe expense, sur le label company = cerenit et sur les dates du 01/12/2016 au 01/06/2020.
# 0 GET : on sait que l'on a qu'une seule série qui correspond à la requête. Donc on ne retient que le 1er élément pour passer d'une liste de GTS à une seule et unique GTS.
# STORE : stocke le résultat dans une variable exp.
[ $readToken 'expense' { 'company' '=cerenit' } '2016-12-01T00:00:00Z' '2020-06-01T00:00:00Z' ] FETCH
0 GET
'exp' STORE

# Idem pour la classe revenue, stockée dans une variable revenue.
[ $readToken 'revenue' { 'company' '=cerenit' } '2016-12-01T00:00:00Z' '2020-06-01T00:00:00Z' ] FETCH
0 GET
'revenue' STORE

# Affiche les 2 séries
$exp
$revenue

A ce stade, vous avez la même représentation graphique que précédemment si vous cliquez sur Dataviz.

warp10 - fetch

Calculons maintenant le résultat mensuel (chiffre d'affaires - dépenses) :

# Authentification auprès de l'instance en lecture
'<readToken>' 'readToken' STORE

# Le même bloc que précédemment
[ $readToken 'expense' { 'company' '=cerenit' } '2016-12-01T00:00:00Z' '2020-06-01T00:00:00Z' ] FETCH
0 GET
'exp' STORE

[ $readToken 'revenue' { 'company' '=cerenit' } '2016-12-01T00:00:00Z' '2020-06-01T00:00:00Z' ] FETCH
0 GET
'revenue' STORE

# Calcul: il suffit se soustraire les deux éléments pour avoir le résultat
$revenue $exp -
# on affiche également les deux autres variables pour la dataviz
$exp
$revenue

warp10 - result

A ce stade :

  • Au niveau de la dataviz, la légende ne fournit aucune information sur la nature de la série
  • Cette donnée n'est pas encore persistée

Jusqu'à présent, nous avons utilisé que le <readToken> pour lire les données. Pour la persistence, nous allons utilier le <writeToken>.

# Authentification auprès de l'instance en lecture
'<readToken>' 'readToken' STORE
# Authentification auprès de l'instance en écriture
'<writeToken>' 'writeToken' STORE

[ $readToken 'expense' { 'company' '=cerenit' } '2016-12-01T00:00:00Z' '2020-06-01T00:00:00Z' ] FETCH
0 GET
'exp' STORE

[ $readToken 'revenue' { 'company' '=cerenit' } '2016-12-01T00:00:00Z' '2020-06-01T00:00:00Z' ] FETCH
0 GET
'revenue' STORE

# La première ligne est inchangée, elle calcule le résultat mensuel et la donnée est de type GTS
# Du coup, comme nous sommes dans une pile et que l'on hérite de ce qu'il s'est passé avant, on peut lui assigner un nom via RENAME
# Puis lui ajouter le label company avec pour valeur cerenit
# Et utiliser la fonction UPDATE pour stocker en base la GTS ainsi obtenue.
$revenue $exp -
"result" RENAME
{ "company" "cerenit" } RELABEL
$writeToken UPDATE

# Comme pour revenue et expense, on récupère les données sous la forme d'une GTS que l'on stocke dans une variable
[ $readToken 'result' { 'company' '=cerenit' } '2016-12-01T00:00:00Z' '2020-06-01T00:00:00Z' ] FETCH
0 GET
'result' STORE

# On vide la pile
CLEAR
# On affiche les variables créées
$revenue
$exp
$result

warp10 - result store

Et voilà !

Prévoyons le futur

Warp10 dispose d'une extension propriétaire et payante permettant d'appliquer des algorithmes de prévisions sur des séries temporelles : warp10-ext-forecasting. Il est possible d'utiliser cette extension sur la Sandbox Warp10 mise à disposition par SenX.

Il existe une fonction AUTO et SAUTO (version saisonnière) qui applique automatiquement des algorythmes d'AutoML sur vos données.

# Authentification auprès de l'instance en lecture
'<readToken>' 'readToken' STORE

# Récupération des trois séries
[ $readToken 'expense' { 'company' '=cerenit' } '2016-12-01T00:00:00Z' '2020-06-01T00:00:00Z' ] FETCH
0 GET
'exp' STORE

[ $readToken 'revenue' { 'company' '=cerenit' } '2016-12-01T00:00:00Z' '2020-06-01T00:00:00Z' ] FETCH
0 GET
'revenue' STORE

[ $readToken 'result' { 'company' '=cerenit' } '2016-12-01T00:00:00Z' '2020-06-01T00:00:00Z' ] FETCH
0 GET
'result' STORE

CLEAR
$revenue
$exp
$result
# MAP: la fonction `AUTO` s'attend à manipuler des nombres au format `DOUBLE` et non des entiers. Il faut donc faire la conversion.
# FORECAST: sur les données obtenues du MAP, on applique la fonction AUTO et on demande les 8 prochaines occurents (pour aller jusqu'à la fin d'année)
# Le .ADDVALUES permet de "fusionner" les prévisions avec la série parente (sans les persister en base à ce stade)
# Commes les 3 projections sont disponibles dans la pile, elles sont également affichées
[ $result mapper.todouble 0 0 0 ] MAP
AUTO 8 FORECAST.ADDVALUES
[ $revenue mapper.todouble 0 0 0 ] MAP
AUTO 8 FORECAST.ADDVALUES
[ $exp mapper.todouble 0 0 0 ] MAP
AUTO 8 FORECAST.ADDVALUES

warp10 - forecast auto

Du fait du FORECAST.ADDVALUES, on pourrait se passer d'afficher les trois premières séries. Mais vistuellement, cela permet de voir la différence entre la série originale et la projection.

Une fois l'effet Whaou passé, on peut se demander quel modèle a été appliqué. Pour cela il y a la fonction MODELINFO

# Authentification auprès de l'instance en lecture
'<readToken>' 'readToken' STORE

# Récupération des trois séries
[ $readToken 'expense' { 'company' '=cerenit' } '2016-12-01T00:00:00Z' '2020-06-01T00:00:00Z' ] FETCH
0 GET
'exp' STORE

[ $readToken 'revenue' { 'company' '=cerenit' } '2016-12-01T00:00:00Z' '2020-06-01T00:00:00Z' ] FETCH
0 GET
'revenue' STORE

[ $readToken 'result' { 'company' '=cerenit' } '2016-12-01T00:00:00Z' '2020-06-01T00:00:00Z' ] FETCH
0 GET
'result' STORE

CLEAR
[ $result mapper.todouble 0 0 0 ] MAP
AUTO MODELINFO
[ $revenue mapper.todouble 0 0 0 ] MAP
AUTO MODELINFO
[ $exp mapper.todouble 0 0 0 ] MAP
AUTO MODELINFO

Dans l'onglet des résultats, on voit l'information: "model": "ARIMA".

warp10 - forecast modelinfo

Si on veut alors faire la même chose en utilisant le modèle ARIMA :

# Authentification auprès de l'instance en lecture
'<readToken>' 'readToken' STORE

# Récupération des trois séries
[ $readToken 'expense' { 'company' '=cerenit' } '2016-12-01T00:00:00Z' '2020-06-01T00:00:00Z' ] FETCH
0 GET
'exp' STORE

[ $readToken 'revenue' { 'company' '=cerenit' } '2016-12-01T00:00:00Z' '2020-06-01T00:00:00Z' ] FETCH
0 GET
'revenue' STORE

[ $readToken 'result' { 'company' '=cerenit' } '2016-12-01T00:00:00Z' '2020-06-01T00:00:00Z' ] FETCH
0 GET
'result' STORE

CLEAR

# SEARCH.ARIMA: applique un modèle ARIMA (ARMA ou ARIMA) sur la GTS passée en paramètre
# FORECAST.ADDVALUES: fait une précision sur les 7 prochaines occurences et les fusionne avec la série sur laquelle la projection est faite.
# Les 3 projections restant dans la pile, elles sont affichées
[ $revenue mapper.todouble 0 0 0 ] MAP
SEARCH.ARIMA
7 FORECAST.ADDVALUES

[ $exp mapper.todouble 0 0 0 ] MAP
SEARCH.ARIMA
7 FORECAST.ADDVALUES

[ $result mapper.todouble 0 0 0 ] MAP
SEARCH.ARIMA
7 FORECAST.ADDVALUES

warp10 - forecast arima

Et nous obtenons bien le même résultat.

On peut se poser alors la question de voir si la projection sur le résultat est la même que la soustraction entre la projection de chiffres d'affaires et de dépenses et mesurer l'éventuel écart.

# Authentification auprès de l'instance en lecture
'<readToken>' 'readToken' STORE

[[ $readToken 'expense' { 'company' '=cerenit' } '2016-12-01T00:00:00Z' '2020-06-01T00:00:00Z' ] FETCH
0 GET
'exp' STORE

[ $readToken 'revenue' { 'company' '=cerenit' } '2016-12-01T00:00:00Z' '2020-06-01T00:00:00Z' ] FETCH
0 GET
'revenue' STORE

[ $readToken 'result' { 'company' '=cerenit' } '2016-12-01T00:00:00Z' '2020-06-01T00:00:00Z' ] FETCH
0 GET
'result' STORE

CLEAR
# La différence sur les 3 projections est que l'on stocke chaque résultat dans une variable
[ $result mapper.todouble 0 0 0 ] MAP
AUTO 8 FORECAST.ADDVALUES
'fresult' STORE

[ $revenue mapper.todouble 0 0 0 ] MAP
AUTO 8 FORECAST.ADDVALUES
'frevenue' STORE

[ $exp mapper.todouble 0 0 0 ] MAP
AUTO 8 FORECAST.ADDVALUES
'fexp' STORE

$frevenue
$fexp
$fresult
# ici on calcule la projection du résultat sur la base des projections de chiffres d'affaires et de dépenses
$frevenue $fexp -

On constate bien un écart entre la courbe orange (la soustraction des projections) et la courbe bleu (la projection du résultat).

warp10 - forecast arima

Nous voilà à la fin de ce billet, j'espère que ce tour du propriétaire vous aura permis d'apprécier Warp10 et ses capacités.

Il ne reste plus qu'à voir en fin d'année dans quelles mesures ces projections seront valides ou pas !

Mon bilan sur Warp10 à ce stade :

  • Il faut absolument suivre le tutoriel sur les cyclones, cela permet de se mettre progressivement à Warpscript et de comprendre les mécanismes de fonctionnement du langage et de la pile (stack).
  • la notation de Warpscript est particulière au début mais on s'y fait petit à petit et sinon FLoWS devrait lever les dernières réticences.
  • Comme on a une pile, il ne faut pas hésiter à utiliser STOP et TYPOEOF notamment pour savoir ce que l'on manipule comme donnée à un instant T ; cf Debugging WarpScript
  • Le WarpStudio ou l'extension VSCode permettent d'avoir un contexte de développement agréable avec l'autocomplétion, la documentation des fonctions, etc.
  • Contrairement à InfluxDB où tout s'articule autour du measurement (équivalent de la classe ici), le fait d'avoir un langage de manipulation (et pas principalement de requêtage avec quelques transformations) de données permet d'avoir une modélisation plus souple et de réconcilier les données ensuite. Le même exercice dans InfluxDB supposerait d'avoir un seul measurement et d'avoir les différentes valeurs en son sein. Du coup, cela empêcherait le calcul et le stockage du résultat par ex. Il aurait fallu le calculer au préalable et l'insérer dans la base en même temps que les données de chiffre d'affaires et de dépenses pour que les données soient ensemble. Certes Flux et InfluxDB 2.0 vont lever quelques contraintes de modélisation et de manipulation de données, mais le prisme de la modélisation autour du measurement reste primordial dans le produit.
  • Avoir un langage de manipulation de données et non uniquement de requêtage et quelques transformation évite aussi de devoir sortir les données pour les analyser puis les renvoyer vers la base de données le cas échéant. Non seulement on reste au plus près de la donnée (éxécution coté serveur) mais on évite aussi les problématiques de drivers, conversion dans les structures de données du langage cible, etc.
  • Les classes de warpscript sont certes mono valeurs (même si le multivalue existe) mais je présume que cela sert surtout pour des données de même nature plutôt que pour des données hétérogènes. En effet, il s'agit d'une liste et non d'un tableau de données. (NDLR: Mathias me précise qu'une multivalue est une GTS et non une simple liste - cela est précisé si on lit bien la totalité de la doc de multivalue)

Une expérience au final positive qui pousse à aller creuser plus loin les fonctionnalités de cette plateforme. Ce sera l'opportunité de rédiger d'autres billets à l'avenir.

Web, Ops & Data - Juillet 2020

terraformacmeletsencryptinfluxdbinfluxdaysquestdbtimeseriesranchersusestashkubedbmaeshwarp10warpscriptflowsptsmrgpdsafe-harbordonnées personnellesgrafanaflux

Cloud

Container et orchestration

  • Announcing Maesh 1.3 : Maesh continue son chemin et ajoute la capacité de surveiller des namespace particuliées (en plus de pouvoir en ignorer), le support du lookup des ports (http -> 80), le support de CoreDNS chez AKS et d'autres améliorations encore.
  • Electro Mpnkeys #9 – Traefik et Maesh : de l’ingress au service mesh avec Michael Matur : si vous voulez en savoir plus sur Traefik et Maesh, je vous conseille cet épisode (et les autres) du podcast Electro Monkeys.
  • Introducing Traefik Pilot: a First Look at Our New SaaS Control Platform for Traefik : Containous, la société derrière Traefik, Maesh et Yaegi sort son offre SaaS pour piloter et monitorer ses instances traefik. Un système de plugins pour les middleware fait également son apparaition. Il faut une version 2.3+ (actuellement en RC) de Traefik pour bénéficier de cette intégration.
  • Relicensing Stash & KubeDB : KubeDB, l'operateur de bases de données et Stash, l'outil de sauvegarde se cherchent un modèle économique et changent de licence. La version gratuite, avec code source disponible, reste disponible pour des usages non commerciaux (voir les détails de la licence pour une slite exacte). Pour un usage commercial, il faudra passer par la version Entreprise qui apporte aussi des fonctionnalités supplémentaires.
  • Suse to acquire Rancher : Suse était sorti de mon radar; c'est donc pour moi l'entrée (ou le retour ?) de Suse dans le monde de kubernetes et de son orchestration. Est-ce une volonté d'aller prendre des parts de marchés à Redhat/Openshift ou de faire face à des rumeurs telles que Google en discussion pour acquérir D2IQ (ex Mesoshphère) ? A voir si cette acquisition va être un tremplin pour Rancher et ses différents projets (rke, rio, k3s, longhorn, etc) comme l'indique son CTO ou pas.

Time Series

Vie privée & données personnelles

Le Privacy Shield, l'accord entre l'Europe et les USA sur le transfert des données des Européens vers les USA (ou les sociétés américaines) vient d'être invalidé par la cour de justice européene. Les flux "absolument nécessaires" peuvent continuer à se faire pour le moment et la cour a validé "les clauses contractuelles types" définies par la Commission Européenne pourront être utilisées par les entreprises. Néanmoins, pour s'y référer, il semble qu'il faut vérifier que l'entreprise protège effectivement les données. Je vous invite à contacter votre juriste ou avocat pour mieux appréhender les impacts de cette invalidation si vous utilisez les plateformes cloud et des services dont les entreprises sont basées aux USA. En tant qu'individu, il peut être intéressant de se poser des questions également. N'étant pas juriste, je vais donc limiter mon interprétation ici et vous laisse lire les liens ci-dessous.

Web, Ops & Data - Mars 2020

ansiblemoleculetestjavaredistimeseriesinfluxdbwarp10

IaC

  • Ansible Molecule 3.0 : l'outil de test des rôles Ansible est passé en version 3.0. Pas mal de changement avec l'externalisation des providers d'infrastructure sous la forme de module python et d'autres rationalisation. Une check list de migration est disponible

Langages

  • What Tens of Millions of VMs Reveal about the State of Java : NewRelic publie une synthèse des versions et configuration de Java déployées dans la nature. ~85% tourne encore sur Java 8 et 11% sur Java 11 et le tout principalement avec les versions fournies par Oracle. On se moquait des communautés PHP (pour PHP 5 à PHP 7) ou Python (2 vers 3) mais visiblement chaque langage d'un certain age rencontre les mêmes soucis.

Time Series

Bilan 2019 et perspectives 2020

bilanperspectivecérénittimeseriesbigdatahebdoinfluxace

Rien de tel que la finalisation du bilan de cette troisième année d'activité pour faire un petit bilan sur l'année écoulée et les perspectives pour 2020. Vous pouvez retrouver le bilan des années précédentes 2018 et 2017.

Bilan 2019

Au global, tout va toujours bien, tant d'un point de vue comptable que d'activité. Une année plutôt bien remplie, voir trop remplie en nombre de jours facturés. Cela change des années précédentes où cela avait pu être un problème.

D'un point de vue comptable, cela donne :

201920182017Variation n/n-1
Chiffre d'affaires~150 K€~132 K€~100 K€+14%
Résultat après impôts~13.5 K€~10 K€~20 K€+31%
Jours facturés~210~190~160+10%
TJM~714€~685€625€+4%

J'avais espéré retrouvé mon delta de chiffre d'affaires de façon plus sensible dans mon résultat. Ce n'est pas le cas principalement pour les raisons suivantes :

  • Le choix de meilleures mutuelle et système de prévoyance, ainsi que la mise en place d'un financement de retraites complémentaires au niveau de l'entreprise,
  • Des investissements matériels du fait d'un travail en remote à mon domicile depuis le mois de juillet et pour le Meetup Paris Time Series,
  • Des dépenses pour la réalisation d'une nouvelle identité graphique pour CérénIT, BigData Hebo et pour le Meetup Paris Time Series.

Comme chaque année, j'en profite pour remercier Fabrice et son équipe pour son accompagnement. Je l'ai déjà dit, mais avoir confiance dans son expert comptable et pouvoir compter sur lui pour apporter de bons conseils aux bons moments et être serein sur la gestion de l'entreprise, c'est indispensable.

D'un point de vue activité, c'est aussi une bonne année :

  • Contraitement aux années précédentes, avoir des missions plus régulières m'a permis de ne pas avoir le déséquilibre entre le premier et le second semestre,
  • Fin d'une mission de 18 mois au final chez LesFurets.com et depuis 6 mois, une mission en cours chez Saagie ; les détails de ces missions sont disponibles sur la page Références.
  • L'investissement sur Kubernetes depuis courant 2018 m'a permis de décrocher plusieurs missions et ainsi d'avoir des références "officielles" en la matière.
  • J'avais fait le choix d'aller à moins de conférences mais je me suis rendu à KubeCon Europe à Barcelone, aux InfluxDays à Londres et au Sysadmin_Days à Paris,
  • J'ai eu le plaisir de rejoindre l'équipe du podcast BigData Hebo et d'être nominé InfluxAces. Nomination qui m'a permis de lancer et d'animer le Meetup Paris Time Series depuis le mois de septembre,
  • J'ai pu rejoindre le réseau d'indépendants CollectiveMakers qui permet de partager de l'information et d'avoir un réseau d'entraide sur des problématiques Ops/Cloud/DevOps/Sécurité.
  • Enfin, j'ai même pu me verser mes premiers dividendes, preuve de la bonne santé de l'entreprise.

Perspectives 2020

Avec la nomination d'InfluxAces, le travail sur le Meetup Paris Time Series et une première mission sur les séries temporelles, l'objectif 2020 est de développer cette activité. Je compte donc passer une partie de l'année à améliorer ma maitrise de la plateforme InfluxDB 2.0 mais aussi de la plateforme Warp10. Pour se faire, je suis passé à 4/5ème sur ma mission actuelle pour avoir un temps dédié à ce sujet et quelques autres projets à finir.

Outre cette activité, j'ai aussi prévu d'améliorer/renforcer mes compétences en matière de développement. Je m'étais rapidement formé à Go en vue d'une mission qui s'est finalement faite en Kotlin. Je compte donc creuser un peu plus le monde de la JVM (Java, Gradle, Kotlin) pour mieux comprendre son fonctionnement mais aussi voir du coté de Rust qui m'a plus séduit que Go. Avoir raté une belle opportunité de projet du fait de ne pas être assez développeur me pousse à vouloir investir sur ce sujet en plus du plaisir que j'ai à développer.

Sur les conférences, j'ai prévu d'aller à Devoxx France et à la prochaine édition des InfluxDays. A voir pour le reste de l'année.

Après un premier chantier de refonte du site suite à la nouvelle identité, je dois encore le faire évoluer pour mieux présenter les offres de service ainsi que d'autres améliorations.

Enfin, comme tous les ans, j'ai prévu de travailler à la pérénité et soutenabilité de CérénIT, d'apprendre plein de nouvelles choses pour rester pertinent et aller de l'avant. A celà s'ajoute le plaisir de contribuer/participer à la communauté BigData Hebo.

Si certains sujets vous interpellent ou si vous avez des contacts à me suggérer, n'hésitez pas à me contacter.

Paris Time Series Meetup - Edition 4 et 3

timeseriesinfluxdbmeetupptsmtelegraffluxtslredistimeseriesredis

L'édition 4 du Paris Time Series Meetup s'est tenue hier soir. J'ai eu le plaisir d'accueillir David McKay, Developer Advocate InfluxData, qui est venu nous présenter la plateforme InfluxDB 2.0, le nouveau langage Flux et l'outil de collecte Telegraf (et les bonnes pratiques associées).

Vous pouvez d'ores et déjà retrouver les vidéos en ligne ; les présentations sont en anglais :

Et pour les ressources complémentaires mentionnées par David McKay :

Concernant l'édition 3 sur TSL et RedisTimeSeries, initiallement prévue en décembre 2019 et replanifiée le 21 janvier, elle aura finalement lieu le mercredi 25 Mars chez OVHCloud. Pour alimenter votre attente et comme indiqué dans le dernier billet de veille mensuelle, OVHCloud a publié erlenmeyer et vient de publier un billet de blog sur le sujet : TSL (or how to query time series databases).

Nous espérons vous y voir nombreux et en attendant, bon visionnage et bonne lecture !

Web, Ops & Data - Janvier 2020

timeseriescloudovhs3object storagedeltagitdifffaascontainerdraspberrypidockerinfluxdbvscodefluxwarp10observabilitédockercnabpostgresqlgrafana

Meilleurs voeux à tous pour cette nouvelle année !

Cloud

  • OVHcloud Object Storage clusters support S3 API : pour ceux qui ne voulaient pas aller chez OVH car leur système de stockage objet est basé sur Openstack/Swift et ne voulaient pas modifier leurs appels d'API S3, une bonne nouvelle : le stockage objet d'OVH Cloud supporte l'API S3.

Container & Orchestration

  • Managing the TICK Stack with Docker App : cet article aurait pu être dans la section Time Series mais le focus étant sur Docker et Docker App, il sera dans la section Container. L'article montre comment déployer la stack TICK (Telegraf, InfluxDB, Chronograf et Kapacitor) tout d'abord via un fichier docker-compose.yml et ensuite il montre les apports de Docker App, qui permet d'avoir un niveau de personnalisation supplémentaire. Ainsi, on peut avoir un seul fichier docker-compose.yml de référence et auquel on rajoute un fichier avec des propriétés par environnement ou par client ou par instance par ex. Une combinaison intéressante pour améliorer l'industrialisation de vos containers.
  • Kubernetes 1.17 disponible sur l'offre kubernetes managé d'OVHCloud

DevOps/SRE

  • The 3 Myths of Observability : l'observabilité ne va pas directement baisser votre nombre d'incidents, l'observabilité n'est pas qu'une suite d'outils et elle n'est pas gratuite.

Outillage

  • delta : pour améliorer le rendu de vos diff et certaines commandes git (diff, show, log, stash, reflog). L'outil est réalisé en rust. Cela donne un rendu à la github/gitlab dans votre console. Sympa !

Raspberry Pi

  • faasd - lightweight Serverless for your Raspberry Pi : si vous jugez k3s encore trop gros pour vos raspberry pi pour faire tourner OpenFaaS ou que vous ne voulez pas déployer du kubernetes, vous pourriez trouver la solution du coté de faasd. Une implémentation du projet basée sur containerd (le runtime utilisée par Docker)
  • HypriotOS v1.12.0 : la distribution optimisée pour Raspberry Pi et fournissant Docker arrive en version 1.12. Elle permet d'utiliser Docker sur tous les modèles de Raspberry (0, 1, 2, 3, 4) avec les dernières versions de docker, docker-compose et docker-machine.

SQL

  • Améliorez votre SQL : utilisez des index filtrés : Postgresql permet de définir des index filtrés : plutôt que de créer un index sur toutes les données d'une table, vous pouvez définir un index qui répond à un filtre et ne faire un index que sur ce sous-ensemble de données.

Time Series

  • Grafana v6.6 Released : nouvelle version de Grafana avec comme d'habitude plein d'améliorations à tous les étages (data source, panels, alerting, explore, etc)
  • Release Announcement: Flux VSCode Support : InfluxData a publié une extension VSCode pour le langage flux.
  • InfluxDB 2.0 Open Source Beta Released : InfluxData passe la version OSS d'iInfluxDB 2.0 en béta après une année de versions alpha. On y trouve notamment une approche Configuration As Code avec la possibilité de définir des Tasks, Dashboards, ainsi que de la configuration via des Manifest en YAML et un système de packages. Flux, le nouveau langage de requêtage continue à s'améliorer et enfin le transpiler InfluxQL vers Flux fait son entrée mais demande à s'améliorer au fil du temps. La beta 2 est sortie aussi.
  • telegaf warp10 output : la prochaine version de Telegraf supportera nativement Warp10.
  • Erlenmeyer: Time Series query translator : OVHCloud vient d'opensourcer le code de leur proxy en go qui leur permet de parser des requêtes de différentes bases de données time series (OpenTSDB, PromQL, Prometheus Remote Read, InfluxQL et Graphite) en Warpscript pour requêter les données stockées dans Warp10. Pour rappel, la solution OVHMetrics est basée sur Warp10.
  • Le traitement et l'utilisation de la data dans l'industry 4.0 : SenX, la société éditrice de Warp10, a réalisé une vidéo intéressante sur le traitement et l'utilisation de la data dans l'industrie 4.0. On y voit notamment les 4 niveaux de maturité quant à la donnée et le rôle d'une base de données temporelles dans ce contexte. Un billet de blog (en anglais) est également disponible.

Web, Ops & Data - Décembre 2019

influxdbdockerkubernetestraefikgrafanadashboardcassandrareaperwarp10timeseriestimescaledbhelmmachine-learning

Rendez-vous le 21 janvier prochain à la troisième édition du Paris Time Series Meetup consacré à TSL (billet introductif à TSL : TSL: a developer-friendly Time Series query language for all our metrics) et le module RedisTimeSeries qui apporte des fonctionnalités et des structures Time Seriies à Redis. Le meetup était prévu initialement le mardi 17 décembre mais a été reporté du fait des grèves.

Container et orchestration

  • DockerSlim : le projet vise à réduire la taille de vos images et à améliorer leur sécurité en procédant à différentes optimisations. Cela peut être intéressant dans une stratégie d'améliorations de vos images docker mais à tester néanmoins. Les exemples données partent d'Ubuntu 14.04 dont l'image fait 60 / 65 Mo alors que l'image Ubuntu 16.04 fait moitié moins et Alpine fait 30 fois moins. Donc certains gains semblent faciles à obtenir, à creuser plus en détail.
  • Kubernetes 1.17: Stability : après une version 1.16 marquée notamment par la dépréciation de certaines APIs, cette version se veut plus une consolidation autour des "Cloud Provider Labels" qui passent en GA, le snapshot de volumes qui passe en beta, ainsi que la couche de stockage CSI avec la poursuite de la migration des plugins "in-tree" vs "out-of-tree". La fin de cette migration est prévue pour les versions 1.19 / 1.20 et le retrait complet des plugins "in-tree" pour les versions 1.21 / 1.22.
  • A visual guide on troubleshooting Kubernetes deployments : un guide du troublehooting des déploiements sous kubernetes avec un joli diagramme des cas possibles et les explications associées en repartant d'un exemple simple.
  • How to migrate from Helm v2 to Helm v3 : les opérations à mener pour migrer de Helm V2 à Helm V3.
  • Traefik 2.1 : le provider Consul Catalog fait son retour (il était absent en 2.0.x) et diverses améliorations sur la CRD Kubernetes ont été apportées pour mieux gérer le mirroring du traffic, les déploiements canary et la gestion des sessions. La migration ne consistant pas seulement à changer le numéro de version et suite à une remarque de ma part, une note a été ajoutée pour la migration 2.0.x vers 2.1.x

Dataviz

NoSQL

  • Cassandra Reaper 2.0 was released : la solution de réparation de vos clusters Cassandra passe en 2.0 ; elle apporte un déploiement en mode sidecar (reaper est lancé dans la même jvm que Cassandra), le support d'Apache Cassandra 4.0 (pas encore officiellement disponible), de nouveaux thèmes, une amélioration du support de Postgresql comme backend de déploiement et pleins d'autres choses.

Time Series

Je n'ai plus qu'à vous souhaiter des bonnes fêtes de fin d'année ; nous nous retrouvons l'année prochaine !

Exporter les métriques Traefik dans InfluxDB dans un contexte Kubernetes

kubernetestraefikinfluxdbmétriquetimeseries

Traefik, depuis sa version V1, permet d'envoyer des métriques vers différents backends (StatsD, Prometheus, InfluxDB et Datadog). J'ai enfin pris le temps d'activer cette fonctionnalité et de creuser un peu le sujet étant donné que le dashboard de Traefik V2 n'affiche plus certaines de ses statistiques.

La documentation de Traefik sur le sujet :

Commençons par créer une base traefik dans InfluxDB (version 1.7.8)

influx
Connected to http://localhost:8086 version 1.7.8
InfluxDB shell version: 1.7.9
> auth
username: XXX
password: XXX
> CREATE DATABASE traefik
> CREATE USER traefik WITH PASSWORD '<password>'
> GRANT ALL ON traefik to traefik
> SHOW GRANTS FOR traefik
database privilege
-------- ---------
traefik  ALL PRIVILEGES
> quit

Dans mon cas, l'accès à InfluxDB se fait en https au travers d'une (autre) instance Traefik. J'utilise donc la connexion en http plutôt qu'en udp.

Cela donne les instructions suivantes en mode CLI :

    --metrics=true
    --metrics.influxdb=true
    --metrics.influxdb.address=https://influxdb.domain.tld:443
    --metrics.influxdb.protocol=http
    --metrics.influxdb.database=traefik
    --metrics.influxdb.username=traefik
    --metrics.influxdb.password=<password>

J'ai gardé les valeurs par défaut pour addEntryPointsLabels (true), addServicesLabels (true) et pushInterval (10s).

Cela donne le Deployment suivant :

apiVersion: apps/v1
kind: Deployment
metadata:
  name: traefik2-ingress-controller
  labels:
    k8s-app: traefik2-ingress-lb
spec:
  replicas: 2
  selector:
    matchLabels:
      k8s-app: traefik2-ingress-lb
  template:
    metadata:
      labels:
        k8s-app: traefik2-ingress-lb
        name: traefik2-ingress-lb
    spec:
      serviceAccountName: traefik2-ingress-controller
      terminationGracePeriodSeconds: 60
      containers:
      - image: traefik:2.0.6
        name: traefik2-ingress-lb
        ports:
          - name: web
            containerPort: 80
          - name: admin
            containerPort: 8080
          - name: secure
            containerPort: 443
        readinessProbe:
          httpGet:
            path: /ping
            port: admin
          failureThreshold: 1
          initialDelaySeconds: 10
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 2
        livenessProbe:
          httpGet:
            path: /ping
            port: admin
          failureThreshold: 3
          initialDelaySeconds: 10
          periodSeconds: 10
          successThreshold: 1
          timeoutSeconds: 2
        args:
          - --entryPoints.web.address=:80
          - --entryPoints.secure.address=:443
          - --entryPoints.traefik.address=:8080
          - --api.dashboard=true
          - --api.insecure=true
          - --ping=true
          - --providers.kubernetescrd
          - --providers.kubernetesingress
          - --log.level=ERROR
          - --metrics=true
          - --metrics.influxdb=true
          - --metrics.influxdb.address=https://influxdb.domain.tld:443
          - --metrics.influxdb.protocol=http
          - --metrics.influxdb.database=traefik
          - --metrics.influxdb.username=traefik
          - --metrics.influxdb.password=<password>

Appliquer le contenu du fichier dans votre cluster Kubernetes

kubectl apply -f deployment.yml -n <namespace>

Sur le dashboard Traefik, dans la section "Features", la boite "Metrics" doit afficher "InfluxDB", comme ci-dessous :

Traefik Dashboard avec les métriques InfluxDB activés

Vous pouvez alors vous connecter à votre instance InfluxDB pour valider que des données sont bien insérées :

influx
Connected to http://localhost:8086 version 1.7.8
InfluxDB shell version: 1.7.9
> auth
username: traefik
password:
> use traefik
Using database traefik
> show measurements
name: measurements
name
----
traefik.config.reload.lastSuccessTimestamp
traefik.config.reload.total
traefik.entrypoint.connections.open
traefik.entrypoint.request.duration
traefik.entrypoint.requests.total
traefik.service.connections.open
traefik.service.request.duration
traefik.service.requests.total

Il ne vous reste plus qu'à utiliser Chronograf ou Grafana pour visualiser vos données et définir des alertes.

Un exemple rapide avec la répartition des codes HTTP dans Grafana :

Graphiques des données de Traefik depuis InfluxDB dans Grafana

← Précédent 3 / 4 Suivant →