Retour à l'accueil

ClickHouse dans Docker : lancez l'analyse en 2 minutes

Guide pour exécuter ClickHouse dans Docker avec quatre scénarios prêts : docker run de base, docker-compose pour le développement avec healthcheck, cluster avec ZooKeeper pour la réplication, stack complète ClickHouse + Kafka + Redis pour l'analyse de jeux. Variables d'environnement, montage de configurations personnalisées, erreurs typiques et leurs solutions sont expliqués.

ClickHouse dans Docker : 4 scénarios prêts avec fichiers compose
Advertisement 728x90

ClickHouse dans Docker : comment j'ai arrêté de m'inquiéter et lancé l'analyse en 2 minutes

Pourquoi Docker — et tout le reste vient après

Je me souviens de la première fois que j'ai configuré ClickHouse en production. Il m'a fallu quatre heures pour configurer les permissions, les limites, éditer manuellement les fichiers de configuration et redémarrer systemd. Un mois plus tard, un nouveau développeur a rejoint l'équipe, et nous avons essayé de reproduire l'environnement sur sa machine — les mêmes écueils.

Docker a tout résolu. Maintenant, j'ai un seul dossier avec docker-compose.yml que je transporte entre les projets. Je lance un cluster d'analyse en une minute, et quand je dois le démonter — docker-compose down -v et c'est propre. Aucun encombrement système.

Ci-dessous, trois scénarios prêts à l'emploi que j'utilise dans des projets réels (d'un projet secondaire de startup à l'analyse de paris). Toutes les configurations sont testées sur Docker Engine 24+.

Google AdInline article slot

Scénario 1. Démarrage rapide : une commande pour tester une hypothèse

Pour le développement local et le prototypage rapide, une seule ligne suffit. Mais pas seulement docker run clickhouse/clickhouse-server — ajoutons ce qui rend ClickHouse utile : le stockage persistant et le mappage de ports.

docker run -d \
  --name clickhouse-dev \
  --restart unless-stopped \
  -p 8123:8123 \
  -p 9000:9000 \
  -v clickhouse-data:/var/lib/clickhouse \
  -v clickhouse-logs:/var/log/clickhouse-server \
  -e CLICKHOUSE_DB=analytics \
  -e CLICKHOUSE_USER=developer \
  -e CLICKHOUSE_PASSWORD=devpass123 \
  -e CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT=1 \
  clickhouse/clickhouse-server:latest

Ce qui est important ici :

  • -v clickhouse-data — un volume nommé, pas un bind mount. La différence : les volumes sont gérés par Docker, ne se perdent pas au redémarrage, et sont plus performants sur macOS (important si vous êtes sur un MacBook — les bind mounts sont lents à cause de la synchronisation).
  • CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT=1 — active le contrôle d'accès. Sans cette variable, l'utilisateur developer est créé mais ne peut pas créer de nouveaux comptes. Nous l'avons appris à nos dépens : en production, nous avons dû entrer dans le conteneur et éditer users.xml.
  • Les ports 9000 (protocole natif) et 8123 (HTTP) — j'ouvre toujours les deux car la moitié des clients (DBeaver, TablePlus) fonctionnent via HTTP, tandis que les applications utilisent le pilote natif.

Vérifiez que ça tourne :

Google AdInline article slot
# Interface HTTP — le test le plus simple
curl "http://localhost:8123/?query=SELECT+version()"
# Sortie : 24.8.2.3

Scénario 2. Docker Compose pour le développement (nœud unique, healthcheck)

Quand le projet devient un peu plus complexe, je passe immédiatement à docker-compose.yml. Ce fichier, je l'utilise sur mes laptops et serveurs de développement :

version: '3.8'

