ClickHouse in Docker: Wie ich aufhörte, mir Sorgen zu machen und Analysen in 2 Minuten startete
Warum Docker – und alles andere kommt danach
Ich erinnere mich an das erste Mal, als ich ClickHouse in der Produktion eingerichtet habe. Es dauerte vier Stunden, um Berechtigungen, Limits und Konfigurationen manuell zu bearbeiten und systemd neu zu starten. Einen Monat später kam ein neuer Entwickler dazu, und wir versuchten, die Umgebung auf seinem Rechner zu replizieren – die gleichen alten Fallstricke.
Docker hat alles gelöst. Jetzt habe ich einen einzigen Ordner mit docker-compose.yml, den ich zwischen Projekten mitnehme. Ich starte einen Analyse-Cluster in einer Minute, und wenn ich ihn abbauen muss – docker-compose down -v und alles ist sauber. Kein Systemmüll.
Im Folgenden finden Sie drei fertige Szenarien, die ich in echten Projekten verwende (von einem Startup-Seitenprojekt bis hin zu Wettanalysen). Alle Konfigurationen sind mit Docker Engine 24+ getestet.
Szenario 1. Schnellstart: Ein Befehl, um eine Hypothese zu testen
Für die lokale Entwicklung und schnelles Prototyping reicht eine einzige Zeile. Aber nicht nur docker run clickhouse/clickhouse-server – fügen wir hinzu, was ClickHouse nützlich macht: persistenten Speicher und Portzuordnung.
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
Was hier wichtig ist:
-v clickhouse-data– ein benanntes Volume, kein Bind Mount. Der Unterschied: Volumes werden von Docker verwaltet, gehen beim Neustart nicht verloren und sind unter macOS leistungsfähiger (wichtig, wenn Sie auf einem MacBook arbeiten – Bind Mounts sind aufgrund der Synchronisation langsam).CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT=1– aktiviert die Zugriffskontrolle. Ohne diese Variable wird der Benutzerdevelopererstellt, kann aber keine neuen Konten anlegen. Das haben wir auf die harte Tour gelernt: In der Produktion mussten wir in den Container gehen undusers.xmlbearbeiten.- Ports 9000 (natives Protokoll) und 8123 (HTTP) – ich öffne immer beide, weil die Hälfte der Clients (DBeaver, TablePlus) über HTTP arbeiten, während Anwendungen den nativen Treiber verwenden.
Überprüfen, ob es läuft:
# HTTP-Schnittstelle – der einfachste Test
curl "http://localhost:8123/?query=SELECT+version()"
# Ausgabe: 24.8.2.3
Szenario 2. Docker Compose für die Entwicklung (Einzelknoten, Healthcheck)
Wenn das Projekt etwas komplexer wird, wechsle ich sofort zu docker-compose.yml. Diese Datei verwende ich auf meinen Laptops und Entwicklungsservern:
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:
Warum ich ulimits hinzugefügt habe: In der Produktion verbraucht ClickHouse bis zu 262144 offene Dateideskriptoren. Ohne dies stürzt es unter hoher Last mit Too many open files ab. Ich habe einmal drei Stunden damit verschwendet, als der Server nach 2 Millionen Zeilen ausfiel.
Healthcheck über /ping: ClickHouse hat einen integrierten /ping-Endpunkt (gibt "Ok." zurück, wenn es läuft). Das ist besser als die Überprüfung über SELECT 1, da es keine Authentifizierung erfordert und keine Logs schreibt.
Standardvariable: ${CLICKHOUSE_PASSWORD:-analyst123} – wenn nicht in .env gesetzt, lautet das Passwort analyst123. Vergessen Sie nicht die .env-Datei für echte Geheimnisse.
Szenario 3. Produktionsähnliches Setup: ClickHouse + Zookeeper für Replikation
Die Tabellenreplikation von ClickHouse erfordert ZooKeeper (oder ClickHouse Keeper, aber ich beginne mit dem Klassiker). Ich verwende dieses Compose zum Testen der Ausfallsicherheit:
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" # zweite Instanz auf einem anderen Port
- "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:
Und hier der Inhalt von config/replicated.xml (in beide Container eingehängt):
<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>
Wann dies wirklich nötig ist: In einem Produktions-Casino-Projekt haben wir Daten verloren, weil wir alles auf einem einzigen Knoten gehalten haben. Danach starte ich immer mindestens zwei replizierte Container zum Testen. Der Preisunterschied sind zwei Container statt einem, aber gut schlafen ist unbezahlbar.
Szenario 4. Vollständiges Glücksspiel-Setup: ClickHouse + Kafka + Redis
Für Echtzeit-Wettanalysen benötige ich Streaming (Kafka) und Caching (Redis). Ich verwende dieses Compose zum lokalen Debuggen der 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:
So verwenden Sie dies im Anwendungscode:
# Beispiel in Python: Wette aus Redis lesen (Cache), in ClickHouse schreiben
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())
# Duplikatsprüfung (Betrugserkennung)
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"Doppelte Wette {bet_id} blockiert")
So hängen Sie Ihre eigene config.xml ein, ohne alles zu zerstören
Ein Fehler, den ich fünfmal gemacht habe: Eine vollständige config.xml einzuhängen, nur um festzustellen, dass eine neue ClickHouse-Version obligatorische Abschnitte hinzugefügt hat. Der Container stürzte mit Config has no <logger> ab.
Der richtige Ansatz: Nur Überschreibungen in config.d/ ablegen. Hier ist eine Struktur, die funktioniert:
docker-clickhouse/
├── docker-compose.yml
├── .env
├── config/
│ ├── config.d/
│ │ ├── memory.xml
│ │ ├── networks.xml
│ │ └── query-log.xml
│ └── users.d/
│ └── profiles.xml
Beispiel 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>
Beispiel 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>
Arbeiten mit clickhouse-client in einem Container
In einen Container springen, um schnelle Abfragen durchzuführen, ist normal. Aber nicht über docker exec -it bash – machen Sie es direkt:
# Eine Abfrage ausführen
docker exec -it clickhouse-dev clickhouse-client --query "SELECT count() FROM system.tables"
# Interaktiver Modus
docker exec -it clickhouse-dev clickhouse-client
# Mit Passwort
docker exec -it clickhouse-dev clickhouse-client --password devpass123
Mein Lifehack: Fügen Sie einen Alias zu ~/.bashrc hinzu:
alias ch-cli='docker exec -it clickhouse-dev clickhouse-client'
Danach einfach ch-cli eingeben und arbeiten, als wäre es eine lokale Datenbank.
Umgebungsvariablen: Was wirklich funktioniert
Das offizielle Image unterstützt nicht alle Variablen, die in Foren versprochen werden. Hier sind die getesteten:
| Variable | Zweck | Beispiel |
|---|---|---|
CLICKHOUSE_DB |
Standard-Datenbankname | analytics |
CLICKHOUSE_USER |
Admin-Benutzer | prod_user |
CLICKHOUSE_PASSWORD |
Passwort | strongpass |
CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT |
RBAC aktivieren (1/0) | 1 |
Was NICHT funktioniert: CLICKHOUSE_HTTP_PORT, CLICKHOUSE_TCP_PORT – der Entrypoint ignoriert sie. Ändern Sie Ports über ports: in Compose oder durch Einhängen einer Konfiguration.
Health Check: Meine vollständige Checkliste
Nach dem Starten eines Compose führe ich Folgendes aus:
# 1. HTTP-Ping (sollte "Ok." zurückgeben)
curl http://localhost:8123/ping
# 2. Version über HTTP
curl "http://localhost:8123/?query=SELECT+version()"
# 3. Testtabelle erstellen
docker exec -it clickhouse-dev clickhouse-client --query "CREATE TABLE test.t (id UInt64) ENGINE = MergeTree ORDER BY id"
# 4. Einfügen und auswählen
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 mit Authentifizierung (falls Passwort gesetzt)
curl -u developer:devpass123 "http://localhost:8123/?query=SELECT+user()"
Was tun, wenn es nicht funktioniert – Häufige Docker-Fehler
Fehler: Code: 210. DB::NetException: Connection refused
Lösung: Der Container wurde noch nicht gestartet. Fügen Sie depends_on und healthcheck hinzu oder verwenden Sie sleep 5 in Skripten.
Fehler: Cannot create directory /var/lib/clickhouse: Permission denied
Lösung: Fügen Sie auf einem Host mit SELinux :Z zum Volume hinzu: -v ./data:/var/lib/clickhouse:Z. Oder verwenden Sie benannte Volumes.
Fehler: Max connections limit reached
Lösung: In der Konfiguration erhöhen: <max_connections>4096</max_connections> und neu starten.
Container-Speicher frisst den Host auf Lösung: Über Docker begrenzen:
docker update --memory=4g --memory-swap=4g clickhouse-dev
Oder in Compose:
deploy:
resources:
limits:
memory: 4G
Fazit: Wann Docker verwenden und wann nicht
Docker ist ideal für ClickHouse in Entwicklung, Staging und kleinen Produktionsumgebungen. Aber wenn Sie einen Cluster mit 10+ Knoten und 100 TB Daten haben – native Pakete ohne zusätzliche Schichten sind besser.
Nehmen Sie vorerst mein Glücksspiel-Setup-Compose, ändern Sie die Passwörter und beginnen Sie, Wetten in Echtzeit zu zählen.
Alle Konfigurationen stammen aus echten Projekten. Namen geändert, Fallstricke bleiben.
← Vorherige: ClickHouse unter Ubuntu/Debian installieren: Eine Schritt-für-Schritt-Anleitung von jemandem, der sich an falschen Berechtigungen verbrannt hat
→ Nächste: ClickHouse-Client: Wie ich mich mit der Konsole und der HTTP-API in einem Glücksspielprojekt anfreundete
— Editorial Team
Noch keine Kommentare.