Snowflake MigrationClickHouse Workshops

01 소스 환경

실제 고객 배포를 반영하는 Snowflake 환경을 프로비저닝한다 — 5천만 행, dbt Medallion 파이프라인, 라이브 트립 프로듀서, 3개의 Superset 대시보드.

시작 지점

모듈 00 완료 상태: 툴체인이 설치되고, 두 클라우드 트라이얼 계정이 살아 있고, 리포지토리가 클론되고, dbt-snowflake virtualenv가 구축되어 있다. 이 모듈은 약 45분이 걸리고 Snowflake 크레딧을 약 2-4개 소비한다.

이유

장난감 수준의 소스로는 마이그레이션을 계획할 수 없다. 행 몇 개짜리 단일 플랫 테이블은 실제 마이그레이션을 어렵게 만드는 모든 결정을 건너뛰게 해 준다. 이 모듈은 대신 실제 고객 배포의 형태를 만든다. 준정형 JSON을 담은 VARIANT 컬럼, CDC 스트림, 예약된 태스크, 증분 MERGE 파이프라인, 그리고 그 모든 것 위에서 읽는 BI 레이어다. 이 하나하나가 모듈 02에서 구체적인 마이그레이션 결정이 된다 — 이 모듈이 존재하는 이유는 그 결정이 등장할 때 추상적인 개념이 아니라 실물을 가리킬 수 있게 하기 위함이다.

개념 — 내부 동작

인프라(Terraform). setup.sh를 실행하면 다음이 프로비저닝된다.

  • 웨어하우스 — TRANSFORM_WH(SMALL, ELT용)와 ANALYTICS_WH(MEDIUM, BI용), 그리고 월 50 크레딧으로 상한을 둔 리소스 모니터(ANALYTICS_WH_MONITOR).
  • 데이터베이스 — NYC_TAXI_DB, 세 개의 스키마 RAW, STAGING, ANALYTICS를 가진다.
  • 역할 — TRANSFORMER_ROLE, ANALYST_ROLE, DBT_ROLE, LOADER_ROLE.

Snowflake 소스 환경: 일회성 합성 생성기와 상시 동작하는 Docker 트립 프로듀서가 NYC_TAXI_DB에 삽입하고, 3개의 Superset 대시보드가 analytics 웨어하우스를 통해 이를 읽는다

Medallion 구조. 데이터는 NYC_TAXI_DB 안에서 세 개의 레이어를 거쳐 이동한다.

  • RAW — TRIPS_RAW(5천만 개의 합성 트립 행. 앱 텔레메트리를 시뮬레이션하는 TRIP_METADATA VARIANT 컬럼 포함 — 이것이 JSON 마이그레이션 과제다)와 차원 테이블 (DIM_TAXI_ZONES, DIM_PAYMENT_TYPE, DIM_VENDOR).
  • STAGING — 타입을 정제하고 VARIANT 컬럼을 평탄화하는 dbt 뷰.
  • ANALYTICS — dbt 테이블과 증분 모델: fact_trips(5천만 행, MERGE 전략), 네 개의 차원 테이블, 그리고 agg_hourly_zone_trips(증분 집계).

두 개의 Snowflake 객체가 dbt와 독립적으로 이 파이프라인을 스스로 돌아가게 유지한다.

  • TRIPS_CDC_STREAM — TRIPS_RAW 위의 Change Data Capture 스트림.
  • CDC_CONSUME_TASK — 5분마다 그 스트림을 읽는다(RAW에서 실행되며, 설정 중에 재개된다) — 그리고 매시간 시간별 집계를 갱신하는 HOURLY_AGG_TASK(STAGING에서 실행되며, dbt 빌드 후 재개된다).

NYC_TAXI_DB 내부: VARIANT 메타데이터 컬럼을 가진 TRIPS_RAW가 CDC 스트림과 예약된 consume 태스크에 데이터를 공급하고, dbt가 staging 뷰를 만든 다음 fact, 차원, 시간별 집계 테이블을 만든다

Superset. 세 대시보드 모두 ANALYTICS_WH를 통해 ANALYTICS 스키마에서 읽는다 — 어느 것도 RAW나 STAGING을 직접 건드리지 않는다. 워크샵 뒤쪽에서 ClickHouse 쪽에 재현하게 되는 것이 바로 그 읽기 경로다.

1단계 — 자격 증명 구성

cd "$(git rev-parse --show-toplevel)/workshop_public/snowflake_migration_lab/01-setup-snowflake"

cp .env.example .env
# Edit .env with your Snowflake credentials

cp dbt/nyc_taxi_dbt/profiles.yml.example ~/.dbt/profiles.yml
# Edit ~/.dbt/profiles.yml with your account details

.env와 ~/.dbt/profiles.yml은 모두 gitignore 대상이다 — Snowflake 계정, 사용자, 비밀번호를 담고 있다. 두 파일 모두 절대 커밋하지 마라.

