Aller au contenu

Troubleshooting Remote Write#

Lorsqu'un incident survient avec la fonctionnalité Remote Write, plusieurs alertes peuvent survenir en même temps. Les causes peuvent varier. Elles peuvent être multiples.

Les alertes sont (liste non exhaustive) :

  • PrometheusContainerReachedMaxAllowedOpenSockets
  • PrometheusContainerTooManyOpenSockets
  • PrometheusOutOfOrderTimestamps
  • PrometheusRemoteStorageFailures
  • PrometheusRemoteWriteBehind
  • PrometheusRemoteWriteDesiredShards
  • PrometheusRemoteWriteRequestErrors

Piste d'investigations#

  • Le nombre d'alertes est-il limité à un client ou global ? S'il est global, il faut privilégier l'étude des Prometheus Centraux (services svc-monitoring-stack-corp-prd-1 et svc-monitoring-stack-corp-prd-2).
  • Le problème se situe-t-il à la source d'un remote-write (un Prometheus Cluster) ou à la destination (un Prometheus Central ou le Prometheus d'un client) ? Si c'est les deux, il faut privilégier l'étude de la destination (Prometheus Central ou du client).
  • Sur un prometheus destination (Central ou du client), le pod redémarre-t-il souvent ? Si oui, vérifier la cause (OOMKill ? Liveness Probe ? Autre ?)

Cas connus#

Le container redémarre en boucle (OOMKill ou Liveness Probe)#

Dans le cas d'un OOMKill, il faudra augmenter (ou supprimer la limite mémoire) au préalable.

# Identifier l'objet Prometheus à modifier
kubectl -n <namespace> get prometheus

# Modifier l'objet Prometheus
kubectl -n <namespace> edit prometheus <prometheus>

Note

Cette méthode n'a encore jamais été testée. Cependant, il s'agit théoriquement de la façon propre de faire.

  • il est recommandé de la tester plutôt que de reprendre l'ancienne méthode (la différence porte sur les probes)
  • ne pas ajouter maximumStartupDurationSeconds dans un premier temps.
  • le paramètre maximumStartupDurationSeconds a pour valeur par défaut 15 minutes. Si Prometheus tarde à redémarrer, il faudra une valeur supérieure à 15 minutes (exprimée en secondes).

Les modifications à apporter sont :

spec:
  resources:
    requests:
      cpu: 4000m                         # Modifier ici la valeur souhaitée en l'augmentant si besoin
      memory: 27000Mi                    # Modifier ici la valeur souhaitée en l'augmentant si besoin
    limits:                              # Modifier ou supprimer cette ligne si besoin
      memory: 20000Mi                    # Modifier ou supprimer cette ligne si besoin
  web:
    maxConnections: 10000                # Indiquer 10000 (une valeur entre 10000 et 60000)
  maximumStartupDurationSeconds: 1200    # Ajouter cette ligne si besoin, avec une valeur supérieure à 900 (15m).

Si maximumStartupDurationSeconds a été ajouté pour éviter un kill au bout de 15 minutes, il faudra déterminer le temps de démarrage effectif et le communiquer à l'équipe Monitoring pour obtenir un changement permanent de cette valeur.

Les modifications à apporter sont :

spec:
  resources:
    requests:
      cpu: 4000m                  # Modifier ici la valeur souhaitée en l'augmentant si besoin
      memory: 27000Mi             # Modifier ici la valeur souhaitée en l'augmentant si besoin
    limits:                       # Modifier ou supprimer cette ligne si besoin
      memory: 20000Mi             # Modifier ou supprimer cette ligne si besoin
  containers:                     # Ajouter cette ligne et les suivantes
  - livenessProbe:                # Ajouter cette ligne
      failureThreshold: 1000000   # Ajouter cette ligne
    name: prometheus              # Ajouter cette ligne
    readinessProbe:               # Ajouter cette ligne
      failureThreshold: 1000000   # Ajouter cette ligne
  web:
    maxConnections: 10000         # Indiquer 10000 (une valeur entre 10000 et 60000)

Note

Ces valeurs peuvent être dispersées dans la définition de l'objet Prometheus. Les ajouts peuvent être placés n'importe où tant que la structure de l'objet reste correcte.

