Docker로 AI 애플리케이션 개발·배포 환경 만들기: GPU 설정부터 이미지 분리까지

NVIDIA Container Toolkit 설치, 개발용 Compose 구성, 배포용 CUDA 이미지 작성과 자주 막히는 지점

Docker로 AI 앱 환경 만들기 썸네일

핵심 요약

  1. GPU 컨테이너에는 호스트의 NVIDIA 드라이버와 NVIDIA Container Toolkit이 필요하며, 설치 후 nvidia-ctk runtime configure와 Docker 재시작을 해야 한다.
  2. 개발용은 코드 폴더를 마운트하는 Compose 구성으로, 배포용은 필요한 패키지만 담은 이미지로 나눈다.
  3. nvidia/cuda runtime 이미지에는 Python이 없으므로 Dockerfile에서 python3와 pip을 먼저 설치한다.
  4. Compose GPU 예약에서 count와 device_ids는 동시에 쓸 수 없다.

AI 애플리케이션은 Python 버전, 프레임워크 버전, CUDA 라이브러리, GPU 드라이버가 서로 맞물려야 동작한다. 한 대의 개발 PC에서 여러 프로젝트를 오가거나, 개발 서버와 운영 서버의 환경을 똑같이 맞춰야 할 때 Docker가 이 문제를 줄여 준다. 이 글은 GPU를 쓰는 AI 애플리케이션을 기준으로 호스트 준비, 개발용 컨테이너, 배포용 이미지를 차례로 만들고, 처음 구성할 때 자주 막히는 지점을 정리한다.

개발용 컨테이너와 배포용 이미지는 목적이 다르다

둘을 한 Dockerfile로 해결하려고 하면 개발 도구가 운영 이미지에 섞이거나, 반대로 개발할 때마다 이미지를 다시 빌드하는 불편이 생긴다. 처음부터 나눠 생각하면 각 설정이 단순해진다.

구분개발용배포용
코드호스트 폴더를 볼륨으로 마운트해 바로 수정이미지 안에 복사해 고정
포함 도구Jupyter, 디버거, 분석 라이브러리실행에 필요한 패키지만
실행 방법docker compose up레지스트리에 올린 이미지를 서버에서 실행
데이터·가중치볼륨으로 호스트에 보존외부 스토리지나 볼륨에서 로드

호스트 준비: 드라이버와 NVIDIA Container Toolkit

컨테이너가 GPU를 쓰려면 호스트에 NVIDIA 드라이버와 NVIDIA Container Toolkit이 있어야 한다. CUDA 라이브러리는 이미지 안에 들어가지만 드라이버는 호스트 것을 공유하는 구조다. NVIDIA 설치 가이드의 Ubuntu·Debian 절차는 다음과 같다.

  1. 배포판 패키지 관리자로 NVIDIA 드라이버를 설치한다(가이드가 권장하는 방식).
  2. NVIDIA 저장소의 GPG 키와 패키지 목록을 등록한다.
  3. nvidia-container-toolkit 패키지를 설치한다.
  4. nvidia-ctk로 Docker 런타임 설정을 추가하고 Docker를 재시작한다.
  5. 샘플 컨테이너에서 nvidia-smi를 실행해 GPU가 보이는지 확인한다.
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey \
  | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list \
  | sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' \
  | sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
sudo apt-get update
sudo apt-get install -y nvidia-container-toolkit

sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker

# 확인: 드라이버·CUDA 버전과 GPU 이름이 표로 나오면 성공
sudo docker run --rm --runtime=nvidia --gpus all ubuntu nvidia-smi

설치 가이드는 패키지 버전을 NVIDIA_CONTAINER_TOOLKIT_VERSION 변수로 고정해 설치하는 예시도 함께 제공한다. 여러 서버의 구성을 똑같이 맞춰야 한다면 버전을 고정하는 쪽을 택한다.

개발 환경: Compose로 GPU와 코드 폴더 연결하기

개발용 컨테이너는 매번 긴 docker run 옵션을 치는 대신 compose.yaml에 적어 두면 팀원 누구나 같은 명령으로 띄울 수 있다. GPU 예약 형식은 Docker Compose GPU 문서를 따른다.

# compose.yaml
services:
  dev:
    image: nvcr.io/nvidia/pytorch:23.08-py3   # NGC 카탈로그에서 필요한 월 버전 태그 확인
    working_dir: /workspace
    volumes:
      - ./:/workspace          # 코드와 결과물을 호스트에 보존
    ports:
      - "8888:8888"
    command: jupyter lab --ip=0.0.0.0 --no-browser --allow-root
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1            # 또는 "all"
              capabilities: [gpu]

문서에 따르면 count는 예약할 GPU 개수(정수 또는 "all")이고, 특정 GPU를 지정하려면 nvidia-smi로 확인한 ID를 device_ids에 적는다. 두 필드는 한 서비스에서 동시에 쓸 수 없다. 컨테이너를 지우면 내부에 쓴 파일은 사라지므로, 학습 결과와 모델 가중치는 반드시 마운트한 경로 아래에 저장한다.

배포용 이미지: CUDA 런타임 위에 Python을 직접 올리기

nvidia/cuda 이미지는 세 종류다. base는 CUDA 런타임(cudart)만, runtime은 여기에 CUDA 수학 라이브러리와 NCCL을, devel은 헤더와 빌드 도구까지 포함한다. 추론 서버처럼 실행만 하는 이미지는 runtime 계열이 적합하다. 이 이미지들은 CUDA 구성 요소 중심이라 Python과 pip은 Dockerfile에서 직접 설치해야 한다. 이 단계를 빠뜨리고 RUN pip install부터 쓰면 빌드가 실패한다.