2단계 — 설정 실행

cd "$(git rev-parse --show-toplevel)/workshop_public/snowflake_migration_lab/01-setup-snowflake"
source .env && ./setup.sh

5-10분이 걸린다고 예상하라. 대부분은 TABLE(GENERATOR)로 5천만 행의 합성 트립 데이터를 생성하는 데 쓰인다. setup.sh는 Terraform 인프라를 프로비저닝하고, TRIPS_RAW를 시딩하고, dbt 빌드를 실행하고, Docker Compose(트립 프로듀서와 Superset)를 한 번에 띄운다.

3단계 — 프로듀서와 Superset 시작

setup.sh가 올바른 환경으로 Docker Compose를 띄우고, Superset에 Snowflake 연결을 등록하고, 세 대시보드를 모두 자동으로 임포트한다.

superset/dashboards/에 커밋된 대시보드 ZIP 파일들은 sqlalchemy_uri가 플레이스홀더 (LAB_USER, MYORG-MYACCOUNT)로 가려져 있다. 자동 임포트는 .env의 값으로 URI를 다시 찍어 주므로 setup.sh가 실행될 때는 이 과정이 투명하게 처리된다. 대신 Superset UI에서 ZIP을 수동으로 임포트하면 생성되는 연결은 그 플레이스홀더를 사용하고 연결되지 않는다 — 이후 연결을 편집해 실제 Snowflake 계정을 가리키게 하라.

Superset을 수동으로 재시작해야 한다면:

cd "$(git rev-parse --show-toplevel)/workshop_public/snowflake_migration_lab/01-setup-snowflake/superset"
docker-compose --env-file ../.env up -d

--env-file ../.env 플래그는 상위 디렉터리에서 환경 변수를 읽어 온다.

Superset 대시보드 데이터 소스: 3개의 운영 대시보드가 analytics 웨어하우스를 통해 analytics 스키마를 읽는다

세 대시보드는 Operations Command Center, Executive Weekly Report, Driver & Quality Analytics(의도적으로 느리게 만들었다 — 워크샵 뒤쪽에서 ClickHouse 벤치마크의 대상이 된다)다. 데이터 소스, 차트, 필터를 포함한 전체 대시보드 구축 과정은 Superset on Snowflake를 참고하라.

4단계 — dbt를 최신 상태로 유지

트립 프로듀서는 TRIPS_RAW에 분당 약 60건의 트립을 계속 삽입한다. 작업하는 동안 fact_trips와 agg_hourly_zone_trips를 최신 상태로 유지하려면 별도의 터미널에서 dbt 갱신 루프를 실행하라.

cd "$(git rev-parse --show-toplevel)/workshop_public/snowflake_migration_lab/01-setup-snowflake"

# Default: refresh every 5 minutes (auto-sources .env)
./scripts/run_dbt.sh

# Custom interval
./scripts/run_dbt.sh --interval 15m

# Run once and exit
./scripts/run_dbt.sh --once

# Include dbt tests after each run
./scripts/run_dbt.sh --test
플래그효과
--interval <n>실행 간격: 30s, 5m, 1h, 또는 초 단위 숫자(기본값: 5m)
--once한 번만 갱신하고 종료한다
--test매 dbt run 뒤에 dbt test를 실행한다

이 스크립트는 항상 증분으로 실행된다 — 절대 --full-refresh를 하지 않으므로 프로듀서가 삽입한 행이 보존된다. 언제든 Ctrl-C로 중지할 수 있다. 랩이 끝날 때까지 자체 터미널에서 계속 실행해 두어라 — 모듈 03도 이것에 의존한다.

5단계 — 쿼리 라이브러리 살펴보기

cd "$(git rev-parse --show-toplevel)/workshop_public/snowflake_migration_lab/01-setup-snowflake"

쿼리 디렉터리(workshop_public/snowflake_migration_lab/01-setup-snowflake/queries/)에는 주석이 달린 SQL 파일 7개가 있다. 각 파일은 방금 구축한 Snowflake 환경에 대해 실행되며, 모듈 02에서 ClickHouse로 변환할 의도적인 마이그레이션 과제를 하나씩 담고 있다.

쿼리구문마이그레이션 과제
Q1DATE_TRUNC, DATEADD사소한 구문 차이
Q2윈도우 ROWS BETWEENClickHouse에서 거의 동일하다
Q3QUALIFYv24.5부터 ClickHouse에서 네이티브 지원 — 여기서는 이식성을 위해 여전히 서브쿼리로 다시 쓴다
Q4LATERAL FLATTEN대응물이 없다 — JSONExtract를 쓰거나 미리 평탄화한다
Q5VARIANT 콜론 경로JSONExtractFloat/JSONExtractString으로 대체한다
Q6MERGE INTO대응물이 없다 — ReplacingMergeTree를 쓴다
Q7Snowflake Streams컷오버 시 폐기된다 — 라이브 쓰기는 프로듀서를 통해 ClickHouse로 직접 간다

