Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

ClickHouse Infrastructure Documentation

Overview

ClickHouse at HungryHub is deployed using ClickStack Helm Chart (v3.0.1) inside an EKS cluster. This stack includes ClickHouse Server, ClickHouse Keeper (ZooKeeper replacement), and HyperDX (observability dashboard).

Main file: hungryhub-terraform/helm-releases/clickhouse.tf


Architecture

┌─────────────────────────────────────────────────────────────┐
│                        EKS Cluster                          │
│                                                             │
│  ┌─────────────┐  ┌──────────────┐  ┌───────────────────┐  │
│  │  ClickHouse  │  │  ClickHouse  │  │     HyperDX       │  │
│  │   Server     │  │   Keeper     │  │   (UI Dashboard)  │  │
│  │  (port 8123  │  │  (port 9000  │  │   (port 3000)     │  │
│  │  native:9000)│  │  internal)   │  │                   │  │
│  └──────┬───────┘  └──────────────┘  └───────────────────┘  │
│         │                                                    │
│  ┌──────┴───────────────────────────────────┐                │
│  │        Kubernetes Services               │                │
│  │  clickhouse (ClusterIP)                  │                │
│  │  clickhouse-headless (ClusterIP=None)    │                │
│  └──────────────────────────────────────────┘                │
│         │                                                    │
│  ┌──────┴───────────────────────────────────┐                │
│  │     NetworkPolicy (restricted ingress)    │                │
│  │  - clickstack pods                       │                │
│  │  - hyperdx pods                          │                │
│  │  - additional workloads via variable     │                │
│  └──────────────────────────────────────────┘                │
└─────────────────────────────────────────────────────────────┘
                              │
                    ┌─────────┴─────────┐
                    │                   │
              ┌─────▼─────┐     ┌──────▼──────┐
              │    ALB     │     │ Cloudflare  │
              │  (dev only)│     │  (prod)     │
              │ HTTPS:8123 │     │  Tunnel     │
              └────────────┘     └─────────────┘

Cluster Sizing

ParameterDevProd
Replicas1 (configurable)3
Shards1 (configurable)2
Storage Classgp3gp3
Storage Size100Gi (configurable)200Gi
CPU2000m2000m
Memory6Gi6Gi
ClickHouse Version24.12.6-alpine24.12.6-alpine
Chart Version3.0.13.0.1

Resources

Kubernetes Resources

ResourceNamePurpose
NamespaceclickhouseClickHouse workload isolation
Secretclickhouse-credentialsAdmin credentials (username: admin)
Secretclickhouse-default-passwordDefault user password (SHA256)
ServiceclickhouseClusterIP — HTTP (8123) + Native (9000)
Serviceclickhouse-headlessHeadless — replica discovery
NetworkPolicyclickhouse-restricted-ingressRestrict access to approved workloads

AWS Resources (dev only)

ResourcePurpose
Security Group clickhouse-albRestrict inbound to VPN CIDRs
ALB IngressHTTPS termination at ALB, port 8123
ALB Access LogsLogs to S3 bucket alb_logs_s3_bucket

Secrets Management

ClickHouse credentials are managed through AWS Secrets Manager + Kubernetes Secrets:

Flow

AWS Secrets Manager (/hungryhub/{env})
  ├── CLICKSTACK_MONGO_URI_{DEV|PROD} → HyperDX MongoDB Atlas
  └── SENDGRID_API_KEY → HyperDX SMTP

Random Generation (Terraform)
  ├── clickhouse_password (admin) → K8s Secret: clickhouse-credentials
  └── clickhouse_default_password → K8s Secret: clickhouse-default-password

Rotation

  • Admin password: Auto-generated (32 chars, special). Rotated when namespace or storage_class changes.
  • Default password: Auto-generated (32 chars, special). Overridden via var.clickhouse_default_password from CI pipeline.
  • Both passwords are not stored in Terraform state or git in plaintext.

Networking & Ingress

NetworkPolicy

Only the following pods may access ClickHouse:

  • clickstack-clickhouse-clickhouse pods (namespace: clickhouse)
  • hyperdx pods (namespace: clickhouse)
  • Additional workloads via var.clickhouse_allowed_ingress_workloads

ALB Ingress (dev only)

  • Host: clickhouse.hhstaging.dev
  • TLS: ACM cert (*.hhstaging.dev) from route53 remote state
  • Security Group: Inbound only from clickhouse_ingress_allowed_cidrs (default: WireGuard VPN)
  • Access Logs: S3 bucket from s3 remote state

Prod Access

Prod uses Cloudflare Tunnel + Zero Trust, not ALB.


Remote State Dependencies

# helm-releases/provider.tf
terraform_remote_state:
  vpc:
    key: vpc/vpc-state.tfstate
    outputs: vpc_id, private_subnets, private_subnets_cidr, db_subnets_cidr, eks_cluster_name

  eks:
    key: eks/eks-state.tfstate
    outputs: eks_cluster_id

  route53:
    key: route53/route53-state.tfstate
    outputs: cloudflare_acm (ACM cert ARN)

  s3:
    key: s3/s3-state.tfstate
    outputs: alb_logs_s3_bucket

