본문 바로가기
Artificial Intelligence/60. Python

[PYTHON] Prefect와 Dagster 워크플로우 의존성 격리 방법 3가지와 환경 충돌 해결을 위한 7가지 실전 전략

by Papa Martino V 2026. 4. 27.
728x90

Prefect vs Dagster
Prefect vs Dagster

 

현대 데이터 엔지니어링에서 PrefectDagster는 에어플로우(Airflow)의 복잡성을 해결하는 차세대 워크플로우 오케스트레이션 도구로 자리 잡았습니다. 그러나 데이터 파이프라인이 복잡해질수록 직면하는 고질적인 문제는 'Python 의존성 지옥(Dependency Hell)'입니다. 예를 들어, 머신러닝 학습 태스크는 PyTorch 2.0이 필요하지만, 데이터 전처리 태스크는 특정 구버전 라이브러리에 의존하는 경우 단일 환경에서 이를 관리하는 것은 불가능에 가깝습니다. 본 포스팅에서는 Prefect와 Dagster를 사용할 때 각 태스크나 잡(Job)별로 파이썬 환경을 완벽하게 격리하여 배포 안정성을 높이는 방법과 실무에서 바로 적용 가능한 7가지 기술적 해결책을 심층 분석합니다.


1. 왜 워크플로우에서 의존성 격리가 중요한가?

오케스트레이터 자체의 실행 환경과 실제 비즈니스 로직이 수행되는 런타임 환경을 분리하지 않으면 다음과 같은 문제가 발생합니다.

  • 버전 충돌: 서로 다른 팀에서 개발한 파이프라인이 하나의 서버에서 충돌하여 전체 시스템이 중단됩니다.
  • 재현 불가능성: 로컬에서는 작동하던 코드가 스테이징이나 프로덕션 서버의 라이브러리 버전 차이로 실패합니다.
  • 비대해진 이미지: 모든 라이브러리를 하나의 컨테이너에 담으면 이미지 크기가 수 GB에 달해 배포 속도가 현저히 느려집니다.

2. Prefect vs Dagster: 환경 격리 메커니즘의 차이

두 도구는 지향하는 철학에 따라 의존성을 격리하는 방식에 뚜렷한 차이를 보입니다.

비교 항목 Prefect (Infrastructure blocks) Dagster (Software-Defined Assets) 핵심 해결 방식
격리 단위 Deployment (Flow 전체) Code Location (프로젝트 단위) 범위의 차이
주요 기술 Docker, Kubernetes, Virtualenv Docker, PEX, Python Virtualenvs 컨테이너화 중심
유연성 동적 인프라 블록 변경 가능 정적 코드 위치 정의 중심 구성 방식의 차이
로컬 개발 Prefect Worker 가동 필요 Dagster Instance 기반 환경 분리 개발 편의성

3. 의존성 격리를 위한 실무 Sample Example (7가지 해결책)

실제 프로덕션 환경에서 파이프라인의 안정성을 보장하기 위해 즉시 복사하여 사용할 수 있는 예제들입니다.

Example 1: Prefect에서 Docker 기반의 격리된 배포 환경 구축

태스크가 실행될 때 전용 도커 컨테이너를 생성하여 환경을 완전히 격리합니다.

from prefect import flow
from prefect.deployments import Deployment
from prefect.infrastructure.docker import DockerContainer

@flow
def my_isolated_flow():
    print("이 코드는 격리된 Docker 컨테이너 내부에서 실행됩니다.")

# Docker 인프라 블록 설정
docker_block = DockerContainer(
    image="my-custom-ds-image:latest",
    auto_remove=True,
    networks=["data-network"]
)

# 배포 설정에 격리 환경 주입
deployment = Deployment.build_from_flow(
    flow=my_isolated_flow,
    name="docker-isolated-deployment",
    infrastructure=docker_block
)

if __name__ == "__main__":
    deployment.apply()
    

Example 2: Dagster에서 Code Location을 이용한 다중 환경 관리

하나의 Dagster 인스턴스 내에서 서로 다른 버전의 라이브러리를 가진 프로젝트들을 공존시킵니다.

# dagster.yaml 또는 workspace.yaml 설정
load_from:
  - python_file: 
      relative_path: "project_v1/repo.py"
      location_name: "ml_training_v1"
      container_image: "ml-v1-image:latest" # V1 환경 격리
  - python_file: 
      relative_path: "project_v2/repo.py"
      location_name: "ml_training_v2"
      container_image: "ml-v2-image:latest" # V2 환경 격리
    

