OpenShift Weblog

Red Hat OpenShiftとNVIDIA GPU Operatorで高速化AIワークロードのデプロイを単純化

2020年2月28日、寄稿者:Sebastian Jug

このブログ記事では、GPU アクセラレーションを使用するワークロードを新しい NVIDIA GPU Operator を使って OpenShift クラスタにデプロイする方法をデモンストレーションします。

この新しい GPU Operator を使うと、アクセラレーションを使用しない従来のワークロードで CPU やメモリーを予約するのと同じような簡単さで、GPGPU を必要とするワークロードを OpenShift で予約できるようになります。GPU ワークロードを持つコンテナを作成し、Pod の作成時に GPU リソースを要求すれば、後の処理は OpenShift が行います。これによりすべての管理をホストマシンレベルではなくクラスタレベルで行えるようになるため、OpenShift クラスタへの GPU ワークロードのデプロイがユーザーと管理者の両方にとって簡単になります。この OpenShift 用 GPU Operator で、データサイエンティストがコンピュート集約型 ML/DL モデリングのタスクを簡単かつ迅速に実行できるようになり、予測タスクをデータセンター、パブリッククラウド、エッジの全体で実行することも可能になります。GPU アクセラレーションのメリットが特に大きいワークロードには、画像認識、音声認識、ビジュアル検索などがあります。

以下のデモンストレーションでは、GPU デバイスを備えた複数のワーカーノードを持つ OpenShift 4.x クラスタがデプロイされていることを前提としています。

$ oc get no
NAME                           STATUS ROLES AGE VERSION
ip-10-0-130-177.ec2.internal   Ready worker 33m v1.16.2
ip-10-0-132-41.ec2.internal    Ready master 42m v1.16.2
ip-10-0-156-85.ec2.internal    Ready worker 33m v1.16.2
ip-10-0-157-132.ec2.internal   Ready master 42m v1.16.2
ip-10-0-170-127.ec2.internal   Ready worker 4m15s v1.16.2
ip-10-0-174-93.ec2.internal    Ready master 42m v1.16.2

まず、OpenShift が各ノードで使用できる機能とデバイスを確認するため、Node Feature Discovery (NFD) Operator をデプロイします (詳しい手順についてはこちらをご覧ください)。

NFD Operator をデプロイしたら、ノードの 1 つを詳しく見てみます。こちらが前後の比較情報です。ノードの機能を示す新しいラベルの中に、次のものがあります。

feature.node.kubernetes.io/pci-10de.present=true

これは、ベンダー ID が 0x10de のメーカー、つまり NVIDIA の PCIe デバイスが少なくとも 1 つあるということを示しています。GPU Operator は、NFD Operator が作成したこれらのラベルを使用して、GPU 用ドライバーコンテナのデプロイ先を決定します。

ただし、GPU Operator をデプロイする前に、クラスタで適切な RHEL 利用権を作成する必要があります (詳しい手順についてはこちらをご覧ください)。クラスタに RHEL 利用権をデプロイしたら、GPU Operator のインストールに進みます。

現時点では GPU Operator のインストールには Helm Chart を使用するため、Helm v3 以降をインストールしておきます1。Helm をインストールしたら、GPU Operator のインストールを開始します。

1) Nvidia の Helm リポジトリを追加します。

$ helm repo add nvidia https://nvidia.github.io/gpu-operator
"nvidia" has been added to your repositories

2) Helm リポジトリを更新します。

$ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "nvidia" chart repository
Update Complete. ⎈ Happy Helming!⎈

3) GPU Operator の Helm Chart をインストールします。

$ helm install --devel https://nvidia.github.io/gpu-operator/gpu-operator-1.0.0.tgz --set platform.openshift=true,operator.defaultRuntime=crio,nfd.enabled=false --wait --generate-name

4) GPU Operator のデプロイを監視します。

$ oc get pods -n gpu-operator-resources -w

このコマンドは Operator がクラスタにロールアウトされる間 gpu-operator-resources 名前空間を監視します。インストールが終了すると、gpu-operator-resources 名前空間はこのようになります。

これを見ると、`nvidia-driver-validation` Pod と `nvidia-device-plugin-validation` Pod が正常に終了していることと、4 つの DaemonSet が Pod を実行しており、それぞれで実行されている Pod の数はノードラベル `feature.node.kubernetes.io/pci-10de.present=true` と一致していることがわかります。ここでもう一度、GPU ノードの詳細を確認します。

すると、ノードに加えられた最新の変更によって、Capacity、Allocatable、Allocated Resources が `nvidia.com/gpu` という新しいリソースについて追加されたことがわかります。この GPU ノードには GPU が 1 つしかないため、それを反映した値になっています。