services:
  clickhouse:
    image: clickhouse/clickhouse-server:latest
    container_name: clickhouse-dev
    hostname: clickhouse
    ports:
      - "8123:8123"
      - "9000:9000"
      - "9009:9009"
    volumes:
      - clickhouse-data:/var/lib/clickhouse
      - clickhouse-logs:/var/log/clickhouse-server
      - ./config/config.d:/etc/clickhouse-server/config.d
      - ./config/users.d:/etc/clickhouse-server/users.d
    environment:
      CLICKHOUSE_DB: betting_analytics
      CLICKHOUSE_USER: analyst
      CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD:-analyst123}
      CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT: 1
    ulimits:
      nofile:
        soft: 262144
        hard: 262144
      nproc:
        soft: 32768
        hard: 32768
    healthcheck:
      test: ["CMD", "wget", "--spider", "-q", "http://localhost:8123/ping"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s
    restart: unless-stopped
    networks:
      - analytics-net

networks:
  analytics-net:
    driver: bridge

volumes:
  clickhouse-data:
  clickhouse-logs:

Pourquoi j'ai ajouté ulimits : En production, ClickHouse consomme jusqu'à 262144 descripteurs de fichiers ouverts. Sans cela, sous forte charge, il plantera avec Too many open files. J'ai perdu trois heures là-dessus quand le serveur a commencé à échouer après 2 millions de lignes.

Healthcheck via /ping : ClickHouse a un endpoint /ping intégré (renvoie "Ok." si vivant). C'est mieux que de vérifier via SELECT 1 car cela ne nécessite pas d'authentification et n'écrit pas dans les logs.

Google AdInline article slot

Variable par défaut : ${CLICKHOUSE_PASSWORD:-analyst123} — si non définie dans .env, le mot de passe sera analyst123. N'oubliez pas le fichier .env pour les vrais secrets.

Scénario 3. Configuration proche de la production : ClickHouse + Zookeeper pour la réplication

La réplication des tables ClickHouse nécessite ZooKeeper (ou ClickHouse Keeper, mais je commence avec le classique). J'utilise ce compose pour tester la tolérance aux pannes :

version: '3.8'

services:
  zookeeper:
    image: confluentinc/cp-zookeeper:latest
    container_name: zookeeper
    environment:
      ZOOKEEPER_CLIENT_PORT: 2181
      ZOOKEEPER_TICK_TIME: 2000
    ports:
      - "2181:2181"
    volumes:
      - zookeeper-data:/var/lib/zookeeper
    networks:
      - ch-cluster

  clickhouse-1:
    image: clickhouse/clickhouse-server:latest
    container_name: clickhouse-1
    hostname: clickhouse-1
    ports:
      - "8123:8123"
      - "9000:9000"
    volumes:
      - ch1-data:/var/lib/clickhouse
      - ./config/replicated.xml:/etc/clickhouse-server/config.d/replicated.xml
    environment:
      CLICKHOUSE_DB: bets
      CLICKHOUSE_USER: replicator
      CLICKHOUSE_PASSWORD: rep_pass
      CLICKHOUSE_SHARD: 1
      CLICKHOUSE_REPLICA: 1
    depends_on:
      - zookeeper
    ulimits:
      nofile:
        soft: 262144
        hard: 262144
    networks:
      - ch-cluster

  clickhouse-2:
    image: clickhouse/clickhouse-server:latest
    container_name: clickhouse-2
    hostname: clickhouse-2
    ports:
      - "8124:8123"  # deuxième instance sur un port différent
      - "9001:9000"
    volumes:
      - ch2-data:/var/lib/clickhouse
      - ./config/replicated.xml:/etc/clickhouse-server/config.d/replicated.xml
    environment:
      CLICKHOUSE_DB: bets
      CLICKHOUSE_USER: replicator
      CLICKHOUSE_PASSWORD: rep_pass
      CLICKHOUSE_SHARD: 1
      CLICKHOUSE_REPLICA: 2
    depends_on:
      - zookeeper
    ulimits:
      nofile:
        soft: 262144
        hard: 262144
    networks:
      - ch-cluster

networks:
  ch-cluster:
    driver: bridge

volumes:
  zookeeper-data:
  ch1-data:
  ch2-data:

Et voici le contenu de config/replicated.xml (monté dans les deux conteneurs) :

<clickhouse>
    <zookeeper>
        <node>
            <host>zookeeper</host>
            <port>2181</port>
        </node>
    </zookeeper>
    <remote_servers>
        <replicated_cluster>
            <shard>
                <replica>
                    <host>clickhouse-1</host>
                    <port>9000</port>
                </replica>
                <replica>
                    <host>clickhouse-2</host>
                    <port>9000</port>
                </replica>
            </shard>
        </replicated_cluster>
    </remote_servers>
    <macros>
        <shard>1</shard>
        <replica>${CLICKHOUSE_REPLICA}</replica>
    </macros>
</clickhouse>

Quand c'est vraiment nécessaire : Dans un projet de casino en production, nous avons perdu des données parce que nous gardions tout sur un seul nœud. Après cela, je lance toujours au moins deux conteneurs répliqués pour les tests. La différence de prix est de deux conteneurs au lieu d'un, mais dormir sur ses deux oreilles n'a pas de prix.

Scénario 4. Configuration complète pour les jeux d'argent : ClickHouse + Kafka + Redis

Pour l'analyse des paris en temps réel, j'ai besoin de streaming (Kafka) et de mise en cache (Redis). J'utilise ce compose pour le débogage local du pipeline :

version: '3.8'

services:
  zookeeper-kafka:
    image: confluentinc/cp-zookeeper:latest
    environment:
      ZOOKEEPER_CLIENT_PORT: 2181
      ZOOKEEPER_TICK_TIME: 2000
    ports:
      - "2181:2181"

  kafka:
    image: confluentinc/cp-kafka:latest
    depends_on:
      - zookeeper-kafka
    environment:
      KAFKA_BROKER_ID: 1
      KAFKA_ZOOKEEPER_CONNECT: zookeeper-kafka:2181
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
    ports:
      - "9092:9092"

  redis:
    image: redis:7-alpine
    container_name: redis-cache
    ports:
      - "6379:6379"
    command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD:-cachepass}
    volumes:
      - redis-data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s

  clickhouse:
    image: clickhouse/clickhouse-server:latest
    container_name: clickhouse-betting
    ports:
      - "8123:8123"
      - "9000:9000"
    volumes:
      - clickhouse-betting-data:/var/lib/clickhouse
      - ./clickhouse-kafka.xml:/etc/clickhouse-server/config.d/kafka.xml
    environment:
      CLICKHOUSE_DB: betting
      CLICKHOUSE_USER: streamer
      CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PW:-stream123}
      CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT: 1
    ulimits:
      nofile:
        soft: 262144
        hard: 262144
    depends_on:
      - kafka
      - redis

  kafka-connector:
    image: clickhouse/clickhouse-kafka-connect:latest
    container_name: kafka-connector
    depends_on:
      - kafka
      - clickhouse
    environment:
      CONNECT_BOOTSTRAP_SERVERS: kafka:9092
      CONNECT_GROUP_ID: clickhouse-group
      CONNECT_CONFIG_STORAGE_TOPIC: connect-configs
      CONNECT_OFFSET_STORAGE_TOPIC: connect-offsets
      CONNECT_STATUS_STORAGE_TOPIC: connect-status
      CONNECT_KEY_CONVERTER: org.apache.kafka.connect.storage.StringConverter
      CONNECT_VALUE_CONVERTER: org.apache.kafka.connect.json.JsonConverter
    ports:
      - "8083:8083"