다음으로 넘어가기 전에 각 파일을 열어 Snowflake 환경에 대해 실행하라. 각 쿼리의 주석 블록에 ClickHouse 대응물이 이미 스케치되어 있다 — 모듈 02가 그쪽을 실제로 작성하고 실행하는 곳이다.

완료 확인 방법

cd "$(git rev-parse --show-toplevel)/workshop_public/snowflake_migration_lab/01-setup-snowflake"
source .env && ./scripts/verify_environment.sh

이 스크립트가 확인하는 항목:

  1. 데이터베이스와 스키마 — NYC_TAXI_DB가 RAW, STAGING, ANALYTICS와 함께 존재한다.
  2. 테이블과 데이터 — TRIPS_RAW에 약 5천만 행이 있고, FACT_TRIPS가 채워져 있고, 차원 테이블이 존재한다.
  3. CDC 스트림 — TRIPS_RAW 위에 TRIPS_CDC_STREAM이 존재한다.
  4. 예약된 태스크 — CDC_CONSUME_TASK와 HOURLY_AGG_TASK가 started 상태다.
  5. CDC 활동 — 태스크가 최근에 실행되었다.
  6. 프로듀서 피드 — 트립 프로듀서가 데이터를 계속 삽입하고 있다.
  7. Superset — BI 대시보드에 http://localhost:8088으로 접근할 수 있다.

직접 확인하고 싶다면 ACCOUNTADMIN 역할로 SHOW TASKS LIKE '%TASK' IN DATABASE NYC_TAXI_DB;를 실행하면(태스크는 그 역할이 소유한다) 두 태스크가 모두 실행 중인지 확인할 수 있다.

정리

앞으로 몇 개 모듈 동안 이 환경으로 반복해서 돌아오게 되는데, ./setup.sh 전체 실행은 5-10분이 걸리고 Terraform 파일이나 dbt 모델을 조금 고칠 때마다 그 비용을 치르고 싶지는 않을 것이다. setup.sh는 정확히 그것을 위한 플래그를 제공한다.

플래그사용 시점
(없음)첫 실행. 모든 것을 프로비저닝하고 5천만 개의 합성 행을 생성한다(총 약 12분).
--skip-seed인프라가 이미 존재하고 TRIPS_RAW에 데이터가 이미 있다. 합성 데이터 생성을 건너뛴다(약 8분 절약).
--skip-dbtSnowflake 객체는 존재하지만 dbt 변환을 다시 실행할 필요가 없다(예: Terraform 변경 테스트).
--skip-supersetDocker가 실행 중이 아니거나 아직 BI 레이어가 필요하지 않다.
--full-refreshdbt가 모든 증분 모델을 처음부터 다시 만들도록 강제한다(예: 스키마 변경 후).

플래그는 조합할 수 있다. 흔한 두 가지 조합:

# Re-run after a Terraform or SQL change — skip the ~10 min data load
./setup.sh --skip-seed

# Iterate on dbt models only — skip everything else
./setup.sh --skip-seed --skip-superset

비용 참고. 데이터 시딩은 약 12분에 2 크레딧(약 $6), 전체 dbt 빌드는 약 8분에 1.5 크레딧(약 $5)이 든다. 8시간짜리 파트너 랩 세션은 약 12 크레딧(약 $36)을 추가로 쓴다 — 웨어하우스는 유휴 상태에서 자동 일시 중단되므로 세션 사이에는 비용이 쌓이지 않는다. 파트너 1인당 하루 총합은 약 16 크레딧, 약 $47이다.

종료 상태

Snowflake가 살아 있다. NYC_TAXI_DB가 완전히 구축되고, CDC 스트림과 두 예약 태스크가 실행 중이고, 트립 프로듀서가 TRIPS_RAW에 분당 약 60건의 트립을 쓰고 있고, 세 개의 Superset 대시보드가 http://localhost:8088에서 올라와 있다.

프로듀서를 계속 실행해 두어라. Docker Compose 스택을 중지하지 말고 ./teardown.sh를 실행하지 마라 — 모듈 02부터 05까지가 이 환경이 살아 있는 것에 의존하며, 모듈 05의 컷오버 단계는 마이그레이션 중 프로듀서가 Snowflake와 ClickHouse 사이에 만드는 정확한 공백을 측정한다. 지금 정리해 버리면 이 단계로 원인을 되짚기 어려운 방식으로 나머지 워크샵이 실패한다. 정리는 여기가 아니라 모듈 05 끝에서 다룬다.

이 페이지의 내용

Track your progress?

Optional. We email a link to confirm your address; progress records once you open it.

Please use your work email address, not a personal one.

Progress tracking also requires accepting the current Terms of Service in Privacy settings.

KO