これで NFD Operator、クラスタの利用権、GPU Operator がデプロイされたので、GPU リソースを使用するワークロードを割り当てることができるようになりました。

まず、GPU デバイスのクラスタ自動スケーリングを設定します。こうすることで、GPU リソースを要求するワークロードを作成したときに、デバイスで待機中となっている要求の量によって GPU ノードが自動的にスケールアップまたはスケールダウンされるようになります。

最初に、ClusterAutoscaler リソース定義を作成します。以下は例です。

$ cat 0001-clusterautoscaler.yaml
apiVersion: "autoscaling.openshift.io/v1"
kind: "ClusterAutoscaler"
metadata:
  name: "default"
spec:
  podPriorityThreshold: -10
  resourceLimits:
    maxNodesTotal: 24
    gpus:
      - type: nvidia.com/gpu
        min: 0
        max: 16
  scaleDown:
    enabled: true
    delayAfterAdd: 10m
    delayAfterDelete: 5m
    delayAfterFailure: 30s
    unneededTime: 10m

$ oc create -f 0001-clusterautoscaler.yaml
clusterautoscaler.autoscaling.openshift.io/default created

ここでは、Autoscaler が使用できる `nvidia.com/gpu` リソースの数を定義します。

ClusterAutoscaler をデプロイしたら、クラスタのスケーリングに使用される MachineSet を参照する MachineAutoscaler リソースをデプロイします

$ cat 0002-machineautoscaler.yaml
apiVersion: "autoscaling.openshift.io/v1beta1"
kind: "MachineAutoscaler"
metadata:
  name: "gpu-worker-us-east-1a"
  namespace: "openshift-machine-api"
spec:
  minReplicas: 1
  maxReplicas: 6
  scaleTargetRef:
    apiVersion: machine.openshift.io/v1beta1
    kind: MachineSet
    name: gpu-worker-us-east-1a

$ oc create -f 0002-machineautoscaler.yaml
machineautoscaler.autoscaling.openshift.io/sj-022820-01-h4vrj-worker-us-east-1c created

メタデータの名前には固有の MachineAutoscaler 名を指定し、ファイル末尾にある MachineSet の名前には既存の MachineSet 名の値を指定する必要があります。

現在このクラスタで利用できる MachineSet を確認します。

$ oc get machinesets -n openshift-machine-api
NAME                                   DESIRED   CURRENT   READY   AVAILABLE   AGE
sj-022820-01-h4vrj-worker-us-east-1a   1         1         1       1           4h45m
sj-022820-01-h4vrj-worker-us-east-1b   1         1         1       1           4h45m
sj-022820-01-h4vrj-worker-us-east-1c   1         1         1       1           4h45m

この例では、3 つめの `sj-022820-01-h4vrj-worker-us-east-1c` が GPU ノードのある MachineSet です。

$ oc get machineset sj-022820-01-h4vrj-worker-us-east-1c -n openshift-machine-api -o yaml 
apiVersion: machine.openshift.io/v1beta1
kind: MachineSet
metadata:
  name: sj-022820-01-h4vrj-worker-us-east-1c
  namespace: openshift-machine-api
...
spec:
  replicas: 1
...
    spec:
      metadata:
          instanceType: p3.2xlarge
          kind: AWSMachineProviderConfig
          placement:
            availabilityZone: us-east-1c
            region: us-east-1

MachineAutoscaler リソース定義を作成します。内容は以下のようになります。

$ cat 0002-machineautoscaler.yaml
apiVersion: "autoscaling.openshift.io/v1beta1"
kind: "MachineAutoscaler"
metadata:
  name: "sj-022820-01-h4vrj-worker-us-east-1c"
  namespace: "openshift-machine-api"
spec:
  minReplicas: 1
  maxReplicas: 6
  scaleTargetRef:
    apiVersion: machine.openshift.io/v1beta1
    kind: MachineSet
    name: sj-022820-01-h4vrj-worker-us-east-1c

$ oc create -f 0002-machineautoscaler.yaml
machineautoscaler.autoscaling.openshift.io/sj-022820-01-h4vrj-worker-us-east-1c created

これで、RAPIDS をデプロイし、複数インスタンス間でストレージを共有させることができるようになりました。さっそく新しいプロジェクトを作成します。

$ oc new-project rapids

