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 で一般公開となります