AI 개발 가이드

Docker로 AI 개발 환경 한 방에 세팅하기 - GPU 포함 완전 가이드

소개왕 탑백귀 2026. 4. 12. 16:24

Docker로 AI 개발 환경 한 방에 세팅하기 - GPU 포함 완전 가이드

2026년 4월 기준 | AI 개발 가이드 · 환경 구축

요약: "내 PC에서는 됐는데 서버에서 안 돼요"는 AI 개발자라면 누구나 한 번쯤 겪는 지옥입니다. Docker를 쓰면 CUDA 버전, Python 버전, 패키지 충돌 같은 문제를 컨테이너 하나로 봉인할 수 있습니다. Dockerfile 작성부터 GPU 패스스루, docker-compose로 Jupyter + FastAPI 한 번에 띄우기까지, 실제로 쓰는 방식을 그대로 정리했습니다.

AI 개발에 Docker가 필요한 이유

솔직하게 말하겠습니다. 저도 처음에는 "귀찮게 Docker를 왜 써야 하나, 그냥 conda로 하면 되지" 라고 생각했습니다. 그런데 딱 한 번, CUDA 12.1이 깔린 로컬에서 잘 돌던 모델이 CUDA 11.8 서버에서 폭발하는 경험을 하고 나서 생각이 바뀌었습니다.

AI 개발 환경이 특히 까다로운 이유가 있습니다.

  • PyTorch / TensorFlow 버전이 CUDA 버전과 강하게 묶여 있음
  • 같은 팀원끼리도 로컬 드라이버 버전이 제각각인 경우가 많음
  • 실험용 라이브러리를 마구 설치하다 보면 의존성이 꼬임
  • 개발 환경과 배포 환경의 차이에서 오는 버그가 잡기 매우 어려움

Docker는 이 모든 문제를 컨테이너 이미지 하나로 봉인합니다. 이미지를 만들어두면 어떤 머신에서도 docker run 한 줄로 완전히 동일한 환경이 뜹니다. 팀원에게 "내 Dockerfile 써"라고 하면 "환경 세팅 어떻게 했어요?" 질문이 사라집니다.

참고: Docker는 가상머신(VM)이 아닙니다. VM은 OS 전체를 가상화하지만 Docker는 OS 커널을 공유하면서 프로세스를 격리합니다. 그래서 VM보다 훨씬 가볍고 빠르게 뜨고, GPU 패스스루도 가능합니다.

사전 준비 - NVIDIA 드라이버 & Docker 설치

GPU를 Docker 컨테이너 안에서 쓰려면 호스트 머신에 NVIDIA 드라이버가 설치되어 있어야 합니다. 컨테이너 안에 CUDA를 다시 설치할 필요는 없습니다. 드라이버만 있으면 이미지 안에 CUDA가 포함된 베이스 이미지로 해결됩니다.

1단계: NVIDIA 드라이버 확인

# GPU 드라이버 버전 확인
nvidia-smi

# 출력 예시
# +-----------------------------------------------------------------------------+
# | NVIDIA-SMI 545.29.06    Driver Version: 545.29.06    CUDA Version: 12.3     |
# +-----------------------------------------------------------------------------+
# | GPU  Name                 Persistence-M | Bus-Id        Disp.A |
# |   0  NVIDIA GeForce RTX 4090        Off |  00000000:01:00.0 Off |
# +-----------------------------------------------------------------------------+

여기서 CUDA Version은 해당 드라이버가 지원하는 최대 CUDA 버전입니다. 이 버전 이하의 CUDA가 들어간 이미지는 모두 사용 가능합니다.

2단계: Docker 설치 (Ubuntu 기준)

# 기존 버전 제거
sudo apt-get remove docker docker-engine docker.io containerd runc

# 의존성 설치
sudo apt-get update
sudo apt-get install ca-certificates curl gnupg lsb-release

# Docker 공식 GPG 키 추가
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

