170 lines
6.0 KiB
YAML
170 lines
6.0 KiB
YAML
apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: nccl-scripts
|
|
data:
|
|
run.sh: |
|
|
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
# ----- 기본 분산 설정 -----
|
|
export MASTER_ADDR="${MASTER_ADDR:-nccl-0.nccl}" # 첫 파드
|
|
export MASTER_PORT="${MASTER_PORT:-23456}"
|
|
export WORLD_SIZE="${WORLD_SIZE:-2}" # 파드 수
|
|
export NUM_GPUS="${NUM_GPUS:-1}" # 파드당 프로세스(=GPU) 수
|
|
|
|
# 파드 이름이 nccl-0, nccl-1 ... 이므로, 끝의 ordinal을 RANK로 사용
|
|
ordinal="$(hostname | awk -F- '{print $NF}')"
|
|
export RANK="${RANK:-$ordinal}"
|
|
|
|
# ----- (선택) 특정 GPU만 사용하고 싶다면: 2번 GPU만 보이도록 고정 -----
|
|
# 예: kubectl patch 로 아래 값을 "2"로 넣으면 그 노드의 물리 GPU #2만 노출됨
|
|
if [[ -n "${CUDA_VISIBLE_DEVICES_OVERRIDE:-}" ]]; then
|
|
export CUDA_VISIBLE_DEVICES="${CUDA_VISIBLE_DEVICES_OVERRIDE}"
|
|
fi
|
|
|
|
# ----- RDMA/IB 강제 -----
|
|
export NCCL_DEBUG=INFO
|
|
export NCCL_DEBUG_SUBSYS=INIT,NET
|
|
export NCCL_NET=IB
|
|
export NCCL_IB_DISABLE=0
|
|
export NCCL_SOCKET_IFNAME="${NCCL_SOCKET_IFNAME:-net1}" # HostDeviceNetwork로 붙인 NIC 이름
|
|
export NCCL_IB_HCA="${NCCL_IB_HCA:-mlx5*}"
|
|
# (RoCE 환경에 따라 필요 시) export NCCL_IB_GID_INDEX=3
|
|
|
|
# 프로세스별 로그 파일 경로 (노드/프로세스마다 별도 파일)
|
|
export NCCL_DEBUG_FILE="/tmp/nccl-%h-%p.log"
|
|
|
|
# ----- 학습 실행 (각 파드가 1개 프로세스씩 구동) -----
|
|
echo "[RUN] RANK=$RANK WORLD_SIZE=$WORLD_SIZE on $(hostname)"
|
|
torchrun \
|
|
--nnodes="${WORLD_SIZE}" \
|
|
--nproc_per_node="${NUM_GPUS}" \
|
|
--rdzv_backend=c10d \
|
|
--rdzv_endpoint="${MASTER_ADDR}:${MASTER_PORT}" \
|
|
/workspace/train_nccl.py 2>&1 | tee /tmp/nccl-aggregate.log
|
|
|
|
# ----- RDMA 사용 여부 집계 -----
|
|
total=0; ok=0
|
|
for f in /tmp/nccl-*.log; do
|
|
[[ -f "$f" ]] || continue
|
|
total=$((total+1))
|
|
if grep -q "NET/IB" "$f"; then
|
|
ok=$((ok+1))
|
|
fi
|
|
done
|
|
echo "[RDMA-CHECK] used IB on $ok / $total local ranks (processes) in $(hostname)"
|
|
|
|
# 모든 랭크가 IB를 썼는지(파드 단위) 판단: 로컬 프로세스 수와 비교
|
|
if [[ "$ok" -eq "$NUM_GPUS" ]]; then
|
|
echo "[RDMA-CHECK] ✅ RDMA path OK for all local GPUs"
|
|
exit 0
|
|
else
|
|
echo "[RDMA-CHECK] ❌ RDMA path NOT used by all local GPUs"
|
|
echo "---- NCCL NET/IB related lines ----"
|
|
grep -h "NET/" /tmp/nccl-*.log || true
|
|
exit 1
|
|
fi
|
|
train_nccl.py: |
|
|
import os, time, torch
|
|
import torch.distributed as dist
|
|
|
|
def main():
|
|
# 분산 초기화
|
|
rank = int(os.getenv("RANK", "0"))
|
|
world_size = int(os.getenv("WORLD_SIZE", "1"))
|
|
local_rank = int(os.getenv("LOCAL_RANK", "0")) # torchrun이 설정
|
|
|
|
# (선택) 특정 GPU 인덱스로 고정하고 싶을 때: LOCAL_RANK 대신 고정값 사용
|
|
device_index = int(os.getenv("GPU_INDEX", str(local_rank)))
|
|
torch.cuda.set_device(device_index)
|
|
|
|
dist.init_process_group(
|
|
backend="nccl",
|
|
init_method="env://",
|
|
world_size=world_size,
|
|
rank=rank,
|
|
timeout=torch.distributed.constants.default_pg_timeout
|
|
)
|
|
|
|
# 더미 학습 스텝: all-reduce로 통신 발생
|
|
x = torch.ones(8 * 1024 * 1024, device=device_index, dtype=torch.float32) # ~32MB
|
|
for i in range(5):
|
|
dist.all_reduce(x, op=dist.ReduceOp.SUM)
|
|
torch.cuda.synchronize()
|
|
if rank == 0:
|
|
print(f"[STEP {i}] all_reduce done; tensor sum={x[0].item():.1f}")
|
|
|
|
# 약간 대기해서 로그 flush
|
|
time.sleep(2.0)
|
|
dist.destroy_process_group()
|
|
|
|
if __name__ == "__main__":
|
|
main()
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: nccl
|
|
labels: { app: nccl }
|
|
spec:
|
|
clusterIP: None # Headless
|
|
selector: { app: nccl }
|
|
ports:
|
|
- name: rdv
|
|
port: 23456
|
|
---
|
|
apiVersion: apps/v1
|
|
kind: StatefulSet
|
|
metadata:
|
|
name: nccl
|
|
spec:
|
|
serviceName: nccl
|
|
replicas: 2 # >=2 노드에서 스케줄되도록 노드 리소스 준비 필요
|
|
selector:
|
|
matchLabels: { app: nccl }
|
|
template:
|
|
metadata:
|
|
labels: { app: nccl }
|
|
annotations:
|
|
k8s.v1.cni.cncf.io/networks: hostdevice-net # HostDeviceNetwork (RDMA)
|
|
spec:
|
|
# RDMA 장치나 /dev/infiniband 노출이 안 보이면 네트워크 오퍼레이터의 RDMA 플러그인 구성을 확인하세요.
|
|
# (환경에 따라 RDMA Shared Device Plugin이 필요할 수 있음)
|
|
containers:
|
|
- name: worker
|
|
image: nvcr.io/nvidia/pytorch:24.10-py3
|
|
imagePullPolicy: IfNotPresent
|
|
securityContext:
|
|
capabilities: { add: ["IPC_LOCK"] }
|
|
command: [ "bash", "-lc", "cp /config_scripts/* /workspace/ && chmod +x /workspace/run.sh && /workspace/run.sh || sleep infinity" ]
|
|
env:
|
|
# --- (선택) 특정 GPU만 사용하려면 아래 값을 물리 인덱스로 설정 (예: "2") ---
|
|
# - name: CUDA_VISIBLE_DEVICES_OVERRIDE
|
|
# value: "2"
|
|
- name: WORLD_SIZE
|
|
value: "2" # StatefulSet replicas와 동일
|
|
- name: NUM_GPUS
|
|
value: "8" # 파드당 사용할 GPU 개수
|
|
- name: MASTER_ADDR
|
|
value: "nccl-0.nccl" # 첫 파드의 DNS 이름
|
|
- name: MASTER_PORT
|
|
value: "23456"
|
|
# RDMA 관련 기본값은 run.sh에서 설정
|
|
resources:
|
|
limits:
|
|
nvidia.com/gpu: "8"
|
|
nvidia.com/hostdev: "8" # SR-IOV Device Plugin이 광고한 RDMA NIC 리소스
|
|
requests:
|
|
nvidia.com/gpu: "8"
|
|
nvidia.com/hostdev: "8"
|
|
volumeMounts:
|
|
- name: scripts
|
|
mountPath: /config_scripts
|
|
volumes:
|
|
- name: scripts
|
|
configMap:
|
|
name: nccl-scripts
|
|
defaultMode: 0755
|
|
|