Aller au contenu principal

Dimensionnement et durcissement de ClickHouse

Recommandations matérielles

Utilisez le tableau ci-dessous comme point de départ pour dimensionner le compute, en fonction du nombre d'endpoints actifs (machines) qui remontent des données :

PalierEndpointsCœurs ClickHouseRAM ClickHouseVolume de cache
Small~100416 GiB30Gi
Medium~1 000624 GiB50Gi
Large~10 0008+32 GiB+100Gi

Le CPU est le principal levier pour les requêtes d'agrégation les plus lourdes : augmentez le nombre de cœurs avec le volume de données plutôt que de sur-provisionner uniquement la RAM. Commencez avec 4 GiB de RAM par cœur (le ratio « général » recommandé par ClickHouse), et passez à 8 GiB de RAM par cœur (leur ratio « data warehousing ») si vous observez un débordement externe de GROUP BY/ORDER BY ou des rejets de requêtes pour limite de mémoire en charge normale : les deux sont des signes concrets que la RAM, et non le CPU, est le véritable goulot d'étranglement de votre charge d'agrégation (voir Métriques clés pour les compteurs exacts à surveiller).

  • Type de nœud : un type d'instance à usage général ou optimisé pour la mémoire avec 4 à 8 GiB de RAM par cœur. Évitez les types d'instances optimisés pour le calcul : ils ont tendance à être sous-provisionnés en RAM pour les charges d'agrégation de ClickHouse.
  • Disque : utilisez du NVMe local ou du SSD attaché au réseau (IOPS élevées) pour les volumes de métadonnées et de cache. N'utilisez jamais de stockage de type HDD ni de systèmes de fichiers réseau (NFS/EFS/Azure Files) ; leurs caractéristiques de latence sont incompatibles avec les schémas de lecture/écriture de ClickHouse.
  • Nœud dédié et stable : planifiez ClickHouse sur un nœud dédié et on-demand (pas spot/preemptible) pour éviter toute perturbation liée à la récupération de nœud. Évitez de co-localiser des charges de type « noisy-neighbor ».
  • Stockage objet : élastique par conception, ce n'est pas un facteur de dimensionnement comme le disque local, car il s'adapte indépendamment de votre cluster.

Cache du système de fichiers

Le cache local du système de fichiers se place devant le stockage objet et n'a besoin de contenir que votre working set (les données réellement sollicitées par les requêtes récentes), pas l'intégralité de votre jeu de données historique. Étant donné que les fonctionnalités d'analyse ne lisent généralement que l'état actuel par machine ou par compte, les données historiques remplacées ne gonflent pas le working set même lorsque votre empreinte totale de stockage objet augmente : le cache suit votre nombre d'endpoints actifs, pas l'ensemble de votre historique accumulé.

Cela dit, le cache reste l'un des leviers les plus efficaces pour la performance des requêtes, et il est toujours préférable de le surdimensionner que de le sous-dimensionner : l'agrandir plus tard implique de redimensionner le template de persistent volume claim d'un StatefulSet, une opération que Kubernetes ne prend pas en charge en place (voir la mise en garde ci-dessous).

Recommandations pour dimensionner le cache :

  • Le chart définit par défaut clickhouse.serverConfig.CACHE_MAX_SIZE à 48Gi, adossé à un persistent volume de cache de 50Gi (le palier Medium ci-dessus) ; le même réglage s'applique quel que soit le fournisseur de stockage objet que vous utilisez. Utilisez la colonne Volume de cache du tableau ci-dessus comme point de départ pour votre propre palier, en associant CACHE_MAX_SIZE à environ 2Gi en dessous de la taille du volume (voir les recommandations de marge ci-dessous). Ajustez-le à la hausse en fonction de la latence de lecture observée : des lectures constamment lentes sur des données précédemment interrogées indiquent que le cache est sous-dimensionné par rapport à votre working set.
  • Réexaminez ce dimensionnement à mesure que votre nombre d'endpoints augmente.
Redimensionner le volume de cache ultérieurement est une opération manuelle

