pile·
DB / 데이터·스캐터랩스캐터랩 (이루다)·

Apache Beam으로 머신러닝 데이터 파이프라인 구축하기 2편 - 개발 및 최적화

Apache Beam으로 머신러닝 데이터 파이프라인을 개발하면서 맞닥뜨린 유지보수성과 성능 문제를 다룬다. Composite PTransform으로 로직을 모듈화하고, key·Fusion·batching을 조절해 Dataflow 병목을 푼다. 모델과 Faiss index처럼 큰 객체를 worker에서 공유하는 방법과 protobuf 2GB 제한을 우회하는 설계까지 이어진다.

핵심 포인트
  • DoFn을 무분별하게 늘리면 schema 변화 추적, 코드 재사용, 단위 테스트가 어려워지므로 여러 변환을 Composite PTransform으로 묶는다.
  • key space가 너무 좁으면 parallel degree가 낮아지고, high-fanout 변환이 Fusion되면 출력이 한 worker에 몰릴 수 있다.
  • `Reshuffle`로 불리한 Fusion을 끊고, 데이터 축소 변환을 앞에 배치하며, 외부 요청은 bundle 단위로 batch 처리한다.
  • Beam `Shared` 객체로 thread가 하나의 모델을 공유하고 `no_use_multiple_sdk_containers`로 worker당 process 수를 하나로 제한할 수 있다.
  • `BatchElements`는 실행 중 적절한 batch size를 찾아 GPU utilization을 높이며, TFX `RunInference`는 모델 공유와 batching을 묶어 제공한다.
  • Faiss용 embedding을 한 element로 합치면 protobuf 2GB 한계를 넘으므로 PCollection을 side input으로 전달해 index를 만든다.
상세 정리
  • Beam 추상화: PCollection은 분산 데이터셋을 메모리 컬렉션처럼 다루게 하고, PTransform은 입력 PCollection의 각 element를 처리해 새 PCollection을 만든다. ParDo의 사용자 로직은 DoFn `process()`에 구현한다.
  • 유지보수 문제: 개별 DoFn이 난립하면 각 단계가 schema를 어떻게 바꾸는지 알기 어렵고 같은 처리가 여러 pipeline에 복제된다. 전체 흐름을 작은 단위로 잘라 테스트하기도 어려워진다.
  • Composite Transform: 여러 ParDo와 조건 분기를 하나의 PTransform `expand()` 안에 캡슐화했다. 입력별 중복을 없애고 condition만으로 출력 schema가 결정되게 해 가독성과 테스트 가능성을 높였다.
  • 분산 key: GroupByKey뿐 아니라 ParDo도 key에 따라 bundle 분배 정도가 달라진다. key 종류가 적으면 random string을 붙이는 방식으로 key space를 늘릴 수 있다.
  • Fusion 역효과: Dataflow는 직렬화 비용을 줄이려고 변환을 합친다. 그러나 high-fanout A와 후속 B가 합쳐지면 A의 수백·수천 출력이 여러 worker로 퍼지지 못하고 한 worker에 머문다.
  • Fusion 차단: aggregation 전후에는 Fusion이 일어나지 않는 특성을 이용해 `Reshuffle`을 삽입한다. 데이터 자체는 바꾸지 않고 실행 graph의 분산 경계를 만든다.
  • 일반 최적화: 데이터를 줄이는 변환은 앞쪽에 둔다. `process()`의 과도한 로그는 Cloud Logging 부하를 만들고, DB·서버 호출은 `start_bundle`과 `finish_bundle`에서 batch 처리한다.
  • 모델 메모리: worker의 한 process가 수백 thread를 만들 수 있어 각 DoFn `setup()`에서 모델을 로드하면 GPU OOM이 난다. Beam 2.24 이후 `Shared.acquire()`로 thread가 모델 하나를 함께 쓰게 했다.
  • Process 한계: Shared는 thread 사이에서만 동작해 process마다 모델이 하나씩 생긴다. CPU 수를 조정하거나 `experiments=no_use_multiple_sdk_containers`로 worker당 process를 하나로 강제한다.
  • GPU 활용: element를 한 개씩 추론하면 작은 입력에서 GPU가 놀게 된다. `BatchElements(min_batch_size, max_batch_size)`는 실행 시 batch 크기를 탐색하며, SavedModel이면 TFX `RunInference`로 같은 최적화를 재사용할 수 있다.
  • Faiss 병목: 모든 embedding을 `ToList()`로 하나의 element에 모으면 worker 간 gRPC 직렬화에 쓰는 protobuf의 element당 2GB 제한을 넘는다.
  • Side input 해법: embedding PCollection을 `AsList` side input으로 넘겨 준비가 끝날 때까지 기다린 뒤 메모리에서 index를 만든다. Shared로 Faiss index도 한 번만 생성해 query transform들이 재사용한다.
