본문으로 건너뛰기
Kreath Archive
TechProjectsBooksAbout
TechProjectsBooksAbout

내비게이션

  • Tech
  • Projects
  • Books
  • About
  • Tags

카테고리

  • AI / ML
  • 웹 개발
  • 프로그래밍
  • 개발 도구

연결

  • GitHub
  • Email
  • RSS
© 2026 Kreath Archive. All rights reserved.Built with Next.js + MDX
홈TechProjectsBooksAbout
//
  1. 홈
  2. 테크
  3. 4장: 컨테이너화 - Docker로 AI 서비스 패키징
2026년 1월 24일·AI / ML·

4장: 컨테이너화 - Docker로 AI 서비스 패키징

GPU 지원 Docker 컨테이너로 AI 서비스를 패키징하는 방법을 다루며, NVIDIA Container Toolkit 설정부터 멀티 스테이지 빌드까지 실전 기법을 소개합니다.

15분953자8개 섹션
mlopskubernetesinfrastructureperformance
공유
ai-deployment4 / 10
12345678910
이전3장: 모델 최적화 - 양자화, 배칭, KV 캐시 전략다음5장: Kubernetes 기초 - AI 워크로드를 위한 클러스터 설계

AI 서비스 컨테이너화의 특수성

일반적인 웹 애플리케이션의 Docker 이미지는 수십~수백 MB 수준입니다. Node.js 애플리케이션은 약 100MB, Go 바이너리는 10MB 이하로도 패키징할 수 있습니다. 그러나 AI 서비스의 컨테이너는 근본적으로 다른 규모와 요구사항을 가집니다.

첫째, CUDA 런타임과 cuDNN 라이브러리가 필요합니다. NVIDIA CUDA 베이스 이미지 자체가 수 GB에 달합니다. 둘째, Python 런타임과 PyTorch, vLLM 등의 ML 프레임워크가 추가로 수 GB를 차지합니다. 셋째, 모델 가중치 파일은 수 GB에서 수백 GB에 이릅니다.

text
AI 서비스 Docker 이미지 크기 구성:
 
  CUDA 베이스 이미지:     약 3-5 GB
  Python + PyTorch:       약 3-5 GB
  vLLM/TGI:              약 1-2 GB
  모델 가중치 (8B, FP16): 약 16 GB
  ─────────────────────────────────
  총 이미지 크기:         약 23-28 GB
 
  (참고: 일반 웹 앱 이미지는 약 0.1-0.5 GB)

이러한 크기 때문에 이미지 빌드, 전송, 저장에 상당한 시간과 비용이 소요됩니다. 효율적인 컨테이너화 전략이 필수적입니다.

NVIDIA Container Toolkit 설정

GPU를 Docker 컨테이너 내에서 사용하려면 NVIDIA Container Toolkit이 호스트 시스템에 설치되어 있어야 합니다. 이 도구는 NVIDIA GPU 드라이버와 CUDA 라이브러리를 컨테이너에 노출시키는 역할을 합니다.

호스트 시스템 준비

NVIDIA 드라이버 확인
bash
nvidia-smi
text
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 550.54.15    Driver Version: 550.54.15    CUDA Version: 12.4     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf  Pwr:Usage/Cap |      Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  NVIDIA A100-SXM4  On    | 00000000:00:04.0 Off |                    0 |
| N/A   32C    P0    52W / 400W |      0MiB / 81920MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+

NVIDIA Container Toolkit 설치

Ubuntu/Debian에서 설치
bash
# NVIDIA Container Toolkit 리포지토리 추가
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
 
# Docker 런타임 설정
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker

GPU 컨테이너 동작 확인

GPU 접근 테스트
bash
docker run --rm --gpus all nvidia/cuda:12.4.0-base-ubuntu22.04 nvidia-smi

이 명령이 호스트와 동일한 nvidia-smi 출력을 보여주면 설정이 완료된 것입니다.

vLLM Docker 이미지 빌드

기본 Dockerfile

vLLM 공식 이미지를 베이스로 사용하는 것이 가장 간단한 방법입니다.

Dockerfile.vllm-basic
dockerfile
FROM vllm/vllm-openai:latest
 
# 환경 변수 설정
ENV MODEL_NAME="meta-llama/Llama-3.1-8B-Instruct"
ENV MAX_MODEL_LEN=4096
ENV GPU_MEMORY_UTILIZATION=0.90
 
# 헬스 체크 스크립트
COPY healthcheck.sh /healthcheck.sh
RUN chmod +x /healthcheck.sh
 
