Valorisez vos métriques Prométheus avec Cloudkitty : tutoriel avec Traefik
Par Martin CAMEY, Développeur CloudKitty & Consultant Cloud @Objectif Libre
Pré-requis
Pour pouvoir suivre ce tutoriel, une machine sous Linux est nécessaire, avec Git, Docker et Docker-compose d’installés, ainsi qu’une connexion internet pour récupérer les images Docker.
Le tutoriel est basé sur les distributions Linux utilisant apt
comme package manager, mais il est possible d’adapter les commandes suivantes pour suivre ce tutoriel sur d’autres distributions.
Design et fonctionnement
L’architecture modulable de Cloudkitty utilise des classes appelées « Collecteur » pour récupérer des métriques depuis leur source. Une fois récupérées, les métriques sont aggrégées et ensuite évaluées, grâce à un ensemble de règles définies par les opérateurs.
L’étape de valorisation signifie que Cloudkitty affecte une valeur pour la consommation de chaque ressource. Ces ressources peuvent être de type cpu, mémoire, espace disque, bande passante réseau ou de n’importe quel autre type. Ces valeurs serviront ensuite à la génération de rapports de valorisation au format json ou csv, pour chaque instance d’application.
Le travail en cours pour le Collecteur Prometheus de Cloudkitty permet de valoriser les métriques de cette solution déjà très utilisée de monitoring. Il sera bientôt ajouté à une branche de développement, avant d’être intégré à une version stable. Couplé à Cloudkitty, son utilisation est très simple, puisque Prometheus est fait pour gérer l’agrégation et la récupération de métriques. Les seules choses à faire sont de spécifier : les requêtes à effectuer vers Prometheus ; ainsi que les règles de valorisation correspondantes.
Tutorial pas-à-pas avec Traefik
Pour le Proof-of-Concept, nous utilisons Traefik (load-balancer et reverse proxy) comme source de métriques. Traefik supporte nativement l’export de métriques vers Prometheus, utilisant une configuration minimaliste.
Exemple avec un cas simple
Prenons un cas basique : deux conteneurs différents, tous deux exposés par Traefik. Nous voulons tarifer différement le nombre de requêtes effectuées pour chaque conteneur.
L’architecture globale des services déployés et de leurs interactions est la suivante :
Nous allons configurer Cloudkitty pour enregistrer des datasets (= des structures de données contenant à la fois les valeurs tarifées et les métadonnées des métriques) pour chaque heure d’exécution de notre application. En tarifant le nombre de requêtes par heure, il est possible de générer des rapports de valorisation par heure, par jour, par semaine ou encore à l’année.
Pour cet exemple, nous utiliserons les images Docker officielles pour Prometheus, Traefik, RabbitMQ et MySQL (utilisés par Cloudkitty). Nous utiliserons également deux conteneurs exposant de simples fichiers statiques en HTML, du type « Hello World », grâce à l’image officielle Nginx. Tous les services cités ci-dessus sont lancés avec Docker-compose.
Configuration
Commençons par définir l’infrastructure pour cette démonstration. Nous utilisons le fichier docker-compose.yml suivant pour déployer les services :
docker-compose.yml
version: "2.2" services: prometheus: image: prom/prometheus hostname: prometheus ports: - "9090:9090" volumes: - /path/to/prometheus.yml:/etc/prometheus/prometheus.yml:ro - prometheus:/prometheus networks: - cloudkitty_prometheus_traefik_net traefik: image: traefik:1.6 hostname: traefik ports: - "80:80" - "8080:8080" volumes: - /var/run/docker.sock:/var/run/docker.sock - /path/to/traefik.toml:/etc/traefik/traefik.toml:ro networks: - cloudkitty_prometheus_traefik_net cloudkitty_db: image: mysql:5.7 hostname: mysql environment: - MYSQL_ROOT_PASSWORD=asecurepassword - MYSQL_USER=cloudkitty - MYSQL_PASSWORD=anothersecurepassword - MYSQL_DATABASE=cloudkitty volumes: - mysql:/var/lib/mysql ports: - "3306:3306" networks: - cloudkitty_prometheus_traefik_net cloudkitty_queue: image: rabbitmq:3.7.5-alpine hostname: rabbitmq environment: - RABBITMQ_DEFAULT_USER=cloudkitty - RABBITMQ_DEFAULT_PASS=asecurepassword volumes: - rabbitmq:/var/lib/rabbitmq:rw ports: - "5672:5672" networks: - cloudkitty_prometheus_traefik_net app1: image: nginx:alpine labels: traefik.frontend.rule: "Host:app1" volumes: - /path/to/html/app1/:/usr/share/nginx/html:ro networks: - cloudkitty_prometheus_traefik_net app2: image: nginx:alpine labels: traefik.frontend.rule: "Host:app2" volumes: - /path/to/html/app2/:/usr/share/nginx/html:ro networks: - cloudkitty_prometheus_traefik_net volumes: prometheus: mysql: rabbitmq: networks: cloudkitty_prometheus_traefik_net:
Notez que certains volumes sont créés pour prévenir toute perte de données en cas de re-démarrage des conteneurs.
Nous pouvons dès à présent préparer les configurations pour chaque composant de notre architecture. Ces fichiers sont utilisés par les conteneurs créés et gérés par le fichier Docker-compose ci-dessus.
prometheus.yml
scrape_configs: - job_name: 'traefik' scrape_interval: 5s static_configs: - targets: ['traefik:8080'] labels: group: 'traefik_group'
traefik.toml
[entryPoints] [entryPoint.traefik] address = ":8080" [entryPoint.http] address = ":80" [api] entryPoint = "traefik" [docker] endpoint = "unix:///var/run/docker.sock" domain = "docker.localhost" watch = true [metrics] [metrics.prometheus] entryPoint = "traefik"
Enfin, nous pouvons démarrer tous nos services définis dans docker-compose.yml
en utilisant la commande suivante :
$ docker-compose up -d
Installation de Cloudkitty
Dans ce tutoriel, nous utiliserons un virtualenv
Python pour installer Cloudkitty.
Commençons par récupérer le code source de Cloudkitty depuis GitHub, puis par installer les dépendances et par préparer la configuration et les dossiers de logs de Clouditty. Les commandes suivantes sont à coupler avec les commandes chown et chmod pour adapter les droits d’accès en fonction de votre poste de travail.
Dans un terminal :
(ck_env) $ sudo mkdir /etc/cloudkitty /var/log/cloudkitty (ck_env) $ cp etc/cloudkitty/api_paste.ini /etc/cloudkitty
Création d’un virtualenv et installation de Cloudkitty :
$ sudo apt install python-virtualenv ... $ virtualenv ck_env ... $ source ck_env/bin/activate (ck_env) $ git clone https://github.com/openstack/cloudkitty.git ... (ck_env) $ cd cloudkitty (ck_env) $ pip install pymysql ... (ck_env) $ pip install -r requirements.txt ... (ck_env) $ python setup.py install ...
Configurons maintenant la nouvelle installation de Cloudkitty :
/etc/cloudkitty/cloudkitty.conf
[DEFAULT] verbose = True log_dir = /var/log/cloudkitty auth_strategy = noauth transport_url = rabbit://cloudkitty:asecurepassword@localhost:5672/ [database] connection = mysql+pymysql://cloudkitty:password@localhost/cloudkitty [collect] fetcher = source collector = prometheus window = 1800 period = 3600 services = compute, volume, network.bw.in, network.bw.out, network.floating, image metrics_conf = /etc/cloudkitty/metrics.yml [storage] backend = sqlalchemy
Maintenant que Cloudkitty est installé et configuré, nous allons initialiser la base de données de Cloudkitty.
(ck_env) $ cloudkitty-dbsync upgrade ... (ck_env) $ cloudkitty-storage-init
Enfin, nous avons besoin de définir les métriques que nous voulons valoriser. Le metrics.yml
est le fichier de configuration de Cloudkitty concernant la métrologie.
Dans notre cas, la métrique Traefik que nous allons valoriser s’appelle traefik_backend_requests_total
. Cette métrique est stockée dans Prometheus comme un Compteur. Il compte simplement le nombre de requêtes effectuées vers Traefik, séparées par backend et par code de retour, et ne fait qu’augmenter. Pour pouvoir générer des rapports de valorisation par heure, Cloudkitty a besoin de connaitre le nombre exact de nouvelles requêtes effectuées chaque heure. Nous allons récupérer un range vector en passant les timestamp start
et stop
en paramètres, en plus de la fonction increase()
fournie par Prometheus. Cette fonction calcule l’augmentation d’une métrique entre deux instants T1 et T2.
Même s’il y a un compteur par backend et par code de retour pour la même métrique, nous allons tous les récupérer en une seule requête, grâce aux labels Docker et Prometheus. Les seules choses que nous avons besoin de faire sont : de spécifier les requêtes PromQL à effectuer dans le fichier metrics.yml
configuration file; de Cloudkitty ; et d’ajouter les règles de valorisation grâce à lacloudkitty-api
. Nous avons déjà défini le champquery
dans la configuration de CloudKitty.
/etc/cloudkitty/metrics.yml
name: Prometheus fetcher: source collector: prometheus period: 3600 # An hour in seconds wait_periods: 1 window: 1800 url: http://localhost:9090/api/v1/ services_objects: compute: instance volume: volume network.bw.out: instance_network_interface network.bw.in: instance_network_interface network.floating: network image: image radosgw.usage: ceph_account metrics: traefik_backend_requests_total: endpoint: query_range query: 'increase(traefik_backend_requests_total[$period])' unit: request metadata: - backend
Il est temps de pousser nos fomules de valorisation en utilisant la cloudkitty-api
.
Premièrement, commençons par démarrer la cloudkitty-api
:
(ck_env) $ cloudkitty-api -p 8889 ... ******************************************************************************** STARTING test server cloudkitty.api.app.build_wsgi_app Available at http://localhost:8889/ ... ********************************************************************************
Une fois lancée, ouvrons un nouveau terminal. Nous allons utiliser les commandes suivantes pour pousser les formules de valorisation, modestement tarifées à 10 centimes la requête pour la première application, et 30 centimes la requête pour la seconde.
Commençons par créér un group ‘request_number’ pour les formules de valorisation :
$ curl -X POST -H 'Content-Type: application/json' \ > -d '{"name": "request_number"}' \ > 'http://localhost:8889/v1/rating/module_config/hashmap/groups'
{"group_id": "8bcea13d-102f-44f5-b164-152e39745865", "name": "request_number"}
$ curl -X POST -H 'Content-Type: application/json' \ > -d '{"name": "traefik_backend_requests_total"}' \ > 'http://localhost:8889/v1/rating/module_config/hashmap/services'
{ "service_id": "7f3a1d40-c91b-470e-bd62-930468e36dc2", "name": "traefik_backend_requests_total" }
$ curl -X POST -H 'Content-Type: application/json' \ > -d '{"service_id": "7f3a1d40-c91b-470e-bd62-930468e36dc2", "name": "backend"}' \ > 'http://localhost:8889/v1/rating/module_config/hashmap/fields'
{ "service_id": "7f3a1d40-c91b-470e-bd62-930468e36dc2", "field_id": "2310d614-fdaf-4040-a336-75e49a98456d", "name": "backend" }
C’est presque terminé ! Nous avons juste besoin de définir les différents taux de valorisation pour nos formules.
Commençons par le premier backend en créant une association entre un groupe, la valeur d’un champ et le taux de valorisation :
$ curl -X POST -H 'Content-Type: application/json' \ > -d '{"group_id": "8bcea13d-102f-44f5-b164-152e39745865", \ > "service_id": "7f3a1d40-c91b-470e-bd62-930468e36dc2", \ > "field_id": "2310d614-fdaf-4040-a336-75e49a98456d", \ > "value": "backend-app1-cloudkitty-prometheus-traefik", "cost": "0.1"}' \ > 'http://localhost:8889/v1/rating/module_config/hashmap/mappings'
{ "tenant_id": null, "field_id": "2310d614-fdaf-4040-a336-75e49a98456d", "value": "backend-app1-cloudkitty-prometheus-traefik", "mapping_id": "6b4bf3a1-9d43-4c92-93d5-06c87a6e6bfb", "cost": "0.1000000", "service_id": null, "group_id": "8bcea13d-102f-44f5-b164-152e39745865", "type": "flat" }
$ curl -X POST -H 'Content-Type: application/json' \ > -d '{"group_id": "8bcea13d-102f-44f5-b164-152e39745865", \ > "service_id": "7f3a1d40-c91b-470e-bd62-930468e36dc2", \ > "field_id": "2310d614-fdaf-4040-a336-75e49a98456d", \ > "value": "backend-app2-cloudkitty-prometheus-traefik", "cost": "0.3"}' \ > 'http://localhost:8889/v1/rating/module_config/hashmap/mappings'
{ "tenant_id": null, "field_id": "2310d614-fdaf-4040-a336-75e49a98456d", "value": "backend-app1-cloudkitty-prometheus-traefik", "mapping_id": "3e077906-629b-493f-9b9b-1cb32b5f405d", "cost": "0.3000000", "service_id": null, "group_id": "8bcea13d-102f-44f5-b164-152e39745865", "type": "flat" }
Et voilà ! Nous avons créé nos règles de valorisation qui sont dorénavant enregistrées dans la base de données de Cloudkitty.
Enfin, nous avons juste à activer le module HashMap
responsable de l’application des formules :
$ curl -X PUT -H 'Content-type: application/json' \ > -d '{"module_id": "hashmap", "enabled": "true"}' \ > 'http://localhost:8889/v1/rating/modules'
Déployer la solution de valorisation Cloudkitty
Maintenant que tous nos composants sont configurés, démarrons le cloudkitty-processor
, le programme responsable du rating et du chargeback.
Nous pouvons arrêter lacloudkitty-api
dans le premier terminal et lancer la commande suivante pour démarrer le cloudkitty-processor
:
(ck_env) $ cloudkitty-processor --config-file /etc/cloudkitty/cloudkitty.conf \ > --log-file /var/log/cloudkitty/processor.log
C’est quasiment fini ! Nous avons juste besoin de générer des requêtes vers nos deux applications précédemment déployées, qui seront ensuite valorisées par Cloudkitty.
Dans ce tutoriel, nous utiliserons ApacheBench
(ou ab
), mais vous pouvez utiliser le client HTTP de votre choix.
Générons des requêtes pour nos deux applications.
Dans un autre terminal, installons le paquet nécessaire :
$ sudo apt install apache2-utils
Nous allons effectuer 150 requêtes pour la première application et 90 pour la seconde :
$ ab -H "Host:app1" -n 150 -c 10 http://localhost/ $ ab -H "Host:app2" -n 90 -c 10 http://localhost/
C’est terminé !
Traefik utilise le header HTTP utilisé dans le commande ci-dessus pour rediriger les requêtes vers le bon backend. Nous pouvons maintenant observer les métriques dans Prometheus en allant sur http://localhost:9090
depuis un navigateur, en plus des backends et codes de retour dans Traefik à l’adresse http://localhost:8080
.
La génération des rapports de valorisation feront l’objet d’un nouvel article. Restez connectés !
Conclusion
Bien que Cloudkitty ait vocation à rester la solution de rating et de chargeback d’OpenStack, il évolue et son architecture modulaire lui permet d’étendre son champ d’action à tout l’écosystème cloud natif. Le Collecteur Prometheus pour Cloudkitty reflète cette évolution, même s’il est encore en cours de développement.
Cloudkitty a besoin de contributeurs ! Si vous avez besoin de fonctionnalités dans le Collecteur Prometheus, ou tout simplement d’un autre collecteur, venez en proposer et en implémenter de nouveaux !