콘텐츠로 이동
Data Prep
상세

GPU 서버 관리

ML/DL 워크로드를 위한 GPU 인프라 구축과 운영. 클라우드 GPU 선택, 드라이버 설정, 모니터링, 비용 최적화를 다룬다.


1. GPU 기초 지식

1.1 GPU vs CPU

항목 CPU GPU
코어 수 8-64개 (강력함) 수천 개 (단순함)
메모리 시스템 RAM (TB 가능) VRAM (최대 80GB)
적합한 작업 순차 처리, 복잡한 로직 병렬 처리, 행렬 연산
ML 용도 전처리, 추론 (일부) 학습, 대규모 추론

1.2 NVIDIA GPU 라인업 (2024)

GPU VRAM 용도 가격대
RTX 4090 24GB 개인/소규모 학습 ~$1,600
A10G 24GB 추론 최적화 클라우드
L4 24GB 추론 (Ada) 클라우드
A100 40/80GB 학습/추론 (범용) ~$15,000
H100 80GB 대규모 학습 (최신) ~$30,000
H200 141GB 초대규모 LLM ~$40,000

1.3 GPU 메모리 (VRAM) 요구사항

모델 크기와 VRAM 관계 (Float16 기준):
- 파라미터 수 x 2 bytes = 모델 크기
- 학습 시: 모델 + Optimizer + Gradients + Activations
  -> 대략 모델 크기의 4-6배 필요

예시:
- LLaMA 7B: 14GB 모델 -> 학습 시 ~56GB+ 필요
- LLaMA 13B: 26GB 모델 -> 학습 시 ~100GB+ 필요
- LLaMA 70B: 140GB 모델 -> 멀티 GPU 필수

추론 시 (Float16):
- 7B 모델: ~14GB
- 13B 모델: ~26GB
- 70B 모델: ~140GB (8x A100 또는 2x H100)

양자화 (INT8/INT4):
- 7B 모델 INT8: ~7GB
- 7B 모델 INT4: ~4GB

2. 클라우드 GPU 인스턴스

2.1 AWS GPU 인스턴스

인스턴스 GPU VRAM vCPU 메모리 비용/시간 (온디맨드)
g4dn.xlarge T4 x1 16GB 4 16GB ~$0.526
g4dn.12xlarge T4 x4 64GB 48 192GB ~$3.912
g5.xlarge A10G x1 24GB 4 16GB ~$1.006
g5.48xlarge A10G x8 192GB 192 768GB ~$16.288
p3.2xlarge V100 x1 16GB 8 61GB ~$3.06
p3.8xlarge V100 x4 64GB 32 244GB ~$12.24
p4d.24xlarge A100 x8 320GB 96 1152GB ~$32.77
p5.48xlarge H100 x8 640GB 192 2TB ~$98.32

2.2 GCP GPU 인스턴스

인스턴스 GPU VRAM 비용/시간 (온디맨드)
n1-standard-4 + T4 T4 x1 16GB ~$0.35 + $0.35
n1-standard-8 + V100 V100 x1 16GB ~$0.38 + $2.48
a2-highgpu-1g A100 x1 40GB ~$3.67
a2-ultragpu-8g A100 x8 640GB ~$29.39
a3-highgpu-8g H100 x8 640GB ~$77.00

2.3 용도별 추천

용도 AWS GCP 특징
개발/실험 g4dn.xlarge T4 저렴, 소규모 모델
추론 서빙 g5.xlarge L4 추론 최적화, 비용 효율
소규모 학습 g5.2xlarge A100 x1 중간 모델 학습
대규모 학습 p4d.24xlarge A100 x8 분산 학습
LLM 학습 p5.48xlarge H100 x8 최고 성능, 최고 비용

3. GPU 환경 설정

3.1 NVIDIA 드라이버 설치 (Ubuntu)

# 1. 기존 드라이버 제거
sudo apt-get purge nvidia*
sudo apt-get autoremove

# 2. 드라이버 설치 (권장)
sudo apt-get update
sudo apt-get install -y nvidia-driver-535

