Aller au contenu principal

Dimensionnement et durcissement de ClickHouse

Recommandations matérielles​

Utilisez le tableau ci-dessous comme point de départ pour le dimensionnement du calcul, 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 les cœurs avec le volume de données plutôt que de surdimensionner la RAM seule. Commencez avec 4 GiB de RAM par cœur (le ratio « general purpose » de ClickHouse), et passez à 8 GiB de RAM par cœur (leur ratio « data warehousing ») si vous observez un débordement (spill) 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, constitue 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 tendent à ê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 les perturbations dues à la récupération de nœud. Évitez de co-localiser des charges de type « voisin bruyant ».
  • Stockage objet : élastique par conception, ce n'est pas un facteur de dimensionnement comme l'est le disque local, puisqu'il s'échelonne 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 touchées par les requêtes récentes), pas l'ensemble de votre jeu de données historique. Comme les fonctionnalités d'analytique lisent généralement uniquement l'état actuel par machine ou par compte, les données historiques remplacées ne gonflent pas le working set même à mesure que votre empreinte totale de stockage objet augmente : le cache suit votre nombre d'endpoints actifs, pas votre historique total 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 modèle de revendication de volume persistant (persistent volume claim template) d'un StatefulSet, une opération que Kubernetes ne prend pas en charge en place (voir l'avertissement ci-dessous).

Dimensionnez clickhouse.cache.size, le volume persistant du cache. Utilisez la colonne Volume de cache du tableau ci-dessus comme point de départ pour votre propre palier. Ajustez-la à la hausse en fonction de la latence de lecture observée : des lectures constamment lentes sur des données déjà interrogées sont un signe que le cache est sous-dimensionné par rapport à votre working set. Réévaluez ce dimensionnement à mesure que votre nombre d'endpoints augmente.

clickhouse:
cache:
size: 100Gi # Large tier from the table above; cacheMaxSize auto-derives to 98Gi
Redimensionner le volume de cache ultérieurement est une opération manuelle

Kubernetes ne permet pas de redimensionner en place le modèle de revendication de volume persistant 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 en amont à l'aide du tableau ci-dessus en fonction de votre nombre d'endpoints attendu, plutôt que de commencer petit en espérant redimensionner facilement plus tard. Si votre environnement est contraint en ressources, nous recommandons quand même de ne pas descendre en dessous de 30Gi (le palier Small) pour le volume de cache.

Mesurez le taux de hit réel du cache

system.events expose les compteurs bruts de hits/miss 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 ratio constamment faible est un signal concret pour augmenter clickhouse.cache.size ci-dessus.

Ne définissez explicitement clickhouse.serverConfig.cacheMaxSize que si vous avez besoin d'une valeur autre que celle dérivée, par exemple pour laisser une marge de sécurité plus large que 2Gi :

clickhouse:
serverConfig:
cacheMaxSize: '90Gi' # explicit override, wider margin against a 100Gi cache volume than the 2Gi default
Gardez cacheMaxSize en dessous de la taille du volume persistant du cache

cacheMaxSize 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. Laissez toujours une marge sur le volume persistant au-dessus de cette valeur, ne la dimensionnez jamais égale ou inférieure. La sortie d'installation/mise à niveau du chart lui-même vous avertit si un cacheMaxSize explicite laisse moins que la marge recommandée de 2Gi par rapport à clickhouse.cache.size ; la valeur par défaut auto-dérivée la respecte toujours.

Si le volume se remplit malgré tout, ClickHouse ne fait pas échouer les requêtes ou les insertions ; il consigne un avertissement et ignore la mise en cache de cette entrée, en se rabattant sur une lecture directe depuis le stockage objet. Si rien n'est fait, cela dégrade toutefois de façon permanente les lectures de 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é par rapport à votre working set : sa propre empreinte croît à 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 peu coûteuse contre les hardlinks FREEZE du sidecar de sauvegarde, qui coexistent brièvement sur ce même volume durant un cycle de sauvegarde (voir Sauvegarde et restauration), ainsi que contre 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 : il s'agit d'un volume porteur de charge, pas d'un cache au mieux (best-effort). S'il se remplit, les insertions et les fusions (merges) échouent avec de vraies erreurs au lieu de se rabattre ailleurs.

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) plutôt que de déstabiliser le nœud en charge. Ceux ci-dessous nécessitent une intervention de votre part ; voir Autres garde-fous pour les autres.

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é en amont au lieu de sur-engager le nœud. Plafonnez le CPU en dessous de la capacité allouable du nœud pour laisser de la marge au kubelet. Ajustez les valeurs ci-dessous pour correspondre à votre palier de dimensionnement :

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

Le sidecar clickhouse-backup dans le même pod fonctionne avec son propre budget distinct (et plus petit) (clickhouse.backup.sidecar.resources), de sorte que le pod dans son ensemble n'atteint pas la classe QoS Guaranteed de Kubernetes. Ceci épingle 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 tandis que ClickHouse y est en cours d'exécution, ce qui détacherait et rattacherait sinon son volume en pleine opération :

clickhouse:
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 plusieurs nœuds dédiés, on-demand, qui lui sont réservés : cela le maintient hors de la capacité spot/preemptible (un nœud spot récupéré signifie une éviction abrupte et un rattachement de volume) et à l'écart des charges de type « voisin bruyant » qui rivalisent pour le même CPU/mémoire :

clickhouse:
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é (tainted) workload=clickhouse:NoSchedule ; ajustez le label et le taint pour correspondre à la convention de votre propre cluster. Le nodeSelector épingle uniquement ClickHouse sur 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 en grande partie une ceinture et des bretelles (rien d'important ne rivalise pour le nœud), mais c'est un garde-fou de durcissement supplémentaire à coût nul : même un nœud dédié exécute encore des DaemonSets (CNI, agents de logs/métriques), et la priorité décide qui le kubelet évince en premier si le nœud subit une pression sur les ressources.

infrastructure-critical n'est pas une classe intégrée de Kubernetes (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:
priorityClassName: infrastructure-critical
Assurez-vous que votre pool de nœuds réservé dispose de capacité dans la zone du PV

Les volumes de métadonnées et de cache (SSD attaché au réseau) sont verrouillés sur une 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 dans une zone différente sans un nouveau volume. Assurez-vous que le pool de nœuds que vous dédiez à ClickHouse couvre (ou peut s'étendre à) chaque zone dans laquelle votre cluster provisionne des volumes.

Autres garde-fous​

Au-delà de la planification au niveau des nœuds, le chart configure également par défaut un ensemble de garde-fous côté serveur (limites de mémoire et de concurrence, marge de disque, seuils de débordement (spill) et de délai d'expiration 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. Ils sont fournis avec des valeurs par défaut raisonnables et ne nécessitent aucune configuration de votre part.

Si vous observez des symptômes tels que du débordement disque (spill), une pression mémoire ou des rejets de requêtes sous une forte concurrence du dashboard, veuillez contacter notre équipe de support pour évaluer une configuration mieux adaptée à votre charge.