Cấu hình ClickHouse HTTP Interface và JDBC Driver
Bước đầu tiên là mở cổng HTTP của ClickHouse để các công cụ như Grafana và API Gateway có thể truy vấn trực tiếp qua HTTP/JSON, thay vì chỉ dùng giao thức native.
Chỉnh sửa file cấu hình /etc/clickhouse-server/config.d/custom.xml trên pod ClickHouse để kích thích HTTP server và cấu hình user mặc định.
cat > /etc/clickhouse-server/config.d/custom.xml
Sau khi áp dụng cấu hình, ClickHouse sẽ lắng nghe trên cổng 8123. Bạn có thể kiểm tra bằng cách curl vào địa chỉ IP của ClickHouse.
curl -s http://:8123/?query=SELECT%201
Kết quả mong đợi: Trả về chuỗi 1 hoặc 1\n. Nếu thấy lỗi Connection refused, cần kiểm tra lại Service của Kubernetes đã expose cổng 8123 chưa.
Để các ứng dụng Java hoặc Python truy vấn ClickHouse hiệu quả, cần chuẩn bị JDBC driver. Chúng ta sẽ sử dụng thư viện clickhouse-jdbc chính chủ.
Tạo file pom.xml hoặc requirements.txt tùy theo ngôn ngữ backend của bạn để thêm dependency.
<dependency>
<groupId>com.clickhouse</groupId>
<artifactId>clickhouse-jdbc</artifactId>
<version>0.5.0</version>
<classifier>jre11</classifier>
</dependency>
Khi khởi tạo connection trong code, sử dụng URL format chuẩn: jdbc:clickhouse://:8123/?user=default&password=.
Verify kết quả: Chạy một đoạn script Java nhỏ hoặc dùng DBeaver để kết nối. Nếu kết nối thành công, bạn sẽ thấy list database và table hiện ra mà không bị lỗi timeout.
Triển khai Grafana để trực quan hóa dữ liệu Real-time
Chúng ta sẽ deploy Grafana vào Kubernetes và cấu hình datasource để kết nối trực tiếp với ClickHouse đã triển khai ở phần trước.
Sử dụng Helm chart chính thức của Grafana để triển khai nhanh chóng và chuẩn hóa.
helm repo add grafana https://grafana.github.io/helm-charts
helm repo update
helm install grafana grafana/grafana --set service.type=LoadBalancer --set adminPassword=admin123
Lệnh trên sẽ deploy Grafana với service type LoadBalancer để expose ra ngoài, đồng thời đặt mật khẩu admin mặc định.
Sau khi pod Grafana chạy, cần tạo ConfigMap để cấu hình Datasource ClickHouse tự động (Provisioning), giúp dữ liệu xuất hiện ngay khi Grafana khởi động.
Tạo file datasources.yaml với nội dung cấu hình datasource ClickHouse.
cat > /tmp/grafana-datasources.yaml
Áp dụng config này vào Kubernetes dưới dạng ConfigMap và mount vào pod Grafana.
kubectl create configmap grafana-datasources --from-file=datasources.yaml -n default
kubectl patch deployment grafana --type='json' -p='[{"op": "add", "path": "/spec/template/spec/volumes/-", "value": {"name": "grafana-datasources", "configMap": {"name": "grafana-datasources"}}}]'
kubectl patch deployment grafana --type='json' -p='[{"op": "add", "path": "/spec/template/spec/containers/0/volumeMounts/-", "value": {"name": "grafana-datasources", "mountPath": "/etc/grafana/provisioning/datasources/datasources.yaml", "subPath": "datasources.yaml"}}]'
Khởi động lại pod Grafana để nạp cấu hình mới.
kubectl rollout restart deployment grafana
Verify kết quả: Truy cập URL của Grafana, đăng nhập bằng admin/admin123. Vào menu Connections > Data sources, bạn sẽ thấy datasource ClickHouse đã được tự động tạo và trạng thái là Health check succeeded.
Viết Query SQL tối ưu để lấy dữ liệu từ ClickHouse
Để dashboard hiển thị mượt mà, query SQL phải khai thác tối đa khả năng của ClickHouse: sử dụng hàm aggregation, windowing, và đặc biệt là engine MergeTree với các key phân vùng.
Giả sử bạn có bảng events với cấu trúc: timestamp, user_id, event_type, duration.
Tạo view ảo (Virtual View) hoặc lưu query mẫu để Grafana sử dụng, tập trung vào việc lấy dữ liệu theo thời gian thực (last 1 hour) và nhóm theo khoảng thời gian (time bucket).
CREATE VIEW IF NOT EXISTS events_1h_realtime AS
SELECT
toStartOfMinute(timestamp) AS bucket,
event_type,
count() AS event_count,
avg(duration) AS avg_duration,
quantile(0.95)(duration) AS p95_duration
FROM events
WHERE timestamp >= now() - INTERVAL 1 HOUR
GROUP BY bucket, event_type
ORDER BY bucket ASC;
Query này sử dụng toStartOfMinute để gom dữ liệu vào từng phút, giúp giảm số lượng điểm dữ liệu (points) gửi lên Grafana, tăng tốc độ render.
Để tối ưu hơn nữa cho các query phức tạp, hãy sử dụng hàm arrayJoin hoặc groupArray nếu cần xử lý dữ liệu mảng, và luôn đảm bảo ORDER BY trùng với PRIMARY KEY của bảng gốc.
Query mẫu cho biểu đồ đường (Line Chart) trong Grafana:
SELECT
bucket,
event_count,
avg_duration
FROM events_1h_realtime
WHERE event_type = 'page_view'
ORDER BY bucket;
Query mẫu cho bảng thống kê (Table) top user:
SELECT
user_id,
count() AS total_events,
max(timestamp) AS last_seen
FROM events
WHERE timestamp >= now() - INTERVAL 24 HOUR
GROUP BY user_id
ORDER BY total_events DESC
LIMIT 10;
Verify kết quả: Chạy trực tiếp query này trên CLI của ClickHouse hoặc qua giao diện ClickHouse Client.
clickhouse-client -q "SELECT * FROM events_1h_realtime LIMIT 5"
Kết quả mong đợi: Trả về dữ liệu nhanh dưới 100ms, không có cảnh báo về việc quét toàn bộ bảng (full table scan).
Tích hợp API Gateway để cung cấp dữ liệu cho Frontend
Frontend thường không nên kết nối trực tiếp đến ClickHouse vì lý do bảo mật và hiệu năng. Chúng ta sẽ dùng API Gateway (ví dụ: Kong hoặc Nginx) để đóng vai trò trung gian, thực hiện query ClickHouse và trả về JSON.
Triển khai Kong Gateway trên Kubernetes (hoặc dùng Nginx Ingress với Lua script) để xử lý request từ frontend.
helm repo add kong https://charts.konghq.com
helm repo update
helm install kong kong/kong --set controller.hostname=api-gateway.example.com --set controller.service.type=LoadBalancer
Kong cần một plugin hoặc service backend để gọi ClickHouse. Ở đây chúng ta sẽ tạo một Service đơn giản trong Kubernetes để map route.
Tạo một Service mới trong Kubernetes trỏ đến ClickHouse nhưng chỉ expose cổng HTTP (8123) để API Gateway có thể gọi.
cat > /tmp/clickhouse-http-svc.yaml
Service này sẽ cho phép các pod trong cluster (bao gồm Kong) truy cập ClickHouse qua http://clickhouse-http-backend:80.
Cấu hình Kong để tạo một route chuyển đổi từ /api/query về ClickHouse, đồng thời thêm header để xác thực.
Sử dụng kubectl kong hoặc CLI của Kong để tạo Service và Route.
kong create service \
--name clickhouse-service \
--connect clickhouse-http-backend.default.svc.cluster.local:80 \
--protocol http
kong create route \
--name clickhouse-query-route \
--service clickhouse-service \
--paths /api/query \
--strip_path
Để frontend gửi query SQL, Kong sẽ cần một plugin để đọc body request và append vào URL của ClickHouse (ClickHouse hỗ trợ query qua GET parameter ?). Tuy nhiên, để an toàn hơn, chúng ta sẽ dùng một microservice nhỏ (Node.js/Python) đặt phía sau Kong, hoặc cấu hình Kong Proxy để forward body.
Giải pháp đơn giản nhất: Tạo một container microservice nhỏ chạy Node.js để nhận request từ Kong, thực thi query ClickHouse, và trả về JSON.
cat > /tmp/api-server/Dockerfile
Và file server.js xử lý logic:
const express = require('express');
const clickhouse = require('clickhouse');
const app = express();
app.use(express.json());
app.post('/api/query', (req, res) => {
const { sql } = req.body;
// Validation SQL ở đây để tránh injection
if (!sql || sql.length > 1000) {
return res.status(400).json({ error: 'Invalid query' });
}
const client = clickhouse.createClient({
host: process.env.CLICKHOUSE_HOST || 'clickhouse-http-backend',
port: 8123,
username: 'default',
password: ''
});
client.query(sql)
.then(result => res.json({ data: result }))
.catch(err => res.status(500).json({ error: err.message }));
});
app.listen(3000, () => console.log('API Server running on port 3000'));
Deploy microservice này lên Kubernetes và tạo Service cho nó, sau đó cấu hình Kong route trỏ về microservice này thay vì trực tiếp ClickHouse.
kubectl create deployment api-server --image=YOUR_REGISTRY/api-server:latest
kubectl expose deployment api-server --port=3000 --name=api-server-svc
kong create service --name api-service --connect api-server-svc.default.svc.cluster.local:3000
kong create route --name api-route --service api-service --paths /api
Verify kết quả: Gửi một request POST từ frontend hoặc Postman vào endpoint API Gateway.
curl -X POST http://api-gateway.example.com/api/query \
-H "Content-Type: application/json" \
-d '{"sql": "SELECT count(*) FROM events WHERE timestamp > now() - INTERVAL 1 HOUR"}'
Kết quả mong đợi: Trả về JSON {"data": [{"count": 1234}]} với thời gian phản hồi nhanh (< 200ms). Nếu thấy lỗi 502, kiểm tra logs của pod api-server và kết nối giữa Kong và api-server.
Điều hướng series:
Mục lục: Series: Series: Xây dựng hệ thống Real-time Analytics và Stream Processing với Apache Kafka, Flink và ClickHouse trên Kubernetes
« Phần 7: Tích hợp Flink Sink để đẩy dữ liệu vào ClickHouse
Phần 9: Giám sát hệ thống: Prometheus, Grafana và Alerting »