Charger les métriques depuis le serveur Prometheus

Voir sur TensorFlow.org Exécuter dans Google Colab Voir la source sur GitHub Télécharger le cahier

Aperçu

Ce tutoriel charges CoreDNS métriques à partir d' un Prometheus serveur dans un tf.data.Dataset , puis utilise tf.keras pour la formation et l' inférence.

CoreDNS est un serveur DNS en mettant l'accent sur la découverte de service, et est largement déployée comme une partie du Kubernetes cluster. Pour cette raison, il est souvent suivi de près par les opérations de devops.

Ce didacticiel est un exemple qui pourrait être utilisé par les développeurs à la recherche d'une automatisation de leurs opérations grâce à l'apprentissage automatique.

Configuration et utilisation

Installez le package tensorflow-io requis et redémarrez le runtime

import os
try:
  %tensorflow_version 2.x
except Exception:
  pass
TensorFlow 2.x selected.
pip install tensorflow-io
from datetime import datetime

import tensorflow as tf
import tensorflow_io as tfio

Installer et configurer CoreDNS et Prometheus

Pour démonstration fins, un serveur CoreDNS localement avec le port 9053 ouvert pour recevoir les requêtes DNS et le port 9153 (defult) ouvert pour exposer des mesures pour gratter. Ce qui suit est une configuration corefile de base pour CoreDNS et est disponible pour téléchargement :

.:9053 {
  prometheus
  whoami
}

Plus de détails sur l' installation n'a pu être trouvée sur CoreDNS de la documentation .

curl -s -OL https://github.com/coredns/coredns/releases/download/v1.6.7/coredns_1.6.7_linux_amd64.tgz
tar -xzf coredns_1.6.7_linux_amd64.tgz

curl -s -OL https://raw.githubusercontent.com/tensorflow/io/master/docs/tutorials/prometheus/Corefile

cat Corefile
.:9053 {
  prometheus
  whoami
}
# Run `./coredns` as a background process.
# IPython doesn't recognize `&` in inline bash cells.
get_ipython().system_raw('./coredns &')

L'étape suivante consiste à configurer le serveur Prometheus et utiliser Prometheus pour racler CoreDNS métriques qui sont exposées sur le port 9153 d' en haut. Le prometheus.yml fichier de configuration est également disponible pour téléchargement :

curl -s -OL https://github.com/prometheus/prometheus/releases/download/v2.15.2/prometheus-2.15.2.linux-amd64.tar.gz
tar -xzf prometheus-2.15.2.linux-amd64.tar.gz --strip-components=1

curl -s -OL https://raw.githubusercontent.com/tensorflow/io/master/docs/tutorials/prometheus/prometheus.yml

cat prometheus.yml
global:
  scrape_interval:     1s
  evaluation_interval: 1s
alerting:
  alertmanagers:

  - static_configs:
    - targets:
rule_files:
scrape_configs:
- job_name: 'prometheus'
  static_configs:
  - targets: ['localhost:9090']
- job_name: "coredns"
  static_configs:
  - targets: ['localhost:9153']
# Run `./prometheus` as a background process.
# IPython doesn't recognize `&` in inline bash cells.
get_ipython().system_raw('./prometheus &')

Afin de montrer une certaine activité, dig commande pourrait être utilisé pour générer quelques requêtes DNS contre le serveur CoreDNS qui a été configuré:

sudo apt-get install -y -qq dnsutils
dig @127.0.0.1 -p 9053 demo1.example.org
; <<>> DiG 9.11.3-1ubuntu1.11-Ubuntu <<>> @127.0.0.1 -p 9053 demo1.example.org
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 53868
;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 3
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: 855234f1adcb7a28 (echoed)
;; QUESTION SECTION:
;demo1.example.org.     IN  A

;; ADDITIONAL SECTION:
demo1.example.org.  0   IN  A   127.0.0.1
_udp.demo1.example.org. 0   IN  SRV 0 0 45361 .

