[[public:prometheus_alerting]]

Recevoir les apels à l'aide de son infra docker swarm

Dans un de mes articles précédents, nous avons vu comment deployer prometheus, un outil de monitoring capable de générer sa propre config de manière dynamique, dans un cluster docker swarm. Il était question de mettre en place plusieurs méthodes de récolte des métriques, de les stocker, et de pouvoir interroger prometheus via son interface web pour tracer des graphiques. Je terminais mon article sur les possibilités supplémentaires de prometheus, notamment en terme de notification et c'est ce que nous allons voir aujourd'hui !

Si nous reprenons le labo de la dernière fois, nous avons :

  • la brique prometheus server en place, qui collecte les métriques sur les targets et découvre sa config en se connectant au démon docker
  • les briques prometheus target qui sont : le demon docker qui expose ses métriques, cadvisor et le node exporter de prometheus.
  • la brique prometheus webui qui nous permet de vérifier l'état de notre serveur prometheus et de faire des requêtes pour afficher les métriques.

Notre cluster swarm reste le même avec ses 6 nodes :

ID                            HOSTNAME          STATUS    AVAILABILITY   MANAGER STATUS   ENGINE VERSION
u1i03hhiq2e4h0pebzm2y41wb *   docker-manager1   Ready     Active         Reachable        20.10.6
d011tsg2fpujizh6og10wub7b     docker-manager2   Ready     Active         Leader           20.10.6
610fs14p3cnpuc0fv15pgdwkv     docker-manager3   Ready     Active         Reachable        20.10.6
jgn4jxmdrlpa0hamzlvq6rim5     docker-worker1    Ready     Active                          20.10.6
ikoj4h0j1nactm1pcqabwhsrq     docker-worker2    Ready     Active                          20.10.6
qvn7z1j4yiwt1pmppczcp7jml     docker-worker3    Ready     Active                          20.10.6

La démarche pour ajouter des notifications à notre système va être de :

  • Ajouter un service spécifique pour discuter avec Teams de Microsoft. L'alertmanager ne le fait pas nativement. Il faut une brique pour faire l'interface
  • Ajouter à notre stack existante le service “alertmanager” de prometheus qui va se charger de router et envoyer les notifications.
  • Configurer le serveur prometheus pour qu'il génère des alertes en fonction des valeurs de certaines métriques. En effet c'est lui qui possède “l’intelligence” des notifications.

Petit rappel sur la procèdure pour creer un canal et y associer un webhook pour ceux qui n'ont jamais fait :

En face de votre équipe, sur les trois petits points vous trouverez l'option ajout un canal

Vous pouvez ensuite lui choisir un nom et surtout qui y a accès

Faites ensuite à nouveau les trois points mais en face de votre cannal ce coup ci pour trouver l'option connecteurs

Trouvez “webhook” et faites le bouton Configurer

Choisissez un nom et faîtes Creer

Notez bien l'URL de votre webhook car on va devoir l'indiquer à l'alertmanager de prometheus !

Vous pouvez créer plusieurs canaux ou plusieurs équipe pour séparer les flux de notifications, par exemple si vous avez deux équipes qui travaillent sur des clusters swarm différents, on pourra particulariser les alertes de chacun vers son équipe respective !

Nous allons utiliser le projet prometheus-msteams. Il faut l'ajouter en temps que service dans la stack avec le bloc suivant :

   teams-forwarder:                                                                                                             
      image: quay.io/prometheusmsteams/prometheus-msteams
      ports:        
        - 2000:2000 
      networks:     
        - metrics

Pas de config nécéssaire.

Si tout va bien, nous avons maintenant un container qui va écouter sur le port 2000. Il va envoyer les requêtes vers le webhook teams dans le bon format en fonction de ce que nous lui envoyons en HTTP. Nous pouvons essayer de construire un requête fictive et lui envoyer pour voir si elle parvient bien jusqu’à notre teams !

Ecrivez le fichier json suivant :

{
    "version": "4",
    "groupKey": "{}:{alertname=\"high_memory_load\"}",
    "status": "firing",
    "receiver": "teams_proxy",
    "groupLabels": {
        "alertname": "high_memory_load"
    },
    "commonLabels": {
        "alertname": "high_memory_load",
        "monitor": "master",
        "severity": "warning"
    },
    "commonAnnotations": {
        "summary": "Server High Memory usage"
    },
    "externalURL": "http://docker.for.mac.host.internal:9093",
    "alerts": [
        {
            "labels": {
                "alertname": "high_memory_load",
                "instance": "10.80.40.11:9100",
                "job": "docker_nodes",
                "monitor": "master",
                "severity": "warning"
            },
            "annotations": {
                "description": "10.80.40.11 reported high memory usage with 23.28%.",
                "summary": "Server High Memory usage"
            },
            "startsAt": "2018-03-07T06:33:21.873077559-05:00",
            "endsAt": "0001-01-01T00:00:00Z"
        }
    ]
}

