SageMaker HyperPod のタスクガバナンス機能を試してみた

SageMaker HyperPod のタスクガバナンス機能を試してみた

Clock Icon2025.01.12

こんにちは!クラウド事業本部コンサルティング部のたかくに(@takakuni_)です。

re:Invent 2024 で Amazon SageMaker HyperPod にタスクガバナンス機能が登場しました。

https://dev.classmethod.jp/articles/sagemaker-hyperpod-task-governance-reinvent2024/

当時は実際に試せてなかったため、試してみたいと思います。

タスクガバナンス

タスクガバナンスはざっくり説明すると、 HyperPod クラスターに所属するコンピュート(CPU や GPU)を、チームごとに管理する機能です。

タスクガバナンスを利用することで、以下の管理をチームごとに設定できるようになります。

  • タスクの優先度
  • 各チームのコンピューティング割り当て
  • 各チームがアイドル状態のコンピューティングリソースを貸し借りする方法
  • チームが自らのタスクを先取りした場合

前提条件

タスクガバナンスの機能は EKS オーケストレータのみサポートしている状況になります。Kubernetes v1.30.0 以上でサポートです。

Kueue を利用してジョブの管理を行うため、すでに Kueue をインストールしている場合はアンインストールが必要になります。加えて、HyperPod タスクガバナンスはアドオンを利用してセットアップを行うのですが、 HyperPod クラスター(ノード)作成後にインストールが必要になります。

https://docs.aws.amazon.com/sagemaker/latest/dg/sagemaker-hyperpod-eks-operate-console-ui-governance-setup-task-governance.html#hp-eks-task-governance-prerequisites

また、利用できるリージョンは。執筆時点でバージニア北部、オハイオ、オレゴンの 3 リージョンです。

Amazon SageMaker HyperPod タスクガバナンスは、米国東部 (バージニア北部)、米国東部 (オハイオ)、米国西部 (オレゴン) の AWS リージョンでご利用いただけるようになりました。HyperPod タスクガバナンスは追加コストなしでご利用いただけます。詳細については、SageMaker HyperPod の製品ページにアクセスしてください。

https://aws.amazon.com/jp/blogs/news/maximize-accelerator-utilization-for-model-development-with-new-amazon-sagemaker-hyperpod-task-governance/

やってみる

それでは早速、タスクガバナンス機能をセットアップします。

GPU が 4 枚搭載された ml.g5.12xlarge を 4 台起動し、 John チームは 3 台、 Smith チームは 1 台使えるような状況とします。

HashiCorp Terraform でセットアップしますが、要所要所ポイントをかいつまんでいきます。

https://github.com/takakuni-classmethod/genai-blog/tree/main/sagemaker_hyperpod_eks_task_governance

EKS アドオンのインストール

それでは、タスクガバナンスの EKS アドオンをインストールします。HyperPod ノードがセットアップ完了したのちにインストールが必要のため、 depends_on で依存関係を追加しています。

eks.tf(抜粋)
resource "aws_eks_addon" "amazon_sagemaker_hyperpod_taskgovernance" {
  cluster_name                = aws_eks_cluster.this.name
  addon_name                  = "amazon-sagemaker-hyperpod-taskgovernance"
  resolve_conflicts_on_create = "OVERWRITE"
  addon_version               = "v1.0.0-eksbuild.4"

  depends_on = [
    awscc_sagemaker_cluster.this,
  ]
}

いくつか kueue-system 名前空間でリソースが作成されていますね。

takakuni@ sagemaker_hyperpod_eks_task_governance % kubectl get all -n kueue-system
NAME                                            READY   STATUS    RESTARTS   AGE
pod/kueue-controller-manager-685d6d4664-j8gq5   2/2     Running   0          10m

NAME                                               TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
service/kueue-controller-manager-metrics-service   ClusterIP   172.20.250.28   <none>        8443/TCP   10m
service/kueue-webhook-service                      ClusterIP   172.20.232.68   <none>        443/TCP    10m

NAME                                       READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/kueue-controller-manager   1/1     1            1           10m