# 또는 runfile로 설치
wget https://us.download.nvidia.com/tesla/535.154.05/NVIDIA-Linux-x86_64-535.154.05.run
sudo sh NVIDIA-Linux-x86_64-535.154.05.run

# 3. 재부팅 후 확인
sudo reboot
nvidia-smi

3.2 CUDA Toolkit 설치

# CUDA 12.1 설치
wget https://developer.download.nvidia.com/compute/cuda/12.1.0/local_installers/cuda_12.1.0_530.30.02_linux.run
sudo sh cuda_12.1.0_530.30.02_linux.run

# 환경 변수 설정
echo 'export PATH=/usr/local/cuda-12.1/bin:$PATH' >> ~/.bashrc
echo 'export LD_LIBRARY_PATH=/usr/local/cuda-12.1/lib64:$LD_LIBRARY_PATH' >> ~/.bashrc
source ~/.bashrc

# 확인
nvcc --version

3.3 cuDNN 설치

# NVIDIA 개발자 등록 후 다운로드
# https://developer.nvidia.com/cudnn

tar -xvf cudnn-linux-x86_64-8.9.7.29_cuda12-archive.tar.xz
sudo cp cudnn-*-archive/include/cudnn*.h /usr/local/cuda/include
sudo cp -P cudnn-*-archive/lib/libcudnn* /usr/local/cuda/lib64
sudo chmod a+r /usr/local/cuda/include/cudnn*.h /usr/local/cuda/lib64/libcudnn*

3.4 PyTorch with CUDA

# CUDA 12.1용 PyTorch
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121

# 확인
python -c "import torch; print(torch.cuda.is_available()); print(torch.cuda.get_device_name(0))"

3.5 Docker with 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
sudo systemctl restart docker

# GPU 컨테이너 실행
docker run --gpus all nvidia/cuda:12.1.0-base-ubuntu22.04 nvidia-smi

# 특정 GPU만 사용
docker run --gpus '"device=0,1"' ...

4. GPU 모니터링

4.1 nvidia-smi

# 기본 정보
nvidia-smi

# 지속적 모니터링 (1초 간격)
nvidia-smi -l 1

# 특정 정보만 (스크립팅용)
nvidia-smi --query-gpu=timestamp,name,pci.bus_id,driver_version,temperature.gpu,utilization.gpu,utilization.memory,memory.total,memory.free,memory.used --format=csv

# 프로세스별 GPU 사용량
nvidia-smi pmon -s um -d 1

# dmon (상세 메트릭)
nvidia-smi dmon -s pucvmet

4.2 nvidia-smi 출력 해석

+-----------------------------------------------------------------------------+
| NVIDIA-SMI 535.154.05   Driver Version: 535.154.05   CUDA Version: 12.2     |
|-------------------------------+----------------------+----------------------+
| 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|   1234MiB / 40960MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+

해석:
- Persistence-M: On = 드라이버 상주 (권장)
- Temp: GPU 온도 (80C 이상 주의)
- Perf: 성능 상태 (P0=최대, P8=절전)
- Pwr:Usage/Cap: 현재/최대 전력
- Memory-Usage: VRAM 사용량
- GPU-Util: GPU 연산 사용률 (학습 중 90%+ 권장)

4.3 DCGM (Data Center GPU Manager)

# 설치
sudo apt-get install datacenter-gpu-manager

# 서비스 시작
sudo systemctl enable nvidia-dcgm
sudo systemctl start nvidia-dcgm

# 메트릭 조회
dcgmi discovery -l
dcgmi dmon -e 100,101,140,150,155,156
# 100: GPU 사용률
# 101: 메모리 사용률  
# 140: 전력 사용량
# 150: 온도
# 155: SM Clock
# 156: Memory Clock

4.4 Prometheus + Grafana

# DCGM Exporter (Kubernetes)
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: dcgm-exporter
  namespace: gpu-monitoring
