AI를 위한 Docker

컨테이너(container)는 "내 컴퓨터에서는 되는데요"라는 말을 과거의 일로 만듭니다.

유형: Build 언어: Python 선수 지식: Phase 0, Lessons 01 and 03 예상 시간: 약 60분

학습 목표

  • Dockerfile에서 CUDA, PyTorch, AI 라이브러리가 포함된 GPU 지원 Docker 이미지(GPU-enabled Docker image)를 빌드합니다.
  • 호스트(host) 디렉터리를 볼륨(volume)으로 마운트(mount)해 모델, 데이터셋, 코드를 컨테이너 재빌드(rebuild) 이후에도 유지합니다.
  • NVIDIA Container Toolkit을 설정해 컨테이너 안에서 GPU를 사용할 수 있게 합니다.
  • Docker Compose로 추론 서버(inference server)와 벡터 데이터베이스(vector database) 같은 다중 서비스(multi-service) AI 애플리케이션을 구성합니다.

문제

내 노트북에는 Python 3.13, PyTorch 2.3, CUDA 12.4가 있습니다. 동료 컴퓨터에는 Python 3.10, PyTorch 2.1, CUDA 11.8이 있습니다. 내 모델이 동료 컴퓨터에서 충돌(crash)합니다. 같은 Dockerfile에서 빌드한 컨테이너는 두 컴퓨터에서 같은 환경을 제공합니다.

AI 프로젝트는 의존성 지옥(dependency nightmare)이 되기 쉽습니다. Python, PyTorch, CUDA 드라이버, cuDNN, 시스템 수준 C 라이브러리, flash-attn처럼 컴파일러 버전에 민감한 패키지가 한 스택 안에 들어옵니다. Docker는 이 환경을 하나의 이미지로 묶어 어디서나 동일하게 실행할 수 있게 합니다.

사전 테스트

2문제 · 이 강의를 시작하기 전에 얼마나 알고 있는지 확인해보세요

1.Docker 컨테이너와 가상머신(virtual machine)의 가장 큰 차이는 무엇인가요?

2.Dockerfile은 무엇인가요?

0/2 답변 완료

개념

Docker는 코드, 런타임, 라이브러리, 시스템 도구를 컨테이너라는 격리 단위(isolated unit)로 감쌉니다. 가벼운 가상머신(virtual machine)처럼 생각할 수 있지만, 컨테이너는 호스트 OS 커널을 공유하므로 VM보다 훨씬 빠르게 시작합니다.

graph TD
    subgraph without["Docker가 없을 때"]
        A1["Your machine<br/>Python 3.13<br/>CUDA 12.4<br/>PyTorch 2.3"] -->|crashes| X1["???"]
        A2["Their machine<br/>Python 3.10<br/>CUDA 11.8<br/>PyTorch 2.1"] -->|crashes| X2["???"]
        A3["Server<br/>Python 3.11<br/>CUDA 12.1<br/>PyTorch 2.2"] -->|crashes| X3["???"]
    end

    subgraph with_docker["Docker를 쓸 때 — 모든 곳에서 같은 image"]
        B1["Your machine<br/>Python 3.13 | CUDA 12.4<br/>PyTorch 2.3 | Your code"]
        B2["Their machine<br/>Python 3.13 | CUDA 12.4<br/>PyTorch 2.3 | Your code"]
        B3["Server<br/>Python 3.13 | CUDA 12.4<br/>PyTorch 2.3 | Your code"]
    end

AI 프로젝트에서 Docker가 특히 중요한 이유

  1. GPU 드라이버는 깨지기 쉽습니다(fragile). CUDA 12.4 코드는 CUDA 11.8 환경에서 그대로 동작하지 않을 수 있습니다. Docker는 컨테이너 안의 CUDA 툴킷을 분리하고, NVIDIA Container Toolkit을 통해 호스트 GPU 드라이버를 공유합니다.

  2. 모델 가중치(model weight)는 큽니다. 70억 파라미터 모델은 fp16 기준 약 14 GB입니다. 컨테이너를 재빌드할 때마다 다시 다운로드하고 싶지 않습니다. Docker 볼륨은 호스트의 모델 디렉터리를 컨테이너에 마운트합니다.

  3. 다중 서비스 아키텍처가 흔합니다. 실제 AI 애플리케이션은 Python 스크립트 하나가 아닙니다. 추론 서버, RAG용 벡터 데이터베이스, 웹 프론트엔드(web frontend)가 함께 동작할 수 있습니다. Docker Compose는 이 서비스들을 한 명령으로 실행합니다.

핵심 용어 정리

용어의미
이미지(Image)읽기 전용(read-only) 템플릿입니다. Dockerfile에서 빌드됩니다.
컨테이너(Container)이미지를 실행한 인스턴스입니다.
Dockerfile이미지를 레이어(layer)별로 빌드하는 명령(instruction)입니다.
볼륨(Volume)컨테이너 재시작 이후에도 남는 영속 저장소(persistent storage)입니다.
docker-compose다중 컨테이너 애플리케이션을 YAML로 정의하는 도구입니다.

