26O07c

QK·5일 전

네. 현재 정리한 validation.openai.com/b300=true label과 GPU Operator 환경을 기준으로 하면 아래 YAML이면 충분합니다.

apiVersion: v1
kind: Pod
metadata:
  name: b300-gpu-1-smoke
  namespace: gpu-validation
  labels:
    app: b300-gpu-smoke
    stage: stage6-preflight
spec:
  restartPolicy: Never

  # Stage6 canonical profile:
  # STAGE6_GPU_NODE_SELECTOR_KEY=validation.openai.com/b300
  # STAGE6_GPU_NODE_SELECTOR_VALUE=true
  nodeSelector:
    validation.openai.com/b300: "true"

  # GPU node에 아래 taint를 적용한 경우 필요:
  # nvidia.com/gpu=true:NoSchedule
  tolerations:
    - key: nvidia.com/gpu
      operator: Equal
      value: "true"
      effect: NoSchedule

  containers:
    - name: cuda-smoke
      image: nvcr.io/nvidia/cuda:13.0.2-base-ubuntu24.04
      imagePullPolicy: IfNotPresent

      command:
        - /bin/bash
        - -c
        - |
          set -euo pipefail

          echo "========================================"
          echo " Stage6 Preflight - 1 GPU Smoke Test"
          echo "========================================"

          echo
          echo "[1] Pod / Node"
          echo "HOSTNAME=${HOSTNAME}"
          echo "NODE_NAME=${NODE_NAME}"

          echo
          echo "[2] NVIDIA_VISIBLE_DEVICES"
          echo "${NVIDIA_VISIBLE_DEVICES:-<unset>}"

          echo
          echo "[3] nvidia-smi"
          nvidia-smi

          echo
          echo "[4] GPU list"
          nvidia-smi -L

          echo
          echo "[5] Allocated GPU count"
          GPU_COUNT="$(nvidia-smi --query-gpu=uuid \
            --format=csv,noheader | wc -l)"

          echo "GPU_COUNT=${GPU_COUNT}"

          if [ "${GPU_COUNT}" -ne 1 ]; then
            echo "FAIL: expected exactly 1 GPU, got ${GPU_COUNT}"
            exit 1
          fi

          echo
          echo "[6] GPU properties"
          nvidia-smi \
            --query-gpu=index,name,uuid,driver_version,memory.total,\
temperature.gpu,power.draw \
            --format=csv

          echo
          echo "========================================"
          echo " PASS: exactly one GPU is visible"
          echo "========================================"

      env:
        - name: NODE_NAME
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName

      resources:
        requests:
          cpu: "1"
          memory: 1Gi
          nvidia.com/gpu: 1
        limits:
          cpu: "1"
          memory: 1Gi
          nvidia.com/gpu: 1

예를 들어 stage6_gpu_1_smoke.yaml로 저장한 뒤 먼저 namespace와 node를 확인합니다.

kubectl create namespace gpu-validation

kubectl get nodes -l validation.openai.com/b300=true \
  -o custom-columns=NAME:.metadata.name,GPU:.status.allocatable.nvidia\\.com/gpu

여기서 기대하는 것은 대략:

NAME           GPU
b300-gpu-01    8

입니다. 그다음 smoke test를 실행합니다.

kubectl apply -f stage6_gpu_1_smoke.yaml

kubectl get pod -n gpu-validation \
  b300-gpu-1-smoke -o wide

kubectl logs -n gpu-validation \
  b300-gpu-1-smoke

정상이라면 핵심적으로:

[3] nvidia-smi
...

[4] GPU list
GPU 0: NVIDIA B300 ...

[5] Allocated GPU count
GPU_COUNT=1

PASS: exactly one GPU is visible

가 나와야 합니다. 호스트에 GPU가 8장 있더라도 이 Pod에서는 정확히 1장만 보여야 한다는 것이 이 시험의 핵심입니다.

Pod가 Pending이면 바로 다음을 보세요.