NAME                                                  DESIRED   CURRENT   READY   AGE
replicaset.apps/kueue-controller-manager-685d6d4664   1         1         1       10m

クラスターポリシーの作成

アドオンが成功したため、クラスターポリシーを設定します。ここはコンソールで行います。

クラスターポリシーは、キューに投げるタスクやアイドル状態のコンピュートをどのように共有するのかを定義します。1 つのポリシーで タスクの優先順位付けアイドル状態のコンピューティング割り当て の 2 軸を設定し、このポリシーはクラスター全体で共有します。

タスクの優先順位付け

タスクの優先順位付けは、キューで待機している PyTorch などのタスクの優先順位を定義します。

先着順でタスクを制御するのか、優先クラスを定義し、特定のクラスごとに優先させるのか定義します。

2025-01-07 at 19.54.36-create-cluster-policy  hyprpd-tskgvrn-hyperpod-cluster  クラスターの管理  Amazon SageMaker AI  us-west-2.png

アイドル状態のコンピューティング割り当て

アイドル状態のコンピューティング割り当ては、使われていないコンピューティングを、チーム全体にどのように割り当てるかを定義します。定義は先着順と適正な共有に分かれます。

適正な共有は、チーム間でアイドル状態のコンピュートを借りられる共通の値(優先度)を設けて共有する仕組みです。この共通の値は後ほど設定します。

2025-01-07 at 19.59.36-Amazon SageMaker AI  us-west-2.png

今回、タスクの優先順位付けは先着順、アイドル状態のコンピューティング割り当ては適正な共有で設定しました。

2025-01-12 at 20.29.08-Amazon SageMaker AI  us-west-2.png

チームの作成

それではチームの作成を行います。コンピューティング割り当てから、チーム名や説明等を設定します。コンピューティング割り当ては、チームごとにどれくらいコンピュート(CPU,GPU,Memory)を利用できるのか定義します。

チームを作成すると、チーム名ごとに名前空間が自動作成されます。

適正な共有の加重が、先ほどのアイドル状態のコンピュートを借りられる優先度を意味します。

2025-01-12 at 19.48.05-Amazon SageMaker AI  us-west-2.png

プリエンプションは、他チームにアイドル状態のコンピュートを貸している状況で、チーム内の割り込みタスクが入った場合に、アイドルで貸していたコンピュートの処理を中断させるかどうかを決定します。

2025-01-12 at 20.41.23-コンピューティング割り当てを編集  zhyj78mpcm2d  hyprpd-tskgvrn-hyperpod-cluster  クラスターの管理  Amazon SageMaker AI  us-west-2 2.png

つづいて、コンピューティングです。

クォータはチームに割り当てるインスタンスの数を設定します。

画面の場合、 ml.g5.12xlarge が 4 台利用可能ですが、インスタンスグループで作成していないインスタンスタイプも指定可能です。

貸借はチームがコンピュートを貸し借りできるかどうかの設定値です。

  • 貸借
    • 他のチームと貸し借り可能
  • 貸す
    • 他のチームに貸すことだけできる。借りることはできない
  • 貸さないでください
    • 他のチームに貸すことができない。借りることもできない

なお、貸借を選んだ場合は借用限度を 1 ~ 500%、デフォルトは 50% で決めることができます。

最終的な設定値

今回は john チームと smith チームに以下を設定しました。

john

  • 適正な共有の加重:0
  • プリエンプション:有効
  • コンピュート:ml.g5.12xlarge 3 台
  • 貸借モード
  • 借用限度:50%

smith

  • 適正な共有の加重:100
  • プリエンプション:有効
  • コンピュート:ml.g5.12xlarge 1 台
  • 貸借モード
  • 借用限度:100%

smith の方が優先して、借りられるようになっています。

ジョブの投入

John チーム

8 GPU を利用する PyTorch FSBP ジョブを John チームで実行します。

ワークショップの内容では ml.g5.8xlarge x 8 ですが、少し内容変えて ml.g5.12xlarge x 4 でやって見ました。

(メモリ不足にならなくて良かったです)

https://catalog.workshops.aws/sagemaker-hyperpod-eks/en-US/02-fsdp