volumes:
  redis-data:
  clickhouse-betting-data:

Comment l'utiliser dans le code applicatif :

# Exemple en Python : lire un pari depuis Redis (cache), écrire dans ClickHouse
import redis
from kafka import KafkaProducer
import json

r = redis.Redis(host='localhost', port=6379, password='cachepass', decode_responses=True)
producer = KafkaProducer(bootstrap_servers='localhost:9092', value_serializer=lambda v: json.dumps(v).encode())

# Vérification des doublons (détection de fraude)
bet_id = "bet_12345"
if r.setnx(bet_id, "processed"):
    bet_event = {"user_id": 101, "amount": 500, "odds": 2.1}
    producer.send('bets-stream', bet_event)
else:
    print(f"Pari en double {bet_id} bloqué")

Comment monter votre propre config.xml sans tout casser

Une erreur que j'ai faite cinq fois : monter un config.xml complet, pour découvrir qu'une nouvelle version de ClickHouse avait ajouté des sections obligatoires. Le conteneur plantait avec Config has no <logger>.

La bonne approche : placer uniquement les surcharges dans config.d/. Voici une structure qui fonctionne :

docker-clickhouse/
├── docker-compose.yml
├── .env
├── config/
│   ├── config.d/
│   │   ├── memory.xml
│   │   ├── networks.xml
│   │   └── query-log.xml
│   └── users.d/
│       └── profiles.xml

Exemple config/config.d/memory.xml :

<clickhouse>
    <max_server_memory_usage>0.75</max_server_memory_usage>
    <max_memory_usage_for_all_queries>0</max_memory_usage_for_all_queries>
    <background_pool_size>16</background_pool_size>