ここでは、cephfs を使用する OpenShift Container Storage のように ReadWriteMany 機能の使える StorageClass があるものとして進めます。次に、PVC を作成して RAPIDS インスタンスをアタッチします (‘storageClassName` は StorageClass の名前です)。

$ cat 0003-pvc-for-ceph.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: rapids-cephfs-pvc
spec:
  accessModes:
  - ReadWriteMany
  resources:
    requests:
      storage: 25Gi
  storageClassName: example-storagecluster-cephfs

$ oc create -f 0003-pvc-for-ceph.yaml
persistentvolumeclaim/rapids-cephfs-pvc created

$ oc get pvc -n rapids
NAME                STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS                    AGE
rapids-cephfs-pvc   Bound    pvc-a6ba1c38-6498-4b55-9565-d274fb8b003e   25Gi       RWX            example-storagecluster-cephfs   33s

これで共有ストレージをデプロイできたので、最後に RAPIDS テンプレートをデプロイし、rapids 名前空間の中に新しいアプリケーションを作成します。

$ oc create -f 0004-rapids_template.yaml
template.template.openshift.io/rapids created
$ oc new-app rapids
--> Deploying template "rapids/rapids" to project rapids

     RAPIDS
     ---------
     Template for RAPIDS

     A RAPIDS pod has been created.


     * With parameters:
        * Number of GPUs=1
        * Rapids instance number=1

--> Creating resources ...
    service "rapids" created
    route.route.openshift.io "rapids" created
    pod "rapids" created
--> Success
    Access your application via route 'rapids-rapids.apps.sj-022820-01.perf-testing.devcluster.openshift.com'
    Run 'oc status' to view your app.

これで、上記のテンプレートで作成したルート (`rapids-rapids.apps.sj-022820-01.perf-testing.devcluster.openshift.com`) をブラウザーで読み込めるようになりました。


図:OpenShift で GPU を使用して実行した Notebook の例

RAPIDS が実行中で GPU リソースが使用されていることも GPU ノードで確認できます。

$ oc describe gpu node

複数の人が Jupyter Playbook を実行する場合を想定して、固有の専用 GPU を持つ RAPIDS インスタンスをもう 1 つ作成します。

$ oc new-app rapids -p INSTANCE=2
--> Deploying template "rapids/rapids" to project rapids

     RAPIDS
     ---------
     Template for RAPIDS

     A RAPIDS pod has been created.


     * With parameters:
        * Number of GPUs=1
        * Rapids instance number=2

--> Creating resources ...
    service "rapids2" created
    route.route.openshift.io "rapids2" created
    pod "rapids2" created
--> Success
    Access your application via route 'rapids2-rapids.apps.sj-022820-01.perf-testing.devcluster.openshift.com'
    Run 'oc status' to view your app.

しかし、GPU ノードに 1 つしかない GPU リソースはすでに使用中であるため、新しい RAPIDS のデプロイ (rapids2) は GPU リソースの不足により予約できません。

$ oc get pods -n rapids
NAME      READY   STATUS    RESTARTS   AGE
rapids    1/1     Running   0          30m
rapids2   0/1     Pending   0          2m44s

rapids2 Pod のイベントステートは次のようになっています。

$ oc describe pod/rapids -n rapids
...
Events:
  Type     Reason            Age        From                Message
  ----     ------            ----       ----                -------
  Warning  FailedScheduling  <unknown>  default-scheduler   0/9 nodes are available: 9 Insufficient nvidia.com/gpu.
  Normal   TriggeredScaleUp  44s        cluster-autoscaler  pod triggered scale-up: [{openshift-machine-api/sj-022820-01-h4vrj-worker-us-east-1c 1->2 (max: 6)}]

ClusterAutoscaler と MachineAutoscaler が動作して上記の MachineSet がスケールアップされるまで、少し時間がかかります。新しいノードが作成されると、以下のようになります。

Ready となった新し

$ oc get no 
NAME                           STATUS   ROLES    AGE     VERSION
(old nodes)
...
ip-10-0-167-0.ec2.internal     Ready    worker   72s     v1.16.2
              

いノードに新しい RAPIDS インスタンスがデプロイされます。ユーザーによる操作は一切必要ありません。

まとめると、新しい NVIDIA GPU Operator を使用することで OpenShift クラスタでの GPU リソースの使用が単純化されます。この記事では、NVIDIA GPU を使用したマルチユーザー RAPIDS 開発のユースケースを想定してデモンストレーションを行いました。さらに、OpenShift Container Storage と ClusterAutoscaler を使用して、アプリケーションからの要求に従ってこの特別なリソースを自動でスケールアップさせました。

お見せしたように、NVIDIA GPU Operator は Helm を使用して比較的簡単にデプロイできます。また、OperatorHub から直接デプロイできるようにする作業が進められているので、デプロイはさらに単純化される見込みです。

NVIDIA GPU Operator と OpenShift の詳細については、NVIDIA の公式ドキュメントをご覧ください。

1 Helm 3 は OpenShift 4.3 ではテクニカルプレビュー版であり、OpenShift 4.4 で一般公開となります

Red Hat OpenShift

お問い合わせ

Red Hat OpenShiftのご質問やご相談は
メールにてお気軽にお問合わせください。