ラベルや名前空間をチームに割り当てられた内容に変更します。

fsbp-john.yaml
apiVersion: v1
kind: Service
metadata:
  name: etcd
+ namespace: hyperpod-ns-john
spec:
  ports:
    - name: etcd-client-port
      port: 2379
      protocol: TCP
      targetPort: 2379
  selector:
    app: etcd

---
apiVersion: apps/v1
kind: Deployment
metadata:
+ namespace: hyperpod-ns-john
  labels:
    app: etcd
  name: etcd
spec:
  replicas: 1
  selector:
    matchLabels:
      app: etcd
  template:
    metadata:
      labels:
        app: etcd
+       kueue.x-k8s.io/queue-name: hyperpod-ns-john-localqueue
    spec:
      containers:
        - name: etcd
          command: ['/usr/local/bin/etcd']
          args:
            - '--data-dir'
            - '/var/lib/etcd'
            - '--enable-v2'
            - '--listen-client-urls'
            - 'http://0.0.0.0:2379'
            - '--advertise-client-urls'
            - 'http://0.0.0.0:2379'
            - '--initial-cluster-state'
            - 'new'
          image: quay.io/coreos/etcd:latest
          ports:
            - containerPort: 2379
              name: client
              protocol: TCP
            - containerPort: 2380
              name: server
              protocol: TCP
      restartPolicy: Always
---
apiVersion: 'kubeflow.org/v1'
kind: PyTorchJob
metadata:
  name: fsdp
+ namespace: hyperpod-ns-john
  labels:
+   kueue.x-k8s.io/queue-name: hyperpod-ns-john-localqueue
spec:
  elasticPolicy:
    rdzvBackend: etcd
    rdzvHost: etcd
    rdzvPort: 2379
    minReplicas: 1
    maxReplicas: 64
    maxRestarts: 100
    metrics:
      - type: Resource
        resource:
          name: cpu
          target:
            type: Utilization
            averageUtilization: 90
  pytorchReplicaSpecs:
    Worker:
      replicas: 2
      restartPolicy: OnFailure
      template:
        metadata:
          labels:
            app: fsdp
+           kueue.x-k8s.io/queue-name: hyperpod-ns-john-localqueue
        spec:
          volumes:
            - name: shmem
              hostPath:
                path: /dev/shm
            - name: local
              hostPath:
                path: /mnt/k8s-disks/0
          #nodeSelector:
          #  node.kubernetes.io/instance-type: "ml.g5.12xlarge"
          containers:
            - name: pytorch
              image: 123456789012.dkr.ecr.us-west-2.amazonaws.com/hyprpd-tskgvrn-fsdp:pytorch2.2
              imagePullPolicy: Always
              resources:
                requests:
                  nvidia.com/gpu: 4
                  vpc.amazonaws.com/efa: 1
                limits:
                  nvidia.com/gpu: 4
                  vpc.amazonaws.com/efa: 1
              env:
                # for P5 FI_* should be commented out
                - name: LOGLEVEL
                  value: 'DEBUG'
                #- name: FI_PROVIDER
                #  value: efa
                #- name: FI_EFA_USE_DEVICE_RDMA
                #  value: "1"
                #- name: FI_EFA_FORK_SAFE
                #  value: "1"
                #- name: FI_LOG_LEVEL
                #  value: "1"
                #- name: FI_EFA_ENABLE_SHM_TRANSFER
                #  value: "1"
                - name: TORCH_DISTRIBUTED_DEBUG
                  value: 'DETAIL'
                - name: TORCH_NCCL_ENABLE_MONITORING
                  value: '1'
                - name: TORCH_NCCL_TRACE_BUFFER_SIZE
                  value: '20000'
                - name: TORCH_NCCL_DUMP_ON_TIMEOUT
                  value: '1'
                - name: TORCH_NCCL_DEBUG_INFO_TEMP_FILE
                  value: '/local/nccl_trace_rank_'
                - name: PYTORCH_CUDA_ALLOC_CONF
                  value: 'expandable_segments:True'
                - name: NCCL_DEBUG
                  value: 'INFO'
                - name: NCCL_SOCKET_IFNAME
                  value: '^lo'
                - name: TORCH_NCCL_ASYNC_ERROR_HANDLING
                  value: '1'
              #- name: TORCH_DIST_INIT_BARRIER
              #  value: "1"
              #- name: NCCL_IGNORE_DISABLED_P2P
              #  value: "1"
              #- name: NCCL_NVLS_ENABLE
              #  value: "0"
              command:
                - /usr/local/bin/torchrun
                - --nproc_per_node=4
                - --nnodes=2
                - /fsdp/train.py
                - --max_context_width=4096
                - --num_key_value_heads=32
                - --intermediate_size=11008
                - --hidden_width=4096
                - --num_layers=32
                - --num_heads=32
                - --model_type=llama_v2
                - --tokenizer=hf-internal-testing/llama-tokenizer
                - --checkpoint_freq=5000
                - --validation_freq=500
                - --max_steps=5000
                - --checkpoint_dir=/checkpoints
                - --dataset=allenai/c4
                - --dataset_config_name=en
                - --resume_from_checkpoint=/checkpoints
                - --train_batch_size=1
                - --val_batch_size=1
                - --sharding_strategy=full
                - --offload_activation=1
              volumeMounts:
                - name: shmem
                  mountPath: /dev/shm
                - name: local
                  mountPath: /local

