VPC 환경에서 이용 가능합니다.
ML expert Platform은 모델 학습을 위한 여러 프레임워크의 Job을 제공합니다.
kubectl을 이용한 모델 학습에서는 가장 많이 사용하는 형태인 단일 노드 학습(Job), 분산 노드 학습(PytorchJob) 형태로 설명합니다.
단일 노드 학습의 경우 Job 사용을 권장합니다.
PytorchJob을 사용할 경우, 분산 노드 학습을 위한 기능으로 인해 Master와 Worker 형태로 배포 관리 되기 때문에 리소스를 불필요하게 낭비할 수 있습니다.
단일 노드 학습 (Job) 실행하기
학습을 위해 Kubernetes Job 명세는 다음 예제와 같이 작성할 수 있습니다.
# job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: mnist
namespace: p-{projectName}
spec:
backoffLimit: 1
template:
metadata:
annotations:
sidecar.istio.io/inject: "false"
spec:
restartPolicy: Never
containers:
- name: main
image: { 학습 이미지 (e.g. example.com/mnist:latest ) } # NVIDIA Base 이미지 기준으로 작성된 학습 코드
imagePullPolicy: Always
resources:
limits:
memory: "8Gi"
cpu: "4"
nvidia.com/gpu: "1"
command: ["python"]
args:
- /opt/mnist/src/mnist.py
- --checkpoint_path
- /opt/mnist/checkpoints/mnist.pt
- --log_path
- /opt/mnist/log
- --data_path
- /opt/mnist/data
- --download_data
kubectl apply -f job.yaml
batch/mnist created
외부 컨테이너 레지스트리 사용
컨테이너 레지스트리에 Secret 정보가 필요할 경우에는 Container Secret 생성하기를 참고하여 생성할 수 있습니다.
생성된 Secret은 다음과 같이 사용 가능합니다.
...
spec:
imagePullSecrets:
- name: my-harbor-secret # 미리 만들어둔 Docker Credential Secret 이름
...
기존 Volume 사용
Volumes를 통해 생성된 Volume을 사용하기 위해서는 다음과 같이 사용할 수 있습니다.
...
spec:
containers:
- name: main
...
volumeMounts:
- mountPath: /data
name: mnist-data # 하단 spec.volumes에 기재한 이름
volumes:
- name: mnist-data
persistentVolumeClaim:
claimName: mnist-data # Volumes 를 통해 생성된 PVC 이름
...
Job 라이프 사이클
Job이 종료될 경우, 일정 시간 동안 컨테이너 로그 및 상태를 남기기 위해 삭제되지 않고 목록에 남아 있게 됩니다.
목록에 남아있을 수 있는 Job의 최대 개수 제한이 있기 때문에 TTL을 통해 라이프사이클을 관리해야합니다. 자세한 정보는 Kubernetes Job API 문서를 참조바랍니다.
Job을 생성하면서 TTL(Time To Live)을 임의로 설정할 수 있습니다. TTL은 Job이 완료(성공/실패)된 후에 활성화되고, TTL만큼 지나면 Job과 Job에 속하는 Pod이 모두 자동으로 삭제됩니다.
아래는 3주간의 TTL을 적용한 예시입니다.
apiVersion: batch/v1
kind: Job
metadata:
name: mnist
namespace: p-{projectName}
spec:
ttlSecondsAfterFinished: 1814400 # TTL을 직접 설정하는 필드 (단위: sec)
분산 노드 학습 (PytorchJob) 실행하기
PyTorchJob 이용 시 아래와 같은 장점이 있습니다.
- 작성된 컨테이너 spec 을 기반하여 Master, Worker Pod을 적절히 생성해줍니다.
- PyTorch 분산 학습에 일반적으로 필요한 환경변수 (i.e., WORLD_SIZE, RANK, MASTER_ADDR 등)를 자동으로 설정합니다.
- 학습에 사용되는 Pod들이 서로 통신할 수 있도록 K8s Service를 자동으로 생성해줍니다. Master는
<pytorch-job-name>-master-0, Worker는<pytorch-job-name>-worker-<idx>와 같은 이름으로 접근할 수 있습니다. - 필요한 경우, Elastic Policy 사용를 통해 torchrun에서 사용할 인자들을 환경 변수들로 생성해줍니다(
--nnodes,--nproc-per-node,--rdzv-endpoint등).
PytorchJob 작성하기
Pytorch로 분산 학습 시에는 torchrun(Elastic Launch)을 사용하는 것이 권장됩니다. 또한, torchrun 사용 시 마스터 Pod을 따로 명시하지 않습니다. Torch Elastic 에서는 마스터 노드 역할을 하는 RANK=0 Pod이 실행 도중 변경될 수 있습니다.
spec.elasticPolicy- torchrun 관련 설정입니다. 여기에 명시한 설정들이 환경 변수로 주입됩니다. 자세한 것은 Elastic Policy 사용를 참고해주세요.spec.runPolicy- PyTorchJob 실행 및 종료 후처리 관련된 파라미터들을 명시할 수 있습니다. 자세한 것은 Run Policy 사용를 참고해주세요.spec.pytorchReplicaSpecs.Worker- 분산 학습을 수행하는 Worker Pod에 대한 설정입니다.
- 학습을 진행하는 컨테이너의 이름 (e.g.
spec.pytorchReplicaSpecs.Worker.template.spec.containers[*].name)은 반드시pytorch이어야 합니다. - 원활한 분산학습을 위하여 Istio Sidecar가 Inject가 되지 않도록
spec.pytorchReplicaSpecs.Worker.template.metadata.annotations에sidecar.istio.io/inject: "false"를 반드시 명기해야합니다. 해당 annotation이 설정되지 않는 경우,RuntimeError: Connection reset by peer등 노드간 통신에 관련된 에러를 볼 수 있습니다.
학습을 위해 PytorchJob 명세는 다음 예제와 같이 작성할 수 있습니다.
# pytorchjob.yaml
apiVersion: kubeflow.org/v1
kind: PyTorchJob
metadata:
name: pytorch-mnist-dist-nccl
namespace: p-{ projectName } # 프로젝트에 해당되는 Kubernetes Namespace 명
spec:
pytorchReplicaSpecs:
Worker:
replicas: 2
restartPolicy: OnFailure
template:
metadata:
annotations:
sidecar.istio.io/inject: "false" # Istio sidecar injection 비활성화는 필수
spec:
nodeSelector:
mlx.navercorp.com/zone: { 제공된 GPU Zone 이름 } # GPU Resources 에서 확인 가능한 Zone 명
containers:
- name: pytorch # PyTorchJob의 container 이름은 반드시 pytorch로 설정
image: examples.com/pytorch-mnist-dist:23.03-py3
imagePullPolicy: Always
command: ["bash", "-c"]
args:
- >
torchrun --nnodes ${PET_NNODES} --nproc_per_node ${PET_NPROC_PER_NODE} --rdzv_id ${PET_RDZV_ID} --rdzv_backend ${PET_RDZV_BACKEND} --rdzv_endpoint ${PET_RDZV_ENDPOINT}
/opt/mnist/src/mnist.py --checkpoint_path /data/checkpoints/mnist.pt --log_path /data/logs --data_path /data/dataset
env:
- name: NCCL_DEBUG
value: INFO
kubectl apply -f pytorchjob.yaml
pytorchjob.kubeflow.org/pytorch-elastic-mnist-nccl created
Elastic Policy 사용
torchrun 을 사용하기 위해 elasticPolicy 를 작성합니다.
...
spec:
...
elasticPolicy:
rdzvId: mnist
rdzvBackend: c10d
minReplicas: 2
maxReplicas: 2
nProcPerNode: 8
...
elasticPolicy 각 필드값을 기반으로 PyTorchJob 에 사용될 환경변수가 설정됩니다. 환경변수들은 torchrun 인자를 설정하는데 쓰일 수 있습니다. torchrun 에서 사용하는 인자 등 대한 자세한 설명은 공식 문서를 참고하기 바랍니다.
elasticPolicy 필드 |
대응 환경변수 | 연관 torchrun 인자 |
설명 |
|---|---|---|---|
rdzvId |
PET_RDZV_ID |
--rdzv-id |
랑데부용 Job ID |
rdzvBackend |
PET_RDZV_BACKEND |
--rdzv-backend |
랑데부 백엔드 (i.e., c10d) |
minReplicas, maxReplicas |
PET_NNODES |
--nnodes |
노드 개수 |
nProcPerNode |
PET_NPROC_PER_NODE |
--nproc-per-node |
노드당 GPU 개수 |
maxRestarts |
PET_MAX_RESTARTS |
--max-restarts |
최대 재시작 횟수 |
Run Policy 사용
torchrun을 사용하기 위해 runPolicy를 작성합니다.
...
spec:
runPolicy:
cleanPodPolicy: None
ttlSecondsAfterFinished: 1814400 # TTL을 직접 설정하는 필드 (단위: sec)
...
spec.runPolicy에는 PyTorchJob 의 실행 및 정리에 관련된 파라미터들을 명시할 수 있습니다. 명시하지 않을 시 기본값을 사용하게 됩니다. spec.runPolicy 하위에 명시할 수 있는 파라미터들은 다음과 같습니다. (참고 자료: Kubeflow Trainer API Reference v1.9)
cleanPodPolicy-PytorchJob이 완료된 이후 Pod들을 어떻게 정리할지 결정합니다.- 기본값:
None None: Job이 완료된 후 Pod들을 삭제하지 않기 때문에 추후 로그를 확인하는데 도움이됩니다.All: Job이 완료된 후 모든 Pod들을 삭제합니다.Running은 Job이 완료된 후 실행 중인 Pod을 제거합니다. 특수한 경우가 아니면 사용할 일이 없습니다.
- 기본값:
ttlSecondsAfterFinished- Job이 완료된 이후 몇 초 후에 Job을 삭제할지 정합니다.activeDeadlineSeconds- Job의 최대 실행 시간입니다. 명시된 시간이 지난 후에는 실패 처리가 됩니다. 설정되지 않은 경우 job의 실행 시간에 제한이 없습니다.backoffLimit- Job이 실패했을 때 최대 재시도 횟수입니다.
Infiniband 사용
- 별도의 RDMA 리소스 설정(request/limit)은 필요하지 않습니다
- GPU Zone 정보는 사용 가능한 GPU Zone 정보 조회를 참고하시기 바랍니다.
InfiniBand 네트워크로 연결된 노드들로 분산 학습을 실행하면 노드간 통신을 가속할 수 있습니다.
이전 섹션에서 보여주었던 예제를 InfiniBand 환경에 맞추기 위해서는 추가해야하는 명세가 몇가지 있는데, 다음과 같이 요약이 가능합니다.
- InfiniBand 네트워크가 구성된 구역 이름을 Annotation으로 명시합니다 (i.e.,
mlx.navercorp.com/zone=ai-infra) - InfiniBand 사용을 위해
IPC_LOCKcapability를securityContext에 추가합니다. - 분산학습을 돕기 위한 Shared Memory 를
volumes로 설정합니다.
Infiniband 사용을 위해서 다음과 같이 사용할 수 있습니다.
...
metadata:
annotations:
mlx.navercorp.com/zone="ai-infra"
...
spec:
...
pytorchReplicaSpecs:
Worker:
template:
containers:
- name: pytorch
securityContext: # Infiniband 를 사용하기 위한 securityContext가 필요합니다.
capabilities:
add: ["IPC_LOCK"]
# shared memory
volumeMounts:
- mountPath: /dev/shm
name: shared-memory
volumes:
- emptyDir:
medium: Memory
name: shared-memory
...
PytorchJob 디버깅
PytorchJob 사용 시, 문제가 발생하여 디버깅이 필요할 때 다음과 같이 환경변수 값을 설정하는 것으로 필요한 정보를 로깅하도록 할 수 있습니다.
NCCL_DEBUG: NCCL과 관련 디버깅TORCH_DISTRIBUTED_DEBUG,TORCH_CPP_LOG_LEVEL: 분산 학습에 대한 디버깅; 자세한 것은 PyTorch 공식 문서를 참조해주세요.
디버깅을위하여 다음과 같이 사용할 수 있습니다.
...
spec:
...
pytorchReplicaSpecs:
Worker:
template:
containers:
- name: pytorch
...
env:
- name: NCCL_DEBUG
value: "INFO"
- name: TORCH_DISTRIBUTED_DEBUG
value: "DETAIL"
- name: TORCH_CPP_LOG_LEVEL
value: "INFO"
...
외부 컨테이너 레지스트리 사용
컨테이너 레지스트리에 Secret 정보가 필요할 경우에는 Container Secret 생성하기를 참고하여 생성할 수 있습니다.
생성된 Secret은 다음과 같이 사용이 가능합니다.
...
spec:
...
pytorchReplicaSpecs:
Worker:
template:
containers:
- name: pytorch
imagePullSecrets:
- name: my-harbor-secret # 미리 만들어둔 Docker Credential Secret 이름
...
기존 Volume 사용
Volumes를 통해 생성된 Volume을 사용하기 위해서는 다음과 같이 사용할 수 있습니다.
...
spec:
...
pytorchReplicaSpecs:
Worker:
template:
containers:
- name: pytorch
volumeMounts:
- mountPath: /data
name: mnist-data # 하단 spec.volumes에 기재한 이름
volumes:
- name: mnist-data
persistentVolumeClaim:
claimName: mnist-data # Volumes 를 통해 생성된 PVC 이름
...
PytorchJob 상태 확인
kubectl get, kubectl describe 를 이용하여 PyTorchJob 의 상태를 확인할 수 있습니다.
kubectl get pytorchjob pytorch-elastic-mnist-nccl
NAME STATE AGE
pytorch-elastic-mnist-nccl Running 12s
kubectl describe pytorchjob pytorch-elastic-mnist-nccl
Status:
Completion Time: 2024-11-22T09:16:58Z
Conditions:
Last Transition Time: 2024-11-22T09:15:43Z
Last Update Time: 2024-11-22T09:15:43Z
Message: PyTorchJob pytorch-elastic-mnist-nccl is created.
Reason: PyTorchJobCreated
Status: True
Type: Created
Last Transition Time: 2024-11-22T09:15:48Z
Last Update Time: 2024-11-22T09:15:48Z
Message: PyTorchJob nb12706/pytorch-elastic-mnist-nccl is running.
Reason: PyTorchJobRunning
Status: False
Type: Running
Last Transition Time: 2024-11-22T09:16:58Z
Last Update Time: 2024-11-22T09:16:58Z
Message: PyTorchJob nb12706/pytorch-elastic-mnist-nccl successfully completed.
Reason: PyTorchJobSucceeded
Status: True
Type: Succeeded
Last Reconcile Time: 2024-11-22T09:15:43Z
Replica Statuses:
Worker:
Selector: training.kubeflow.org/job-name=pytorch-elastic-mnist-nccl,training.kubeflow.org/operator-name=pytorchjob-controller,training.kubeflow.org/replica-type=worker
Succeeded: 2
Start Time: 2024-11-22T09:15:44Z
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreatePod 4m21s pytorchjob-controller Created pod: pytorch-elastic-mnist-nccl-worker-0
Normal SuccessfulCreatePod 4m21s pytorchjob-controller Created pod: pytorch-elastic-mnist-nccl-worker-1
Normal SuccessfulCreateService 4m21s pytorchjob-controller Created service: pytorch-elastic-mnist-nccl-worker-0
Normal SuccessfulCreateService 4m21s pytorchjob-controller Created service: pytorch-elastic-mnist-nccl-worker-1
Normal ExitedWithCode 3m7s (x3 over 3m8s) pytorchjob-controller Pod: nb12706.pytorch-elastic-mnist-nccl-worker-1 exited with code 0
Normal ExitedWithCode 3m7s (x2 over 3m8s) pytorchjob-controller Pod: nb12706.pytorch-elastic-mnist-nccl-worker-0 exited with code 0
Normal PyTorchJobSucceeded 3m7s pytorchjob-controller PyTorchJob nb12706/pytorch-elastic-mnist-nccl successfully completed.
Normal JobTerminated 3m6s (x4 over 3m7s) pytorchjob-controller Job has been terminated. Deleting PodGroup
Normal SuccessfulDeletePodGroup 3m6s (x4 over 3m7s) pytorchjob-controller Deleted PodGroup: pytorch-elastic-mnist-nccl
학습 Pod이 제대로 생성되지 않는 경우 아래처럼 Events 를 통해 원인을 파악해볼 수 있습니다.
kubectl describe pytorchjob pytorch-elastic-mnist-nccl
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedCreatePod 47m (x3 over 103m) pytorchjob-controller Error creating: Pods "job-worker-1" is forbidden: exceeded quota: normal-quota, requested: requests.nvidia.com/gpu=1, used: requests.nvidia.com/gpu=2, limited: requests.nvidia.com/gpu=2
분산 학습 시 주의 사항
MLXP에서 분산 학습 시 주의해야 할 사항을 안내합니다.
NVIDIA Driver와 호환되는 CUDA 버전 사용
CUDA Runtime 버전은 노드에 설치된 NVIDIA Driver와 호환되어야 합니다. CUDA Runtime이 요구하는 최소 Driver 버전을 충족하지 못할 경우 CUDA 초기화 오류 또는 런타임 오류가 발생하며 학습 작업이 정상적으로 실행되지 않을 수 있습니다.
IPv6 사용 불가로 인한 GLOO_SOCKET_FAMILY=INET 설정
GLOO는 노드 간 상태 확인 및 Rank 관리를 위해 빈번한 TCP 연결을 생성합니다. IPv6를 사용할 수 없는 환경에서 대규모 학습을 수행할 경우, 각 연결 시도마다 IPv6 연결이 먼저 시도된 후 실패하고 IPv4로 재시도됩니다. 이러한 이중 시도 과정이 반복되면 파일 디스크립터 고갈 등의 자원 문제가 발생할 수 있으며, 결과적으로 학습이 중단될 수 있습니다.
이 문제를 방지하기 위해 GLOO_SOCKET_FAMILY=AF_INET 환경 변수를 설정하여 IPv4만 사용하도록 구성하는 것을 권장합니다.
GLOO에서 IPv4를 강제하려면 다음과 같이 설정할 수 있습니다.
...
spec:
...
pytorchReplicaSpecs:
Worker:
template:
containers:
- name: pytorch
...
env:
- name: GLOO_SOCKET_FAMILY
value: AF_INET
...
PyTorchJob 이름 길이 제한 (권장)
PyTorchJob 이름은 Kubernetes 리소스(Pod, Service 등) 생성 시 접두어로 사용되므로 50자 이내로 설정하는 것을 권장합니다. 50자를 초과하면 파생 리소스 이름이 길이 제한을 초과하여 생성이 실패하거나, worker index 파싱에 문제가 발생할 수 있습니다.
Shared Memory 설정 시 memory limit 설정 제거
Shared Memory(/dev/shm)는 용량 제한을 두지 않거나 충분히 크게 설정하는 것을 권장합니다. /dev/shm이 부족하면 DataLoader의 멀티프로세싱 worker가 비정상 종료될 수 있으며, NCCL의 통신 성능 저하로 이어질 수 있습니다.
Memory limit을 제거한 Shared Memory 설정 예시는 다음과 같습니다.
...
spec:
...
pytorchReplicaSpecs:
Worker:
template:
containers:
- name: pytorch
# shared memory
volumeMounts:
- mountPath: /dev/shm
name: shared-memory
volumes:
- emptyDir:
medium: Memory
name: shared-memory