Kubernetes ne permet pas de redimensionner en place le template de persistent volume claim d'un StatefulSet, donc agrandir le volume de cache après l'installation (bien que tout à fait possible) nécessite une procédure manuelle, et non un simple changement de valeurs. Dimensionnez dès le départ à l'aide du tableau ci-dessus en fonction du nombre d'endpoints attendu, plutôt que de commencer petit en comptant redimensionner facilement plus tard. Si votre environnement est contraint en ressources, nous recommandons tout de même de ne pas descendre en dessous de 30Gi (le palier Small) pour le volume de cache.

Mesurez le taux de succès réel du cache

system.events expose les compteurs bruts de succès/échec derrière le cache, un signal plus précis que la latence seule :

SELECT round(sumIf(value, event = 'CachedReadBufferReadFromCacheHits') / (sumIf(value, event = 'CachedReadBufferReadFromCacheHits') + sumIf(value, event = 'CachedReadBufferReadFromCacheMisses')), 4) AS cache_hit_ratio FROM system.events;

Un taux constamment bas est un signal concret pour augmenter CACHE_MAX_SIZE et le persistent volume de cache ci-dessus.

clickhouse:
serverConfig:
CACHE_MAX_SIZE: '98Gi' # pairs with a 100Gi cache volume from the Large tier above
Dimensionnez le persistent volume de cache plus grand que CACHE_MAX_SIZE

CACHE_MAX_SIZE est une limite souple que ClickHouse applique lui-même via l'éviction LRU, et non une garantie stricte contre le remplissage du volume sous-jacent. Donnez toujours au persistent volume une marge au-dessus, ne le dimensionnez jamais égal ou inférieur : par exemple, CACHE_MAX_SIZE: '28Gi' sur un volume de 30Gi convient.

Si le volume se remplit malgré tout, ClickHouse ne fait pas échouer les requêtes ni les insertions ; il journalise un avertissement et ignore la mise en cache de cette entrée, se rabattant sur une lecture directe depuis le stockage objet. Laissé sans réponse, cela dégrade toutefois de façon permanente les lectures sur ces données vers le chemin le plus lent au lieu du chemin mis en cache.

Volume de métadonnées

Contrairement au cache, ce volume n'est pas dimensionné en fonction de votre working set : sa propre empreinte augmente à peine avec le volume de données. Les métadonnées locales du disque S3 (un fichier de mapping par objet stocké, quelques dizaines d'octets chacun) et les définitions de tables restent de l'ordre de quelques mégaoctets, même à grande échelle, quel que soit le nombre d'endpoints ou la rétention.

Commencez tout de même avec un minimum de 20Gi. L'empreinte elle-même n'a pas besoin d'autant, mais c'est une marge bon marché face aux hardlinks FREEZE du sidecar de sauvegarde, qui coexistent brièvement sur ce même volume pendant un cycle de sauvegarde (voir Sauvegarde et restauration), et à toute table système locale que vous réactiveriez au-delà des valeurs par défaut du chart.

Ce qui se passe si ce volume se remplit

Contrairement au disque de cache, ClickHouse ne se dégrade pas gracieusement ici : c'est un volume porteur, pas un cache best-effort. S'il se remplit, les insertions et les merges échouent avec de vraies erreurs au lieu de se rabattre ailleurs. Réservez vous-même de l'espace libre sur ce disque, via keep_free_space_bytes du disque <default> dans votre propre 00-object-storage.xml (voir Stockage ClickHouse), afin que ClickHouse refuse les nouvelles écritures de façon prévisible dès que l'espace vient à manquer, au lieu d'épuiser le disque jusqu'à 0 octet libre.

Planification et fiabilité

Le chart applique par défaut un ensemble de garde-fous, afin que ClickHouse échoue de façon prévisible (rejets propres) au lieu de déstabiliser le nœud en charge. Ceux ci-dessous nécessitent une intervention de votre part ; voir Autres garde-fous pour le reste.

Ressources réservées

Définissez requests égal à limits pour le CPU et la mémoire du conteneur ClickHouse principal, afin que le planificateur réserve la totalité dès le départ au lieu de sur-engager le nœud. Plafonnez le CPU en dessous de la capacité allouable du nœud pour laisser une marge au kubelet. Ajustez les valeurs ci-dessous pour correspondre à votre palier de dimensionnement :