ジョブを投げると、ダッシュボードの GPU 利用率がちょうど 50 % になりました。(4 枚使っているので想定通りですね)

2025-01-12 at 21.08.05-Amazon SageMaker AI  us-west-2.png

ちょうど 50 % ほどになっていますね。ただ、50%しか使えていないため、もったいないですね。

2025-01-12 at 21.24.25-Amazon SageMaker AI  us-west-2.png

2025-01-12 at 21.27.32-Amazon SageMaker AI  us-west-2.png

Smith

Smith チームにも、同じようにジョブを投入してみます。

fsbp-smith.yaml
apiVersion: v1
kind: Service
metadata:
  name: etcd
  namespace: hyperpod-ns-smith
spec:
  ports:
    - name: etcd-client-port
      port: 2379
      protocol: TCP
      targetPort: 2379
  selector:
    app: etcd

---
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: hyperpod-ns-smith
  labels:
    app: etcd
  name: etcd
spec:
  replicas: 1
  selector:
    matchLabels:
      app: etcd
  template:
    metadata:
      labels:
        app: etcd
        kueue.x-k8s.io/queue-name: hyperpod-ns-smith-localqueue
    spec:
      containers:
        - name: etcd
          command: ['/usr/local/bin/etcd']
          args:
            - '--data-dir'
            - '/var/lib/etcd'
            - '--enable-v2'
            - '--listen-client-urls'
            - 'http://0.0.0.0:2379'
            - '--advertise-client-urls'
            - 'http://0.0.0.0:2379'
            - '--initial-cluster-state'
            - 'new'
          image: quay.io/coreos/etcd:latest
          ports:
            - containerPort: 2379
              name: client
              protocol: TCP
            - containerPort: 2380
              name: server
              protocol: TCP
      restartPolicy: Always
---
apiVersion: 'kubeflow.org/v1'
kind: PyTorchJob
metadata:
  name: fsdp
  namespace: hyperpod-ns-smith
  labels:
    kueue.x-k8s.io/queue-name: hyperpod-ns-smith-localqueue
