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.
Medallion 구조. 데이터는 NYC_TAXI_DB 안에서 세 개의 레이어를 거쳐 이동한다.
- RAW —
TRIPS_RAW(5천만 개의 합성 트립 행. 앱 텔레메트리를 시뮬레이션하는TRIP_METADATAVARIANT컬럼 포함 — 이것이 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 빌드 후 재개된다).
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.sh5-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 플래그는 상위 디렉터리에서 환경 변수를 읽어 온다.
세 대시보드는 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로 변환할 의도적인 마이그레이션 과제를 하나씩 담고 있다.
| 쿼리 | 구문 | 마이그레이션 과제 |
|---|---|---|
| Q1 | DATE_TRUNC, DATEADD | 사소한 구문 차이 |
| Q2 | 윈도우 ROWS BETWEEN | ClickHouse에서 거의 동일하다 |
| Q3 | QUALIFY | v24.5부터 ClickHouse에서 네이티브 지원 — 여기서는 이식성을 위해 여전히 서브쿼리로 다시 쓴다 |
| Q4 | LATERAL FLATTEN | 대응물이 없다 — JSONExtract를 쓰거나 미리 평탄화한다 |
| Q5 | VARIANT 콜론 경로 | JSONExtractFloat/JSONExtractString으로 대체한다 |
| Q6 | MERGE INTO | 대응물이 없다 — ReplacingMergeTree를 쓴다 |
| Q7 | Snowflake 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이 스크립트가 확인하는 항목:
- 데이터베이스와 스키마 —
NYC_TAXI_DB가RAW,STAGING,ANALYTICS와 함께 존재한다. - 테이블과 데이터 —
TRIPS_RAW에 약 5천만 행이 있고,FACT_TRIPS가 채워져 있고, 차원 테이블이 존재한다. - CDC 스트림 —
TRIPS_RAW위에TRIPS_CDC_STREAM이 존재한다. - 예약된 태스크 —
CDC_CONSUME_TASK와HOURLY_AGG_TASK가started상태다. - CDC 활동 — 태스크가 최근에 실행되었다.
- 프로듀서 피드 — 트립 프로듀서가 데이터를 계속 삽입하고 있다.
- 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-dbt | Snowflake 객체는 존재하지만 dbt 변환을 다시 실행할 필요가 없다(예: Terraform 변경 테스트). |
--skip-superset | Docker가 실행 중이 아니거나 아직 BI 레이어가 필요하지 않다. |
--full-refresh | dbt가 모든 증분 모델을 처음부터 다시 만들도록 강제한다(예: 스키마 변경 후). |
플래그는 조합할 수 있다. 흔한 두 가지 조합:
# 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 끝에서 다룬다.