HEALTHCHECK --interval=30s --timeout=10s --start-period=120s --retries=3 \
  CMD ["/healthcheck.sh"]
 
EXPOSE 8000
 
# 시작 스크립트
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
 
ENTRYPOINT ["/entrypoint.sh"]
entrypoint.sh
bash
#!/bin/bash
set -e
 
exec vllm serve "$MODEL_NAME" \
  --host 0.0.0.0 \
  --port 8000 \
  --max-model-len "$MAX_MODEL_LEN" \
  --gpu-memory-utilization "$GPU_MEMORY_UTILIZATION" \
  --dtype auto \
  --disable-log-requests
healthcheck.sh
bash
#!/bin/bash
curl -sf http://localhost:8000/health || exit 1

모델을 이미지에 포함하지 않는 전략

모델 가중치를 Docker 이미지에 직접 포함하면 이미지가 거대해집니다. 대신 모델을 외부 스토리지에서 런타임에 로드하는 것이 일반적인 패턴입니다.

Dockerfile.vllm-external-model
dockerfile
FROM vllm/vllm-openai:latest
 
# 모델은 볼륨 마운트 또는 S3에서 다운로드
ENV HF_HOME=/models
ENV TRANSFORMERS_CACHE=/models
 
COPY entrypoint-with-download.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
 
EXPOSE 8000
ENTRYPOINT ["/entrypoint.sh"]
entrypoint-with-download.sh
bash
#!/bin/bash
set -e
 
# 모델이 로컬에 없으면 Hugging Face Hub에서 다운로드
if [ ! -d "/models/$MODEL_NAME" ]; then
    echo "모델 다운로드 중: $MODEL_NAME"
    huggingface-cli download "$MODEL_NAME" \
      --local-dir "/models/$MODEL_NAME" \
      --local-dir-use-symlinks False
fi
 
exec vllm serve "/models/$MODEL_NAME" \
  --host 0.0.0.0 \
  --port 8000 \
  --max-model-len "$MAX_MODEL_LEN" \
  --gpu-memory-utilization "$GPU_MEMORY_UTILIZATION" \
  --dtype auto

실행 시 모델 디렉토리를 볼륨으로 마운트합니다.

외부 모델 볼륨 마운트
bash
docker run --gpus all \
  -v /data/models:/models \
  -e MODEL_NAME=meta-llama/Llama-3.1-8B-Instruct \
  -e MAX_MODEL_LEN=4096 \
  -e GPU_MEMORY_UTILIZATION=0.90 \
  -p 8000:8000 \
  my-vllm-server:latest
Tip

프로덕션 환경에서는 모델을 S3, GCS, 또는 공유 파일 시스템(EFS, Filestore)에 저장하고, 컨테이너 시작 시 로컬 NVMe SSD로 복사하는 패턴이 일반적입니다. NVMe에서 모델을 로드하면 네트워크 파일 시스템 대비 로딩 시간이 크게 단축됩니다.

멀티 스테이지 빌드

커스텀 코드나 추가 의존성이 필요한 경우, 멀티 스테이지 빌드로 이미지 크기를 최적화합니다.

Dockerfile.multistage
dockerfile
# 1단계: 의존성 설치
FROM python:3.11-slim AS builder
 
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt
 
# 2단계: 런타임 이미지
FROM vllm/vllm-openai:latest
 
# 추가 의존성만 복사
COPY --from=builder /install /usr/local
 
# 커스텀 미들웨어, 전처리 코드 등
COPY src/ /app/src/
COPY configs/ /app/configs/
 
WORKDIR /app
 
EXPOSE 8000
ENTRYPOINT ["python", "-m", "src.server"]

TGI Docker 이미지 활용

TGI는 공식 Docker 이미지를 직접 사용하는 것이 표준 패턴입니다.

TGI 기본 실행
bash
docker run --gpus all --shm-size 1g \
  -v /data/models:/data \
  -p 8080:80 \
  ghcr.io/huggingface/text-generation-inference:latest \
  --model-id meta-llama/Llama-3.1-8B-Instruct \
  --max-input-tokens 4096 \
  --max-total-tokens 8192

TGI를 래핑하는 커스텀 이미지가 필요한 경우에는 다음과 같이 구성합니다.

Dockerfile.tgi-custom
dockerfile
FROM ghcr.io/huggingface/text-generation-inference:latest
 
# 커스텀 설정 파일
COPY tgi-config.json /config/tgi-config.json
 
# 헬스 체크
HEALTHCHECK --interval=30s --timeout=10s --start-period=180s --retries=3 \
  CMD curl -sf http://localhost:80/health || exit 1
 