kubectl describe pod -n gpu-validation \
  b300-gpu-1-smoke

특히 Events에서 Insufficient nvidia.com/gpu, node selector mismatch, taint/toleration 문제를 확인하면 됩니다.

시험 후에는:

kubectl delete -f stage6_gpu_1_smoke.yaml

로 정리하면 됩니다.

한 가지 추가로, 이 테스트는 Device Plugin → container runtime → GPU device injection smoke test이지 CUDA 연산 검증까지 하는 것은 아닙니다. 이게 PASS하면 다음 단계에서 GPU 8장 Pod + nvidia-smi topo -m + CUDA compute smoke를 묶은 YAML을 실행하고, 그 다음 기존 6c → 6a NCCL로 넘어가는 흐름이 가장 깔끔합니다.

===

네. 이번에는 8 GPU 할당 검증 + nvidia-smi topo -m + GPU별 CUDA 실제 연산을 한 Pod에서 수행하도록 만들면 좋습니다.

단순 nvidia-smi만 보는 게 아니라 CUDA/PyTorch로 8개 GPU 각각에 실제 matrix multiplication을 수행하게 하겠습니다. 이 테스트가 PASS한 뒤 Stage6c → Stage6a NCCL로 넘어가면 됩니다.

apiVersion: v1
kind: Pod
metadata:
  name: b300-gpu-8-smoke
  namespace: gpu-validation
  labels:
    app: b300-gpu-smoke
    stage: stage6-preflight
spec:
  restartPolicy: Never

  # Stage6 canonical profile:
  # STAGE6_GPU_NODE_SELECTOR_KEY=validation.openai.com/b300
  # STAGE6_GPU_NODE_SELECTOR_VALUE=true
  nodeSelector:
    validation.openai.com/b300: "true"

  # GPU node에 아래 taint를 적용한 경우:
  # nvidia.com/gpu=true:NoSchedule
  tolerations:
    - key: nvidia.com/gpu
      operator: Equal
      value: "true"
      effect: NoSchedule

  containers:
    - name: gpu-smoke
      # PyTorch + CUDA가 포함된 이미지.
      # 실제 운영에서는 내부 registry에 검증한 버전을 pinning하는 것을 권장.
      image: nvcr.io/nvidia/pytorch:24.08-py3
      imagePullPolicy: IfNotPresent

      env:
        - name: NODE_NAME
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName

      command:
        - /bin/bash
        - -c
        - |
          set -euo pipefail

          echo "================================================="
          echo " Stage6 Preflight - 8 GPU / Topology / CUDA Smoke"
          echo "================================================="

          echo
          echo "-------------------------------------------------"
          echo "[1] Pod / Node"
          echo "-------------------------------------------------"
          echo "HOSTNAME=${HOSTNAME}"
          echo "NODE_NAME=${NODE_NAME}"

          echo
          echo "-------------------------------------------------"
          echo "[2] NVIDIA_VISIBLE_DEVICES"
          echo "-------------------------------------------------"
          echo "${NVIDIA_VISIBLE_DEVICES:-<unset>}"

          echo
          echo "-------------------------------------------------"
          echo "[3] nvidia-smi"
          echo "-------------------------------------------------"
          nvidia-smi

          echo
          echo "-------------------------------------------------"
          echo "[4] GPU list"
          echo "-------------------------------------------------"
          nvidia-smi -L

          GPU_COUNT="$(nvidia-smi \
            --query-gpu=uuid \
            --format=csv,noheader | wc -l)"

          echo
          echo "Detected GPU count: ${GPU_COUNT}"

          if [ "${GPU_COUNT}" -ne 8 ]; then
            echo
            echo "FAIL: expected exactly 8 GPUs"
            echo "      detected=${GPU_COUNT}"
            exit 1
          fi

          echo
          echo "PASS: exactly 8 GPUs are visible"

          echo
          echo "-------------------------------------------------"
          echo "[5] GPU properties"
          echo "-------------------------------------------------"

          nvidia-smi \
            --query-gpu=index,name,uuid,driver_version,memory.total,\