Lorsque l'édition est finie, Prometheus-Operator devrait redéployer le pod Prometheus.

Il faut suivre l'évolution du pod. Au redémarrage, dans les logs du container Prometheus, on devrait observer :

  • les informations de démarrage
  • le WAL est rejoué (opération longue)
  • relecture de la configuration
  • ouverture du service (et souvent, des lignes out of order)

Il peut être normal d'observer certains dysfonctionnements suite au redémarrage :

  • logs out of order ;
  • alertes PrometheusRemoteWriteDesiredShards sur les Prometheus sources ;
  • alertes PrometheusRemoteWriteBehind sur les Prometheus sources ;

Les alertes peuvent mettre plusieurs minutes (plus de 30 minutes parfois) à disparaître.

A ce stade, il importe que Prometheus ne redémarre pas (ou une ou deux fois éventuellement, mais pas en boucle).

Ces expressions permettent de suivre l'évolution de Prometheus sur Grafana. Il faut les adapter au cas d'usage.

# Mémoire des containers Prometheus sur les clusters kub-34 et kub-53
max(container_memory_working_set_bytes{cluster=~"(kub-34|kub-53)", namespace="monitoring-stack-corp-obs-corp-prd", pod="prometheus-obs-corp-prd-prometheus-0", container="prometheus"} / 1024/1024) without (node,instance,id,image,name)

# Nombre de sockets des containers Prometheus sur les clusters kub-34 et kub-53
container_sockets{cluster=~"(kub-34|kub-53)", namespace="monitoring-stack-corp-obs-corp-prd", pod="prometheus-obs-corp-prd-prometheus-0", container="prometheus"}

Lorsque l'incident est fini et les alertes résorbées, on peut remettre le paramétrage initial en place, ou un nouveau paramétrage en fonction de l'incident.

Hash du mot de passe "BasicAuth" de l'Ingress Controller#

Si le problème persiste malgré les tentatives pour le corriger, le problème pourrait venir du mot de passe BasicAuth sur l'Ingress Controller. Le hash peut avoir été créé avec un cost différent de la valeur par défaut 05.

Pour voir si l'incident correspond à ce cas d'usage, effectuer ces vérifications :

  • récupérer le secret contenant le hash de l'ingress dans Vault. Il se trouve ici : secrets/secret/zones/fe/<service_zone_name>/prometheus-ingress/prometheus-ingress-YYYYMMDD-HHMMSSYYYYMMDD-HHMMSS indique la date et l'heure de création du secret ;
  • le hash doit avoir ce format : metrics-<stg|prd>-YYYYMMDD-HHMMSS:$2y$**XX**$abcdefghijklmnopqrstuvwxyzXX représente le cost du hash et devrait avoir la valeur 05 (la valeur correcte dans ce cas est $2y$**05**$abcdefghijklmnopqrstuvwxyz). Si ce n'est pas le cas, et que le cost (XX) a une valeur différente, cela pourraît être l'origine de l'incident.

Voici comment corriger :

  • générer un nouveau secret pour l'ingress en suivant la procédure standard ;
  • vérifier que le nouveau mot de passe a un cost (XX) égal à 05. Si ce n'est pas le cas, le problème se situe au niveau de la génération du hash.

Logs "out of order sample" nombreuses et récurrentes#

Diagnostic#

Lorsqu'on observe le message "out of order sample" de façon trop récurrente dans les logs du Prometheus Corp (central), il est possible que l'on se trouve dans un cas où Prometheus a du retard et n'arrive pas à résorber son retard.

Note

Ce cas est différent du cas où l'on observe également le message "out of order sample", mais de façon sporadique, avec series=\"{__name__=\\\"ALERTS\\\" ou series=\"{__name__=\\\"ALERTS_FOR_STATE\\\" dans ces mêmes logs. Pour ce cas, voir la section suivante.

Ce cas peut être exacerbé si Prometheus et Loki cohabitent sur le même cluster. Dans ce cas, Prometheus, qui provoque la génération de ces messages de logs plus nombreux qu'à l'accoutumée, se retrouve ralenti par la présence de Loki qui doit absorber ce surplus de logs. C'est un cercle vicieux dans lequel Prometheus provoque lui-même son propre ralentissement avec la complicité de Loki.