# TGI 기본 포트는 80
EXPOSE 80

Docker Compose로 로컬 개발 환경 구성

개발 및 테스트를 위해 Docker Compose로 전체 스택을 구성할 수 있습니다.

docker-compose.yml
yaml
services:
  vllm:
    image: vllm/vllm-openai:latest
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities: [gpu]
    ports:
      - "8000:8000"
    volumes:
      - model-cache:/root/.cache/huggingface
    environment:
      - HUGGING_FACE_HUB_TOKEN
    command: >
      --model meta-llama/Llama-3.1-8B-Instruct
      --host 0.0.0.0
      --port 8000
      --max-model-len 4096
      --gpu-memory-utilization 0.90
    healthcheck:
      test: ["CMD", "curl", "-sf", "http://localhost:8000/health"]
      interval: 30s
      timeout: 10s
      start_period: 120s
      retries: 3
 
  proxy:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      vllm:
        condition: service_healthy
 
  prometheus:
    image: prom/prometheus:latest
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
      - prometheus-data:/prometheus
 
  grafana:
    image: grafana/grafana:latest
    ports:
      - "3000:3000"
    volumes:
      - grafana-data:/var/lib/grafana
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
 
volumes:
  model-cache:
  prometheus-data:
  grafana-data:
nginx.conf
text
events {
    worker_connections 1024;
}
 
http {
    upstream vllm_backend {
        server vllm:8000;
    }
 
    server {
        listen 80;
 
        location / {
            proxy_pass http://vllm_backend;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_read_timeout 300s;
            proxy_send_timeout 300s;
 
            # SSE 스트리밍 지원
            proxy_buffering off;
            proxy_cache off;
            chunked_transfer_encoding on;
        }
 
        location /health {
            proxy_pass http://vllm_backend/health;
        }
    }
}
전체 스택 시작
bash
docker compose up -d

이미지 최적화 전략

레이어 캐싱 활용

Docker 이미지의 빌드 속도를 높이려면 레이어 캐싱을 최대한 활용해야 합니다. 변경 빈도가 낮은 레이어(베이스 이미지, 시스템 패키지)를 먼저 배치하고, 변경 빈도가 높은 레이어(애플리케이션 코드)를 나중에 배치합니다.

레이어 캐싱 최적화
dockerfile
FROM vllm/vllm-openai:latest
 
