Déployer Thanos et Prometheus sur un cluster K8s

26 Avril 2023 • 7 min

Cet article est le second d’une série de 3 sur le monitoring avec un focus sur Thanos.


Dans deux précédents articles, nous avons vu comment superviser Kubernetes avec Prometheus et le fonctionnement de Thanos pour stocker ses métriques sur le long terme.

Aujourd’hui, place à l’action : on fait chauffer notre cluster Kubernetes et on se penche sur comment déployer Thanos.

Note : on se concentre ici sur un Prometheus et un Thanos sur Kubernetes mais on peut aussi utiliser et déployer Thanos en dehors de Kubernetes.

Déployer Thanos sur K8s

Il nous faut un stockage objet pour que Thanos y conserve les métriques. Dans cet article, on utilise un stockage de type S3 avec une instance MinIO dans le cluster K8s.

Dans la vraie vie, il est tout aussi pertinent d’utiliser du stockage objet fourni par notre cloud provider.

# On installe le MinIO
helm repo add bitnami https://charts.bitnami.com/bitnami
helm install minio bitnami/minio --set persistence.enabled=false

# On récupère les credentials
export ROOT_USER=$(kubectl get secret --namespace monitoring minio -o jsonpath="{.data.root-user}" | base64 -d)
export ROOT_PASSWORD=$(kubectl get secret --namespace monitoring minio -o jsonpath="{.data.root-password}" | base64 -d)

# On créé un bucket 'thanos'
kubectl run --namespace monitoring minio-client --rm --tty -i --restart='Never' --env MINIO_SERVER_ROOT_USER=$ROOT_USER --env MINIO_SERVER_ROOT_PASSWORD=$ROOT_PASSWORD --env MINIO_SERVER_HOST=minio --image docker.io/bitnami/minio-client -- mb -p minio/thanos

Pour cette démo, par simplicité je n’utilise pas de stockage persistant. À ne pas reproduire en production bien évidemment, sous peine de voir ses données disparaître en un claquement de doigts 🙂

De façon similaire, j’agis en tant qu'admin par simplicité, mais il faut normalement un utilisateur dédié avec les bonnes permissions (par exemple pour du S3)

Configuration de l’accès de Thanos au stockage objet

Thanos a besoin de savoir comment se connecter au stockage objet. On crée le Secret pour utiliser le stockage que l’on vient de créer :

export ENDPOINT="minio:9000" BUCKET=thanos
cat > objstore.yml << EOF
type: S3
config:
  endpoint: "${ENDPOINT}"
  bucket: "${BUCKET}"
  access_key: "${ROOT_USER}"
  secret_key: "${ROOT_PASSWORD}"
  insecure: true
EOF
kubectl create secret generic thanos-objstore --from-file=objstore.yml -o yaml --dry-run=client | kubectl apply -f -

Configuration du Thanos sidecar

Je vous présente ici deux options qui fonctionnent bien :

  • via prometheus-operator
  • via le Helm chart officiel Prometheus

Avec le prometheus-operator

Custom Resource Prometheus

Le prometheus-operator ajoute automatiquement le sidecar lors du remplissage de la section thanos de la Custom Resource Prometheus.

Il suffit donc de fournir la configuration pour le stockage objet, avec le secret créé précédemment :

spec:
  thanos:
    objectStorageConfig:
      name: thanos-objstore
      key: objstore.yml

Si besoin, il est possible de configurer d’autres éléments du sidecar, comme l’image exacte à utiliser (voir la documentation).

Helm chart kube-prometheus-stack

Le chart kube-prometheus-stack permet aussi de configurer la Custom Resource Prometheus, mais également d’y ajouter un ServiceMonitor pour récupérer les métriques du sidecar, avec les values suivantes :

prometheus:
  prometheusSpec:
    thanos:
      objectStorageConfig:
        name: thanos-objstore
        key: objstore.yml
  thanosService:
    enabled: true
  thanosServiceMonitor:
    enabled: true

Via le Helm chart Prometheus officiel

Dans le cas hautement improbable où prometheus-operator n’est pas utilisé dans le cluster (pour pallier à ce problème 😉), mais plutôt le chart Prometheus, il est également possible de configurer le sidecar. Cela demande des values un poil plus longues :

server:
  extraFlags:
    - web.enable-lifecycle
    - web.enable-admin-api
    - storage.tsdb.min-block-duration=2h
    - storage.tsdb.max-block-duration=2h

  global:
    external_labels:
      prometheus_from: myprom

  service:
    gRPC:
      enabled: true

  sidecarContainers:
    thanos-sidecar:
      image: quay.io/thanos/thanos:v0.31.0
      imagePullPolicy: IfNotPresent
      args:
        - sidecar
        - --prometheus.url=http://localhost:9090/
        - --tsdb.path=/prometheus
        - --grpc-address=[$(POD_IP)]:10901
        - --http-address=[$(POD_IP)]:10902
        - --objstore.config=$(OBJSTORE_CONFIG)
      volumeMounts:
        - mountPath: /prometheus
          name: storage-volume
      env:
        - name: POD_IP
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: status.podIP
        - name: OBJSTORE_CONFIG
          valueFrom:
            secretKeyRef:
              name: thanos-objstore
              key: objstore.yml
      ports:
        - name: http
          containerPort: 10902
          protocol: TCP
        - name: grpc
          containerPort: 10901
          protocol: TCP