;; Query time: 0 msec
;; SERVER: 127.0.0.1#9053(127.0.0.1)
;; WHEN: Tue Mar 03 22:35:20 UTC 2020
;; MSG SIZE  rcvd: 132
dig @127.0.0.1 -p 9053 demo2.example.org
; <<>> DiG 9.11.3-1ubuntu1.11-Ubuntu <<>> @127.0.0.1 -p 9053 demo2.example.org
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 53163
;; flags: qr aa rd; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 3
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: f18b2ba23e13446d (echoed)
;; QUESTION SECTION:
;demo2.example.org.     IN  A

;; ADDITIONAL SECTION:
demo2.example.org.  0   IN  A   127.0.0.1
_udp.demo2.example.org. 0   IN  SRV 0 0 42194 .

;; Query time: 0 msec
;; SERVER: 127.0.0.1#9053(127.0.0.1)
;; WHEN: Tue Mar 03 22:35:21 UTC 2020
;; MSG SIZE  rcvd: 132

Désormais, un serveur CoreDNS dont les métriques sont récupérées par un serveur Prometheus et prêtes à être consommées par TensorFlow.

Créer un ensemble de données pour les métriques CoreDNS et l'utiliser dans TensorFlow

Créer un dataset pour les mesures de CoreDNS qui est disponible à partir du serveur PostgreSQL, qui pourrait être fait avec tfio.experimental.IODataset.from_prometheus . Au minimum, deux arguments sont nécessaires. query est transmise au serveur Prometheus pour sélectionner les paramètres et la length est la période que vous voulez charger dans Dataset.

Vous pouvez commencer par "coredns_dns_request_count_total" et "5" (secs) pour créer le Dataset ci - dessous. Depuis plus tôt dans le tutoriel deux requêtes DNS ont été envoyés, il est prévu que les mesures pour "coredns_dns_request_count_total" seront "2.0" à la fin de la série chronologique:

dataset = tfio.experimental.IODataset.from_prometheus(
      "coredns_dns_request_count_total", 5, endpoint="http://localhost:9090")


print("Dataset Spec:\n{}\n".format(dataset.element_spec))

