AI/데이터 분석 프로젝트에 참여할 때, 환경 구성이나 라이브러리 버전 관리는 첫 번째이자 가장 까다로운 벽 중 하나입니다. '내 컴퓨터에서는 작동하는데 다른 PC에서는 에러가 난다', '운영 환경에 배포했더니 작동하지 않게 되었다'와 같은 경험을 한 엔지니어는 적지 않습니다.
본 기사에서는 이러한 과제를 근본적으로 해결하고, 현장에서 요구하는 재현성(Reproducibility)과 개발 효율성을 모두 갖춘 모던 AI/데이터 분석 환경 구축 방법에 대해 Docker, 그리고 Python의 최신 패키지 관리 도구인 Poetry와 uv를 조합하여 설명합니다.
AI/데이터 분석 프로젝트에서는 Pandas, scikit-learn, PyTorch, TensorFlow 등 매일 진화하는 다양한 Python 라이브러리를 조합하여 복잡한 의존 관계를 가진 환경을 구축하는 것이 일반적입니다. 하지만 이 '환경' 자체가 프로젝트의 발목을 잡는 가장 큰 요인이 되기도 합니다.
환경 재현성의 부족: 개발자 PC마다 OS나 Python 버전이 다르고, 설치되는 라이브러리 버전도 제각각인 경우가 많습니다.
pip freeze > requirements.txt
는 해당 시점의 패키지 목록을 생성하지만, 추이적 의존 관계(어떤 라이브러리가 의존하는 다른 라이브러리)의 버전까지 엄격하게 고정할 수는 없습니다. 결과적으로 환경 간에 미묘한 동작 차이를 만들어냅니다. 특히 AI 모델 학습이나 추론에서는 GPU 환경이 필요한 경우가 많아, 그 설정 또한 복잡해지기 쉽습니다.
패키지 관리의 복잡성: 개발 환경과 운영 환경에서 필요한 패키지가 다르더라도, 기존의 requirements.txt만으로는 이를 명확하게 분리하기 어렵습니다.
가상 환경 관리가 수동적이 되기 쉬워 프로젝트마다 적절한 가상 환경을 전환하는 번거로움이 발생합니다.
개발 효율성의 저하: 신규 멤버 투입 시나, 다른 프로젝트를 전환할 때마다 매번 처음부터 환경을 구축해야 하는 수고가 발생합니다.
- 라이브러리 설치 속도가 느리면 작은 변경에도 대기 시간이 생겨 개발 사이클이 지연됩니다.
이러한 과제들은 프로젝트 지연이나 품질 저하로 직결되며, 현장에서는 환경으로 인한 디버깅에 많은 시간을 할애하게 됩니다. 또한, 의존 관계를 엄격하게 관리하지 못하면 취약점을 가진 라이브러리가 의도치 않게 포함될 위험성도 높아집니다.
이러한 과제들을 해결하기 위해, Docker를 이용한 환경 컨테이너화, Poetry를 통한 엄격한 의존 관계 관리, 그리고 uv를 통한 초고속 패키지 설치를 결합하는 접근 방식을 권장합니다.
Docker는 애플리케이션과 그 실행 환경을 컨테이너로 묶어 관리하는 기술입니다. OS 레벨에서 의존 관계를 분리하여, 어떤 환경에서도 동일하게 동작하는 포터블한 실행 환경을 제공합니다.
- 재현성: 팀원 모두가 같은 Dockerfile을 사용해 컨테이너를 빌드함으로써 완전히 동일한 환경이 구축됩니다.
- 분리와 독립성: 호스트 OS의 환경을 오염시키지 않고, 프로젝트별로 독립된 환경을 구성할 수 있습니다.
- 배포 용이성: 개발 환경과 운영 환경 간의 격차(Gap)를 메우고, 컨테이너 이미지를 그대로 배포함으로써 일관성과 안정성을 확보합니다.
Poetry는 Python 프로젝트의 의존 관계 관리와 패키징을 통합적으로 수행하는 도구입니다.
- : 프로젝트의 메타데이터와 직접적인 의존 관계를 단일 파일에 기술합니다. 개발용과 운영용 의존 관계도 명확하게 분리할 수 있습니다.
pyproject.toml
으로 의존 관계 정의 -
:
poetry.lock
을 통해 엄격한 버전 고정
poetry install
를 실행하면, 추이적 의존 관계까지 모든 패키지 버전이 poetry.lock
파일에 고정됩니다.
- 가상 환경 자동 관리: 프로젝트별로 가상 환경을 자동으로 생성하여 호스트 환경과의 충돌을 방지합니다.
uv는 Rust로 작성된 새로운 Python 패키지 설치 도구입니다.
- 놀라운 속도: 수백~수천 개의 패키지를 가진 환경에서도 몇 초에서 수십 초 만에 설치를 완료하여, 개발 사이클을 현저하게 가속화합니다.
- Poetry/표준 구성과의 호환성:
pyproject.toml
이나 poetry.lock
으로부터 의존 관계를 해결하고 설치하는 것이 가능합니다.
다음으로, Docker, Poetry, uv를 결합한 실질적인 구성 예시를 보여드립니다.
# --- 베이스 스테이지 ---
FROM python:3.10-slim-buster AS base
RUN apt-get update && \
...
[tool.poetry]
name = "my-data-project"
version = "0.1.0"
...
:
poetry.lock
생성
로컬에서 의존성을 잠급니다. poetry lock
Docker 이미지 빌드: docker build -t my-data-project:latest .
Docker 컨테이너 실행: docker run --rm -v "$(pwd):/app" my-data-project:latest
실제 운영 시 발생하기 쉬운 문제점과 해결책입니다.
로컬 환경에서 pyproject.toml을 업데이트했을 때, poetry lock을 실행하지 않고 빌드하면 오래된 잠금 파일(lock file)을 기반으로 컨테이너가 생성됩니다.
pyproject.toml을 변경할 때는 반드시 poetry lock을 실행하여 Git 커밋한 후에 빌드를 진행하는 운영 방식을 철저히 지켜야 합니다.
COPY . .를 의존성 설치 전에 배치하면, 소스 코드를 변경할 때마다 무거운 패키지 설치 처리의 캐시가 파기됩니다.
작성 순서는 '시스템 패키지 ➔ 의존성 정의 파일 복사 ➔ uv pip install ➔ 애플리케이션 코드 복사' 순서로 배치해 주세요.
uv는 기본적으로 .venv(가상 환경)를 생성하려고 합니다. Docker 내부에서 시스템 전체에 직접 설치시키고 싶다면, 위 레이어 예시처럼 --system 플래그를 명시적으로 지정하여 실행합니다.
GPU 활용: CUDA 환경이 필요한 경우, nvidia/cuda와 같은 공식 베이스 이미지를 선택합니다.
대규모 데이터: 대용량 데이터셋은 컨테이너 이미지에 포함하지 않고, Docker Volume이나 S3 등의 스토리지를 마운트하거나 참조하는 설계로 합니다.
단순히 도구별 명령어 암기에 그치지 않고, '왜 Docker가 필요한지(환경 분리 및 재현성)', '왜 Poetry가 필요한지(선언적 의존성 관리)', '왜 uv가 필요한지(압도적인 빌드 속도)'와 같은 본질적인 문제 해결 구조를 인식하는 것이 중요합니다. 에러가 발생했을 때는 로그를 주의 깊게 해석하는 습관을 들이세요.
노하우를 README.md나 Wiki에 명시하고, CI/CD 파이프라인에 컨테이너 빌드를 통합함으로써 팀 전체의 온보딩 시간 단축과 빌드 자동화가 가능해집니다.
재현성과 효율성을 겸비한 환경을 구축하여, 본래의 AI/데이터 분석 업무에 집중합시다.
DokuPro에서는 가르치고 싶은 사람과 배우고 싶은 사람을 연결하며, 신규 등록(선생님・학생)자를 모집하고 있습니다.
자세히 보기: https://dokupro.dev/