왜 읽나Beam과 Dataflow에서 ML 추론·vector search를 운영하며 분산 실행 구조와 메모리 병목을 함께 해결해야 하는 데이터 엔지니어에게 유용하다.
스캐터랩
스캐터랩 (이루다) 블로그
원문은 여기서 이어서 읽을 수 있어요
원문 읽기
읽음 (0)

이 글과 비슷한

  1. DB / 데이터·spotify-engSpotify Engineering·

    데이터 레이크 온라인 포인트 쿼리 인덱싱 — RAP

    Spotify 엔지니어링이 페타바이트 규모 데이터 레이크(GCS)에 저장된 Parquet 파일에서 한 자릿수 밀리초 레이턴시로 포인트 쿼리를 수행하는 RAP(Random Access Parquet) 방식을 공개했다. Trino·BigQuery 같은 분산 SQL 엔진은 단일 행 조회도 스케줄링·플래닝 비용으로 수 초가 걸리지만, RAP는 외부 인덱스로 키→(파일, 행 번호)를 직접 매핑해 의존적 read chain을 병렬화함으로써 이 문제를 해결했다. 기존 KV 스토어(Bigtable)와 달리 GB당 비용이 클라우드 스토리지 read 수준에 불과해 장기 이력 데이터나 롱테일 피처도 인터랙티브 조회 대상이 된다.

    #data-engineering#indexing#data-lake+2
  2. DB / 데이터·여기어때 (GC컴퍼니)여기어때 (GC컴퍼니)·

    데이터 통합— MongoDB 원칙으로 document를 통합하고 동기화를 재설계하다 (3/3)

    여기어때 전시개발팀은 ES 단일 대문서 → MongoDB v1 RDB 1:1 파편화 → v2 도메인 단위 통합이라는 세 차례 구조 전환을 거쳐 전체 동기화 시간을 약 1시간에서 10분으로 단축했다. 이 글은 3부작의 마지막으로, v2 통합 문서 구조에 맞춰 "$set/$unset 필드 단위 부분 갱신 + 이벤트 발행" 기반 동기화를 어떻게 재설계했는지 구체적인 MongoDB 패턴과 함께 설명한다.

    #denormalization#data-sync#mongodb+2
  3. DB / 데이터·pinterest-engPinterest Engineering·

    Pinterest 차세대 DB 수집 프레임워크의 자동화된 스키마 진화

    Pinterest의 CDC 기반 DB 수집 파이프라인은 MySQL에서 Kafka, Flink, Spark, Iceberg를 거치는 다층 구조다. 스키마 변경이 생기면 모든 계층을 동시에 업데이트해야 해 드리프트, 배포 실패, 데이터 불일치가 반복됐다. Pinterest 엔지니어링 팀은 이를 해결하기 위해 가산적 변경만 자동화하는 스키마 진화 프레임워크를 구축하고, PR 기반 롤아웃과 SLA 기반 일관성 모델을 도입했다.

    #data-pipeline#apache-flink#cdc+2