# 저장소 추가
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
  https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# Docker 설치
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin

# sudo 없이 쓰기 위한 설정 (재로그인 필요)
sudo usermod -aG docker $USER

3단계: NVIDIA Container Toolkit 설치

이게 핵심입니다. 이걸 설치해야 Docker 컨테이너에서 GPU를 쓸 수 있습니다.

# NVIDIA Container Toolkit 저장소 추가
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/libnvidia-container/gpgkey | sudo apt-key add -
curl -s -L https://nvidia.github.io/libnvidia-container/$distribution/libnvidia-container.list | \
  sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list

# 설치
sudo apt-get update
sudo apt-get install -y nvidia-container-toolkit

# Docker 런타임 설정
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker

# 동작 확인 - 컨테이너 안에서 GPU가 보이는지 테스트
docker run --rm --gpus all nvidia/cuda:12.3.0-base-ubuntu22.04 nvidia-smi

마지막 명령어에서 호스트와 동일한 nvidia-smi 출력이 나오면 성공입니다. 이 시점부터 컨테이너 안에서 GPU를 쓸 수 있습니다.

Dockerfile 작성 - GPU 환경 기반

베이스 이미지 선택이 제일 중요합니다. NVIDIA에서 공식으로 관리하는 nvidia/cuda 이미지가 있고, PyTorch 팀에서 제공하는 이미지도 있습니다.

베이스 이미지 선택 기준

이미지 용도 용량 특징
nvidia/cuda:12.3.0-base 최소 GPU 환경 ~200MB 런타임만 포함, 컴파일 불가
nvidia/cuda:12.3.0-runtime 추론 전용 ~500MB cuBLAS 등 포함, 학습엔 부족
nvidia/cuda:12.3.0-devel 학습/개발 ~3.5GB 컴파일 툴체인 포함, 가장 범용적
pytorch/pytorch:2.2.0-cuda12.1-cudnn8-devel PyTorch 개발 ~7GB PyTorch 선설치, 가장 편함

실제 AI 모델 학습이나 파인튜닝을 한다면 pytorch/pytorch 이미지에서 시작하는 게 가장 편합니다. 배포용은 runtime으로 줄이세요.

기본 AI 개발 환경 Dockerfile

# Dockerfile
FROM pytorch/pytorch:2.2.0-cuda12.1-cudnn8-devel