Nous pouvons ensuite tester avec curl en fesant un requête POST sur l'url du container suivie de celle de votre webhook comme ceci :

curl -X POST -d @/chemin/vers/le/file/json http://ip_d'un_node_de_votre_swarm:2000/_dynamicwebhook/url_de_votre_webhook_donnée_par_teams'

Nous passons en fait l'url de votre webhook dans le path de la requête HTTP, d'ou le nom “dynamicwebhook” devant… Ça nous évite de configurer prometheus-msteams. Vous devriez recevoir une carte dans teams, si ce n'est pas le cas, merci de contacter votre administrateur système ^^

Il nous suffit de declarer un container docker de plus dans la stack avec le bout de yaml suivant :

   alert-manager:                                                                                                               
      image: quay.io/prometheus/alertmanager
      command:
        - --config.file=/etc/prometheus/alertmanager.yml
      ports:
        - 9093:9093
      networks:
        - metrics
      configs:
        - source: geco-alertmanager
          target: /etc/prometheus/alertmanager.yml

Rien de particulier ici, nous ajoutons juste un container, avec un port et nous lui passons une config. C'est sur cette dernière que nous nous attarderons

global:
  smtp_smarthost: 'xxx.geco-it.net:587'
  smtp_from: 'prometheus@labo.geco-it.net'     # Définition des params pour l'envoie de mail, je ne l'utilise pas mais ils sont la pour exemple.
  smtp_auth_username: 'xxx@geco-it.fr'
  smtp_auth_password: 'xxx'
 
 
# A partir de la nous définissons la logique de distribution des alertes, la clause "route" défini le chemin par défaut d'une alerte, et dessous on pourra
# déclarer des "routes", notez bien le "S", enfant qui seront des bifurcations par rapport au chemin de base.
# Je m'explique : ci dessous notre route de base, on envoie à geco-team qui est un récepteur défini plus bas. Les directives group_* permettent de grouper les alertes par paquets. Et le repeat sert
# a renvoyer une alerte toutes les 3h tant qu'elle n'est pas traitée !
 

route:                                        
  receiver: 'geco-team'                        
  group_by: ['alertname', 'cluster']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 3h
 
# Ici je définie une route alternative qui envoie les alertes sur un autre récepteur. Pour décider quelle alerte va emprunter quelle route, nous utilisons la directive "match" qui va permettre de sélectionner les alertes par labels. Dans mon cas, je redirige les alertes qui possèdent la valeur critical pour le label severity vers le recepteur geco-hugo.
# Pour rappel, dans prometheus, chaque métriques se voit associer un ensemble de label qui sont simplement des groupes clé / valeur. Certaines sont induites par défaut, d'autre peuvent être ajoutées par votre config, à votre guise !
 

  routes:
   - match:
      severity: critical
      receiver: geco-hugo
 
# Ici nous trouvons le bloc des règles d'inhibition qui permettent de désactiver certaines alertes en fonction de certains critère. La règle désactive les alertes qui matchent avec le target, si la source matche. Donc dans l'exemple si dessus on désactive les warnings, si on trouve un alerte avec le même nom mais en critique. Ça évite d'en avoir deux pour un même problème.
      
inhibit_rules:
- source_matchers:
    - severity="critical"
  target_matchers:
    - severity="warning"
  equal: ['alertname']

receivers:                           # Ici nous declarons les "récepteurs" des alertes, ça sera votre ou vos canal / canaux teams, voir slack ou autre. 
- name: 'geco-team'
  webhook_configs:
    - send_resolved: true            # Send resolved permet d'envoyer ou nom un notification sur le retour à la normale d'une alerte. 
      url: 'http://teams-forwarder:2000/_dynamicwebhook/<url de votre webhook teams>'
- name: 'geco-hugo'
  webhook_configs:
    - send_resolved: true
      url: 'http://teams-forwarder:2000/_dynamicwebhook/<url de votre webhook teams>'
 

Plus de détails sur les routes, et la config de l'atermanager possible ici

Il nous reste à modifier la configuration du prometheus lui même pour deux raisons :

  • Déclarer le container qui fait tourner l'alertmanager, pour que prometheus lui envoie les dites alertes
  • Déclarer un fichier “rules” dans lequel on définira justement nos alertes !