La quantité de ces logs peut être visualisée avec ce LogQL (datasource Loki) :

rate({namespace="monitoring-stack-corp-obs-corp-prd", pod=~"prometheus-obs-corp-prd-prometheus-0", container="prometheus", cluster=~"kub-34|kub-53"}[5m])

On peut également visualiser le retard des Prometheus via la métrique qui indique le nombre de shards (plus il y en a, plus le retard est grand) avec ce PromQL (datasource thanos) :

min by(cluster,prometheus) (avg_over_time(prometheus_remote_storage_shards{remote_name=~"prometheus-mon[12]"}[5m]) ) > 10

Le diagnostic est établi quand ces deux courbes sont non nulles pendant plus de 10 minutes.

Solutions#

Dans certains cas, la situation peut se résorber d'elle même (cela a été observé plusieurs fois, au bout d'une heure ou une heure et demie). Mais il n'est pas possible de le prévoir à l'avance et, pendant ce temps, des métriques des clusters et des zones clientes peuvent se perdre.

Il est donc préférable de redémarrer tous les Prometheus Cluster en même temps pour arrêter cet incident.

Deux solutions possibles:

Solution 1 (Avec Kswitch)#

Note

Cette solution n'utilise pas Rancher. Elle est plus pérenne que la suivante.

Cependant, la différence de temps pour redémarrer tous les Prometheus-clusters entre les deux solutions est significative et en faveur de l'autre solution.

Préférer donc la solution 2 avec Rancher.

Nous utilisons ce script (appelé rn.sh) :

#! /bin/bash

if [ "$1" = "" ]; then
    echo "$0 <cluster>"
    exit 1
fi
kswitch "$1"
sleep 2

kubectl -n monitoring get pod | grep cluster
kubectl rollout restart -n monitoring statefulset/prometheus-cluster-prometheus

Il faut créer la liste des clusters (se référer à ngot-zones) :

curl -sL 'https://git.corp.caascad.com/caascad/terraform/envs-ng/-/raw/master/gen/zones_static/zones.json' \
      | jq -r '. | map(select(.metadata.line == "prod") | select(.type == "cluster")) |.[].name'           \
      | sort \
      > liste

Vérifier le fichier liste et supprimer d'éventuels noeuds injoignables (ou, en équipe, se partager le travail).

Lancer le redémarrage :

for i in $(cat liste); do ./rn.sh $i; done

Warning

Ne pas lancer ce script plusieurs fois en parallèle. En effet, il utilise kswitch qui ne permet pas de paralléliser.

Lorsque tous les Prometheus de tous les clusters sont redémarrés, il faut vérifier :

for i in $(cat liste); do
    echo "$i";
    kswitch $i > /dev/null 2>&1;
    sleep 2;
    kubectl -n monitoring get pod |grep cluster;
done

Solution 2 (sans Kswitch avec le user rancher)#

Il faut avoir au préalable un fichier de l'ensemble des clusters disponible sur rancher.

  • Récupérer le mot de passe admin : here
  • Se connecter sur Rancher en tant qu'admin : here
  • Aller dans Cluster Management
  • Selection all cluster et download kubeconfig

Rancher

  • Se placer dans un répertoire de travail et copier, renommer le fichier Kubeconfig en kubeconfig_all.yaml.

Lancer le redémarrage :

export KUBECONFIG=$(pwd)/kubeconfig_all.yaml

# Obtenir la liste des clusters (modifier "prod" en "staging" si besoin)
KUB=($(sd get zones | jq -r '. | map(select(.type == "cluster" and .metadata.line == "prod")) | .[].name'))

# Vérifier la liste :
for k in "${KUB[@]}"; do echo "$k"; done
# Lancer le redémarrage :
date
for k in "${KUB[@]}"; do
  echo "** $k"
  kubectl --context="$k" rollout restart -n monitoring statefulset/prometheus-cluster-prometheus
  echo "** "
done
date

Action post solution#

Attendre quelques minutes (sleep 300 par exemple) puis lancer une vérification :

for k in "${KUB[@]}"; do
  kubectl --context="$k" -n monitoring get pod | sed -e "s/^/$k : /g"| grep prometheus-cluster
done