spec:
  selector:
    matchLabels:
      app: dcgm-exporter
  template:
    metadata:
      labels:
        app: dcgm-exporter
    spec:
      containers:
        - name: dcgm-exporter
          image: nvcr.io/nvidia/k8s/dcgm-exporter:3.2.5-3.1.8-ubuntu22.04
          ports:
            - containerPort: 9400
              name: metrics
          securityContext:
            runAsNonRoot: false
            runAsUser: 0
          volumeMounts:
            - name: pod-gpu-resources
              readOnly: true
              mountPath: /var/lib/kubelet/pod-resources
      volumes:
        - name: pod-gpu-resources
          hostPath:
            path: /var/lib/kubelet/pod-resources
---
apiVersion: v1
kind: Service
metadata:
  name: dcgm-exporter
  namespace: gpu-monitoring
spec:
  selector:
    app: dcgm-exporter
  ports:
    - port: 9400
      name: metrics

5. 멀티 GPU 학습

5.1 GPU 선택

import torch

# 사용 가능한 GPU 확인
print(f"GPU 수: {torch.cuda.device_count()}")
for i in range(torch.cuda.device_count()):
    print(f"GPU {i}: {torch.cuda.get_device_name(i)}")

# 특정 GPU 사용
device = torch.device("cuda:0")  # 첫 번째 GPU
device = torch.device("cuda:1")  # 두 번째 GPU

# 환경 변수로 제한
import os
os.environ["CUDA_VISIBLE_DEVICES"] = "0,1"  # GPU 0, 1만 사용

5.2 DataParallel (단순, 비권장)

import torch.nn as nn

model = MyModel()
if torch.cuda.device_count() > 1:
    model = nn.DataParallel(model)
model = model.to("cuda")

# 단점:
# - GIL 병목
# - GPU 0에 메모리 집중
# - 통신 오버헤드

5.3 DistributedDataParallel (권장)

import torch
import torch.distributed as dist
import torch.multiprocessing as mp
from torch.nn.parallel import DistributedDataParallel as DDP

def setup(rank, world_size):
    os.environ['MASTER_ADDR'] = 'localhost'
    os.environ['MASTER_PORT'] = '12355'
    dist.init_process_group("nccl", rank=rank, world_size=world_size)

def cleanup():
    dist.destroy_process_group()

def train(rank, world_size):
    setup(rank, world_size)

    # 모델을 해당 GPU로 이동
    model = MyModel().to(rank)
    model = DDP(model, device_ids=[rank])

    # 데이터 샘플러 설정
    sampler = torch.utils.data.distributed.DistributedSampler(
        dataset, num_replicas=world_size, rank=rank
    )
    dataloader = DataLoader(dataset, sampler=sampler, batch_size=32)

    for epoch in range(epochs):
        sampler.set_epoch(epoch)  # 셔플을 위해 필요
        for batch in dataloader:
            # 학습 로직
            pass

    cleanup()

if __name__ == "__main__":
    world_size = torch.cuda.device_count()
    mp.spawn(train, args=(world_size,), nprocs=world_size)

5.4 torchrun 사용 (권장)

# 단일 노드, 4 GPU
torchrun --nproc_per_node=4 train.py

# 멀티 노드 (2노드, 각 4 GPU)
# Node 0:
torchrun --nnodes=2 --nproc_per_node=4 --node_rank=0 \
    --master_addr=10.0.0.1 --master_port=29500 train.py

# Node 1:
torchrun --nnodes=2 --nproc_per_node=4 --node_rank=1 \
    --master_addr=10.0.0.1 --master_port=29500 train.py
# train.py (torchrun용)
import torch.distributed as dist
from torch.nn.parallel import DistributedDataParallel as DDP

def main():
    dist.init_process_group(backend="nccl")

    local_rank = int(os.environ["LOCAL_RANK"])
    torch.cuda.set_device(local_rank)

    model = MyModel().to(local_rank)
    model = DDP(model, device_ids=[local_rank])

    # 학습 로직

    dist.destroy_process_group()

if __name__ == "__main__":
    main()

6. 비용 최적화