# Dockerfile (FastAPI 추론 서버 예시)
FROM nvidia/cuda:12.9.0-runtime-ubuntu22.04

RUN apt-get update \
 && apt-get install -y --no-install-recommends python3 python3-pip \
 && rm -rf /var/lib/apt/lists/*

WORKDIR /app
COPY requirements.txt .
RUN pip3 install --no-cache-dir -r requirements.txt

COPY . .
EXPOSE 8000
CMD ["python3", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
# .dockerignore — 이미지에 들어가지 않게 할 것
.git
__pycache__/
.venv/
data/
checkpoints/
.env

requirements.txt를 코드보다 먼저 복사하는 이유는 레이어 캐시 때문이다. 코드만 바뀐 경우 패키지 설치 레이어를 다시 실행하지 않아 빌드가 빨라진다. 연관된 apt-get 명령을 &&로 한 RUN에 묶고 같은 줄에서 패키지 목록 캐시를 지우는 것도 레이어 크기를 줄이는 방법이다. 비밀 값이 담긴 .env는 .dockerignore에 넣고 실행 시 환경 변수로 주입한다.

빌드 도구가 필요하면 멀티 스테이지로 분리

C 확장을 컴파일해야 하는 패키지가 있으면 devel 이미지에서 빌드하고 결과물만 runtime 이미지로 옮긴다. 멀티 스테이지 빌드 문서의 방식대로 FROM … AS build로 단계를 나누고 COPY --from=build로 필요한 파일만 가져오면, 컴파일러와 헤더가 최종 이미지에 남지 않는다.

이렇게 만든 이미지 안의 애플리케이션 구조, 예를 들어 외부 모델 API 호출을 비동기로 처리하는 엔드포인트 설계는 FastAPI 비동기 API 설계 기초에서 다룬다. 배포 후 응답 시간과 오류율을 어떻게 지켜볼지는 LLMOps 모니터링 항목을 참고한다.

처음 구성할 때 자주 막히는 지점

  • could not select device driver "" with capabilities: [[gpu]] — 호스트에 NVIDIA Container Toolkit이 없거나 nvidia-ctk runtime configure 후 Docker를 재시작하지 않은 경우에 흔히 나온다. 위 확인 명령부터 다시 실행한다.
  • 컨테이너 안 nvidia-smi의 CUDA 버전과 이미지 CUDA 버전이 다르다 — nvidia-smi에 표시되는 값은 호스트 드라이버가 지원하는 최대 버전이다. 이미지의 CUDA 버전이 그보다 높으면 드라이버를 올리거나 더 낮은 CUDA 태그를 고른다.
  • Ubuntu 24.04 기반 이미지에서 pip install이 거부된다 — 시스템 Python을 배포판이 관리하도록 표시해 두어 전역 설치를 막기 때문이다. 가상 환경을 만들어 그 안에 설치하거나, 위 예시처럼 22.04 기반을 쓴다.
  • 학습 결과가 사라졌다 — 마운트하지 않은 경로에 저장한 뒤 컨테이너를 지운 경우다. 저장 경로가 볼륨 아래인지 확인한다.

컨테이너는 호스트 커널을 공유하므로 가상 머신만큼 격리되지 않는다. 출처가 불분명한 이미지 대신 공식 이미지를 쓰고, 배포 이미지는 레지스트리의 취약점 스캔 기능으로 정기적으로 점검한다.

초보자가 자주 실수하는 포인트

  • nvidia/cuda 이미지에서 Python 설치 없이 바로 pip install을 실행해 빌드가 실패하는 경우
  • Toolkit 설치 후 Docker를 재시작하지 않아 --gpus 옵션이 드라이버 선택 오류를 내는 경우
  • 마운트하지 않은 경로에 모델 가중치를 저장했다가 컨테이너 삭제와 함께 잃는 경우
  • .env나 데이터 폴더를 .dockerignore에 넣지 않아 이미지에 비밀 값과 대용량 파일이 들어가는 경우

체크리스트

  • 호스트에서 sudo docker run --rm --runtime=nvidia --gpus all ubuntu nvidia-smi가 GPU 표를 출력하는가
  • 이미지의 CUDA 버전이 호스트 nvidia-smi의 CUDA 버전 이하인가
  • 개발용 Compose에서 결과물 저장 경로가 볼륨 아래에 있는가
  • 배포용 Dockerfile이 requirements.txt를 코드보다 먼저 복사하는가
  • .dockerignore에 .git, .env, 데이터·체크포인트 폴더가 들어 있는가

자주 묻는 질문

Windows에서도 같은 방식으로 GPU 컨테이너를 쓸 수 있나요?

Windows에서는 WSL 2 기반 Docker 환경에서 GPU를 사용하며 설치 절차가 Linux와 다르다. NVIDIA와 Docker의 WSL 관련 문서를 따로 확인해야 한다.

base, runtime, devel 중 무엇을 골라야 하나요?

실행만 하는 추론 서버는 runtime, CUDA 코드를 컴파일해야 하면 devel을 빌드 단계에만 쓰고 결과물을 runtime 이미지로 옮기는 멀티 스테이지 구성이 일반적이다. base는 CUDA 런타임만 필요할 때 쓴다.

참고 자료 · 검증 기준

위 자료와 내용을 대조한 날짜: . 도구·서비스 정책은 이후 바뀔 수 있으므로 적용 전 공식 문서를 다시 확인하세요.

이 글은 실제 실무 환경 테스트 및 검증된 기술 자료를 바탕으로 작성되었으며, 환경 변화에 따라 지속적으로 갱신됩니다.