대용량 데이터 파이프라인 실무 — GPU 가속 집계부터 실시간 대시보드까지

RAPIDS·Dask-cuDF GPU 가속과 Spark Structured Streaming, ClickHouse 서빙으로 구성하는 대용량 데이터·실시간 대시보드 파이프라인 설계를 정리한다.

2026-08-12 · 최초 발행 2024-04-29

수십 GB에서 수 TB급 데이터셋을 안정적으로 적재·정제·집계·서빙하려면 배치·스트리밍 파이프라인을 함께 설계해야 한다. 여기에 CUDA 코어 기반 병렬 연산으로 ETL·통계·ML 워크로드를 가속하는 GPU 컴퓨팅과, 초·분 단위로 갱신되는 KPI를 스트리밍으로 수집·처리·가시화하는 실시간 대시보드까지 결합하면 대용량 데이터 실무의 표준 구조가 완성된다.

계층형 파이프라인과 GPU 가속 스택

기본 구조는 수집(로그/이벤트/Kafka) → 저장(Data Lake/Delta) → 처리(배치·스트림) → 서빙(OLAP/Cache/Dashboard) 계층을 분리하는 것이다. 스키마 관리, 파티셔닝, 체크포인트·리트라이를 계층마다 포함시켜야 신뢰성과 재현성을 확보할 수 있다.

GPU 가속 분석 스택은 RAPIDS cuDF/cuML과 Dask-cuDF로 단일·다중 GPU 병렬 프레임 처리를 수행하고, Spark RAPIDS Accelerator로 SQL/ETL을 가속한다. 가속 효과는 고카디널리티 groupby, 조인, 벡터화 가능 연산에서 크게 나타나지만 데이터 전송·메모리 한계를 함께 고려해야 한다. 실시간 처리 구간은 Kafka/Flink/Spark Structured Streaming으로 저지연 파이프라인을 구성하고 Watermark·Exactly-once를 보장하며, 집계 결과는 ClickHouse·Redis·Pinot 같은 실시간 저장소에 담아 Superset·Grafana로 서빙한다. 운영·거버넌스 측면에서는 스키마 레지스트리·Great Expectations로 데이터 품질을 관리하고, 메타·계보(Lineage)를 추적하며, DLQ 기반 장애 대응과 Terraform·Helm을 통한 형상·인프라 코드화로 일관성을 유지한다.

전체 아키텍처를 도식화하면 다음과 같다.

DashboardServingProcessingIngestionExactly-once+Checkpointparse erroroffline fix/reprocessStorageData Lake (S3/HDFS/Delta)API/Logs/IoTKafkaBatch: Spark SQLRAPIDSAccelerator(Optional)GPU ETL: RAPIDScuDF/Dask-cuDFStream: Spark StructuredStreaming(Checkpoint/Offset)Schema ValidationDedup/Watermark(ClickHouse/Trino)(Redis/Pinot)(Delta/Parquet Z-Order)SupersetGrafanaDead Letter Queue

핵심 흐름은 입력→검증(스키마·중복 제거·워터마크)→처리(배치/스트림, GPU 가속 선택)→출력(Delta/OLAP/Cache)→대시보드다. 장애 시에는 DLQ로 격리하고 체크포인트 기반으로 재처리한다.

실무 적용 사례

클릭스트림 세션 집계에서는 1억 행/일 규모의 웹 이벤트가 Kafka로 인입되면 Spark Streaming이 watermark 10분 기준으로 세션별 집계를 수행해 Delta에 upsert하고, ClickHouse로 ETL 동기화한다. 일별 재집계나 히스토리 리빌드처럼 대량 재처리가 필요할 때는 Dask-cuDF를 사용해 10~30배 가속 효과를 얻을 수 있다.

제조 설비 이상 탐지에서는 10k msg/sec 규모의 센서 스트림을 입력받아 GPU 기반 cuML Isolation Forest로 배치 추론을 수행하고, 임계치 초과 건은 Redis로 알림을 푸시한다. 대시보드는 Grafana와 RedisTimeSeries 조합으로 구성하며, 이상 이벤트에 TTL을 설정해 메모리를 안정화한다.

실시간 매출 KPI 사례에서는 POS 거래 이벤트를 거래ID 기반 Upsert 키로 Exactly-once 처리하고 분단위 윈도우 집계 테이블을 유지한다. 서빙은 Superset에서 사전 집계 뷰를 쿼리하는 방식으로, SLA 2초 이하 응답을 목표로 한다.

설계 절차: 요구사항부터 트랜잭션 보장까지

먼저 지연(초/분/시간), 주 지표, 보존기간, 데이터 볼륨·카디널리티를 명세하고 스트림 우선 여부와 마이크로배치 병행 가능성을 결정한다. 이어서 단일 머신·GPU로 충분한지 분산이 필요한지 판단하는데, 집계 중심(SQL) 연산은 Spark/ClickHouse, 프레임 기반 ETL은 RAPIDS, ML 추론은 GPU/ONNX/TensorRT로 갈리는 것이 일반적인 선택 매트릭스다.