clickhouse-server:
resources:
requests:
cpu: 8
memory: 32Gi
limits:
cpu: 8
memory: 32Gi

Le sidecar clickhouse-backup dans le même pod fonctionne sur son propre budget, séparé (et plus petit), de sorte que le pod dans son ensemble n'atteint pas la classe QoS Guaranteed de Kubernetes. Ceci fixe uniquement les ressources du conteneur principal.

Stabilité du nœud

Vous devez empêcher votre autoscaler Kubernetes, le cas échéant, de consolider ou d'évincer le nœud pendant que ClickHouse s'y exécute, ce qui détacherait et rattacherait sinon son volume en pleine opération :

clickhouse-server:
podAnnotations:
karpenter.sh/do-not-disrupt: 'true' # adjust the annotation for your own cluster autoscaler if not using Karpenter

Dans la mesure du possible, épinglez ClickHouse sur un ou des nœud(s) dédié(s) et on-demand qui lui sont réservés : cela l'empêche d'utiliser de la capacité spot/preemptible (un nœud spot récupéré signifie une éviction brutale et un rattachement de volume) et l'éloigne des charges « noisy-neighbor » qui se disputent le même CPU/mémoire :

clickhouse-server:
nodeSelector:
workload: clickhouse
tolerations:
- key: workload
operator: Equal
value: clickhouse
effect: NoSchedule

Ceci suppose un pool de nœuds dédié que vous avez provisionné en on-demand (pas spot/preemptible), étiqueté workload: clickhouse et taché workload=clickhouse:NoSchedule ; ajustez le label et le taint selon la convention de votre propre cluster. Le nodeSelector épingle uniquement ClickHouse à ce pool, il ne sélectionne pas le type de capacité du pool, donc le maintenir hors du spot dépend du fait que le pool lui-même soit on-demand.

Sur un nœud dédié et non partagé, vous pouvez aussi attribuer au pod ClickHouse un priorityClassName élevé. Là, c'est surtout de la ceinture-et-bretelles (rien d'important ne se dispute le nœud), mais c'est un garde-fou de durcissement supplémentaire sans coût : même un nœud dédié exécute encore des DaemonSets (CNI, agents de logs/métriques), et la priorité détermine qui le kubelet évince en premier si le nœud subit une pression sur les ressources.

infrastructure-critical n'est pas une classe Kubernetes intégrée (les seules natives, system-node-critical et system-cluster-critical, sont réservées aux pods du control-plane/système), vous devez donc la créer au préalable dans votre cluster (le chart ne le fait pas) :

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: infrastructure-critical
value: 1000000 # above your default workloads, well below the system-* classes (~2e9)
globalDefault: false
description: 'High scheduling priority for infrastructure-critical workloads'

Puis référencez-la sur le pod ClickHouse :

clickhouse-server:
priorityClassName: infrastructure-critical
Assurez-vous que votre pool de nœuds réservé a de la capacité dans la zone du PV

Les volumes de métadonnées et de cache (SSD attaché au réseau) sont verrouillés en zone une fois provisionnés. Si le nœud de ClickHouse est perdu et que votre pool de nœuds réservé ne peut pas provisionner de capacité de remplacement dans cette même zone, le pod reste Pending indéfiniment : il ne peut pas se replanifier sur un nœud d'une autre zone sans nouveau volume. Assurez-vous que le pool de nœuds que vous dédiez à ClickHouse couvre (ou peut monter en charge dans) chaque zone dans laquelle votre cluster provisionne des volumes.

Autres garde-fous

Au-delà de la planification au niveau du nœud, le chart configure également par défaut un ensemble de garde-fous côté serveur (limites de mémoire et de concurrence, marge disque, seuils de débordement et de timeout des requêtes, et déduplication des insertions) afin que ClickHouse échoue de façon prévisible au lieu de se déstabiliser en charge. Ceux-ci sont livrés avec des valeurs par défaut raisonnables et ne nécessitent aucune configuration de votre part.

Si vous observez des symptômes tels qu'un débordement disque, une pression mémoire ou des rejets de requêtes en cas de forte concurrence sur le dashboard, veuillez contacter notre équipe de support pour évaluer une configuration mieux adaptée à votre charge.