Kubeflow Trainer 사용
Kubeflow Trainer로 단일 노드 및 다중 노드 분산 학습 튜토리얼을 수행합니다.
지원 도구
| 도구 | 버전 | 설명 |
|---|---|---|
| Kubeflow Trainer | v2 | - 분산 AI 플랫폼으로, 대규모 LLM 파인튜닝 및 모델 학습 지원 - HPC 환경에서 멀티 노드, 멀티 GPU 분산 학습을 효율적로 오케스트레이션 하여 대규모 학습 워크로드 처리가능 - 다양한 ML 프레임워크를 지원, 유연한 학습 실행 환경 제공 - Cloud Native AI 생태계와 통합되어 스케줄링, 워크로드 관리, 확장성 제공 |
시나리오 소개
본 튜토리얼에서는 Kubeflow Trainer v2를 사용하여 Kanana 모델을 대상으로 다음 두 가지 학습 시나리오를 수행합니다.
- 단일 노드 환경에서 다중 GPU를 활용한 튜닝
- 다중 노드 환경에서 대규모 모델 분산 튜닝
이를 통해 단일 머신 기반 멀티 GPU 학습과 멀티 노드 기반 분산 학습의 실행 방식과 차이를 확인합니다.
사용 리소스 개요
Kubeflow Trainer v2는 분산 학습 실행을 위해 다음 두 가지 핵심 리소스를 사용합니다.
- TrainingRuntime
- 플랫폼 관리자가 정의하는 학습 런타임 템플릿
- 컨테이너 이미지, Job 구조, 기본 정책 등을 포함
- namespace-scoped 리소스
- TrainJob
- 사용자가 실제 학습 실행을 정의하는 리소스
- 노드 수, 프로세스 수, 커맨드, 환경 변수, 리소스(GPU 등)를 지정
- runtimeRef를 통해 TrainingRuntime을 참조하여 워크로드 실행
즉, TrainingRuntime은 “학습 실행 환경 템플릿”, TrainJob은 “실제 실행 요청 객체” 역할을 합니다.
튜토리얼 진행 순서
본 튜토리얼은 다음 순서로 분산 학습을 실행합니다.
-
TrainingRuntime 생성: 학습에 사용할 기본 런타임 정의
-
TrainJob 생성: 단일 노드 / 다중 노드 분산 학습 실행
-
학습 상태 확인: Pod 상태 및 로그 확인
용어 설명
- MPI(OpenMPI): 여러 노드에 걸쳐 여러 프로세스를 실행하고 서로 통신하게 만드는 표준/구현체이며, 이 튜토리얼에서는
mpirun으로 분산 프로세스를 띄우는 런처 역할을 합니다. (MPI Forum) - DeepSpeed: 대규모 모델을 더 적은 GPU 메모리로 학습하기 위해 ZeRO 같은 최적화와 분산 학습 기능을 제공하는 학습 프레임워크입니다. (Hugging Face)
- ZeRO(Zero Redundancy Optimizer): 데이터 병렬에서 각 GPU가 중복으로 들고 있던 옵티마이저 상태/그레디언트/파라미터를 프로세스들에 분할(partition) 해서 GPU 메모리 사용을 줄이는 기법입니다. (DeepSpeed)
- NCCL: 여러 GPU(단일 노드/멀티 노드) 사이의 all-reduce 같은 집단 통신을 고속으로 수행하는 NVIDIA 통신 라이브러리입니다. (NVIDIA Docs)
- Rank / World Size: 분산 학습에서 Rank는 각 프로세스의 고유 번호이고, World Size는 전체 프로세스 개수(예: 노드 수 × GPU 수)입니다.
- InfiniBand(IB)*: 멀티 노드 학습에서 노드 간 통신이 지나가는 네트워크로 사용되며, IB는 일반적으로 이더넷보다 지연이 낮고 대역폭이 높아 통신 병목을 줄이는 데 유리합니다. (NVIDIA Developer)
*단일 노드 학습이라도 InfiniBand/RDMA 구성이 되어 있다면, GPUDirect Storage(GDS)를 활용한 스토리지 I/O 최적화가 가능합니다.
Step 1. 사전 준비
노트북 생성하기
- Kubeflow 대시보드에 접속하여 Notebooks 탭을 클릭한 후, [New Notebook] 버튼을 클릭합니다.
- New Notebook 화면에서 아래의 표와 같은 필요한 정보를 입력하고 [LAUNCH] 버튼을 클릭하여 노트북 인스턴스를 생성합니다
| 항목 | 구분 | 값 | 설명 |
|---|---|---|---|
| Name | Name | trainer-tutorial | Kubeflow 대시보드에서 노트북 인스턴스 식별에 사용 |
| Type | JupyterLab / VSCode / Rstudio | VSCode | VSCode 선택 |
| Custom Notebook | Image | kc-kubeflow-registry/kc-jupyter-pytorch-full:natl.py311.cu130.1b | 이미지 선택 |
| CPU / RAM | Minimum CPU | 32 | CPU 코어의 수이며, 노트북 인스턴스가 사용할 CPU 리소스 양 지정 |
| Minimum Memory Gi | 180 | 메모리 리소스의 단위(GiB)이며 노트북 인스턴스가 사용할 메모리 리소스의 양 지정 | |
| GPUs | Number of GPUs | 1 | GPU 리소스 양 |
| Workspace Volume | Name | workspace-pvc | 볼륨 이름 지정 |
| Size in Gi | 1000 | 볼륨 사이즈 지정 | |
| Access Mode | ReadWriteMany | Access Mode 는 ReadWriteMany 로 지정 |
노트북 환경에서 Tokenizer 작업을 진행하므로 충분한 리소스 할당을 권장드립니다.
노트북 설정하기
- 노트북 생성 시 만들어진 Volume명을 확인합니다.
- 생성 된 VSCode에 접속 후,
/home/jovyan경로에 아래 경로에 맞춰 디렉토리를 생성하고, 첨부된 파일들을 업로드 합니다.
/home/jovyan/ : 상위 폴더
├── kanana-8b/ : kanana 8b 모델 폴더
├── kanana-30b/ : kanana 30b 모델 폴더
├── dataset/ : 학습에 필요한 토큰화된 데이터셋 폴더
├── reports/ : 학습 과정에 생성되는 지표들을 저장하는 폴더
├── preprocessing.py : 데이터셋 처리, 모델 저장들의 전처리 작업 코드
├── train.py : 모델 학습을 위한 코드
├── ds_config.json : 분산 학습 관련 설정 파일
└── cmd/ : 학습에 사용할 이미지를 빌드하기 위한 폴더
├── requirements.txt
├── Dockerfile.deepspeed
└── Dockerfile.torch
첨부 파일
(선택) 이미지 빌드하기
- 추가 패키지가 필요한 경우, 노트북 환경에서 분산학습에 필요한 이미지를 빌드합니다.
- 클러스터에서 기본 제공하는 이미지를 사용하여도 진행 가능합니다.
- mlops.kr-central-2.kcr.dev/kc-kubeflow-registry/pytorch:2.9.1-cuda13.0-cudnn9-runtime.kbm.1a
- mlops.kr-central-2.kcr.dev/kc-kubeflow-registry/deepspeed-runtime:v2.1.0.kbm.1d
기본 제공 이미지의 패키지 정보
| 패키지명 | 버전 |
|---|---|
| datasets | 4.4.2 |
| transformers | 4.57.3 |
| peft | 0.18.1 |
| tensorboard | 2.20.0 |
cd /home/jovyan/cmd
docker build -t {이미지명} -f Dockerfile.torch .
docker push {이미지명}
docker build -t {이미지명} -f Dockerfile.deepspeed .
docker push {이미지명}
모델, 데이터셋 전처리
- 학습 과정에서 빠른 모델, 데이터셋 로드를 위해 볼륨에 모델과 전처리된 데이터셋을 저장합니다.
- 이미지에서 사용하는 패키지 버전과 전처리에서 사용하는 패키지의 버전이 다른 경우 에러가 발생할 수 있습니다.
cd /home/jovyan/
pip install transformers==4.57.3 datasets==4.4.2
python preprocessing.py
Tensorboard 생성
- 학습 과정 모니터링을 위해 텐서보드를 생성합니다.
- 이후 TrainJob에서 해당 경로에 맞춰서 로그 경로를 지정해야 합니다.
| 항목 | 구분 | 값 | 설명 |
|---|---|---|---|
| Name | Name | kanana-tuning | Tensorboard 대시보드에서 텐서보드 인스턴스 식별에 사용 |
| Storage Type | Object Storage / PVC | PVC | PVC 선택 |
| PVC Name | PVC Name | workspace-pvc | Notebook 생성 시 생성한 PVC 이름 |
| Mount Path | reports/ | 학습 리포트를 저장하는 경로 |
Step 2. 단일 노드에서 다중 GPU를 사용한 모델 튜닝
Training Runtime 생성
아래는 첨부된 TrainingRuntime 예시입니다.
반드시 namespace를 실제 네임스페이스로 변경해서 적용합니다.
중요
- TrainingRuntime 이름:
torch-distributed- 이후 TrainJob의
runtimeRef.name이 이 이름과 일치해야 합니다.- 학습에 필요한 라이브러리가 있는 경우, 라이브러리가 설치된 이미지를 빌드하여 사용합니다.
apiVersion: trainer.kubeflow.org/v1alpha1
kind: TrainingRuntime
metadata:
# TrainingRuntime 이름은 TrainJob의 spec.runtimeRef.name과 반드시 일치해야 합니다.
name: torch-distributed
# 이 Runtime은 namespace-scoped 리소스입니다.
# TrainJob과 같은 네임스페이스에 생성해야 참조됩니다.
namespace: {USER_NAMESPACE}
spec:
# MLPolicy: 분산 실행 시 torch.distributed 기반 런칭 정책을 정의합니다.
mlPolicy:
# (Runtime 기본값) 노드 수. 실제 실행은 TrainJob의 numNodes로 결정됩니다.
numNodes: 1
# PyTorch distributed 설정
torch:
# (Runtime 기본값) 노드당 프로세스 수.
# auto로 설정하면 GPU 개수 또는 환경에 맞게 자동으로 결정됩니다.
# 실제 실행에서는 TrainJob의 numProcPerNode로 덮어쓰는 것이 일반적입니다.
numProcPerNode: auto
# template: Trainer v2가 실제로 생성할 node 워크로드 템플릿입니다.
template:
spec:
replicatedJobs:
# ----------------------
# node 워크로드 템플릿
# ----------------------
- groupName: default
name: node
# node Pod replica 수.
# 실제 분산 학습에서는 TrainJob의 numNodes 설정으로 override 됩니다.
replicas: 1
template:
metadata:
labels:
# Trainer v2 내부에서 사용하는 ancestor step label
trainer.kubeflow.org/trainjob-ancestor-step: trainer
spec:
template:
metadata:
annotations:
# Istio 사이드카 자동 주입 환경에서 torch.distributed 통신 문제를 방지하기 위해 비활성화합니다.
sidecar.istio.io/inject: 'false'
spec:
containers:
- image: >-
# PyTorch distributed 실행을 위한 runtime 이미지
# CUDA, cuDNN, PyTorch 등이 포함되어 있어야 합니다.
mlops.kr-central-2.kcr.dev/kc-kubeflow-registry/pytorch:2.9.1-cuda13.0-cudnn9-runtime.kbm.1a
name: node
Notebook 터미널에서 위 파일을 trainruntime-torch.yaml 로 저장한 후 아래 명령어를 실행합니다.
kubectl apply -f trainruntime-torch.yaml
kubectl get trainingruntime
kubectl describe trainingruntime torch-distributed
TrainJob 생성
TrainJob 예시의 핵심 값은 아래와 같습니다.
spec.runtimeRef.name: torch-distributed- spec.trainer.resourcesPerNodes.requests.nvidia.com/mlnxnics: infiniband 사용을 위한 VF 리소스 할당 요청
- spec.podTemplateOverrides.metadata.annotation: k8s.v1.cni.cncf.io/networks → infiniband 사용을 위한 세컨더리 네트워크 설정 (GPUDirect Storage 를 위해 필요)
- 실행 파일
python3 /workspace/train.py
아래 YAML은 위 수정 사항을 반영한 예시입니다.
apiVersion: trainer.kubeflow.org/v1alpha1
kind: TrainJob
metadata:
name: kanana-8b-tuning
namespace: {USER_NAMESPACE}
spec:
# TrainJob은 runtimeRef로 TrainingRuntime을 참조합니다.
runtimeRef:
apiGroup: trainer.kubeflow.org
kind: TrainingRuntime
name: torch-distributed # TrainingRuntime.metadata.name과 일치
trainer:
# 분산 학습 노드 수 / 노드당 프로세스 수
numNodes: 1
numProcPerNode: 4
# 노드당 자원 요구량/제한
resourcesPerNode:
limits:
cpu: '40'
memory: 400Gi
nvidia.com/gpu: '4'
nvidia.com/mlnxnics: "1" # SR-IOV/IB를 사용하기 위한 설정
env:
- name: MODEL_PATH
value: "/workspace/kanana-8b"
- name: DATASET_PATH
value: "/workspace/dataset/ultrachat_tokenized"
- name: BF16
value: "true"
- name: TENSORBOARD_LOGDIR
value: "/workspace/reports/kanana-8b"
- name: OUTPUT_DIR
value: "/workspace/kanana-8b/out"
args:
- /bin/bash
- '-lc'
- |-
set -euo pipefail
torchrun \
--nnodes=${PET_NNODES} \
--nproc_per_node=${PET_NPROC_PER_NODE} \
--node_rank=${PET_NODE_RANK} \
--master_addr=${PET_MASTER_ADDR} \
--master_port=${PET_MASTER_PORT} \
/workspace/train.py
podTemplateOverrides:
- targetJobs:
- name: node
metadata:
annotations:
# Istio 자동 주입 환경에서는 통신 문제를 피하기 위해 비활성
sidecar.istio.io/inject: "false"
# InfininBand 사용을 위한 네트워크 어태치먼트 지정
k8s.v1.cni.cncf.io/networks: |-
kbm-g-operation
spec:
containers:
- name: node
volumeMounts:
- mountPath: /workspace
name: workspace
volumes:
# 학습 코드/설정/로그를 올릴 PVC, 앞서 생성한 volume 이름 지정
- name: workspace
persistentVolumeClaim:
claimName: workspace-pvc
대시보드 > TrainJobs > + New TrainJob 을 클릭하고 위의 yaml 파일을 넣어 생성합니다. 혹은 Notebook 터미널에서 다음과 같이 생성할 수 있습니다.
kubectl apply -f kanana-8b-tuning.yaml
kubectl get trainjob
kubectl describe trainjob kanana-8b-tuning
Step 3. 다중 노드에서 대규모 모델 튜닝
Training Runtime 생성
아래는 첨부된 TrainingRuntime 예시입니다.
반드시 namespace를 실제 네임스페이스로 변경해서 적용합니다.
중요
- TrainingRuntime 이름:
deepspeed-llm- 이후 TrainJob의
runtimeRef.name이 이 이름과 일치해야 합니다.- 학습에 필요한 라이브러리가 있는 경우, 라이브러리가 설치된 이미지를 빌드하여 사용합니다.
apiVersion: trainer.kubeflow.org/v1alpha1
kind: TrainingRuntime
metadata:
# TrainingRuntime 이름은 TrainJob의 spec.runtimeRef.name과 반드시 일치해야 합니다.
name: deepspeed-llm
# 이 Runtime은 namespace-scoped 리소스입니다.
# TrainJob과 같은 네임스페이스에 생성해야 참조됩니다.
namespace: {USER_NAMESPACE} # 예: kbm-g-company
spec:
# MLPolicy: 분산 런처/통신 방식 등의 정책을 정의합니다.
mlPolicy:
mpi:
# OpenMPI 기반으로 런칭합니다.
mpiImplementation: OpenMPI
# (Runtime 기본값) 노드당 프로세스 수. 실제 학습에서는 TrainJob에서 numProcPerNode로 덮어쓰는 패턴이 일반적입니다.
numProcPerNode: 1
# launcher도 node 역할로 함께 실행(구성에 따라 필요)
runLauncherAsNode: true
# MPI/SSH 인증키 마운트 경로 (Runtime 이미지/구성에 맞게)
sshAuthMountPath: /home/mpiuser/.ssh
# (Runtime 기본값) 노드 수. 실제 실행은 TrainJob의 numNodes로 결정됩니다.
numNodes: 1
# template: Trainer v2가 실제로 생성할 launcher/node 워크로드 템플릿입니다.
template:
metadata: {}
spec:
network:
# headless service/endpoint 구성에서 NotReady Pod도 주소로 포함시키는 설정
publishNotReadyAddresses: true
replicatedJobs:
# -------------------------
# launcher 워크로드 템플릿
# -------------------------
- groupName: default
name: launcher
replicas: 1
template:
metadata:
labels:
trainer.kubeflow.org/trainjob-ancestor-step: trainer
spec:
template:
metadata:
annotations:
# Istio 사이드카 자동 주입 환경이기 때문에 MPI/SSH 통신 문제를 피하기 위해 비활성화가 필요합니다.
sidecar.istio.io/inject: "false"
spec:
containers:
- name: node
# 학습 런타임 이미지 (DeepSpeed/MPI/SSH 등이 포함된 이미지)
image: mlops.kr-central-2.kcr.dev/kc-kubeflow-registry/deepspeed-runtime:v2.1.0.kbm.1d
resources: {}
securityContext:
runAsUser: 1000
# ----------------------
# node 워크로드 템플릿
# ----------------------
- groupName: default
name: node
replicas: 1
template:
metadata: {}
spec:
template:
metadata:
annotations:
sidecar.istio.io/inject: "false"
spec:
containers:
- name: node
image: mlops.kr-central-2.kcr.dev/kc-kubeflow-registry/deepspeed-runtime:v2.1.0.kbm.1d
# node Pod는 SSH 데몬을 띄워 MPI가 원격 프로세스를 실행할 수 있도록 합니다.
command: ["/usr/sbin/sshd"]
args: ["-De", "-f", "/home/mpiuser/.sshd_config"]
readinessProbe:
# SSH 포트 오픈 확인(이미지/sshd 설정에 맞게)
initialDelaySeconds: 5
tcpSocket:
port: 2222
resources: {}
securityContext:
runAsUser: 1000
# launcher가 성공하면 전체 성공으로 간주하는 정책(구성에 따라 조정)
successPolicy:
operator: All
targetReplicatedJobs:
- launcher
Notebook 터미널에서 위 파일을 trainruntime-deepspeed.yaml 로 저장한 후 아래 명령어를 실행합니다.
kubectl apply -f trainruntime-deepspeed.yaml
kubectl get trainingruntime
kubectl describe trainingruntime deepspeed-llm
TrainJob 생성
TrainJob 예시의 핵심 값은 아래와 같습니다.
spec.runtimeRef.name: deepspeed-llm- spec.trainer.resourcesPerNodes.requests.nvidia.com/mlnxnics: infiniband 사용을 위한 VF 리소스 할당 요청
- spec.podTemplateOverrides.metadata.annotation: k8s.v1.cni.cncf.io/networks → infiniband 사용을 위한 세컨더리 네트워크 설정 (노드간 고대역폭 통신을 위해 사용)
- 실행파일
python3 /workspace/train.py
아래 YAML은 위 수정 사항을 반영한 예시입니다.
apiVersion: trainer.kubeflow.org/v1alpha1
kind: TrainJob
metadata:
name: kanana-30b-tuning
namespace: {USER_NAMESPACE} # 예: kbm-g-company
spec:
# TrainJob은 runtimeRef로 TrainingRuntime을 참조합니다.
runtimeRef:
apiGroup: trainer.kubeflow.org
kind: TrainingRuntime
name: deepspeed-llm # TrainingRuntime.metadata.name과 일치
trainer:
# 분산 학습 노드 수 / 노드당 프로세스 수
numNodes: 4
numProcPerNode: 8
# MPI 기반 런칭: mpirun이 각 노드에 bash -lc 스크립트를 실행하고,
# 스크립트 내부에서 RANK/WORLD_SIZE/MASTER_ADDR 등을 구성 후 python 학습을 실행합니다.
command:
- mpirun
- "--hostfile"
- /etc/mpi/hostfile
- "-np"
- "32" # 전체 프로세스 수 = numNodes * numProcPerNode
- "-x"
- NCCL_IB_DISABLE
- "-x"
- JOBSET_NAME
- "-x"
- MODEL_PATH
- "-x"
- DATASET_PATH
- "-x"
- DEEPSPEED_CONFIG
- "-x"
- TENSORBOARD_LOGDIR
- "-x"
- OUTPUT_DIR
# mpirun이 실행할 실제 커맨드
- bash
- "-lc"
- |
set -euo pipefail
# OpenMPI가 주입하는 환경변수를 PyTorch/HF에서 사용하는 표준 변수로 매핑
export RANK=${OMPI_COMM_WORLD_RANK}
export WORLD_SIZE=${OMPI_COMM_WORLD_SIZE}
export LOCAL_RANK=${OMPI_COMM_WORLD_LOCAL_RANK}
# JobSet 이름 기반으로 launcher Pod의 headless DNS를 구성
export MASTER_ADDR=${JOBSET_NAME}-launcher-0-0.${JOBSET_NAME}
export MASTER_PORT=29500
echo "RANK=${RANK} LOCAL_RANK=${LOCAL_RANK} WORLD_SIZE=${WORLD_SIZE}"
python3 /workspace/train.py
# 학습 실행에 필요한 환경변수
env:
# NCCL 설정
- name: NCCL_IB_DISABLE
value: "0" # 멀티 노드에서 IB 사용(0: 사용, 1: 비활성)
# JobSet 컨트롤러가 부여하는 라벨에서 jobset-name을 가져와 launcher DNS 이름을 구성
- name: JOBSET_NAME
valueFrom:
fieldRef:
fieldPath: metadata.labels['jobset.sigs.k8s.io/jobset-name']
# 학습 파라미터
- name: MODEL_PATH
value: "/workspace/kanana-30b"
- name: DATASET_PATH
value: "/workspace/dataset/ultrachat_tokenized"
- name: DEEPSPEED_CONFIG
value: "/workspace/ds_config.json"
- name: BF16
value: "true"
- name: TENSORBOARD_LOGDIR
value: "/workspace/reports/kanana-30b"
- name: OUTPUT_DIR
value: "/workspace/kanana-30b/out"
# 노드당 자원 요구량/제한
# (예시) 8xGPU 노드, CPU/메모리는 환경에 맞게 조정
resourcesPerNode:
requests:
cpu: "64"
memory: "256Gi"
nvidia.com/gpu: "8"
nvidia.com/mlnxnics: "1" # SR-IOV/IB를 사용하기 위한 설정
limits:
cpu: "64"
memory: "256Gi"
nvidia.com/gpu: "8"
nvidia.com/mlnxnics: "1"
# podTemplateOverrides: launcher/node에 대한 Pod 스펙 오버라이드
podTemplateOverrides:
# launcher Pod 오버라이드
- targetJobs:
- name: launcher
metadata:
annotations:
# Istio 자동 주입 환경에서는 MPI/SSH 통신 문제를 피하기 위해 비활성
sidecar.istio.io/inject: "false"
# InfininBand 사용을 위한 네트워크 어태치먼트 지정
k8s.v1.cni.cncf.io/networks: |-
kbm-g-operation
spec:
volumes:
# 학습 코드/설정/로그를 올릴 PVC, 앞서 생성한 volume 이름 지정
- name: workspace
persistentVolumeClaim:
claimName: workspace-pvc
containers:
- name: node
volumeMounts:
- name: workspace
mountPath: /workspace
# node Pod 오버라이드
- targetJobs:
- name: node
metadata:
annotations:
sidecar.istio.io/inject: "false"
k8s.v1.cni.cncf.io/networks: |-
kbm-g-operation
spec:
volumes:
- name: workspace
persistentVolumeClaim:
claimName: workspace-pvc
containers:
- name: node
volumeMounts:
- name: workspace
mountPath: /workspace
대시보드 > TrainJobs > + New TrainJob 을 클릭하고 위의 yaml 파일을 넣어 생성합니다. 혹은 Notebook 터미널에서 다음과 같이 생성할 수 있습니다.
kubectl apply -f kanana-30b-tuning.yaml
kubectl get trainjob
kubectl describe trainjob kanana-30b-tuning
Step 4. 학습 상태 확인
TrainJobs 탭에서 학습 로그를 확인할 수 있습니다.

Tensorboard 탭에서 학습 리포트를 확인할 수 있습니다.

참고자료
- Kubeflow Trainer 공식 문서
- Runtime Guide (TrainingRuntime/ClusterTrainingRuntime)
- Migrating to Kubeflow Trainer v2 (TrainJob/TrainingRuntime 소개)