spec:
  elasticPolicy:
    rdzvBackend: etcd
    rdzvHost: etcd
    rdzvPort: 2379
    minReplicas: 1
    maxReplicas: 64
    maxRestarts: 100
    metrics:
      - type: Resource
        resource:
          name: cpu
          target:
            type: Utilization
            averageUtilization: 90
  pytorchReplicaSpecs:
    Worker:
      replicas: 2
      restartPolicy: OnFailure
      template:
        metadata:
          labels:
            app: fsdp
            kueue.x-k8s.io/queue-name: hyperpod-ns-smith-localqueue
        spec:
          volumes:
            - name: shmem
              hostPath:
                path: /dev/shm
            - name: local
              hostPath:
                path: /mnt/k8s-disks/0
          #nodeSelector:
          #  node.kubernetes.io/instance-type: "ml.g5.12xlarge"
          containers:
            - name: pytorch
              image: 123456789012.dkr.ecr.us-west-2.amazonaws.com/hyprpd-tskgvrn-fsdp:pytorch2.2
              imagePullPolicy: Always
              resources:
                requests:
                  nvidia.com/gpu: 4
                  vpc.amazonaws.com/efa: 1
                limits:
                  nvidia.com/gpu: 4
                  vpc.amazonaws.com/efa: 1
              env:
                # for P5 FI_* should be commented out
                - name: LOGLEVEL
                  value: 'DEBUG'
                #- name: FI_PROVIDER
                #  value: efa
                #- name: FI_EFA_USE_DEVICE_RDMA
                #  value: "1"
                #- name: FI_EFA_FORK_SAFE
                #  value: "1"
                #- name: FI_LOG_LEVEL
                #  value: "1"
                #- name: FI_EFA_ENABLE_SHM_TRANSFER
                #  value: "1"
                - name: TORCH_DISTRIBUTED_DEBUG
                  value: 'DETAIL'
                - name: TORCH_NCCL_ENABLE_MONITORING
                  value: '1'
                - name: TORCH_NCCL_TRACE_BUFFER_SIZE
                  value: '20000'
                - name: TORCH_NCCL_DUMP_ON_TIMEOUT
                  value: '1'
                - name: TORCH_NCCL_DEBUG_INFO_TEMP_FILE
                  value: '/local/nccl_trace_rank_'
                - name: PYTORCH_CUDA_ALLOC_CONF
                  value: 'expandable_segments:True'
                - name: NCCL_DEBUG
                  value: 'INFO'
                - name: NCCL_SOCKET_IFNAME
                  value: '^lo'
                - name: TORCH_NCCL_ASYNC_ERROR_HANDLING
                  value: '1'
              #- name: TORCH_DIST_INIT_BARRIER
              #  value: "1"
              #- name: NCCL_IGNORE_DISABLED_P2P
              #  value: "1"
              #- name: NCCL_NVLS_ENABLE
              #  value: "0"
              command:
                - /usr/local/bin/torchrun
                - --nproc_per_node=4
                - --nnodes=2
                - /fsdp/train.py
                - --max_context_width=4096
                - --num_key_value_heads=32
                - --intermediate_size=11008
                - --hidden_width=4096
                - --num_layers=32
                - --num_heads=32
                - --model_type=llama_v2
                - --tokenizer=hf-internal-testing/llama-tokenizer
                - --checkpoint_freq=5000
                - --validation_freq=500
                - --max_steps=5000
                - --checkpoint_dir=/checkpoints
                - --dataset=allenai/c4
                - --dataset_config_name=en
                - --resume_from_checkpoint=/checkpoints
                - --train_batch_size=1
                - --val_batch_size=1
                - --sharding_strategy=full
                - --offload_activation=1
              volumeMounts:
                - name: shmem
                  mountPath: /dev/shm
                - name: local
                  mountPath: /local

余していた GPU が使いきれました。

2025-01-12 at 21.29.31-Amazon SageMaker AI  us-west-2.png

グラフも 50% から 100% に変化しています。

2025-01-12 at 21.29.58-Amazon SageMaker AI  us-west-2.png

チームのサマリも Smith チームは John チームの余していた GPU を借りていますね。無事コンピュートを貸し借りできました。

2025-01-12 at 21.30.36-Amazon SageMaker AI  us-west-2.png

まとめ

以上、「SageMaker HyperPod のタスクガバナンス機能を試してみた」でした。

各チームに一定割合のコンピュートを割り当てつつ、アイドル状態のリソースがあれば、貸し借りできるルールを設けられるのは非常に便利ですね。

今回はアイドル状態のコンピュートに重きを置いていましたが、ガバナンス周りはまた別のブログで書ければと思います。

このブログがどなたかの参考になれば幸いです。クラウド事業本部コンサルティング部のたかくに(@takakuni_)でした!

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.