
Airflow 3 PoC 과정에서 파헤친 멀티프로세싱 성능 제약과 내부 동작 아키텍처 완벽 가이드
본 아티클은 Airflow 3.0 PoC 진행 중 발견한 Python 멀티프로세싱의 한계를 다루는 시리즈의 1편입니다. Airflow가 분리된 태스크 실행을 위해 왜 멀티프로세싱을 선택했는지 설명하고, Python의 대표적 프로세스 생성 방식인 fork와 spawn의 메모리 및 실행 속도 차이를 실제 벤치마크 데이터를 통해 명쾌하게 비교합니다. 최종적으로 Airflow가 이 두 방식 중 어떤 타협점을 선택했는지와 supervisor 기반 태스크 실행 모델을 공유합니다.
배치 워크플로 플랫폼을 운영하거나 대규모 Python 분산 프로세싱을 설계할 때, 자식 프로세스의 무거운 모듈 임포트 비용과 메모리 오버헤드를 제어하기 위해 반드시 프로세스 스타트 메소드(fork/spawn)의 동작 특성과 안정성 트레이드오프를 사전에 검토해야 합니다.
Apache Airflow는 다수의 태스크를 병렬로 실행하면서도, 각 태스크의 독립성을 보장하고 부모 프로세스의 전역 모듈 오염을 방지하기 위해 격리성이 확보된 태스크 실행 환경이 필요합니다. 하지만 Python의 CPython 표준 구현체는 GIL(Global Interpreter Lock) 제약이 있어 단일 인터프리터 내에서 멀티코어 CPU의 병렬 처리 이점을 활용하지 못합니다.
Airflow는 태스크당 독립된 프로세스를 생성하여 실행하고 종료 시 폐기하는 멀티프로세싱 방식을 도입하였으며, 이를 구현하기 위해 Python의 fork와 spawn 멀티프로세싱 방식의 물리적 메모리 점유(PSS/RSS) 및 프로세스 생성 속도 차이를 비교 분석합니다.
비교 실험 결과, fork 방식은 부모의 페이지 테이블만 복사하는 COW(Copy-on-Write) 방식을 사용하여 프로세스 생성(4.3ms) 및 모듈 임포트(0ms)가 매우 빠르고 총 메모리 사용량(120.8MB)도 적은 반면, spawn 방식은 새 인터프리터를 기동해 전체를 다시 임포트하므로 생성(206.6ms)과 임포트(2694.8ms)가 느리고 총 메모리(918.2MB)도 프로세스 수에 비례하여 급증함을 확인했습니다.
Trade-off
fork 방식은 자식 프로세스가 부모의 모든 메모리 공간과 파일 디스크립터를 공유하므로 리소스 효율이 극대화되지만, 멀티스레드 환경에서 fork를 수행할 시 데드락이 발생하거나 불안정한 상태를 초래할 수 있는 잠재적 위험(부작용)이 존재하며, spawn 방식은 완벽히 격리되나 리소스 비용이 급증하는 트레이드오프가 있습니다.
CPython에서 한 번에 하나의 스레드만 Python 바이트코드를 실행하도록 제어하는 뮤텍스입니다. 멀티스레드를 사용하더라도 CPU 집약적인 작업에서는 멀티코어 병렬 처리의 이점을 누릴 수 없게 만드는 제약 요인입니다.
자식 프로세스를 생성할 때 실제 메모리 페이지를 물리적으로 복사하지 않고 부모 프로세스의 페이지를 공유하다가, 어느 한쪽이 쓰기 작업을 수행할 때 비로소 메모리 페이지를 복사하는 운영체제의 메모리 관리 기법입니다.
프로세스가 점유하고 있는 물리 메모리 크기를 나타내는 지표 중 하나로, 여러 프로세스가 공유하고 있는 메모리 페이지의 크기를 공유 프로세스의 수로 나누어 산출한 값입니다.