AI에서 흔한 컨테이너 패턴

개발 컨테이너(Dev Container)
  완전한 툴킷. 에디터 지원. Jupyter. 디버깅 도구.
  개발과 실험 중 사용합니다.

훈련 컨테이너(Training Container)
  최소 구성. 훈련 스크립트와 의존성만.
  GPU 클러스터에서 실행합니다. 에디터나 Jupyter는 보통 없습니다.

추론 컨테이너(Inference Container)
  서빙(serving)에 최적화. 작은 이미지. 빠른 콜드 스타트(cold start).
  프로덕션 서빙 환경에서 사용합니다.

직접 만들기

Step 1: Docker 설치

# macOS
brew install --cask docker
open /Applications/Docker.app

# Ubuntu
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
# group 변경을 적용하려면 logout 후 다시 login합니다.

확인합니다.

docker --version
docker run hello-world

Step 2: NVIDIA Container Toolkit 설치 (Linux + NVIDIA GPU)

이 도구가 있어야 Docker 컨테이너 안에서 GPU를 사용할 수 있습니다. macOS와 Windows WSL2 사용자는 플랫폼별 GPU 패스스루(passthrough) 방식이 다르므로 Docker Desktop 문서를 확인합니다.

distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
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/$distribution/libnvidia-container.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

컨테이너 안에서 GPU가 보이는지 확인합니다.

docker run --rm --gpus all nvidia/cuda:12.4.1-base-ubuntu22.04 nvidia-smi

GPU 정보가 보이면 툴킷이 동작합니다.

Step 3: 베이스 이미지(Base image) 이해

베이스 이미지 선택은 디버깅 시간을 크게 줄입니다.

nvidia/cuda:12.4.1-devel-ubuntu22.04
  완전한 CUDA 툴킷. 컴파일러 포함.
  사용처: nvcc가 필요한 패키지 빌드 (flash-attn, bitsandbytes)
  크기: 약 4 GB

nvidia/cuda:12.4.1-runtime-ubuntu22.04
  CUDA 런타임만 포함. 컴파일러 없음.
  사용처: 이미 빌드된 코드 실행
  크기: 약 1.5 GB

pytorch/pytorch:2.3.1-cuda12.4-cudnn9-runtime
  CUDA 위에 PyTorch가 미리 설치되어 있음.
  사용처: PyTorch 설치 단계를 생략
  크기: 약 6 GB

python:3.13-slim
  CUDA 없음. CPU 전용.
  사용처: CPU 추론, 가벼운 도구
  크기: 작음

Step 4: AI 개발(development)용 Dockerfile 작성

code/Dockerfile은 CUDA 베이스 이미지 위에 uv로 Python 3.13 환경을 만들고 PyTorch와 AI 라이브러리를 설치합니다.

FROM nvidia/cuda:12.4.1-devel-ubuntu22.04

ENV DEBIAN_FRONTEND=noninteractive
ENV PYTHONUNBUFFERED=1