temperature.gpu,power.draw \
            --format=csv

          echo
          echo "-------------------------------------------------"
          echo "[6] GPU topology"
          echo "-------------------------------------------------"

          nvidia-smi topo -m

          echo
          echo "-------------------------------------------------"
          echo "[7] NVLink status"
          echo "-------------------------------------------------"

          # 지원되지 않는 driver/platform 조합에서도
          # 전체 smoke test를 바로 중단시키지는 않음.
          nvidia-smi nvlink --status || true

          echo
          echo "-------------------------------------------------"
          echo "[8] PyTorch / CUDA environment"
          echo "-------------------------------------------------"

          python3 - <<'PY'
          import torch

          print("PyTorch version :", torch.__version__)
          print("CUDA version    :", torch.version.cuda)
          print("CUDA available  :", torch.cuda.is_available())
          print("GPU count       :", torch.cuda.device_count())

          if not torch.cuda.is_available():
              raise SystemExit(
                  "FAIL: torch.cuda.is_available() == False"
              )

          if torch.cuda.device_count() != 8:
              raise SystemExit(
                  f"FAIL: expected 8 CUDA devices, "
                  f"got {torch.cuda.device_count()}"
              )

          print()
          print("CUDA device list")

          for i in range(torch.cuda.device_count()):
              p = torch.cuda.get_device_properties(i)

              print(
                  f"GPU {i}: "
                  f"{p.name}, "
                  f"memory={p.total_memory / 1024**3:.1f} GiB, "
                  f"capability={p.major}.{p.minor}"
              )
          PY

          echo
          echo "-------------------------------------------------"
          echo "[9] CUDA compute smoke - all 8 GPUs"
          echo "-------------------------------------------------"

          python3 - <<'PY'
          import sys
          import time
          import torch

          expected_gpus = 8

          if not torch.cuda.is_available():
              print("FAIL: CUDA unavailable")
              sys.exit(1)

          if torch.cuda.device_count() != expected_gpus:
              print(
                  f"FAIL: expected {expected_gpus} GPUs, "
                  f"got {torch.cuda.device_count()}"
              )
              sys.exit(1)

          failures = []

          # Smoke test 목적이므로 너무 큰 matrix를 사용하지 않는다.
          # Stage1 stress/burn test의 대체가 아님.
          matrix_size = 4096

          print(
              f"Matrix multiplication: "
              f"{matrix_size} x {matrix_size}"
          )
          print()

          for gpu in range(expected_gpus):

              try:
                  torch.cuda.set_device(gpu)
                  device = torch.device(f"cuda:{gpu}")

                  # BF16은 B300 inference 환경에서
                  # 기본 CUDA compute sanity 확인용으로 사용.
                  a = torch.randn(
                      matrix_size,
                      matrix_size,
                      device=device,
                      dtype=torch.bfloat16,
                  )

                  b = torch.randn(
                      matrix_size,
                      matrix_size,
                      device=device,
                      dtype=torch.bfloat16,
                  )

                  # 초기 CUDA context / kernel 준비
                  _ = torch.matmul(a, b)
                  torch.cuda.synchronize(device)

                  start = time.perf_counter()

                  c = torch.matmul(a, b)

                  torch.cuda.synchronize(device)

                  elapsed = time.perf_counter() - start

                  if not torch.isfinite(c).all().item():
                      raise RuntimeError(
                          "non-finite value detected"
                      )

                  print(
                      f"GPU {gpu}: PASS "
                      f"time={elapsed:.4f}s "
                      f"result_mean={c.float().mean().item():.6f}"
                  )

                  del a, b, c

                  torch.cuda.empty_cache()

              except Exception as exc:
                  print(
                      f"GPU {gpu}: FAIL: {exc}"
                  )
                  failures.append((gpu, str(exc)))

          print()
          print("-----------------------------------------")

          if failures:
              print("CUDA COMPUTE SMOKE: FAIL")

              for gpu, reason in failures:
                  print(
                      f"  GPU {gpu}: {reason}"
                  )

              sys.exit(1)

          print(
              "CUDA COMPUTE SMOKE: "
              "PASS - all 8 GPUs"
          )
          PY

          echo
          echo "-------------------------------------------------"
          echo "[10] Final GPU state"
          echo "-------------------------------------------------"

          nvidia-smi \
            --query-gpu=index,name,temperature.gpu,power.draw,\