Example 3: Prefect Virtualenv를 이용한 가벼운 로컬 격리

컨테이너까지는 필요 없는 가벼운 작업에서 파이썬 가상환경을 자동으로 생성하고 실행합니다.

from prefect import flow
from prefect.infrastructure import Process

# 실행 시점에 특정 가상환경의 python 인터프리터를 사용하도록 강제
process_block = Process(
    command=["/home/user/venv/bin/python", "-m", "prefect.engine"],
)

@flow
def venv_flow():
    import pandas as pd
    print(f"Pandas version: {pd.__version__}")

# 해당 flow 실행 시 지정된 venv 내의 패키지만 사용함
    

Example 4: Dagster Pipes를 이용한 외부 프로세스 격리 실행

오케스트레이터 외부의 격리된 환경(예: 다른 서버, 다른 컨테이너)에서 스크립트를 실행하고 결과만 수집합니다.

from dagster import asset, open_dagster_pipes
from dagster_k8s import PipesK8sClient

@asset
def k8s_isolated_asset(context, pipes_k8s_client: PipesK8sClient):
    # 외부 K8s 포드에서 완전히 다른 파이썬 환경으로 실행
    return pipes_k8s_client.run(
        context=context,
        image="complex-dependency-image:latest",
        base_pod_spec={...}
    ).get_results()
    

Example 5: PEX(Python Executable) 파일을 활용한 배포 격리

모든 의존성이 포함된 단일 실행 파일을 만들어 라이브러리 설치 과정 없이 즉시 실행합니다.

# 쉘 명령어로 PEX 파일 생성 (Dagster/Prefect 공통 활용 가능)
# pex pandas requests -o my_app.pex -m my_module.main

# Prefect Flow 내부에서 사용
@flow
def pex_flow():
    import subprocess
    subprocess.run(["./my_app.pex"])
    

Example 6: Conda 환경 기반의 동적 태스크 격리 (Dagster)

Dagster의 Resource 기능을 활용하여 태스크 실행 직전에 Conda 환경을 활성화합니다.

from dagster import job, op, ResourceDefinition

@op
def run_in_conda():
    import os
    # 특정 환경 변수와 경로를 조작하여 conda 환경 시뮬레이션
    os.system("conda run -n science_env python my_script.py")

@job
def conda_job():
    run_in_conda()
    

Example 7: 다중 베이스 이미지를 활용한 CI/CD 파이프라인 구축

GitHub Actions를 통해 태스크별로 최적화된 격리 이미지를 자동으로 빌드하는 전략입니다.

# Dockerfile 예시
FROM python:3.11-slim as base
WORKDIR /app

FROM base as spark-job
RUN pip install pyspark==3.4.0

FROM base as ml-job
RUN pip install scikit-learn==1.3.0

# 이후 Prefect/Dagster 배포 시 해당 타겟 이미지를 참조하도록 설정
    

4. 런타임 오버헤드와 격리 수준의 최적화 방법

격리 수준이 높을수록(예: 매번 새 컨테이너 생성) 안정성은 올라가지만 실행 속도는 느려집니다. 이를 해결하기 위한 전략입니다.

  • Warm Pools: 미리 생성된 컨테이너 풀을 유지하여 기동 시간을 단축합니다.
  • Layer Caching: Docker 이미지 빌드 시 의존성이 자주 바뀌지 않는 레이어를 상단에 배치합니다.
  • Micro-dependencies: pip install 대신 필요한 바이너리만 포함된 slim 이미지를 사용하여 전송 오버헤드를 줄입니다.

5. 결론: 어떤 도구와 격리 방법을 선택해야 하는가?

결론적으로 빠른 이터레이션과 유연성이 중요하다면 Prefect의 Infrastructure blocks가 유리하며, 엄격한 타입 체크와 자산 중심의 안정성이 우선이라면 Dagster의 Code Locations 방식이 적합합니다. 어떤 도구를 사용하든 핵심은 '비즈니스 로직과 환경의 분리'에 있습니다. 오늘 소개한 7가지 방법을 통해 더 이상 의존성 충돌로 밤을 지새우지 않는 견고한 파이프라인을 구축하시길 바랍니다.


참고 문헌 및 출처:

  • Prefect Documentation: "Infrastructure and Deployments Guide" (2026)
  • Dagster Insights: "Managing Python Dependencies in Production"
  • Python Packaging User Guide: "Freezing Dependencies for Distributed Flows"
  • Docker Best Practices for Python Data Science Applications
728x90