데이터 모델·파티셔닝 단계에서는 이벤트일자·조직/지역·고유키 Hash를 혼합한 파티션 키를 설계하고, 파일 크기는 256MB~1GB를 타깃으로 소파일을 방지하며 Z-Order로 주 조회 키를 정렬한다. 트랜잭션·일관성 확보를 위해서는 Kafka offset과 Spark checkpoint, idempotent upsert(Delta MERGE 또는 ClickHouse ReplacingMergeTree)를 조합해 Exactly-once를 구현하고, event_time 기반 허용 지연으로 워터마크·중복 제거를 처리하며 late data는 DLQ 후 배치로 보정한다. 동시성 문제는 테이블 레벨 락을 피하고 파티션 단위로 병렬 머지해 완화한다. 마지막으로 역압(backpressure)과 재시도 지수 증가, 실패 배치 재처리, Avro/Schema Registry 기반 스키마 진화 관리(Backward/Full 호환성 정책)로 장애·에러 핸들링 체계를 갖춘다.

실행 방식별 특성을 비교하면 다음과 같다.

접근 방식 성능 확장성 일관성 안정성 운영 편의
Pandas(단일 CPU) 소~중규모 양호, 대용량 취약 낮음 프로세스 단위 중간 매우 높음
RAPIDS 단일 GPU 대규모 집계 10~50배 가속 GPU 메모리 한계 있음 프로세스 단위 GPU 메모리 관리 필요 중간
Dask-cuDF 다중 GPU 선형에 가까운 스케일아웃 높음 파티션 일관성 클러스터 안정화 필요 중간
Spark CPU 클러스터 대용량 안정 처리 매우 높음 체크포인트/원자성 높음 높음
Spark + RAPIDS SQL/ETL 가속 + 분산 매우 높음 Spark 보장 준수 높음 중간

워크로드·데이터 특성에 따라 결과가 달라지므로 PoC로 목표 SLA·비용을 검증하는 것을 권장한다.

코드로 보는 최소 구현

전제조건은 NVIDIA GPU(CUDA 12.x), Python 3.10이며 드라이버·라이브러리 호환성은 최신 정보를 확인해야 한다.

RAPIDS cuDF로 단일 GPU에서 대용량 집계하기. 환경 설치는 conda create -n rapids-24.08 -c rapidsai -c conda-forge -c nvidia rapids=24.08 python=3.10 cuda-version=12.2 명령으로 시작한다.

# python >=3.10, RAPIDS 24.08
import os
import rmm
import cudf

# 메모리 풀 초기화(Out-of-memory 감소)
rmm.reinitialize(pool_allocator=True, initial_pool_size=8_000_000_000)  # 8GB 풀

# Parquet 읽기 및 집계
df = cudf.read_parquet("s3://bucket/events/date=2025-11-01/*.parquet",
                       columns=["user_id", "event_time", "amount"])

# 고카디널리티 키 집계
g = df.groupby("user_id").agg({"amount": "sum"})
g = g.sort_values("amount", ascending=False)

# 결과 저장
g.to_parquet("s3://bucket/output/top_users/2025-11-01/", compression="snappy")

GPU 메모리보다 데이터가 큰 경우에는 Dask-cuDF로 다중 GPU에 분산시킨다.

from dask_cuda import LocalCUDACluster
from dask.distributed import Client
import dask_cudf as dc

cluster = LocalCUDACluster()  # 모든 로컬 GPU 사용
client = Client(cluster)

ddf = dc.read_parquet("s3://bucket/events/date=2025-11-*/")
ddf = ddf[["user_id", "amount"]].repartition(npartitions=8*len(client.scheduler_info()["workers"]))

result = ddf.groupby("user_id").amount.sum().persist()
out = result.compute()  # cuDF Series 반환
out.to_frame().to_parquet("s3://bucket/output/top_users/all/", compression="zstd")

통신 비용을 줄이려면 user_id 기준 해시로 사전 파티션하는 것이 최적화 팁이다.

Spark Structured Streaming으로 Kafka에서 Delta까지 실시간 집계하기. 의존성은 Spark 3.4+, Delta Lake 2.x, Kafka connector이며, RAPIDS 가속을 선택적으로 붙일 수 있다(spark.plugins=com.nvidia.spark.SQLPlugin, spark.rapids.sql.enabled=true).

# pyspark-shell 실행 예:
# pyspark --packages io.delta:delta-spark_2.12:2.4.0,org.apache.spark:spark-sql-kafka-0-10_2.12:3.4.1 \
#         --conf spark.sql.extensions=io.delta.sql.DeltaSparkSessionExtension \
#         --conf spark.sql.catalog.spark_catalog=org.apache.spark.sql.delta.catalog.DeltaCatalog