Helm Values Template

File: helm-releases/values/clickhouse.yaml.tft

SectionConfig
clickhouse.cluster.specreplicas, shards, image tag, resources, storage
clickhouse.cluster.spec.settings.defaultUserPassword.secretK8s Secret ref for default user
clickhouse.cluster.spec.settings.extraUsersConfigdefault user grants: GRANT ALL ON *.* WITH GRANT OPTION
clickhouse.keeper.specKeeper replicas + storage
clickhouse.prometheusPort 9363
mongodb.enabledfalse — using MongoDB Atlas
hyperdx.enabledtrue — UI dashboard
hyperdx.configFRONTEND_URL, MONGO_URI, SMTP settings
otel-collector.enabledfalse

Monitoring (dev only)

File: helm-releases/clickhouse-monitoring.tf

ServiceMonitor

  • Scrapes ClickHouse native Prometheus endpoint (port 9363)
  • Interval: 15s, timeout: 10s
  • Target: clickstack-clickhouse-clickhouse-headless Service

PrometheusRule Alerts

AlertConditionSeverity
ClickHouseMemoryUsagecontainer_memory_working_set_bytes > 800MB for 5mwarning
ClickHouseMemoryCriticalcontainer_memory_working_set_bytes > 950MB for 1mcritical
ClickHouseUnreachableup == 0 for 1mcritical

Outputs

clickhouse_http_endpoint    = "http://clickstack-clickhouse.clickhouse.svc.cluster.local:8123"
clickhouse_native_endpoint  = "clickstack-clickhouse.clickhouse.svc.cluster.local:9000"
hyperdx_ui_endpoint         = "http://clickstack-hyperdx.clickhouse.svc.cluster.local:3000"
clickhouse_alb_dns_name     = ""  # (dev only, populated after ALB provisioned)
clickhouse_alb_zone_id      = ""  # (dev only)
clickhouse_alb_security_group_id = ""  # (dev only)

Variables

VariableDefaultDescription
namespaceclickhouseK8s namespace
clickhouse_chart_version3.0.1ClickStack chart version
clickhouse_version24.12.6-alpineClickHouse image tag
clickhouse_replicas1Replicas (dev)
clickhouse_shards1Shards (dev)
clickhouse_cpu2000mCPU request
clickhouse_memory6GiMemory request
clickhouse_storage_size100GiPV size (dev)
clickhouse_storage_classgp3StorageClass
clickhouse_password""Admin password override
clickhouse_default_password""Default user password override
clickhouse_ingress_enabledtrueEnable ALB ingress
clickhouse_ingress_hostclickhouse.hhstaging.devIngress hostname
clickhouse_ingress_allowed_cidrs["5.223.47.105/32"]Allowed CIDRs


DMS (Database Migration Service)

Overview

DMS is used for two pipelines:

  1. MySQL → Aurora MySQL (CDC replication)
  2. MySQL → S3 Parquet (dev only — data pipeline)

Main file: hungryhub-terraform/dms/main.tf, dms_s3_pipeline.tf


Architecture

┌──────────────────────────────────────────────────────┐
│                     DMS Pipeline                     │
│                                                      │
│  ┌──────────┐     ┌──────────────┐     ┌──────────┐ │
│  │  MySQL   │────▶│ Replication  │────▶│  Target  │ │
│  │  Source   │     │  Instance    │     │          │ │
│  │  (RDS)   │     │  dms.t3.large│     │          │ │
│  └──────────┘     └──────────────┘     └──────────┘ │
│       │                                      │       │
│       │                                      ▼       │
│       │                              ┌──────────────┐│
│       │                              │ Aurora MySQL  ││
│       │                              │ (prod target) ││
│       │                              └──────────────┘│
│       │                                              │
│       │              ┌──────────────┐                │
│       └─────────────▶│  S3 Bucket   │ (dev only)     │
│                      │  dms-target  │                │
│                      │  (Parquet)   │                │
│                      └──────────────┘                │
└──────────────────────────────────────────────────────┘

DMS Replication Instance

ParameterValue
Classdms.t3.large
Engine3.5.4
AZap-southeast-1c
Multi-AZfalse
Storage50 GB
Public Accesstrue
Maintenancesun:10:30-sun:14:30

Pipeline 1: MySQL → Aurora MySQL (CDC)

Source Endpoint

ParameterValue
EngineMySQL
Hosthh-production-db-master.cgfqflgzmmxy.ap-southeast-1.rds.amazonaws.com
Port3306
Databasebooking_production
SSLnone
Usernameroot (via variable)

Target Endpoint

ParameterValue
EngineAurora
Hostdata.terraform_remote_state.rds.outputs.mysql_rds_cluster_endpoint
Port3306
Databasebooking_production
SSLnone

Replication Tasks

TaskSchemaMigration Type
{env}-dms-replication-taskbooking_production.%full-load-and-cdc
{env}-dms-replication-task-menuhh_menu_production.%full-load-and-cdc
  • start_replication_task = false (manual start)
  • Task settings: task_settings.json