6.1 Spot/Preemptible 인스턴스

클라우드 이름 절감율 주의사항
AWS Spot Instance 60-90% 2분 전 경고 후 중단
GCP Preemptible/Spot VM 60-91% 24시간 후 반드시 중단
Azure Spot VM 최대 90% 언제든 중단 가능
# 체크포인트 필수
def save_checkpoint(model, optimizer, epoch, path):
    torch.save({
        'epoch': epoch,
        'model_state_dict': model.state_dict(),
        'optimizer_state_dict': optimizer.state_dict(),
    }, path)

# 주기적 저장 (매 에폭 또는 N 스텝)
if step % 1000 == 0:
    save_checkpoint(model, optimizer, epoch, f"checkpoint_{step}.pt")

6.2 비용 비교 (월 기준, 24/7 기준)

AWS p4d.24xlarge (A100 x8):
- 온디맨드: $32.77 x 720h = $23,594/월
- Spot: ~$10,000/월 (가용시)
- Reserved 1년: ~$15,000/월

GCP a2-ultragpu-8g (A100 x8):
- 온디맨드: $29.39 x 720h = $21,161/월
- Spot: ~$6,000-8,000/월
- CUD 1년: ~$15,000/월

6.3 비용 절감 전략

1. 학습/추론 분리
   - 학습: Spot/Preemptible 사용
   - 추론: 작은 인스턴스 (g5, T4)

2. 인스턴스 타입 최적화
   - 모델 크기에 맞는 VRAM 선택
   - 추론 시 양자화로 작은 GPU 사용 가능

3. 오토스케일링
   - 트래픽 기반 스케일링
   - 야간/주말 축소

4. Reserved/Committed Use
   - 지속적 워크로드는 예약 인스턴스
   - 1년 약정 시 30-60% 절감

5. 혼합 전략
   - 기본 용량: 예약 인스턴스
   - 피크 트래픽: Spot/온디맨드

7. 트러블슈팅

7.1 일반적인 문제

증상 원인 해결
CUDA out of memory VRAM 부족 batch size 줄임, gradient accumulation
GPU 사용률 낮음 데이터 로딩 병목 num_workers 증가, 데이터 전처리
학습 속도 느림 통신 오버헤드 NCCL 설정, 네트워크 확인
nvidia-smi 안됨 드라이버 문제 드라이버 재설치
CUDA version mismatch 버전 불일치 PyTorch/CUDA 버전 맞춤

7.2 CUDA OOM 해결

# 1. Batch size 줄이기
batch_size = 16  # 원래 32

# 2. Gradient Accumulation
accumulation_steps = 4
for i, batch in enumerate(dataloader):
    loss = model(batch) / accumulation_steps
    loss.backward()

    if (i + 1) % accumulation_steps == 0:
        optimizer.step()
        optimizer.zero_grad()

# 3. Mixed Precision Training
from torch.cuda.amp import autocast, GradScaler

scaler = GradScaler()
for batch in dataloader:
    with autocast():  # FP16으로 forward
        loss = model(batch)

    scaler.scale(loss).backward()
    scaler.step(optimizer)
    scaler.update()
    optimizer.zero_grad()

# 4. Gradient Checkpointing
from torch.utils.checkpoint import checkpoint

class MyModel(nn.Module):
    def forward(self, x):
        x = checkpoint(self.block1, x)  # 메모리 절약
        x = checkpoint(self.block2, x)
        return x

# 5. 명시적 메모리 정리
torch.cuda.empty_cache()

7.3 디버깅 명령어

# GPU 프로세스 확인
fuser -v /dev/nvidia*

# GPU 메모리 상세
nvidia-smi --query-compute-apps=pid,used_memory --format=csv

# CUDA 에러 디버깅
CUDA_LAUNCH_BLOCKING=1 python train.py

# NCCL 디버깅
NCCL_DEBUG=INFO python train.py

# PyTorch 메모리 디버깅
python -c "import torch; print(torch.cuda.memory_summary())"

참고 자료