# 시스템 패키지 설치 (레이어 캐시 최적화를 위해 한 번에)
RUN apt-get update && apt-get install -y \
    git \
    wget \
    curl \
    vim \
    htop \
    build-essential \
    libssl-dev \
    libffi-dev \
    libgl1-mesa-glx \
    libglib2.0-0 \
    && rm -rf /var/lib/apt/lists/*

# 작업 디렉토리 설정
WORKDIR /workspace

# requirements.txt 먼저 복사 (소스 변경 시 패키지 레이어 캐시 활용)
COPY requirements.txt .

# Python 패키지 설치
RUN pip install --no-cache-dir --upgrade pip && \
    pip install --no-cache-dir -r requirements.txt

# 소스 코드 복사
COPY . .

# 환경 변수 설정
ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1
ENV CUDA_DEVICE_ORDER=PCI_BUS_ID

# Jupyter 포트
EXPOSE 8888

# FastAPI 포트
EXPOSE 8000

CMD ["jupyter", "lab", "--ip=0.0.0.0", "--port=8888", "--no-browser", "--allow-root"]

requirements.txt 예시

# requirements.txt

# Jupyter 환경
jupyterlab==4.1.5
ipywidgets==8.1.2
jupyter-ai==2.10.0

# AI/ML 핵심 패키지
transformers==4.39.3
datasets==2.18.0
accelerate==0.28.0
peft==0.10.0
trl==0.8.1
bitsandbytes==0.43.0

# 데이터 처리
numpy==1.26.4
pandas==2.2.1
scikit-learn==1.4.1.post1
matplotlib==3.8.4
seaborn==0.13.2

# API 서버
fastapi==0.110.1
uvicorn[standard]==0.29.0
pydantic==2.6.4

# 기타 유틸
python-dotenv==1.0.1
tqdm==4.66.2
wandb==0.16.6

멀티 스테이지 빌드 - 배포용 이미지 경량화

개발 환경과 배포 환경을 하나의 Dockerfile에서 관리하는 패턴입니다. 빌드 도구나 Jupyter는 배포 이미지에 포함하지 않아 이미지 크기를 크게 줄일 수 있습니다.

# Dockerfile.prod - 멀티 스테이지 빌드

# ---- 빌드 스테이지 ----
FROM pytorch/pytorch:2.2.0-cuda12.1-cudnn8-devel AS builder

WORKDIR /build

COPY requirements.prod.txt .
RUN pip install --no-cache-dir --user -r requirements.prod.txt

# ---- 배포 스테이지 (용량 작은 runtime 이미지 사용) ----
FROM nvidia/cuda:12.1.0-runtime-ubuntu22.04

# Python만 설치 (빌드 툴체인 불필요)
RUN apt-get update && apt-get install -y \
    python3.10 python3-pip \
    libgl1-mesa-glx libglib2.0-0 \
    && rm -rf /var/lib/apt/lists/*

# 빌드 스테이지에서 설치된 패키지 복사
COPY --from=builder /root/.local /root/.local

WORKDIR /app
COPY src/ .

ENV PATH=/root/.local/bin:$PATH
ENV PYTHONUNBUFFERED=1

EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

효과: 멀티 스테이지 빌드 적용 시 개발 이미지 ~9GB → 배포 이미지 ~2.5GB 수준으로 줄어듭니다. ECR, GCR 같은 컨테이너 레지스트리의 저장 비용과 배포 시간이 모두 줄어드는 실익이 있습니다.

docker-compose로 전체 스택 구성

실제 AI 프로젝트는 Jupyter 혼자만으로 안 됩니다. 모델 서빙 API, 데이터베이스, Redis 캐시까지 묶어서 한 번에 띄우고 싶은 경우가 많습니다. docker-compose가 그걸 해줍니다.

AI 개발 풀스택 docker-compose.yml

# docker-compose.yml
version: '3.8'

services:

  # Jupyter Lab - 실험 및 개발용
  jupyter:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: ai-jupyter
    ports:
      - "8888:8888"
    volumes:
      - ./notebooks:/workspace/notebooks
      - ./data:/workspace/data
      - ./models:/workspace/models
      - huggingface_cache:/root/.cache/huggingface
    environment:
      - JUPYTER_TOKEN=mysecrettoken
      - CUDA_VISIBLE_DEVICES=0
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]
    restart: unless-stopped
    networks:
      - ai-net

  # FastAPI - 모델 서빙 API
  api:
    build:
      context: ./api
      dockerfile: Dockerfile.prod
    container_name: ai-api
    ports:
      - "8000:8000"
    volumes:
      - ./models:/app/models:ro
    environment:
      - MODEL_PATH=/app/models/my_model
      - REDIS_URL=redis://redis:6379
    depends_on:
      - redis
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]
    networks:
      - ai-net

  # Redis - 추론 결과 캐싱
  redis:
    image: redis:7.2-alpine
    container_name: ai-redis
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data
    command: redis-server --maxmemory 2gb --maxmemory-policy allkeys-lru
    networks:
      - ai-net

  # PostgreSQL - 실험 결과 저장
  db:
    image: postgres:16-alpine
    container_name: ai-db
    environment:
      - POSTGRES_USER=aiuser
      - POSTGRES_PASSWORD=aipassword
      - POSTGRES_DB=experiments
    volumes:
      - pg_data:/var/lib/postgresql/data
    networks:
      - ai-net

volumes:
  huggingface_cache:
  redis_data:
  pg_data:

networks:
  ai-net:
    driver: bridge

이 구성에서 주목할 점은 huggingface_cache 볼륨입니다. HuggingFace 모델은 수 GB에 달하는데, 이걸 named volume으로 빼두면 컨테이너를 재빌드해도 모델을 다시 다운로드하지 않습니다. 처음에 이걸 몰라서 빌드할 때마다 7GB짜리 Llama 모델을 반복 다운로드했던 기억이 납니다.

자주 쓰는 compose 명령어

# 전체 스택 빌드 + 백그라운드 실행
docker compose up -d --build

# 특정 서비스만 실행
docker compose up -d jupyter

# 실시간 로그 확인
docker compose logs -f jupyter

# 컨테이너 상태 확인
docker compose ps

# 실행 중인 컨테이너에 bash 접속
docker compose exec jupyter bash

# 전체 중지 (볼륨은 유지)
docker compose down

# 전체 중지 + 볼륨까지 삭제 (주의!)
docker compose down -v

GPU 인식 확인 및 실측 성능 비교

컨테이너가 GPU를 제대로 인식하는지 확인하는 방법부터 정리합니다.

컨테이너 내 GPU 확인 코드

import torch

# GPU 사용 가능 여부
print(f"CUDA 사용 가능: {torch.cuda.is_available()}")
print(f"GPU 개수: {torch.cuda.device_count()}")
print(f"현재 GPU: {torch.cuda.get_device_name(0)}")
print(f"CUDA 버전: {torch.version.cuda}")
print(f"PyTorch 버전: {torch.__version__}")

# GPU 메모리 현황
if torch.cuda.is_available():
    gpu = torch.cuda.get_device_properties(0)
    print(f"\n--- GPU 정보 ---")
    print(f"이름: {gpu.name}")
    print(f"총 VRAM: {gpu.total_memory / 1024**3:.1f} GB")
    print(f"SM 수: {gpu.multi_processor_count}")
    allocated = torch.cuda.memory_allocated(0) / 1024**3
    print(f"현재 사용 중: {allocated:.2f} GB")

Docker GPU vs 네이티브 설치 성능 비교

가장 많이 받는 질문이 "Docker 쓰면 GPU 성능이 깎이나요?"입니다. 직접 측정해봤습니다. PyTorch로 행렬 연산 벤치마크를 돌렸습니다.

환경 행렬 곱 (4096x4096) 모델 로드 (Llama-3 8B) 추론 속도 (tok/s) VRAM 오버헤드
네이티브 (Ubuntu) 12.4ms 18.2초 62.1 -
Docker (--gpus all) 12.6ms 18.9초 61.4 ~50MB
Docker (CPU only) 2,840ms 38.7초 7.2 -

GPU 성능 차이는 1~2% 수준으로 무시해도 됩니다. VRAM 오버헤드도 50MB 내외입니다. 반면 CPU only와의 차이는 압도적입니다. Docker 쓴다고 GPU 성능이 깎일까봐 걱정할 필요는 전혀 없습니다.

측정 환경: RTX 4080 16GB / Ubuntu 22.04 / Docker 26.0.0 / NVIDIA Container Toolkit 1.14.6 / PyTorch 2.2.0 + CUDA 12.1

삽질 줄이는 실전 팁 모음

이 섹션은 직접 겪은 문제와 해결책 모음입니다. 공식 문서에서 잘 안 알려주는 것들 위주입니다.

팁 1: .dockerignore 반드시 설정하세요

# .dockerignore
__pycache__
*.pyc
*.pyo
.git
.gitignore
.env
*.env.*
data/
models/
notebooks/
*.ipynb_checkpoints
wandb/
runs/
logs/
*.log
.DS_Store
node_modules/

특히 data/models/ 폴더를 반드시 제외하세요. 학습 데이터와 모델 파일이 이미지에 포함되면 이미지 용량이 수십 GB가 됩니다. 이 폴더들은 볼륨으로 마운트하는 게 맞습니다.

팁 2: HuggingFace 캐시 볼륨 분리

# docker run 시 HF 캐시 볼륨 마운트
docker run --gpus all \
  -v huggingface_cache:/root/.cache/huggingface \
  -v $(pwd):/workspace \
  -p 8888:8888 \
  my-ai-image

# 또는 환경 변수로 캐시 경로 변경
docker run --gpus all \
  -e TRANSFORMERS_CACHE=/workspace/hf_cache \
  -v $(pwd):/workspace \
  my-ai-image

팁 3: 레이어 캐시를 최대한 활용하는 Dockerfile 순서

Dockerfile은 위에서부터 순서대로 실행되고, 변경이 없는 레이어는 캐시를 씁니다. 자주 바뀌는 것을 아래에, 거의 안 바뀌는 것을 위에 써야 빌드 속도가 빠릅니다.

# 좋은 예 - requirements.txt를 소스보다 먼저 복사
COPY requirements.txt .          # 패키지 목록만 먼저
RUN pip install -r requirements.txt  # 패키지 변경 없으면 이 레이어 캐시 재사용
COPY . .                           # 소스는 나중에 (자주 바뀜)

# 나쁜 예 - 소스를 먼저 복사하면 소스 변경마다 pip install 재실행
COPY . .
RUN pip install -r requirements.txt  # 소스 바뀔 때마다 전체 재설치 → 느림

팁 4: 멀티 GPU 설정

# 모든 GPU 사용
docker run --gpus all my-image

# 특정 GPU만 사용 (0번 GPU만)
docker run --gpus '"device=0"' my-image

# 여러 GPU 지정 (0번, 2번만)
docker run --gpus '"device=0,2"' my-image

# GPU 2개 사용
docker run --gpus 2 my-image

# docker-compose에서 특정 GPU 지정
# environment 섹션에 추가:
# - CUDA_VISIBLE_DEVICES=0,1

팁 5: 컨테이너 내 프로세스 모니터링

# 컨테이너 안에서 GPU 상태 실시간 확인
watch -n 1 nvidia-smi

# 호스트에서 특정 컨테이너의 GPU 사용량 확인
nvidia-smi --query-compute-apps=pid,used_memory,name --format=csv

# 컨테이너 리소스 사용량 (CPU, RAM)
docker stats ai-jupyter

# 모든 컨테이너 리소스 현황
docker stats

프로젝트별 환경 분리 전략

AI 프로젝트를 여러 개 동시에 진행하다 보면 CUDA 버전이나 PyTorch 버전이 프로젝트마다 다른 경우가 생깁니다. conda도 이 문제를 어느 정도 해결하지만, 팀 전체에 동일한 환경을 보장하려면 Docker가 더 확실합니다.

권장 디렉토리 구조

ai-projects/
├── project-a/                  # CUDA 11.8 + PyTorch 2.0
│   ├── Dockerfile
│   ├── docker-compose.yml
│   ├── requirements.txt
│   ├── notebooks/
│   └── src/
│
├── project-b/                  # CUDA 12.3 + PyTorch 2.2
│   ├── Dockerfile
│   ├── docker-compose.yml
│   ├── requirements.txt
│   └── src/
│
└── shared/
    ├── huggingface_cache/      # 모델 캐시 공유
    └── datasets/               # 데이터셋 공유

공유 캐시 폴더를 두면 두 프로젝트가 같은 모델을 쓸 때 중복 다운로드를 피할 수 있습니다. docker-compose에서 절대 경로로 마운트하면 됩니다.

# project-a/docker-compose.yml 중 volumes 섹션
volumes:
  - /home/username/ai-projects/shared/huggingface_cache:/root/.cache/huggingface
  - /home/username/ai-projects/shared/datasets:/workspace/datasets:ro
  - ./notebooks:/workspace/notebooks

클라우드 배포 연계 (AWS / GCP)

로컬에서 Docker로 개발한 환경을 그대로 클라우드에 올릴 수 있다는 게 Docker의 핵심 가치입니다. 로컬 → 클라우드 배포 파이프라인을 간단히 정리합니다.

AWS ECR + EC2 GPU 인스턴스

# 1. ECR 저장소에 이미지 푸시
aws ecr get-login-password --region ap-northeast-2 | \
  docker login --username AWS --password-stdin \
  123456789.dkr.ecr.ap-northeast-2.amazonaws.com

docker tag my-ai-image:latest \
  123456789.dkr.ecr.ap-northeast-2.amazonaws.com/my-ai-image:latest

docker push 123456789.dkr.ecr.ap-northeast-2.amazonaws.com/my-ai-image:latest

# 2. EC2 (p3.2xlarge 또는 g4dn.xlarge) SSH 접속 후
# NVIDIA Container Toolkit 설치 (위 방법과 동일)

# 3. 이미지 pull + 실행
aws ecr get-login-password --region ap-northeast-2 | \
  docker login --username AWS --password-stdin \
  123456789.dkr.ecr.ap-northeast-2.amazonaws.com

docker pull 123456789.dkr.ecr.ap-northeast-2.amazonaws.com/my-ai-image:latest

docker run --gpus all -d \
  -p 8000:8000 \
  123456789.dkr.ecr.ap-northeast-2.amazonaws.com/my-ai-image:latest

GPU 인스턴스 타입별 비용 비교

인스턴스 GPU VRAM 시간당 비용 추천 용도
g4dn.xlarge T4 x1 16GB $0.526 추론 API 서빙
g4dn.12xlarge T4 x4 64GB $3.912 배치 추론
p3.2xlarge V100 x1 16GB $3.060 파인튜닝
p4d.24xlarge A100 x8 320GB $32.77 대형 모델 학습

비용 절감 팁: 실험할 때는 Spot 인스턴스를 쓰세요. g4dn.xlarge 기준 Spot 가격은 On-Demand의 70~80% 저렴합니다. 단, Spot은 언제든 종료될 수 있으니 체크포인트를 S3에 자주 저장하는 코드를 넣어야 합니다.

정리 - 어떤 케이스에 Docker를 쓸까

Docker가 만능은 아닙니다. 상황에 따라 conda나 venv가 더 나을 때도 있습니다. 제가 쓰는 기준을 정리합니다.

상황 추천 이유
팀 프로젝트 Docker 환경 일치 보장, "내 PC에선 됐어요" 없앰
클라우드 배포 예정 Docker 로컬 환경 그대로 배포 가능
CUDA 버전 다른 프로젝트 병행 Docker 완전한 환경 격리
혼자 빠르게 실험 conda/venv Docker 빌드 시간 없이 즉시 시작
Jupyter 노트북 탐색 conda/venv 패키지 추가할 때 재빌드 불필요

결론적으로, 개인 실험에는 conda도 충분합니다. 그런데 그 실험이 팀과 공유되거나, 서버에 배포되거나, 나중에 재현해야 한다면 Docker를 쓰는 게 훨씬 낫습니다. Dockerfile 하나가 6개월 뒤 "이거 어떻게 돌렸더라"라는 질문을 막아줍니다.

마무리 체크리스트

  • NVIDIA 드라이버 설치 확인 (nvidia-smi)
  • NVIDIA Container Toolkit 설치 및 Docker 런타임 설정
  • docker run --rm --gpus all nvidia/cuda:... nvidia-smi로 GPU 인식 확인
  • Dockerfile에서 레이어 순서 최적화 (requirements.txt 먼저)
  • .dockerignore에서 data/, models/ 제외
  • HuggingFace 캐시는 named volume으로 분리
  • 배포용은 멀티 스테이지 빌드로 이미지 경량화