Event Subscription

  • Categories: failure, state change, deletion, configuration change, creation
  • SNS Topic: {env}-hungryhub-dms

Pipeline 2: MySQL → S3 Parquet (dev only)

Gated: var.environment == "dev"

Source Endpoint

ParameterValue
EngineMySQL
Hosthungryhub-dev-mysql-proxy.proxy-c2i8lks3rhzq.ap-southeast-1.rds.amazonaws.com
Port3306
Databasebooking_production

Target Endpoint

ParameterValue
EngineS3
Bucketdms-target-hungryhub-dev-s3
Folderbronze/
FormatParquet (v2.0)
EncodingRLE Dictionary
EncryptionSSE-S3
Timestamp Columndms_timestamp

S3 Output Settings

  • Row group length: 10,000
  • Data page size: 1 MB
  • Dict page size limit: 1 MB
  • Max file size: 1 MB
  • CDC batch interval: 180s
  • CDC min file size: 32 KB

Table Mappings

  • 104 selection rules
  • 4 column-type transformations (date → string(50)):
    • vouchers.expiry_date
    • updated_inventories.start_date
    • updated_inventories.end_date
    • reservations.date
  • File: table_mappings_s3_pipeline.json

S3 Target Bucket

Bucket: dms-target-hungryhub-{env}-s3

ConfigValue
VersioningEnabled
Public AccessBlocked (all 4)
Lifecycle (bronze/)30d → STANDARD_IA, 90d → GLACIER, 365d → expire
Non-current30d → STANDARD_IA, 90d → expire

IAM Roles

RolePurpose
dms-access-{env}-endpointDMS service assume role
dms-cloudwatch-{env}-logs-roleCloudWatch Logs for DMS
dms-vpc-{env}-roleVPC management for DMS
dms-s3-{env}-roleS3 access for DMS pipeline

dms-s3 Policy

{
  "s3:PutObject", "s3:GetObject", "s3:DeleteObject",
  "s3:AbortMultipartUpload", "s3:ListMultipartUploadParts",
  "s3:ListBucketMultipartUploads", "s3:PutObjectTagging"
}
# Resource: dms-target bucket/*

Security Groups

DMS SG ({env}-dms)

  • Inbound: MySQL 3306 from private + db subnets
  • Egress: All (0.0.0.0/0)

ClickHouse ALB SG (clickhouse-alb) — dev only

  • Inbound: HTTPS 8123 from clickhouse_ingress_allowed_cidrs
  • Egress: All (0.0.0.0/0)

RDS Connections

Source RDS (External)

Host: hh-production-db-master.cgfqflgzmmxy.ap-southeast-1.rds.amazonaws.com
Port: 3306
Engine: MySQL

Dev MySQL Proxy RDS

Host: hungryhub-dev-mysql-proxy.proxy-c2i8lks3rhzq.ap-southeast-1.rds.amazonaws.com
Port: 3306
Engine: MySQL

Target Aurora Cluster

Endpoint: data.terraform_remote_state.rds.outputs.mysql_rds_cluster_endpoint
User: data.terraform_remote_state.rds.outputs.mysql_rds_user_name
Password: data.terraform_remote_state.rds.outputs.mysql_rds_password

DMS Outputs

dms_security_group_id              # ID of DMS security group
dms_s3_target_bucket_name          # dms-target-hungryhub-{env}-s3
dms_s3_pipeline_replication_instance_arn  # Replication instance ARN
dms_mysql_to_s3_task_arn           # MySQL→S3 task ARN (dev only)

Relationship: ClickHouse ↔ DMS ↔ S3 ↔ RDS

                    ┌─────────────┐
                    │  ClickHouse │
                    │  (EKS)      │
                    └──────┬──────┘
                           │
              ┌────────────┼────────────┐
              │            │            │
              ▼            ▼            ▼
        ┌──────────┐ ┌──────────┐ ┌──────────┐
        │ MongoDB  │ │ HyperDX  │ │ Metrics  │
        │ Atlas    │ │  (UI)    │ │Prometheus│
        └──────────┘ └──────────┘ └──────────┘

                    ┌─────────────┐
                    │     DMS     │
                    └──────┬──────┘
                           │
              ┌────────────┼────────────┐
              ▼            ▼            ▼
        ┌──────────┐ ┌──────────┐ ┌──────────┐
        │  MySQL   │ │  Aurora  │ │  S3      │
        │  Source  │ │  Target  │ │ (Parquet)│
        │  (RDS)   │ │  (RDS)   │ │          │
        └──────────┘ └──────────┘ └──────────┘

Notes:

  • ClickHouse and DMS are not directly connected. They are separate infrastructure.
  • ClickHouse receives data from APM agents (OpenTelemetry) and queries from HyperDX/BI tools.
  • DMS moves data MySQL → Aurora (CDC) and MySQL → S3 (analytics pipeline).
  • DMS S3 bucket can be used as a data source for ClickHouse (via S3 table function or materialized view), but no direct connection is defined in Terraform currently.