RUN apt-get update && apt-get install -y --no-install-recommends \
    ca-certificates \
    git \
    curl \
    build-essential \
    && rm -rf /var/lib/apt/lists/*

RUN curl -LsSf https://astral.sh/uv/install.sh | sh

ENV PATH="/root/.local/bin:/opt/venv/bin:${PATH}"
ENV VIRTUAL_ENV=/opt/venv
ENV UV_LINK_MODE=copy

RUN uv python install 3.13 \
    && uv venv /opt/venv --python 3.13

빌드합니다.

docker build -t ai-dev -f phases/00-setup-and-tooling/07-docker-for-ai/code/Dockerfile .

처음에는 CUDA 베이스 이미지와 PyTorch를 내려받으므로 시간이 걸립니다. 다음 빌드부터는 변경되지 않은 레이어(unchanged layer)가 캐시됩니다.

실행합니다.

docker run --rm -it --gpus all \
    -v $(pwd):/workspace \
    -v ~/models:/models \
    ai-dev python -c "import torch; print(f'PyTorch {torch.__version__}, CUDA: {torch.cuda.is_available()}')"

컨테이너 안에서 Jupyter를 실행합니다.

docker run --rm -it --gpus all \
    -v $(pwd):/workspace \
    -v ~/models:/models \
    -p 8888:8888 \
    ai-dev jupyter notebook --ip=0.0.0.0 --port=8888 --no-browser --allow-root

Step 5: 데이터와 모델을 위한 볼륨 마운트

볼륨 마운트는 AI 작업에서 중요합니다. 없으면 컨테이너가 멈출 때 내부에 받은 14 GB 모델도 사라질 수 있습니다.

# 코드 마운트
-v $(pwd):/workspace

# 공유 모델 디렉터리
-v ~/models:/models

# 데이터셋
-v ~/datasets:/data

훈련 스크립트에서는 마운트된 경로에서 모델을 읽습니다.

from transformers import AutoModel

model = AutoModel.from_pretrained("/models/llama-7b")

모델은 호스트 파일시스템(filesystem)에 남습니다. 컨테이너를 여러 번 재빌드해도 다시 다운로드할 필요가 없습니다.

Step 6: 다중 서비스 AI 앱을 위한 Docker Compose

RAG 애플리케이션에는 추론 서버와 벡터 데이터베이스가 함께 필요합니다. Docker Compose는 둘을 한 명령으로 실행합니다.

code/docker-compose.yml을 봅니다.

services:
  ai-dev:
    build:
      context: .
      dockerfile: Dockerfile
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities: [gpu]
    volumes:
      - ../../../:/workspace
      - ~/models:/models
      - ~/datasets:/data
    ports:
      - "8888:8888"
    stdin_open: true
    tty: true
    command: jupyter notebook --ip=0.0.0.0 --port=8888 --no-browser --allow-root

  qdrant:
    image: qdrant/qdrant:v1.12.5
    ports:
      - "6333:6333"
      - "6334:6334"
    volumes:
      - qdrant_data:/qdrant/storage

volumes:
  qdrant_data:

실행합니다.

cd phases/00-setup-and-tooling/07-docker-for-ai/code
docker compose up -d

AI 개발 컨테이너는 http://qdrant:6333 서비스 이름(service name)으로 벡터 데이터베이스에 접근할 수 있습니다. Docker Compose가 공유 네트워크(shared network)를 자동으로 만듭니다.

from qdrant_client import QdrantClient

client = QdrantClient(host="qdrant", port=6333)
print(client.get_collections())

중지합니다.

docker compose down

Qdrant 볼륨까지 지우려면 -v를 붙입니다.

docker compose down -v

Step 7: AI 작업에서 자주 쓰는 Docker 명령

docker ps
docker images
docker system prune -a
docker exec -it <container_id> nvidia-smi
docker cp <container_id>:/workspace/results.csv ./results.csv
docker logs -f <container_id>

docker system prune -a는 사용하지 않는 이미지를 크게 정리합니다. 필요한 캐시까지 지울 수 있으므로 실행 전 무엇을 지우는지 확인합니다.

사용해보기

이제 재현 가능한 AI 개발 환경(reproducible AI development environment)이 있습니다. 이후 과정에서는 아래 기준을 사용합니다.

  • docker compose up으로 개발 환경과 벡터 데이터베이스를 함께 시작합니다.
  • 코드, 모델, 데이터는 볼륨으로 마운트해 재빌드 사이에 사라지지 않게 합니다.
  • 강의에서 새 Python 패키지가 필요하면 Dockerfile에 추가하고 재빌드합니다.
  • Dockerfile을 동료와 공유하면 같은 환경을 재현할 수 있습니다.

GPU가 없다면

--gpus all 플래그와 NVIDIA deploy 블록을 제거합니다. CPU 기반 강의에서는 컨테이너가 계속 동작합니다. PyTorch는 CUDA가 없으면 CPU로 폴백(fallback)합니다.

연습문제

  1. Dockerfile을 빌드하고 컨테이너 안에서 python -c "import torch; print(torch.__version__)"를 실행합니다.
  2. docker-compose 스택을 시작하고 AI 컨테이너에서 http://qdrant:6333/collections에 접근할 수 있는지 확인합니다.
  3. Dockerfile에 flask를 추가하고 재빌드한 뒤 5000 포트의 간단한 API 서버를 실행합니다. -p 5000:5000으로 포트를 매핑합니다.
  4. docker images로 이미지 크기를 측정합니다. 베이스 이미지를 devel에서 runtime으로 바꿔 크기 차이를 비교합니다.

핵심 용어

용어흔한 설명실제 의미
컨테이너(Container)가벼운 VM호스트 커널을 공유하는 격리된 프로세스와 파일시스템
이미지 레이어(Image layer)캐시된 단계Dockerfile 명령마다 생기는 레이어이며 변경되지 않으면 재사용된다
NVIDIA Container ToolkitDocker에서 GPU 사용--gpus 플래그로 호스트 GPU를 컨테이너에 노출하는 런타임 훅(runtime hook)
볼륨 마운트(Volume mount)공유 폴더호스트 디렉터리를 컨테이너 안 경로로 연결해 컨테이너가 멈춰도 데이터를 유지한다
베이스 이미지(Base image)시작점Dockerfile의 FROM 이미지이며 미리 설치된 스택(pre-installed stack)을 결정한다

더 읽을거리

실습 코드

이 강의의 실습 코드 1개

docker compose
Code

확인 문제

3문제 · 모두 맞추면 완료 표시가 가능합니다

1.Docker를 사용하는 AI 개발에서 볼륨 마운트가 중요한 이유는 무엇인가요?

2.NVIDIA Container Toolkit은 무엇을 가능하게 하나요?

3.AI용 Docker Compose 파일에서 `ai-dev` 서비스는 `qdrant` 벡터 데이터베이스에 어떻게 접근하나요?

0/3 답변 완료