from pyspark.sql import SparkSession, functions as F

spark = (SparkSession.builder.appName("kafka-to-delta")
         .config("spark.sql.shuffle.partitions", "200")
         .getOrCreate())

events = (spark.readStream.format("kafka")
          .option("kafka.bootstrap.servers", "broker:9092")
          .option("subscribe", "events")
          .option("startingOffsets", "latest")
          .load())

# JSON 파싱
schema = "event_time timestamp, user_id string, amount double, tx_id string"
parsed = (events.select(F.from_json(F.col("value").cast("string"), schema).alias("j"))
          .select("j.*"))

# 워터마크 + 중복 제거
clean = (parsed
         .withWatermark("event_time", "10 minutes")
         .dropDuplicates(["tx_id"]))

agg = (clean.groupBy(F.window("event_time", "1 minute"), F.col("user_id"))
       .agg(F.sum("amount").alias("amt_sum")))

# Delta 싱크(Exactly-once: checkpoint + idempotent append)
query = (agg.writeStream
            .format("delta")
            .outputMode("append")
            .option("checkpointLocation", "s3://bucket/chk/events_min/")
            .option("path", "s3://bucket/delta/events_min/")
            .trigger(processingTime="30 seconds")
            .start())

query.awaitTermination()

서빙 단계에서는 Superset에서 Delta를 Trino/Presto 카탈로그로 연결하거나, ClickHouse로 동기화한 뒤 뷰를 사용한다.

ClickHouse로 실시간 조회용 집계 테이블·뷰를 구성하기.

-- 원천: 외부 ETL로 주입된 events_min(user_id, window_start, amt_sum)
CREATE TABLE kpi_min
(
  user_id String,
  window_start DateTime,
  amt_sum Float64
)
ENGINE = MergeTree
PARTITION BY toYYYYMMDD(window_start)
ORDER BY (user_id, window_start);

-- 대시보드용 뷰(최근 24시간 탑 N)
CREATE OR REPLACE VIEW v_top_users_24h AS
SELECT user_id, sum(amt_sum) AS total
FROM kpi_min
WHERE window_start >= now() - INTERVAL 24 HOUR
GROUP BY user_id
ORDER BY total DESC
LIMIT 100;

Superset에서는 v_top_users_24h를 차트 소스로 지정하고 자동 새로고침 주기를 10~30초로 설정한다.

보안·운영 트레이드오프

전송 구간은 TLS로, 저장 구간은 KMS로 암호화하고 민감정보는 마스킹하며 토픽/테이블 ACL을 분리하는 것이 기본이다. 카탈로그·스키마 계층에서는 스키마 레지스트리와 테이블 진화 정책(Backward 호환)을 적용하고 파이프라인 버전을 태깅해 추적성을 확보한다.

비용과 성능은 트레이드오프 관계다. GPU는 단위 시간 비용이 높지만 처리량·절감 시간으로 상쇄되며, 스팟 인스턴스·오토스케일링을 병행하면 비용 부담을 줄일 수 있다. 신뢰성과 지연도 마찬가지다. Exactly-once·머지 업서트는 지연을 늘리므로, 실시간성 요구가 낮다면 마이크로배치로 단순화하는 편이 낫다. 운영 편의 측면에서는 Prometheus/Grafana 통합 모니터링, 배포 전후 데이터 품질 게이트, Seed·환경 고정을 통한 재현성 확보가 실무 부담을 줄여준다.

이런 구조를 도입한 조직 전반에서 나타나는 효과로는 ETL·집계가 GPU/RAPIDS 기준 1050배 가속되고 지연 0.55초 수준의 실시간 지표를 달성한 사례, 파티셔닝·클러스터 스케일아웃으로 일 단위 수십억 이벤트를 처리하게 된 사례를 들 수 있다. 비용 측면에서는 동일 SLA 기준 노드 수를 30~70% 절감할 수 있고 야간 배치 윈도우도 단축되며, Exactly-once·DLQ·체크포인트로 재처리 일관성이 확보돼 데이터 누락·중복이 줄고, IaC/CI로 배포 시간을 50% 이상 절감하며 처리량·지연·오류율 같은 관측성 지표를 표준화할 수 있다.

계층형 데이터 파이프라인, GPU 가속, 스트리밍 실시간 서빙 체계를 통합적으로 설계하되, PoC로 워크로드 적합성을 먼저 검증하고 트랜잭션 보장·스키마 관리·모니터링을 표준화해 운영 리스크를 최소화하는 순서를 권장한다. Spark+RAPIDS 혹은 Dask-cuDF로 가속하고 ClickHouse/Redis 기반으로 서빙하면 저지연 대시보드를 구현할 수 있다.

데이터파이프라인GPU가속실시간대시보드SparkClickHouse