memory.used,memory.total \
            --format=csv

          echo
          echo "================================================="
          echo " PASS: Stage6 8-GPU preflight smoke completed"
          echo "================================================="

      resources:
        requests:
          cpu: "8"
          memory: 16Gi
          nvidia.com/gpu: 8
        limits:
          cpu: "8"
          memory: 16Gi
          nvidia.com/gpu: 8

파일명을 예를 들어 stage6_gpu_8_smoke.yaml로 두면 됩니다.

먼저 GPU allocatable을 확인합니다.

kubectl get node b300-gpu-01 \
  -o custom-columns=\
NAME:.metadata.name,\
GPU-CAPACITY:.status.capacity.nvidia\\.com/gpu,\
GPU-ALLOCATABLE:.status.allocatable.nvidia\\.com/gpu

반드시 대략:

NAME           GPU-CAPACITY   GPU-ALLOCATABLE
b300-gpu-01    8              8

이어야 합니다.

그다음:

kubectl apply -f stage6_gpu_8_smoke.yaml

kubectl get pod \
  -n gpu-validation \
  b300-gpu-8-smoke \
  -o wide

실시간으로 보는 편이 편합니다.

kubectl logs \
  -n gpu-validation \
  -f b300-gpu-8-smoke

성공 시 핵심적으로 다음 결과가 나와야 합니다.

Detected GPU count: 8

PASS: exactly 8 GPUs are visible

GPU topology
...
nvidia-smi topo -m 결과
...

CUDA available  : True
GPU count       : 8

CUDA compute smoke - all 8 GPUs

GPU 0: PASS
GPU 1: PASS
GPU 2: PASS
GPU 3: PASS
GPU 4: PASS
GPU 5: PASS
GPU 6: PASS
GPU 7: PASS

CUDA COMPUTE SMOKE: PASS - all 8 GPUs

PASS: Stage6 8-GPU preflight smoke completed

여기서 nvidia-smi topo -m은 단순히 명령 성공만 보지 말고 8 GPU가 모두 나타나는지와 GPU 간 NVLink/NVSwitch topology가 예상대로 노출되는지를 확인하는 게 중요합니다. 이 YAML의 CUDA compute는 GPU마다 독립적으로 연산하므로 GPU 간 통신 검증은 아닙니다. GPU 간 collective communication은 바로 다음 Stage6a NCCL에서 검증하는 게 맞습니다.

따라서 현재 Stage6 진입 순서를 조금 더 명확하게 만들면:

GPU Operator 설치
        ↓
nvidia.com/gpu = 8
        ↓
1-GPU Smoke
        ↓
8-GPU Smoke
 ├─ GPU allocation = 8
 ├─ nvidia-smi
 ├─ topo -m
 ├─ NVLink visibility
 └─ CUDA compute GPU0~7
        ↓
Stage6c
 GPU Operator / topology 정식 검사
        ↓
Stage6a
 NCCL TP8 collective
        ↓
Stage6b
 Network
        ↓
Stage6e
 AIStor
        ↓
Stage6k
 vLLM

로 가져가면 됩니다.

참고로 FP8/FP4 smoke는 이 YAML에 일부러 넣지 않았습니다. 여기서는 K8s device injection + CUDA 기본 연산까지만 확인하고, FP8/FP4는 Stage1에서 이미 실제 연산 검증을 했으므로 Stage6에서는 별도의 짧은 container precision sanity로 분리하는 편이 장애 원인을 구분하기 좋습니다.

profile
engineer

0개의 댓글