Il faut vérifier ici pour chaque cluster que :

  • il y a bien 2 pods ;
  • ils sont bien 2/2 Running ;
  • ils ont bien un uptime de quelques minutes (et non de quelques heures ou jours).

Il faut ensuite retourner dans Grafana voir si les erreurs out of order sample ont diminué sérieusement.

Il faut surveiller la métrique prometheus_remote_storage_shards qui doit être à 0 here.

Logs "out of order sample" pour ALERTS ou ALERTS_FOR_STATE#

Diagnostic#

Lorsqu'on observe le message "out of order sample" dans les logs du Prometheus Corp (central), et que ces mêmes logs indiquent également series=\"{__name__=\\\"ALERTS\\\" ou series=\"{__name__=\\\"ALERTS_FOR_STATE\\\", nous sommes dans le cas d'un problème double. D'une part la métrique mentionnée est refusée. D'autre part elle témoigne d'une autre alerte qu'il faut également traiter.

{"log":"ts=2024-08-12T14:05:42.719Z caller=write_handler.go:134 level=error comp
onent=web msg=\"Out of order sample from remote write\" err=\"out of order sampl
e\" series=\"{__name__=\\\"ALERTS\\\", alertname=\\\"PrometheusTSDBBlocksLoadedL
ow\\\", alertstate=\\\"firing\\\", cluster=\\\"kub-nn\\\", container=\\\"prometh
eus\\\", endpoint=\\\"web\\\", instance=\\\"172.16.0.48:9090\\\", job=\\\"obs-xx
x-prometheus\\\", namespace=\\\"monitoring-stack-client-obs-xxx\\\", ngot_contra
ct=\\\"obs-xxx\\\", ngot_service=\\\"svc-monitoring-stack-client-xxx\\\", obs_cl
ient=\\\"xxx\\\", pod=\\\"prometheus-obs-xxx-prometheus-1\\\", prometheus=\\\"mo
nitoring/cluster-prometheus\\\", prometheus_replica=\\\"prometheus-cluster-prome
theus-0\\\", service=\\\"obs-xxx-prometheus\\\", severity=\\\"warning\\\"}\" tim
estamp=1723471541535\n","stream":"stderr","time":"2024-08-12T14:05:42.719475459Z
"}

Dans cet exemple, les problèmes sont :

  • un envoi de la métrique ALERTS refusé par le Prometheus central (sans impact client) ;
  • une alerte PrometheusTSDBBlocksLoadedLow à traiter par ailleurs.

Solution#

Note

La raison du refus de la métrique n'est pas connue. Contactez rapidement l'équipe Monitoring pour aider à investiguer sur ce point.

Le traitement de l'autre alerte est à prioriser selon la procédure adéquate.

En effet, la résolution de l'autre alerte mettra fin à l'envoi de la métrique ALERTS ou ALERTS_FOR_STATE et résoudra les deux incidents.

Logs "context canceled"#

Diagnostic#

Des alertes PrometheusRemoteWriteBehind sont observées.

Dans les logs de Prometheus-cluster, on retrouve plusieurs fois context canceled. Exemple :

{"log":"ts=2024-09-03T13:51:16.335Z caller=dedupe.go:112 component=remote level=error remote_name=prometheus-mon4 url=https://remote-write-mon4-0.obs-corp-prd.cloudservicesfactory.com/api/v1/receive msg=\"non-recoverable error\" count=151 exemplarCount=0 err=\"context canceled\"\n","stream":"stderr","time":"2024-09-03T13:51:16.33658912Z"}

Explication#

L'erreur context canceled survient quand le client HTTP (Prometheus-cluster) perd la connexion avec la cible. Noter que lorsque cela arrive, il n'y a déjà plus de connexion avec la cible. Il ne peut donc pas y avoir de trace dans les logs de la cible ou de l'Ingress Controller.

Cette erreur survient en général suite à un incident réseau.

Prometheus conserve les métriques et tente de les renvoyer à nouveau, ce qui explique le retard et l'alerte PrometheusRemoteWriteBehind.

Solution#

Il faut redémarrer Prometheus-cluster.

Note

S'il faut redémarrer Prometheus sur tous les clusters, on peut s'inspirer de la procédure proposée pour le cas out of order sample