# 1. 시스템 패키지 (거의 변경 없음)
RUN apt-get update && apt-get install -y --no-install-recommends \
    curl && \
    rm -rf /var/lib/apt/lists/*
 
# 2. Python 의존성 (가끔 변경)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
 
# 3. 설정 파일 (가끔 변경)
COPY configs/ /app/configs/
 
# 4. 애플리케이션 코드 (자주 변경)
COPY src/ /app/src/

컨테이너 레지스트리 선택

대용량 AI 서비스 이미지를 효율적으로 관리하려면 적절한 컨테이너 레지스트리를 선택해야 합니다.

  • Amazon ECR: AWS 환경에서 운영하는 경우 최적입니다. 같은 리전 내에서의 이미지 전송이 빠르고 무료입니다.
  • Google Artifact Registry: GCP 환경에서 권장됩니다. 취약점 스캔을 기본 제공합니다.
  • GitHub Container Registry: CI/CD가 GitHub Actions 기반인 경우 통합이 용이합니다.
ECR에 이미지 푸시
bash
# ECR 리포지토리 생성
aws ecr create-repository --repository-name ai-serving
 
# 로그인
aws ecr get-login-password --region ap-northeast-2 \
  | docker login --username AWS --password-stdin \
  123456789012.dkr.ecr.ap-northeast-2.amazonaws.com
 
# 태깅 및 푸시
docker tag my-vllm-server:latest \
  123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/ai-serving:latest
 
docker push \
  123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/ai-serving:latest

보안 고려 사항

AI 서비스 컨테이너에서는 다음과 같은 보안 사항을 고려해야 합니다.

보안 강화 Dockerfile
dockerfile
FROM vllm/vllm-openai:latest
 
# 비루트 사용자 생성
RUN groupadd -r vllm && useradd -r -g vllm -d /app vllm
 
# 불필요한 패키지 제거
RUN apt-get purge -y --auto-remove && \
    rm -rf /var/lib/apt/lists/*
 
WORKDIR /app
COPY --chown=vllm:vllm . .
 
# 비루트 사용자로 전환
USER vllm
 
EXPOSE 8000
Warning

Hugging Face 토큰이나 AWS 자격 증명 등의 시크릿을 Docker 이미지에 직접 포함하면 안 됩니다. 환경 변수나 시크릿 관리 서비스(AWS Secrets Manager, Kubernetes Secrets 등)를 통해 런타임에 주입해야 합니다.

GPU 특정 최적화

CUDA 버전 호환성

NVIDIA CUDA에는 두 가지 호환성 규칙이 있습니다.

  • 전방 호환성(Forward Compatibility): 최신 GPU 드라이버는 이전 CUDA 버전으로 빌드된 애플리케이션을 실행할 수 있습니다.
  • 후방 호환성(Backward Compatibility): CUDA 런타임은 같은 메이저 버전 내에서 하위 호환됩니다.

컨테이너의 CUDA 버전은 호스트 GPU 드라이버가 지원하는 CUDA 버전 이하여야 합니다.

text
호환성 확인:
  호스트 드라이버: 550.54 --> CUDA 12.4 이하 지원
  컨테이너 CUDA:  12.4   --> 호환
  컨테이너 CUDA:  12.6   --> 비호환 (드라이버 업그레이드 필요)

공유 메모리 설정

멀티 GPU 환경에서 텐서 병렬 처리를 사용하는 경우, GPU 간 통신을 위해 공유 메모리(Shared Memory) 크기를 충분히 설정해야 합니다.

공유 메모리 설정
bash
# Docker 실행 시 --shm-size 옵션으로 설정
docker run --gpus all --shm-size 4g my-vllm-server:latest
 
# 또는 /dev/shm을 tmpfs로 마운트
docker run --gpus all \
  --mount type=tmpfs,destination=/dev/shm,tmpfs-size=4294967296 \
  my-vllm-server:latest

공유 메모리가 부족하면 NCCL(NVIDIA Collective Communications Library) 통신이 실패하거나 성능이 크게 저하될 수 있습니다. 일반적으로 GPU 수에 비례하여 1~2GB씩 할당하는 것을 권장합니다.

정리

AI 서비스의 컨테이너화는 대용량 이미지, GPU 드라이버 호환성, 모델 관리 등 고유한 과제가 있습니다. 모델을 이미지 외부에서 관리하고, 멀티 스테이지 빌드와 레이어 캐싱을 활용하며, 보안 모범 사례를 따르는 것이 핵심입니다.

다음 장에서는 컨테이너화된 AI 서비스를 Kubernetes 클러스터에서 운영하기 위한 기초 개념을 다루겠습니다. AI 워크로드에 특화된 클러스터 설계와 GPU 노드 관리를 중심으로 설명합니다.

이 글이 도움이 되셨나요?

관련 주제 더 보기

#mlops#kubernetes#infrastructure#performance

관련 글

AI / ML

5장: Kubernetes 기초 - AI 워크로드를 위한 클러스터 설계

Kubernetes의 핵심 개념을 AI 워크로드 관점에서 설명하고, GPU 노드 구성과 AI 서비스에 적합한 클러스터 아키텍처를 설계합니다.

2026년 1월 26일·14분
AI / ML

3장: 모델 최적화 - 양자화, 배칭, KV 캐시 전략

LLM 추론 성능을 극대화하기 위한 양자화 기법, 배칭 전략, KV 캐시 튜닝 방법을 실전 예제와 함께 체계적으로 다룹니다.

2026년 1월 22일·22분
AI / ML

6장: Kubernetes 배포 실전 - GPU 노드와 모델 서빙 배포

Kubernetes에서 GPU 기반 AI 서비스를 배포하는 실전 과정을 다루며, 프로브 설정, 리소스 관리, 무중단 배포 전략을 구현합니다.

2026년 1월 28일·18분
이전 글3장: 모델 최적화 - 양자화, 배칭, KV 캐시 전략
다음 글5장: Kubernetes 기초 - AI 워크로드를 위한 클러스터 설계

댓글

목차

약 15분 남음
  • AI 서비스 컨테이너화의 특수성
  • NVIDIA Container Toolkit 설정
    • 호스트 시스템 준비
    • NVIDIA Container Toolkit 설치
    • GPU 컨테이너 동작 확인
  • vLLM Docker 이미지 빌드
    • 기본 Dockerfile
    • 모델을 이미지에 포함하지 않는 전략
    • 멀티 스테이지 빌드
  • TGI Docker 이미지 활용
  • Docker Compose로 로컬 개발 환경 구성
  • 이미지 최적화 전략
    • 레이어 캐싱 활용
    • 컨테이너 레지스트리 선택
    • 보안 고려 사항
  • GPU 특정 최적화
    • CUDA 버전 호환성
    • 공유 메모리 설정
  • 정리