Introducción
ClickHouse es un sistema de gestión de bases de datos columnares de código abierto que destaca por la ejecución de consultas OLAP de alto rendimiento y análisis en tiempo real. Sin embargo, escalar el almacenamiento de datos para ClickHouse puede ser un desafío, especialmente a medida que crece el volumen de datos. Una solución eficiente es usar almacenamiento por niveles, que permite migrar datos de acceso poco frecuente a un almacenamiento más rentable, manteniendo los de acceso frecuente en un almacenamiento más rápido y costoso. ClickHouse admite diversos backends de almacenamiento de datos, incluyendo discos locales y opciones remotas como DigitalOcean Spaces. Al gestionar grandes volúmenes de datos, es común usar múltiples dispositivos de almacenamiento.
DigitalOcean Spaces es un servicio de almacenamiento de objetos que se integra como una capa en la arquitectura de almacenamiento por niveles de ClickHouse. Esta guía le guiará por los pasos para configurar DigitalOcean Spaces como una opción de almacenamiento por niveles para su clúster de ClickHouse.
En esta guía, configuraremos una aplicación Go sencilla que envía registros por lotes a ClickHouse. Primero, los registros se almacenan en almacenamiento activo (el disco predeterminado, también llamado local) y, tras un intervalo de tiempo especificado (2 minutos), se transfieren a almacenamiento en frío (por ejemplo, almacenamiento basado en S3 como DO Spaces).
Requisitos previos
Antes de comenzar, asegúrese de tener lo siguiente:
- Una cuenta en la nube en DigitalOcean.
- Un depósito de DigitalOcean Spaces.
- Claves de acceso a DigitalOcean Spaces.
- Introducción básica a las configuraciones de ClickHouse y al almacenamiento de objetos.
Paso 1: Crear y configurar un espacio DigitalOcean
Inicie sesión en su cuenta de DigitalOcean Cloud y cree un nuevo espacio (bucket). Este bucket se usará como almacenamiento por niveles para datos de acceso poco frecuente.
- Vaya a la sección Espacios del panel de control.
- Haga clic en “Crear nuevo espacio”, ingrese un nombre, seleccione una región y configure los permisos.
- Anote la URL del punto final.
- Anote la clave de acceso y la clave secreta, ya que las necesitará en los próximos pasos.
En este punto, su bucket está creado y listo para integrarse con ClickHouse.
Paso 2: Configuración del servidor ClickHouse en un contenedor Docker
Para comenzar, cree una carpeta y llámela “clickhouse”.
mkdir clickhouseCree un Dockerfile en esta carpeta y reemplace los valores {YOUR_AWS_ACCESS_KEY_ID} y {YOUR_AWS_SECRET_ACCESS_KEY} con su clave de acceso y clave secreta.
FROM clickhouse/clickhouse-server:latest
# Copy the config file to the container
COPY storage.xml /etc/clickhouse-server/config.d/storage.xml
# Copy the S3 table creation script
COPY create.sql /docker-entrypoint-initdb.d/
# Set environment variables for S3 credentials
ENV AWS_ACCESS_KEY_ID={YOUR_AWS_ACCESS_KEY_ID}
ENV AWS_SECRET_ACCESS_KEY={YOUR_AWS_SECRET_ACCESS_KEY}
# Expose ClickHouse HTTP and native ports
EXPOSE 8123 9000
USER clickhouse
# --config-file ./programs/server/config.xml
CMD ["clickhouse-server", "--config-file", "/etc/clickhouse-server/config.xml"]Descripción:
- access_key_id y secret_access_key: estas son sus credenciales para DigitalOcean Spaces.
Advertencia: Hemos incluido las credenciales en el Dockerfile para simplificar esta guía, pero este enfoque no se recomienda en entornos de producción.
Paso 3: Configurar ClickHouse para almacenamiento compatible con S3
En este paso, configurará ClickHouse para usar DigitalOcean Spaces como un nivel en su sistema de almacenamiento. Esto implica agregar la configuración de almacenamiento al archivo config.xml de su instalación de ClickHouse.
Para usar el contenedor Spaces como disco de almacenamiento, primero debe declararlo en el archivo de configuración de ClickHouse. Puede modificar el archivo config.xml existente o, preferiblemente, agregar un nuevo archivo en la carpeta conf.d, que posteriormente se fusionará con config.xml.
<clickhouse>
<storage_configuration>
<disks>
<s3>
<type>s3</type>
<endpoint>{YOUR_S3_SPACES_BUCKET_URL}</endpoint>
<use_environment_credentials>true</use_environment_credentials>
</s3>
</disks>
</storage_configuration>
</clickhouse>Esta sección configura un disco remoto (DigitalOcean Space) que ClickHouse puede usar para almacenar datos disponibles con poca frecuencia.
Nota: Reemplace los valores {YOUR_S3_SPACES_BUCKET_URL} con la URL de su punto final.
Paso 4: Crear una tabla con almacenamiento por niveles
Ahora, cree una tabla en ClickHouse que utilice almacenamiento por niveles. Puede especificar varias políticas de almacenamiento para determinar qué datos se almacenan en discos locales y cuáles en discos remotos (espacios).
Defina una política de retención que traslade datos antiguos a DigitalOcean Spaces después de un período de tiempo específico:
CREATE TABLE IF NOT EXISTS tiered_logs (
event_time DateTime,
level String,
message String
) ENGINE = MergeTree
ORDER BY (event_time)
TTL toDateTime(event_time) + INTERVAL 2 MINUTE TO VOLUME 'cold'
SETTINGS storage_policy = 's3_tiered';Descripción:
- predeterminado: este es el almacenamiento en disco local donde se almacenan los datos recientes o utilizados con frecuencia.
- s3: es el almacenamiento remoto (DigitalOcean Spaces) donde se mueven los datos más antiguos.
Esta configuración garantiza que los datos nuevos se escriban en el disco local y que los datos más antiguos se migren automáticamente a DigitalOcean Spaces.
Paso 5: Ejecute el servidor ClickHouse
Para iniciar el servidor ClickHouse, ejecute el siguiente comando:
docker build -t clickhouse-demo .
docker run -d --name clickhouse-demo -p 8123:8123 -p 9000:9000 clickhouse-demoPuertos de red
- 8123: Este es el puerto HTTP utilizado para comunicarse con ClickHouse mediante la interfaz HTTP (http://localhost:8123/play). Puede usar este puerto para ejecutar consultas SQL mediante un navegador o herramientas de línea de comandos como curl o Postman. Se utiliza a menudo para aplicaciones web o clientes que interactúan con ClickHouse mediante HTTP.
- 9000: Este puerto TCP es el puerto principal para que los clientes y servidores de ClickHouse se comuniquen entre sí utilizando el protocolo principal de ClickHouse.
- Referencia: https://clickhouse.com/docs/es/guides/sre/network-ports
Verificar usando:
docker ps
Paso 6: Ejecute un programa Go simple que envíe registros
En una nueva carpeta, un archivo llamado principal.go Crea uno que envíe registros a ClickHouse.
package main
import (
"database/sql"
"fmt"
"log"
"os"
"time"
"github.com/ClickHouse/clickhouse-go"
"github.com/sirupsen/logrus"
)
type ClickHouseHook struct {
db *sql.DB
entries []logrus.Entry
batchSize int
}
// NewClickHouseHook establishes a connection to ClickHouse using the provided DSN.
func NewClickHouseHook(dsn string, batchSize int) (*ClickHouseHook, error) {
db, err := sql.Open("clickhouse", dsn)
if err != nil {
return nil, err
}
if err := db.Ping(); err != nil {
if exception, ok := err.(*clickhouse.Exception); ok {
log.Fatalf("[%d] %s \n%s\n", exception.Code, exception.Message, exception.StackTrace)
} else {
log.Fatal(err)
}
}
return &ClickHouseHook{db: db, batchSize: batchSize}, nil
}
// Fire is triggered by Logrus to log entries to ClickHouse.
func (hook *ClickHouseHook) Fire(entry *logrus.Entry) error {
hook.entries = append(hook.entries, *entry)
if len(hook.entries) >= hook.batchSize {
if err := hook.flush(); err != nil {
return err
}
}
return nil
}
// flush sends the collected log entries to ClickHouse in a batch.
func (hook *ClickHouseHook) flush() error {
tx, err := hook.db.Begin()
if err != nil {
return err
}
stmt, err := tx.Prepare("INSERT INTO tiered_logs (event_time, level, message) VALUES (?, ?, ?)")
if err != nil {
return err
}
defer stmt.Close()
for _, entry := range hook.entries {
if _, err := stmt.Exec(entry.Time, entry.Level.String(), entry.Message); err != nil {
return err
}
}
if err := tx.Commit(); err != nil {
return err
}
// Clear the entries after flushing
hook.entries = nil
return nil
}
// Levels returns the logging levels for which the hook is triggered.
func (hook *ClickHouseHook) Levels() []logrus.Level {
return logrus.AllLevels
}
func main() {
// ClickHouse DSN (replace with your credentials and host)
dsn := "tcp://localhost:9000?database=default&username=default&password=&debug=true"
// Create ClickHouse hook with a batch size of 5
hook, err := NewClickHouseHook(dsn, 5)
if err != nil {
log.Fatalf("failed to connect to ClickHouse: %v", err)
}
defer hook.db.Close()
// Set up logrus
logger := logrus.New()
logger.Out = os.Stdout
logger.SetFormatter(&logrus.TextFormatter{
FullTimestamp: true,
})
logger.AddHook(hook)
// Log some entries
for i := 0; i < 10; i++ {
logger.WithFields(logrus.Fields{
"iteration": i,
}).Info("This is an info log entry")
time.Sleep(time.Second)
}
// Flush any remaining log entries before exiting
if err := hook.flush(); err != nil {
log.Fatalf("failed to flush logs to ClickHouse: %v", err)
}
fmt.Println("Logs sent to ClickHouse.")
}Para instalar las dependencias del paquete:
go mod init example.com/clickhouse-logging
go get github.com/ClickHouse/clickhouse-go
go get github.com/sirupsen/logrusPara ejecutar la aplicación:
go run main.goPaso 7 — Confirmar los resultados
Para iniciar el cliente ClickHouse, ejecute el siguiente comando:
docker exec -it clickhouse-demo clickhouse-clientPara verificar registros en el clúster de ClickHouse:
SELECT * FROM tiered_logsAl examinar el disco de almacenamiento para esta entrada de registro, vemos que está almacenada en el disco predeterminado (local), también conocido como almacenamiento activo.
SELECT name, disk_name FROM system.parts WHERE table = 'tiered_logs';
Después del intervalo de dos minutos especificado en la consulta CREATE TABLE, puede verificar que estos informes se hayan movido a discos S3 (espacio/depósito remoto), también llamado almacenamiento en frío.
También podemos ver en la interfaz de usuario de DigitalOcean Cloud que nuestro depósito ahora contiene datos:
Resultado
Al seguir esta guía, ha configurado correctamente DigitalOcean Spaces como una opción de almacenamiento por niveles para ClickHouse. Esta configuración le permite optimizar los costos de almacenamiento y mejorar el rendimiento al migrar datos de acceso poco frecuente a un almacenamiento de objetos rentable, a la vez que mantiene un almacenamiento de alto rendimiento para los datos activos.























