kubectl を利用したモデル学習

Prev Next

VPC環境で利用できます。

ML expert Platformは、モデル学習のための様々なフレームワークの Jobを提供します。
kubectlを利用したモデル学習では、最も一般的な形態である単一ノード学習(Job)、分散ノード学習(PytorchJob)について説明します。

Job vs PytorchJob

単一ノード学習の場合、Jobの使用をお勧めします。
PytorchJobを使用する場合、分散ノード学習のための機能により Masterや Workerの形式でリリース・管理されるため、リソースを無駄に消費する可能性があります。

単一ノード学習(Job)を実行する

学習のための Kubernetes Jobの定義は、以下のユースケースのように作成できます。

注意
  • 高性能ストレージをマウントして使用する場合、学習イメージ内の UIDおよび GIDは500に設定する必要があります。
  • Podの securityContextfsGroupを設定しないでください。fsGroupが設定されると、Kubernetesがマウントされたボリューム内のすべてのファイルの所有権を再帰的に変更します。そのため、大容量のデータが保存された高性能ストレージの場合は、Podの初期化時間が大幅に長くなる可能性があります。
# 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作成時に Time To Live(TTL)を任意に設定できます。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.annotationssidecar.istio.io/inject: "false"が自動的に設定されます。当該 annotationが設定されない場合、RuntimeError: Connection reset by peerなどのノード間通信にエラーが発生します。
  • 高性能ストレージをマウントして使用する場合、学習イメージ内の UIDおよび GIDは500に設定する必要があります。
  • Podの securityContextfsGroupを設定しないでください。fsGroupが設定されると、Kubernetesがマウントされたボリューム内のすべてのファイルの所有権を再帰的に変更します。そのため、大容量のデータが保存された高性能ストレージの場合は、Podの初期化時間が大幅に長くなる可能性があります。

学習のための 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"  # 自動的に設定される(省略可能)
                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
                      - name: GLOO_SOCKET_FAMILY
                        value: AF_INET
                      securityContext:  # Infinibandを使用するための securityContextが必要です。
                        capabilities:
                            add: ["IPC_LOCK"]
                      # shared memory
                      volumeMounts:
                      - mountPath: /dev/shm
                        name: shared-memory
                    volumes:
                    - emptyDir:
                        medium: Memory
                      name: shared-memory
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=ai-infra)
  • InfiniBandを使用するために IPC_LOCK capabilityを 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_DEBUGTORCH_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 getkubectl 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