Pour se faire nous ajoutons le bloc suivant :

  alerting:
    alertmanagers:
      - static_configs:
          - targets: ['alert-manager:9093']
     
  rule_files:
    - /etc/prometheus/alerts.rules

L'ajout des rules se fait donc dans le fichier séparé que l'on a renseigné juste avant. Les rules prometheus ont le format suivant :

- alert: Le nom de votre alerte
  expr: expression qui va servir à définir une valeur et son état critique ou non
  for: la période pendant laquelle la condition ci-dessus devra être remplie pour enclencher l'alerte
    labels:
      votre_label: la valeur que vous voulez                                                                                          
    annotations:                                                                                                    
      summary: Un résumé écrit de l'alerte pour pouvoir donner plus d'infos que dans le nom. Vous pouvez utiliser des valeurs de label dans ce résumé, nous allons voir comment !

Pour construire vos alertes, le mieux est de tester votre expression dans la webui de prometheus, nous allons construire ensemble une règle pour alerter sur une utilisation disque importante. Admettons que nous voulions notifier quand il ne reste que 5Go d'espace libre sur le disque d'une machine. Nous pouvons utiliser l'expression suivante et la tester dans la webui:

node_filesystem_free_bytes / (1024*1024*1024)  <= Nous devons faire la division pour convertir en Go

Vous obtenez donc un graph qui valide votre expression, notez aussi les labels que prometheus associe à votre requête car vous aller pouvoir les utiliser et agrémenter votre alerte avec !

Nous trouvons ici par exemple les labels “instance” pour la machine concernée et “mountpoint” pour le point de montage concerné, deux infos très utiles que l'on va utiliser.

Créons donc notre première règle avec notre expression précédemment testée :

 groups:
    - name: example-alerting
      rules:
        - alert: TooMuchDiskUsage
          expr: node_filesystem_free_bytes / (1024*1024*1024) < 5
          for: 1m
          labels:
            severity: critical                                                                                                    
          annotations:
            summary: Filesystem mounted on {{ $labels.mountpoint }} on {{ $labels.instance }} has less than 5Go free #Les valeurs entre {{}} vont automatiquement être remplacée par celles des labels.

Si vous cliquez sur la partie alerte de l'interface, vous devriez retrouver votre alerte :

On va se connecter en shell sur un des workers docker et créer un énorme file avec la commande dd

admin@docker-worker1:~$ df -h /
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1        20G   11G  8,2G  57% /
 
admin@docker-worker1:~$ echo $((1024*6))
6144
 
admin@docker-worker1:~$ sudo dd if=/dev/zero of=/opt/bigfile bs=1M count=6144
6144+0 records in
6144+0 records out
6442450944 bytes (6,4 GB, 6,0 GiB) copied, 53,7303 s, 120 MB/s
admin@docker-worker1:~$ df -h /
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1        20G   17G  2,2G  89% /

On constate qu'il nous reste 2.2go de libre donc notre alerte devrait se lancer vu qu'on est sous les 5Go. Visitons l'interface web pour confirmer :

L'alerte est en pending, cela veut dire que prometheus va attendre une minute avec de confirmer la situation. Dans le cas de la place disque, ce n'est pas forcément pertinent mais sur l'utilisation du CPU par exemple, ça permet d'effacer les faux positifs liés à un pic de charge de courte durée. Le temps que j'écrive ces mots, l'alerte a du passer en “firing” :

Quand on atteint la minute, l'alerte est envoyée à l'alertmanager qui se chargera de la dispatcher vers la ou les personne(s) concernée(s). Si le seuil repasse à un niveau normal avant la minute d'attente, rien n'est fait. On peut d'ailleurs bien vérifier sur la webui de l'alertmanager lui même sur le port 9093 :

Et finalement la carte doit se trouver dans notre teams :

Comme dans le cadre de l'article précédent sur prometheus, je n'ai fait qu'effleurer les possibilités offertes par le produit. Le but, au delà de faire des alertes pour chacun de vos services importants, est d'optimiser ces dernières. Toute la puissance de prometheus réside dans le fait de pouvoir faire des alertes fines, sur des objets précis et de pouvoir les dispatcher entre différents équipes ou canaux ( SMS, mail, slack, etc… ). Il faudra aussi évidemment jouer sur le seuil et la close “for” pour écarter tout les soucis non critiques dans le but de recevoir seulement des notifications utiles. Le but des alertes en supervision c'est de ne jamais en recevoir. Plus on en reçois, plus on a tendance à la ignorer, alors que si elles sont ponctuelles, elles sont beaucoup plus marquantes.

  • public/prometheus_alerting.txt
  • Dernière modification: 01/04/2022/ 11:58
  • par h.campion