kubectl을 이용한 모델 학습

Prev Next

VPC 환경에서 이용 가능합니다.

모델 학습을 위한 여러 프레임워크의 Job을 제공해주고 있습니다.
학습에 기본이 되는 단일 노드 학습, 분산 노드 학습을 가장 많이 사용되는 형태인 단일 노드 학습(Job), 분산 노드 학습(PytorchJob) 형태로 설명합니다.

Job vs 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에 대한 설정입니다.

학습을 위해 PytorchJob 명세는 다음 예제와 같이 작성할 수 있습니다.

사용 시 아래 사항을 반드시 준수해야합니다.
  • 학습을 진행하는 컨테이너의 이름 (e.g. spec.pytorchReplicaSpecs.Worker.template.spec.containers[*].name)은 반드시 pytorch 이어야 합니다.
  • 원활한 분산학습을 위하여 Istio Sidecar가 Inject가 되지 않도록 spec.pytorchReplicaSpecs.Worker.template.metadata.annotationssidecar.istio.io/inject: "false"를 반드시 명기해야합니다. 해당 annotation이 설정되지 않는 경우, RuntimeError: Connection reset by peer 등 노드간 통신에 관련된 에러를 볼 수 있습니다.
# 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 사용

InfiniBand 네트워크로 연결된 노드들로 분산 학습을 실행하면 노드간 통신을 가속할 수 있습니다.

이전 섹션에서 보여주었던 예제를 InfiniBand 환경에 맞추기 위해서는 추가해야하는 명세가 몇가지 있는데, 다음과 같이 요약이 가능합니다.

  • InfiniBand 네트워크가 구성된 구역 이름을 Annotation으로 명시합니다 (i.e., mlx.navercorp.com/zone=a100-ib)
  • InfiniBand 사용을 위해 IPC_LOCK capability를 securityContext 에 추가합니다.
  • ResourceRequest를 작성할 때 InfiniBand 용 리소스(rdma/hca_shared_devices_a: 1)를 할당 받도록 설정합니다.
  • 분산학습을 돕기 위한 Shared Memory 를 volumes로 설정합니다.

Infiniband 사용을 위해서 다음과 같이 사용할 수 있습니다.

...
spec:
    ...
    pytorchReplicaSpecs:
        Worker:
            template:
                containers:
                - name: pytorch
                   securityContext:  # Infiniband 를 사용하기 위한 securityContext가 필요합니다.
                        capabilities:
                            add: ["IPC_LOCK"]
                   resources:
                        limits:
                            ...
                            rdma/hca_shared_devices_a: 1
                            ...
                        requests:
                            ...
                            rdma/hca_shared_devices_a: 1
                            ...
                    # 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