Il y a plusieurs choses à configurer pour Prometheus:

  • Activer l’api pour que le sidecar puisse reload en cas de changement de configuration
  • Activer l’api d’admin pour récupérer les metadatas comme les external_labels
  • S’assurer que les blocs générés par Prometheus ne sont pas compactés (min == max duration)
  • Avoir au moins un external label pour identifier l’instance Prometheus

Puis on configure le Thanos sidecar et le service pour l’exposer.

Déployer les autres composants Thanos

On va utiliser le chart bitnami/thanos qui est assez complet. On utilise les values suivantes :

existingObjstoreSecret: thanos-objstore

query:
  dnsDiscovery:
    sidecarsService: "prometheus-operated"
    sidecarsNamespace: "monitoring"

compactor:
  enabled: true

storegateway:
  enabled: true

metrics:
  enabled: true
  serviceMonitor:
    enabled: true

Puis on déploie avec :

helm upgrade --install thanos bitnami/thanos --values thanos.values.yaml

On a alors 4 composants Thanos qui sont installés:

  • query, qui va utiliser le DNS service discovery pour joindre la StoreAPI du sidecar et de la storegateway
  • query-frontend, configuré pour cibler le query
  • storegateway, qui expose le contenu du stockage objet
  • compactor, qui s’occupera de la compaction et de la rétention dans le stockage objet

Par défaut, le compactor et la storegateway ont besoin d’un volume persistent. Dans la réalité, ils peuvent cependant s’en passer, le démarrage sera juste plus long pour la storegateway (le temps de synchroniser les metadata depuis le stockage objet) et le compactor travaillera sur de l'ephemeral-storage (attention à mettre des resources limits dans ce cas). Pour la simplicité de la démo je vais le désactiver :

compactor:
  enabled: true
  persistence:
    enabled: false

storegateway:
  enabled: true
  persistence:
    enabled: false

Le chart est suffisamment flexible pour configurer les différents paramètres de Thanos (cache, durée de rétention, …) et des manifests Kubernetes (ingress, resources, affinity/taint, …).

Un exemple de configuration :

query:
  replicaCount: 3
  replicaLabel: prometheus_replica
  podAntiAffinityPreset: hard
  pdb:
    create: true
  existingSDConfigmap: thanos-storeapi-file-sd
  extraFlags:
    - --query.promql-engine=thanos

queryFrontend:
  replicaCount: 2
  podAntiAffinityPreset: hard
  pdb:
    create: true
  extraFlags:
    - '--query-frontend.downstream-tripper-config="max_idle_conns_per_host": 100'
  config: |-
    type: REDIS
    config:
      addr: 'redis:6379'    

compactor:
  enabled: true
  retentionResolutionRaw: 90d
  retentionResolution5m: 180d
  retentionResolution1h: 2y

storegateway:
  enabled: true
  replicaCount: 2
  podAntiAffinityPreset: hard
  pdb:
    create: true
  config: |-
    type: REDIS
    config:
      addr: 'redis:6379'
      cache_size: '1G'    

Avec ces values, on a:

  • Le query, query-frontend et la storegateway avec plusieurs réplicas, une anti-affinity (requiredDuringScheduling) entre eux et des PodDisruptionBudgets
  • Le choix du label qui différencie les replicas Prometheus HA sur le query (replicaLabel)
  • L’utilisation d’une ConfigMap avec la liste des StoreAPI pour le service discovery du query
  • Des arguments supplémentaires pour le query et query-frontend pour configurer certaines options
  • La configuration de Redis comme cache pour le query-frontend et la storegateway
  • Les durées de rétention des différentes métriques appliquées par le compactor

Utiliser Thanos à la place de Prometheus

Pour les requêtes PromQL via la WebUI

Le Thanos query fournit une interface similaire à celle de Prometheus. On peut y accéder via un port-forwarding ou bien configurer un ingress. Si le query-frontend est utilisé, c’est dans ses values qu’il faut configurer l'ingress :

queryFrontend:
  ingress:
    enabled: true
    hostname: thanos.example.com
    ingressClassName: public

On retrouve de quoi faire des requêtes PromQL, comme avec Prometheus :

Thanos Query WebUI

Les alertes, targets et rules des différents Prometheus sont également accessibles :

Thanos Query WebUI status menu

Et l’on peut lister les différentes StoreAPI configurées sur le query :

Thanos Query WebUI StoreAPI listing

Thanos en datasource pour Grafana

L’utilisation la plus commune de Thanos reste derrière un Grafana. Thanos query expose la même API que Prometheus, il suffit donc de rajouter une datasource de type Prometheus dans Grafana et de cibler le Thanos query (ou query-frontend si déployé) :

Ajouter Thanos via une datasource Grafana

Il est possible d’indiquer à Grafana le type de datasource Prometheus et fournir des paramètres pour les requêtes. Par exemple pour indiquer que l’on veut récupérer les version downsamplées des métriques automatiquement en fonction de la résolution :

Détail datasource Thanos

Il ne reste qu’à l’utiliser comme datasource d’un panel ou lors de l’import d’un dashboard :

Sélection datasource panel Grafana

Ou encore mieux, configurer la nouvelle datasource comme celle par défaut, puisque Thanos query permet d’avoir plusieurs instances Prometheus requêtables derrière.

Datasource Grafana par défaut

Le dernier article de cette série sur Thanos aborde justement comment et dans quels cas il est pertinent d'exposer plusieurs Prometheus derrière Thanos… A bientôt !


Ne ratez pas nos prochains articles DevOps et Cloud Native! Suivez Enix sur Twitter!