Architecture et Optimisation de la Surveillance Kubernetes via kube-state-metrics

Dans les environnements cloud natifs, la supervision de l'état des ressources Kubernetes est cruciale. Alors que les outils traditionnels comme cAdvisor se concentrent sur les métriques de performance matérielle (CPU, mémoire), kube-state-metrics (KSM) comble une lacune essentielle : l'exposition de l'état des objets de l'API Kubernetes sous forme de séries chronologiques compatibles avec Prometheus.

Mécanisme de Collecte et Architecture

KSM fonctionne comme un service autonome qui interroge l'API Server de Kubernetes via le mécanisme List-Watch. Il maintient un cache interne des objets et génère dynamiquement des métriques à la demande. Cette approche découplée garantit que la charge de calcul et de formatage des indicateurs n'impacte pas directement le plan de contrôle du cluster.

Couverture des Ressources et Gestion des Labels

Le composant prend en charge nativement plus de cinquante types de ressources. Voici une classification des principales catégories surveillées et leurs indicateurs associés :

Domaine Objets Kubernetes Exemples de Métriques
Calcul Pods, Deployments, StatefulSets kube_pod_status_phase
Réseau Services, Ingresses, Endpoints kube_service_info
Stockage PersistentVolumes, StorageClasses kube_persistentvolume_status_phase
Sécurité NetworkPolicies, Roles kube_networkpolicy_labels

Pour assurer la compatibilité avec le modèle de données de Prometheus, KSM sanitise les labels Kubernetes. Voici une implémentation alternative illustrant la logique de normalisation des chaînes de caractères et de résolution des conflits :

package main

import (
	"crypto/sha256"
	"fmt"
	"regexp"
	"strings"
)

var invalidChars = regexp.MustCompile(`[^a-zA-Z0-9_]`)

func SanitizeLabel(key string) string {
	// Remplacement des caractères non alphanumériques par des underscores
	normalized := invalidChars.ReplaceAllString(key, "_")
	return strings.ToLower(normalized)
}

func ResolveConflict(labelA, labelB string) (string, string) {
	// Génération d'un suffixe unique basé sur un hachage SHA-256 en cas de collision
	hashA := fmt.Sprintf("%x", sha256.Sum256([]byte(labelA)))[:6]
	hashB := fmt.Sprintf("%x", sha256.Sum256([]byte(labelB)))[:6]
	return SanitizeLabel(labelA) + "_" + hashA, SanitizeLabel(labelB) + "_" + hashB
}

Déploiement Standard et Configuraton RBAC

L'installation de KSM nécessite des permissions spécifiques pour lire l'état du cluster. Voici une configuration de déploiement restructurée utilisant des labels personnalisés et une exposition de ports modifiée :

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ksm-collector
  namespace: monitoring
  labels:
    app.kubernetes.io/component: metrics-exporter
spec:
  replicas: 2
  selector:
    matchLabels:
      app: ksm-exporter
  template:
    metadata:
      labels:
        app: ksm-exporter
    spec:
      serviceAccountName: ksm-reader
      containers:
      - name: metrics-agent
        image: registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.10.1
        args:
        - --port=9090
        - --telemetry-port=9091
        - --resources=deployments,pods,nodes,services
        ports:
        - containerPort: 9090
          name: metrics
        - containerPort: 9091
          name: telemetry

Le rôle RBAC associé doit accorder les verbes de lecture sur les groupes d'API requis :

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: ksm-cluster-reader
rules:
- apiGroups: [""]
  resources: ["nodes", "pods", "services", "configmaps", "namespaces"]
  verbs: ["list", "watch"]
- apiGroups: ["apps"]
  resources: ["deployments", "statefulsets", "daemonsets"]
  verbs: ["list", "watch"]
- apiGroups: ["batch"]
  resources: ["jobs", "cronjobs"]
  verbs: ["list", "watch"]

Stratégies de Mise à l'Échelle Avancées

Partitionnement Horizontal (Sharding)

Pour les clusters de grande envergure, l'empreinte mémoire de KSM peut devenir problématique. Le sharding permet de diviser la charge entre plusieurs instances. L'algorithme de distribution peut être modélisé comme suit en utilisant une fonction de hachage cryptographique pour une répartition uniforme :

import hashlib

def assign_shard(resource_uid: str, current_shard: int, max_shards: int) -> bool:
    """Détermine si une instance doit traiter un objet basé sur son UID."""
    digest = hashlib.sha256(resource_uid.encode('utf-8')).hexdigest()
    numeric_val = int(digest, 16)
    return (numeric_val % max_shards) == current_shard

