Ken
원문: How Canva achieved 10x faster search and 70% lower costs with ClickHouse
월간 활성 사용자 2.4억 명의 그래픽 디자인 플랫폼 Canva는 사용자가 디자인을 만들고 게시할 때마다 엄청난 양의 로그를 만들어냅니다. Canva 소프트웨어 엔지니어 Zjan Carlo Turla는 이렇게 말합니다.
"That number is growing rapidly. It's doubling year over year—and the demands on our infrastructure are growing along with it."
이 글에서는 Canva가 어떻게 ClickHouse로 옮기면서 검색 성능 10배, 비용 70% 절감을 달성했는지 그 여정을 살펴봅니다.
규모의 문제
Canva가 처리하는 관측성 데이터의 규모:
- 초당 약 300만 span
- 초당 약 300만 log
- 매년 2배씩 증가 중
- 인프라 부담도 같은 속도로 커지는 중
인프라 구성 — "심플하게 유지"
Canva는 ClickHouse를 직접 운영하기로 결정했고, 구조를 단순하게 유지했습니다.
- 5 shard × 3 replica, AZ 분산
- 노드당 약 60 vCPU, 400 GB RAM
- Argo CD로 배포
- Jsonnet으로 Kubernetes 매니페스트와 ClickHouse config를 템플릿화
- 저장소는 EBS gp3 SSD 단일 티어, 노드당 다중 디스크(JBOD)
1. Smarter Ingestion — 70% 비용 절감
초기에는 분산 테이블 + async insert로 ClickHouse가 배치와 분산을 알아서 처리하게 했습니다. "성능은 괜찮았지만, 더 필요했다"고 Zjan은 말합니다.
돌파구: 배치를 Collector 단으로 옮기다
Canva의 OTEL collector는 이미 메모리에 상당량의 트레이스 데이터를 버퍼링하고 있었기 때문에, 이를 활용해 배치를 ClickHouse 바깥으로 이동시켰습니다.
- 한 배치당 약 20만 span (≈ 160 MiB)
- 분산 테이블이 아닌 로컬 테이블에 직접 insert
- 결과: ClickHouse 부담 감소 + insert 횟수 감소 + insert 크기 증가
결과
- 14배 압축률
- 저장 비용 약 70% 절감
- 쿼리 성능 개선을 위한 헤드룸 확보
2. 검색 최적화 — 30초 → 2.5초
Canva 엔지니어들은 Jaeger를 주로 사용하며, service name·span name·trace ID 필터링이 핵심입니다.
스키마 설계
- 표준 trace context 속성 + 자주 쿼리되는 span·resource 필드를 기본 컬럼으로
- Schemaless 속성(서비스마다 달라지는 속성)은 "키 배열"과 "값 배열"의 쌍으로 저장
mapFromArrays같은 함수로 쿼리 시점에 재조립- 임의의 속성 이름을 효율적으로 저장 가능
Sorting key 전략
Jaeger UI가 service name, span name, timestamp 순으로 필터를 노출하므로, 동일한 컬럼들을 ClickHouse sorting key로 지정 — 스캔할 파트 수를 크게 줄임.
파티셔닝과 TTL
- 일(day) 단위 파티셔닝
- 14일 TTL로 데이터 사이클링, 파트가 너무 커지는 것 방지
Trace ID Lookup용 Materialized View
Trace ID는 primary filter를 우회하므로, materialized view 기반 lookup 테이블을 별도로 구축.
- Trace ID로 조회 → 해당 trace가 속한 service/span name 획득
- 메인 테이블에서 정확한 파트만 스캔
클라이언트 측 최적화
- Sliding search window with backoff — 시간 범위를 한 번에 모두 스캔하지 않고, 필요한 만큼만 확장
- "트레이스는 해당 기간의 모든 결과를 보여줄 필요가 없다"는 통찰
결과
- P90 trace search: 30초 → 2.5초 (10배 개선)
3. 로그도 빠르고 유연하게 — 그리고 익숙하게
트레이스가 잘 동작하자 Canva는 로깅 인프라도 ClickHouse로 옮겼습니다.
로그 파이프라인
- Kinesis Data Streams + AWS Lambda → ClickHouse
- Lambda는 메모리에 큰 배치를 유지할 수 없음 → ClickHouse의 async insert가 배치 역할 수행
- 테이블별 async insert 파라미터를 "엄청 많은 실험"을 통해 튜닝
- 결과: 단 2개 노드로 100% 로그 ingest 가능 — 축소 운영 시에도 가용성 확보
자유 텍스트 검색 (Free Text Search)
기존 OpenSearch 사용자들이 익숙한 "검색창에 아무거나 쳐서 즉시 결과" 경험을 재현해야 했습니다.
- 로그 객체(body, resource, metadata)를 단일 문자열로 평탄화
- ngram Bloom filter 적용
- 모든 필드에 대한 빠른 substring 검색 가능 — 표준 ClickHouse 인덱싱으로는 불가능했던 부분
개발자 경험 (DSL + 자체 UI)
- 키/값 배열 스키마는 강력하지만 "개발자가 다루기엔 곰 같다(a bear to work with)"
- 커스텀 DSL → ClickHouse 쿼리로 변환하는 query gateway 구축
- Kibana 같은 쿼리 기능 + query builder, code search, autocomplete가 있는 자체 로깅 플랫폼 제작
4. 프로덕션 안정화
전사 롤아웃 전 마지막 마무리 작업:
- Read/write 분리 replica와 write-only replica로 격리
- 시간/일 단위 S3 백업
- ingestion을 차단하지 않도록 별도 테이블에 복구하는 잡 구성
- AWS Graviton ARM 프로세서 벤치마크:
- Graviton3: 1.7배 성능 향상
- Graviton4: 2배 이상 성능 향상
교훈과 시사점
Ingest 최적화는 ClickHouse 바깥에서도 가능하다
Canva는 ClickHouse 내부 튜닝뿐 아니라 OTEL collector에서 배치 책임을 가져오는 결정으로 큰 비용 절감을 달성했습니다. 데이터 파이프라인 전체를 함께 보는 시각이 중요합니다.
쿼리 패턴에 맞춘 스키마가 모든 것을 좌우한다
Jaeger의 필터 순서를 그대로 sorting key로 사용한 결정이 10배 성능 향상의 핵심이었습니다. "실제로 어떤 쿼리가 들어오는가"에서 시작하는 스키마 설계의 중요성을 보여줍니다.
익숙한 사용자 경험을 만드는 것도 엔지니어링
ClickHouse의 강력함은 그대로 두고, 그 위에 Kibana 같은 검색창과 DSL을 얹어 OpenSearch 사용자에게 매끄러운 전환을 제공한 점이 도입 성공의 중요한 요인이었습니다.
결론
"Moving to ClickHouse, we managed to get 70% cheaper costs and improved our search performance by 10x. We're doing more and storing more with less." — Zjan Carlo Turla, Software Engineer at Canva
Canva의 사례는 "창의성을 돕는 플랫폼"의 뒤에서, 더 적은 리소스로 더 많이 처리하고 저장하는 관측성이 어떻게 구축되는지를 보여줍니다. ClickHouse는 그 핵심에 있습니다.