Recevoir les appels à l’aide de son infra docker swarm

Recevoir les appels à l’aide de son infrastructure Docker Swarm

 

Dans un de mes articles précédents, nous avons vu comment déployer prometheus, un outil de monitoring capable de générer sa propre configuration 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 afin de 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 configuration en se connectant au démon docker,
  • les briques prometheus target qui sont : la 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 la suivante :

  • 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 créer un canal et y associer un webhook pour ceux qui ne l’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 définir les accès :

Appuyez ensuite sur les trois points, mais en face de votre canal ce coup-ci afin de trouver l’option connecteurs :

Trouvez “webhook” et appuyez sur Configurer

Définissez un nom et appuyez sur Creer

Notez bien l’URL de votre webhook, car nous allons devoir l’indiquer à l’alertmanager de prometheus !

Vous pouvez créer plusieurs canaux ou plusieurs équipes 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 configuration nécessaire.

Si tout va bien, on a 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 nous même en HTTP. Donc on peut essayer nous-même de construire une 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"
        }
    ]
}

On peut ensuite tester avec curl en faisant 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'

On passe en fait l’url de votre webhook dans le path de la requête HTTP, d’où 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 déclarer 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, on ajoute juste un container, avec un port et on lui passe une config. C’est sur cette dernière que nous allons nous attarder.

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 example.
  smtp_auth_username: 'xxx@geco-it.fr'
  smtp_auth_password: 'xxx'
 
 
# A partir de la on défini la logique de distribution des alertes, la clause "route" defini le chemin par defaut d'une alerte, et dessous on pourra
# déclarer des "routes", notez bien le "S", enfant qui seront des bifurcation par rapport au chemin de base.
# Je m'explique : ci dessous notre route de base, on envoie a geco-team qui est un recepteur defini 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éfinis une route alternative qui envoie les alertes sur un autre récepteur. Pour décider quelle alerte va emprunter quelle route, on utilise 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 on trouve le bloc des règles d'inhibition qui permettent de désactiver certaines alertes en fonction de certains critères. La règle désactive les alertes qui matchent avec le target, si la source matche. Dans l'exemple ci-dessus on désactive les warnings, si on trouve une 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 on déclare 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 une notification sur le retour à la normale d'une alerte. 
      url: 'http://teams-forwarder:2000/_dynamicwebhook/'
- name: 'geco-hugo'
  webhook_configs:
    - send_resolved: true
      url: 'http://teams-forwarder:2000/_dynamicwebhook/'
 

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 !

On ajoute 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 suivant et la tester dans la webui:

node_filesystem_free_bytes / (1024*1024*1024)  <= on fait 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 allez pouvoir les utiliser et agrémenter votre alerte !

On trouve 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: warning                                                                                                    
          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
udev            991M     0  991M   0% /dev
tmpfs           200M   21M  180M  11% /run
/dev/sda1        20G   11G  8,2G  57% /
tmpfs           999M     0  999M   0% /dev/shm
tmpfs           5,0M     0  5,0M   0% /run/lock
tmpfs           999M     0  999M   0% /sys/fs/cgroup
 
 
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
udev            991M     0  991M   0% /dev
tmpfs           200M   21M  180M  11% /run
/dev/sda1        20G   17G  2,2G  89% /
tmpfs           999M     0  999M   0% /dev/shm
tmpfs           5,0M     0  5,0M   0% /run/lock
tmpfs           999M     0  999M   0% /sys/fs/cgroup

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 a 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érentes équipes ou différents canaux ( SMS, mail, slack, etc… ). Il faudra aussi évidemment jouer sur le seuil et la close “for” pour écarter tous 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çoit, plus on a tendance à l’ignorer, alors que si elles sont ponctuelles, elles sont beaucoup plus marquantes.

Comments are closed.