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+.
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'utilisateurdeveloperest 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 éditerusers.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 :
# 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.
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: Installer ClickHouse sur Ubuntu/Debian : Guide pas à pas par quelqu'un qui s'est brûlé avec les mauvaises permissions
→ Suivante: Client ClickHouse : Comment j'ai apprivoisé la console et l'API HTTP dans un projet de paris
— Editorial Team
Aucun commentaire pour le moment.