Côté configuration Kubernetes, cela se traduit par l'ajout des arguments suivants au conteneur :

args:
  - --shard=1
  - --total-shards=5
  - --use-apiserver-cache=true

Collecte par Nœud via DaemonSet

Une approche alternative pour optimiser la collecte des métrriques liées aux Pods consiste à déployer KSM en tant que DaemonSet, en restreignant chaque instance aux objets hébergés sur son nœud local :

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: ksm-node-agent
spec:
  selector:
    matchLabels:
      tier: node-monitoring
  template:
    metadata:
      labels:
        tier: node-monitoring
    spec:
      containers:
      - name: ksm
        image: registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.10.1
        args:
        - --resources=pods
        - --node=$(HOST_NODE)
        env:
        - name: HOST_NODE
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName

Filtrage des Métriques

Réduire la cardinalité est essentiel pour maîtriser les coûts de stockage TSDB. Les options de filtrage incluent :

  • Sélection stricte des ressources : --resources=deployments,statefulsets
  • Listes d'autorisation par expression régulière : --metric-allowlist="kube_pod_status.*"
  • Restriction par espace de noms : --namespaces=kube-system,production

Cas d'Usage et Règles d'Alerte Prometheus

Les métriques générées permettent de superviser la conformité de l'état désiré par rapport à l'état réel. Voici des exemples de règles d'alerte ajustées avec des seuils et des sémantiques personnalisées :

groups:
- name: k8s.workload.health
  rules:
  - alert: PodFrequentRestarts
    expr: sum by (namespace, pod) (increase(kube_pod_container_status_restarts_total[2h])) > 10
    for: 5m
    labels:
      priority: high
    annotations:
      summary: "Redémarrages excessifs détectés sur {{ $labels.pod }}"
      description: "Le conteneur dans le pod {{ $labels.pod }} a redémarré plus de 10 fois au cours des 2 dernières heures."

  - alert: DeploymentUnavailableReplicas
    expr: (kube_deployment_spec_replicas - kube_deployment_status_available_replicas) > 0
    for: 15m
    labels:
      priority: critical
    annotations:
      summary: "Déficit de réplicas pour le déploiement {{ $labels.deployment }}"
      description: "Le déploiement {{ $labels.deployment }} n'arrive pas à maintenir le nombre de réplicas disponibles requis depuis 15 minutes."

Dimensionnement et Dépannage

Le dimensionnement de KSM dépend directement du nombre d'objets présents dans le cluster. Voici des recommandations pour les requêtes de ressources :

Taille du Cluster CPU (Request) Mémoire (Request) Instances
Petit (moins de 50 nœuds) 150m 256Mi 1
Moyen (50 à 250 nœuds) 300m 512Mi 2
Grand (plus de 250 nœuds) 1000m 2Gi 3+ (avec sharding)

En cas d'anomalies de collecte, vérifiez systématiquement les points suivants :

  • Absence de données : Validez les bindings RBAC et assurez-vous que le type de ressource est explicitement inclus dans le drapeau --resources.
  • Latence API : Activez l'option --use-apiserver-cache pour décharger le serveur etcd et réduire la latence de synchronisation.
  • Fuites de mémoire : Surveillez la métrique process_resident_memory_bytes. Un nombre excessif de ConfigMaps ou de Secrets peut saturer le cache interne de l'application.

Extensions et Évolutions Futures

Surveillance des Ressources Personnalisées (CRD)

La fonctionnalité Custom Resource State permet d'étendre KSM pour extraire des métriques à partir de définitions personnalisées. Voici une configuration alternative définissant une jauge basée sur un champ de statut spécifique :

kind: CustomResourceStateMetrics
spec:
  resources:
    - groupVersionKind:
        group: app.example.io
        version: v1beta1
        kind: Microservice
      metrics:
        - name: microservice_active_connections
          help: "Nombre de connexions actives sur le microservice"
          each:
            type: Gauge
            gauge:
              path: [status, currentConnections]
              labelsFromPath:
                service_tier: [metadata, labels, tier]

Les prochaines itérations de l'outil intègrent également une prise en charge native des protocoles OpenTelemetry, facilitant l'exportation des données vers des pipelines d'observabilité unifiés sans nécessiter de proxys de traduction supplémentaires.

Étiquettes: kubernetes kube-state-metrics Prometheus cloud-native Monitoring

Publié le 27 juin à 23h32