Aujourd’hui, nous allons voir comment nous pouvons coupler le Service Discovery de Prometheus avec Netbox ! C’est parti !
Service Discovery Prometheus
Prometheus, système de métrologie dont la réputation n’est plus à faire, présente la particularité de venir chercher lui même les metrics auprès de l’équipement (ou target) qu’il surveille. Si ce mode de fonctionnement, dit “pull” (par opposition au “push”) possède bien des avantages en termes de scalabilité et de praticité il implique néanmoins que chacun des services à surveiller soit déclaré auprès du serveur de supervision.
Dans le cadre d’une infrastructure très dynamique (par exemple, si elle est pilotée par un système d’orchestration comme Kubernetes), il devient assez vite pénible de maintenir manuellement la liste des services à surveiller et l’idée d'automatiser tout cela émerge naturellement. Fort heureusement, Prometheus propose différents mécanismes de découverte automatique des services à travers de nombreux service discovery modules.
Le code contient déjà une dizaine de modules permettant d’interroger automatiquement les composants de différents systèmes tels que Kubernetes, OpenStack, Google Cloud ou encore AWS. Un mécanisme de Prometheus particulièrement intéressant pour sa flexibilité vient du module file_sd
. Celui-ci propose un moyen générique, en passant par des fichiers, d'implémenter un processus de service discovery. C’est même maintenant le moyen préféré des développeurs de Prometheus, plutôt que d’ajouter de nouveaux modules de service discovery spécifiques dans leur code, complexifiant sa maintenance. C’est donc en utilisant le module file_sd
que nous allons pouvoir implémenter notre propre système de service discovery Prometheus.
Netbox, source of truth pour Prometheus
Depuis quelques années maintenant, nous utilisons Netbox afin d'inventorier la totalité de notre infrastructure. Au fil du temps, Netbox a fini par s’imposer comme notre source of trust, c’est à dire l’endroit où l’infrastructure est décrite comme elle est supposée exister, et à laquelle les outils de déploiement automatiques et les humains doivent se référer pour réconcilier la réalité avec cette description. Il n’est cependant pas simple de maintenir une telle base de données car elle peut très vite diverger de ce qui est effectivement en place. Le maillon faible est dans ce cas (et comme souvent) l’humain, en particulier lorsqu’il s’agit d’effectuer des tâches un peu ingrates de saisie de données. Nous avons donc tout intérêt à coupler le plus possible nos systèmes de déploiement avec Netbox, notre source of trust.
La déclaration d’un équipement dans Netbox devrait idéalement pouvoir orchestrer tout ce qui gravite autour de l’existence de cet équipement :
- la création de l’équipement en lui-même
- la configuration du réseau
- la création des accès aux personnes devant administrer l’équipement
- la configuration de l’outil de sauvegarde
- la création des entrées DNS
- […]
- et… la configuration du serveur de supervision. Vous l’aviez deviné, c’est ce dont il est question aujourd’hui avec un serveur Prometheus.
Nous avons, chez Enix, déjà développé ou intégré plusieurs mécanismes de ce type. Il nous est par exemple possible de déployer et d’intégrer un nouvel hyperviseur dans l’un de nos clusters OpenStack très simplement, en renseignant quelques champs dans notre instance de Netbox.
Notre dernière intégration en date concerne la supervision, et plus particulièrement l'intégration entre Netbox et Prometheus. Étant donné le faible couplage entre notre morceau de code et l’infrastructure d’Enix, il nous a semblé utile de le publier afin d’en faire profiter les autres utilisateurs de ces deux outils.
Le projet, très originalement baptisé “netbox-prometheus-sd” est disponible sur Github sous license MIT. Vous êtes bien entendu invités à nous faire des retours et à y contribuer si le cœur vous en dit !
En attendant, voici les quelques étapes qui vous permettront de mettre en place ce projet dans votre infrastructure.
1. Configuration de Netbox
Ajout des “custom fields”
Un custom field doit être ajouté dans Netbox afin de porter les différents labels attachés à un équipement. Pour cela, rendez-vous dans la zone d’administration de Netbox et ajoutez un nouveau custom field avec les paramètres suivants :
Création du token d’API entre Prometheus et Netbox
Un token d’API est également nécessaire afin de permettre à netbox-prometheus-sd de se connecter à l’API REST de Netbox. La création du token se fait dans votre profil utilisateur, section “API Tokens”. Créez un nouveau token avec les paramètres suivants :
Configuration d’un équipement dans Netbox
Afin qu’un équipement soit découvert et remonté à Prometheus par netbox-prometheus-sd, il est nécessaire que celui-ci comporte une primary_ip sur l’une de ses interfaces. Il faut également saisir son custom field prom_labels. Ce champ doit être encodé au format JSON, sous la forme suivante :
{"foo": "bar"}
Ajoute sur la cible Prometheus un label “foo” avec la valeur “bar”.
Il est aussi possible de remonter plusieurs cibles par équipement. Cela peut être utile dans le cas où plusieurs exporters Prometheus sont lancés sur le même équipement ou lors de l’utilisation de l'exporter_exporter (voir plus bas). Pour cela, utilisez une liste d’objets JSON :
[{"foo": "bar"}, {"bar": "foo"}]
Certains labels renseignés ici sont spécialement interprétés :
__metrics_path__
modifie le chemin d’URL de la cible__scheme__
modifie le schéma d’URL de la cible (typiquement, http ou https)__meta_netbox_port
modifie le port de l’URL de la cible- les labels commençant par
__param_
spécifient les paramètres de l’URL de la cible - tous les labels commençant par
__
sont accessibles pendant la phase de relabeling de Prometheus mais ne sont pas enregistrés
Notons que définir le label __address__
à ce niveau ne permet pas de changer l’adresse de la cible. Dans le cas d’un exporter ne fonctionnant pas directement sur l’équipement pointé par Netbox (cas spécial d’un serveur interrogé en SNMP par exemple), il faudra utiliser une astuce (voir plus bas, la section “Cas spéciaux / Utilisation du SNMP exporter”)
Des labels sont également automatiquement ajoutés par netbox-prometheus-sd depuis les données récupérées dans Netbox :
__meta_netbox_name
: nom de l’équipement__meta_netbox_tenant
: slug du tenant auquel l’équipement appartient__meta_netbox_tenant_group
: slug du group auquel le tenant de l’équipement appartient__meta_netbox_cluster
: nom du cluster auquel l’équipement appartient__meta_netbox_asset_tag
: asset tag défini pour l’équipement__meta_netbox_role
: slug du rôle auquel l’équipement appartient__meta_netbox_type
: type de model de l’équipement__meta_netbox_rack
: nom du rack dans lequel l’équipement est localisé__meta_netbox_pop
: slug du POP dans lequel l’équipement est localisé__meta_netbox_serial
: numéro de série de l’équipement__meta_netbox_parent
: nom du parent de l’équipement
Ces labels sont préfixés par __meta_netbox
et sont automatiquement exclus des métriques enregistrées par Prometheus.
Il faudra donc utiliser des règles de relabeling pour les conserver (voir la partie “Configuration de Prometheus”).
2. Lancement de netbox-prometheus-sd
Ce projet étant un simple script Python, il suffira de le copier sur le serveur hébergeant l’instance de Prometheus puis de le lancer de manière régulière, par exemple par l’intermédiaire d’une Crontab.
La ligne de commande à lancer est typiquement la suivante :
$ netbox-prometheus-sd.py https://nebox.your-company.com/ 'API_TOKEN' '/path/to/generated/output.json'
Après avoir lancé cette commande, vous devriez pouvoir observer vos cibles dans le fichier JSON généré.
Notre infrastructure de supervision étant conteneurisée, nous fournissons également un Dockerfile dans le repository Git du projet. Il faudra prendre le soin de partager un volume entre le conteneur Prometheus et le conteneur netbox-prometheus-sd afin que celui-ci puisse accéder au fichier JSON généré par ce dernier.
3. Configuration de Prometheus
Enfin, il faudra configurer un job dans le fichier prometheus.yaml
afin que Prometheus puisse apprendre ses cibles depuis le fichier JSON généré par netbox-prometheus-sd par l’intermédiaire du file service discovery :
- job_name: 'netbox'
file_sd_configs:
- files:
- '/path/to/generated/output.json'
relabel_configs:
- regex: __meta_netbox_(.+)
replacement: netbox_$1
action: labelmap
La section relabel_configs
permet de renommer tous les labels définis par Netbox sous la forme __meta_netbox_foo
vers la forme foo
. Cela permet de garder les informations provenant des Netbox puisque Prometheus se débarrasse de
tous les labels commençant par __
avant d’enregistrer une métrique.
Redémarrez Prometheus et vous devriez voir apparaître vos cibles dans la page Status -> Targets de la page d’administration de Prometheus.
Cas spéciaux
Utilisation de l’exporter_exporter
L'exporter_exporter est un reverse proxy très léger conçu pour agréger plusieurs exporters Prometheus écoutant sur des ports différents. Ces exporters sont alors accessibles en les sélectionnant via le paramètre HTTP module
.
Avec une configuration de l’exporter_exporter similaire à ceci :
modules:
node:
method: http
http:
port: 9100
cadvisor:
verify: false
method: http
http:
port: 4194
Il faudra attacher la configuration JSON suivante à notre équipement dans Netbox afin que chacun des exporters Prometheus soit interrogé :
[{"__param_module": "node"}, {"__param_module": "cadvisor"}]
Utilisation du snmp_exporter
Le snmp_exporter est un cas intéressant car il illustre bien le problème que peut poser un exporter Prometheus ne fonctionnant pas directement sur l’équipement à interroger. Le fonctionnement est similaire à un serveur proxy : on s’adresse à l’exporter en lui indiquant l’adresse réelle du dispositif à interroger en SNMP.
Le snmp_exporter intègre en outre un concept de module qui permet de lui indiquer les différents paramètres à utiliser pour puller un dispositif (communauté, OIDs à poller etc).
Lorsque Prometheus interroge le snmp_exporter, il doit donc lui fournir l’adresse de la cible réelle et le module à utiliser. Pour cela nous allons introduire un label “__snmp_module___
” qui contiendra le nom du module à utiliser. De plus, quand il est défini, ce label permettra de distinguer les cibles à interroger via l'exporter_snmp de celles à interroger directement. L'adresse de la cible SNMP sera quant à elle récupérée depuis le label __adress__
lors du relabeling.
Nous allons modifier la configuration des jobs Prometheus de la manière suivante :
- job_name: 'netbox'
file_sd_configs:
- files:
- '/path/to/generated/output.json'
relabel_configs:
- regex: __meta_netbox_(.+)
replacement: netbox_$1
action: labelmap
- source_labels: [__snmp_module__]
regex: .+
action: drop
- job_name: 'netbox_snmp'
file_sd_configs:
- files:
- '/path/to/generated/output.json'
relabel_configs:
- regex: __meta_netbox_(.+)
replacement: netbox_$1
action: labelmap
- source_labels: [__snmp_module__]
regex: .+
action: keep
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: instance
- target_label: __address__
replacement: snmp-exporter:9116
Le champ prom_labels
de notre équipement serait configuré ainsi :
{"__snmp_module__": "arista_sw"}
Note : le module arista_sw est défini dans la configuration par défaut du snmp_exporter
Une fois la cible générée, elle devrait ressembler à ceci dans Prometheus :
- Cible :
http://snmp-exporter:9116/proxy
__param_target
:11.22.33.44
(primary_ip de l’équipement)instance
:11.22.33.44
__snmp_module__
:arista_sw
- […]
Conclusion
Et voilà, on a réussi à coupler le mécanisme de Service Discovery de Prometheus avec Netbox : votre Prometheus peut découvrir automatiquement les nouvelles cibles de votre infrastructure à superviser.
On espère que ce projet pourra vous faire gagner un peu de temps. N’hésitez pas à nous faire part de vos remarques et à contribuer au projet sur Github !!
Ne ratez pas nos prochains articles DevOps et Cloud Native! Suivez Enix sur Twitter!