print("CoreDNS Time Series:")
for (time, value) in dataset:
  # time is milli second, convert to data time:
  time = datetime.fromtimestamp(time // 1000)
  print("{}: {}".format(time, value['coredns']['localhost:9153']['coredns_dns_request_count_total']))
Dataset Spec:
(TensorSpec(shape=(), dtype=tf.int64, name=None), {'coredns': {'localhost:9153': {'coredns_dns_request_count_total': TensorSpec(shape=(), dtype=tf.float64, name=None)} } })

CoreDNS Time Series:
2020-03-03 22:35:17: 2.0
2020-03-03 22:35:18: 2.0
2020-03-03 22:35:19: 2.0
2020-03-03 22:35:20: 2.0
2020-03-03 22:35:21: 2.0

Examen plus approfondi des spécifications de l'ensemble de données :

(
  TensorSpec(shape=(), dtype=tf.int64, name=None),
  {
    'coredns': {
      'localhost:9153': {
        'coredns_dns_request_count_total': TensorSpec(shape=(), dtype=tf.float64, name=None)
      }
    }
  }
)

Il est évident que l'ensemble de données se compose d'un (time, values) tuple où les values champ est un dict python élargi en:

"job_name": {
  "instance_name": {
    "metric_name": value,
  },
}

Dans l'exemple ci - dessus, 'coredns' est le nom du travail, 'localhost:9153' est le nom de l' instance, et 'coredns_dns_request_count_total' est le nom de la mesure. Notez qu'en fonction de la requête Prometheus utilisée, il est possible que plusieurs travaux/instances/métriques soient renvoyés. C'est aussi la raison pour laquelle python dict a été utilisé dans la structure de l'ensemble de données.

Prenez une autre requête "go_memstats_gc_sys_bytes" à titre d'exemple. Puisque les deux CoreDNS et Prométhée sont écrits en golang, "go_memstats_gc_sys_bytes" métrique est disponible pour les "coredns" emploi et "prometheus" emploi:

dataset = tfio.experimental.IODataset.from_prometheus(
    "go_memstats_gc_sys_bytes", 5, endpoint="http://localhost:9090")

print("Time Series CoreDNS/Prometheus Comparision:")
for (time, value) in dataset:
  # time is milli second, convert to data time:
  time = datetime.fromtimestamp(time // 1000)
  print("{}: {}/{}".format(
      time,
      value['coredns']['localhost:9153']['go_memstats_gc_sys_bytes'],
      value['prometheus']['localhost:9090']['go_memstats_gc_sys_bytes']))
Time Series CoreDNS/Prometheus Comparision:
2020-03-03 22:35:17: 2385920.0/2775040.0
2020-03-03 22:35:18: 2385920.0/2775040.0
2020-03-03 22:35:19: 2385920.0/2775040.0
2020-03-03 22:35:20: 2385920.0/2775040.0
2020-03-03 22:35:21: 2385920.0/2775040.0

La création Dataset est prêt à passer à tf.keras directement soit à des fins de formation ou inférence maintenant.

Utiliser l'ensemble de données pour l'entraînement du modèle

Avec des mesures Dataset créé, il est possible de passer directement à l'Dataset tf.keras pour la formation de modèle ou de l' inférence.

À des fins de démonstration, ce tutoriel utilisera simplement un modèle LSTM très simple avec 1 fonctionnalité et 2 étapes en entrée :

n_steps, n_features = 2, 1
simple_lstm_model = tf.keras.models.Sequential([
    tf.keras.layers.LSTM(8, input_shape=(n_steps, n_features)),
    tf.keras.layers.Dense(1)
])

simple_lstm_model.compile(optimizer='adam', loss='mae')

L'ensemble de données à utiliser est la valeur de 'go_memstats_sys_bytes' pour CoreDNS avec 10 échantillons. Cependant, comme une fenêtre glissante de window=n_steps et shift=1 sont formés, des échantillons supplémentaires sont nécessaires (pour les deux éléments de consecute, la première est prise en tant que x et la seconde est considérée comme y pour la formation). Le total est 10 + n_steps - 1 + 1 = 12 secondes.

La valeur de données est également mis à l' échelle à [0, 1] .

n_samples = 10

dataset = tfio.experimental.IODataset.from_prometheus(
    "go_memstats_sys_bytes", n_samples + n_steps - 1 + 1, endpoint="http://localhost:9090")

# take go_memstats_gc_sys_bytes from coredns job 
dataset = dataset.map(lambda _, v: v['coredns']['localhost:9153']['go_memstats_sys_bytes'])

# find the max value and scale the value to [0, 1]
v_max = dataset.reduce(tf.constant(0.0, tf.float64), tf.math.maximum)
dataset = dataset.map(lambda v: (v / v_max))

# expand the dimension by 1 to fit n_features=1
dataset = dataset.map(lambda v: tf.expand_dims(v, -1))

# take a sliding window
dataset = dataset.window(n_steps, shift=1, drop_remainder=True)
dataset = dataset.flat_map(lambda d: d.batch(n_steps))


# the first value is x and the next value is y, only take 10 samples
x = dataset.take(n_samples)
y = dataset.skip(1).take(n_samples)

dataset = tf.data.Dataset.zip((x, y))

# pass the final dataset to model.fit for training
simple_lstm_model.fit(dataset.batch(1).repeat(10),  epochs=5, steps_per_epoch=10)
Train for 10 steps
Epoch 1/5
10/10 [==============================] - 2s 150ms/step - loss: 0.8484
Epoch 2/5
10/10 [==============================] - 0s 10ms/step - loss: 0.7808
Epoch 3/5
10/10 [==============================] - 0s 10ms/step - loss: 0.7102
Epoch 4/5
10/10 [==============================] - 0s 11ms/step - loss: 0.6359
Epoch 5/5
10/10 [==============================] - 0s 11ms/step - loss: 0.5572
<tensorflow.python.keras.callbacks.History at 0x7f1758f3da90>

Le modèle entraîné ci-dessus n'est pas très utile en réalité, car le serveur CoreDNS qui a été configuré dans ce tutoriel n'a aucune charge de travail. Cependant, il s'agit d'un pipeline fonctionnel qui pourrait être utilisé pour charger des métriques à partir de vrais serveurs de production. Le modèle pourrait ensuite être amélioré pour résoudre le problème réel de l'automatisation devops.