</clickhouse>

Exemple config/users.d/profiles.xml :

<clickhouse>
    <profiles>
        <default>
            <max_memory_usage>10000000000</max_memory_usage>
            <timeout_before_checking_execution_speed>0</timeout_before_checking_execution_speed>
        </default>
        <analyst>
            <readonly>1</readonly>
            <max_execution_time>300</max_execution_time>
        </analyst>
    </profiles>
</clickhouse>

Travailler avec clickhouse-client à l'intérieur d'un conteneur

Entrer dans un conteneur pour des requêtes rapides est normal. Mais pas via docker exec -it bash — faites-le directement :

# Exécuter une requête
docker exec -it clickhouse-dev clickhouse-client --query "SELECT count() FROM system.tables"

# Mode interactif
docker exec -it clickhouse-dev clickhouse-client

# Avec mot de passe
docker exec -it clickhouse-dev clickhouse-client --password devpass123

Mon astuce : Ajouter un alias dans ~/.bashrc :

alias ch-cli='docker exec -it clickhouse-dev clickhouse-client'

Après cela, tapez simplement ch-cli et travaillez comme s'il s'agissait d'une base de données locale.

Variables d'environnement : ce qui fonctionne réellement

L'image officielle ne supporte pas toutes les variables promises sur les forums. Voici celles qui sont testées :

Variable Objectif Exemple
CLICKHOUSE_DB Nom de la base de données par défaut analytics
CLICKHOUSE_USER Utilisateur administrateur prod_user
CLICKHOUSE_PASSWORD Mot de passe strongpass
CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT Activer RBAC (1/0) 1

Ce qui NE FONCTIONNE PAS : CLICKHOUSE_HTTP_PORT, CLICKHOUSE_TCP_PORT — le point d'entrée les ignore. Changez les ports via ports: dans compose ou en montant une configuration.

Health Check : ma checklist complète

Après avoir démarré un compose, j'exécute :

# 1. Ping HTTP (doit renvoyer "Ok.")
curl http://localhost:8123/ping

# 2. Version via HTTP
curl "http://localhost:8123/?query=SELECT+version()"

# 3. Créer une table de test
docker exec -it clickhouse-dev clickhouse-client --query "CREATE TABLE test.t (id UInt64) ENGINE = MergeTree ORDER BY id"

# 4. Insérer et sélectionner
docker exec -it clickhouse-dev clickhouse-client --query "INSERT INTO test.t SELECT number FROM numbers(1000)"
docker exec -it clickhouse-dev clickhouse-client --query "SELECT count() FROM test.t"

# 5. HTTP avec authentification (si mot de passe défini)
curl -u developer:devpass123 "http://localhost:8123/?query=SELECT+user()"

Que faire si ça ne marche pas — erreurs Docker courantes

Erreur : Code: 210. DB::NetException: Connection refused Solution : Le conteneur n'a pas encore démarré. Ajoutez depends_on et healthcheck, ou faites sleep 5 dans les scripts.

Erreur : Cannot create directory /var/lib/clickhouse: Permission denied Solution : Sur un hôte avec SELinux, ajoutez :Z au volume : -v ./data:/var/lib/clickhouse:Z. Ou utilisez des volumes nommés.

Erreur : Max connections limit reached Solution : Augmentez dans la configuration : <max_connections>4096</max_connections> et redémarrez.

Le conteneur consomme toute la mémoire de l'hôte Solution : Limitez via Docker :

docker update --memory=4g --memory-swap=4g clickhouse-dev

Ou dans compose :

deploy:
  resources:
    limits:
      memory: 4G

Conclusion : quand utiliser Docker et quand ne pas l'utiliser

Docker est idéal pour ClickHouse en développement, staging et petites productions. Mais si vous avez un cluster de 10+ nœuds avec 100 To de données — les paquets natifs sans couches supplémentaires sont meilleurs.

Pour l'instant, prenez mon compose de configuration pour les jeux d'argent, changez les mots de passe, et commencez à compter les paris en temps réel.

Toutes les configurations proviennent de projets réels. Les noms ont été changés, les pièges demeurent.


Précédente:
Suivante: Client ClickHouse : Comment j'ai apprivoisé la console et l'API HTTP dans un projet de paris

— Editorial Team

Advertisement 728x90

Lire ensuite