DockerとKubernetes：デバイスプラグインとGPU管理

シニアテクニカルエキスパートのChe Yangが、一般的なデバイスプラグインと、**Docker**や**Kubernetes**で**GPU**を使用する方法を解説します。

著者：アリババのシニアテクニカルエキスパート、Che Yang (Biran)氏

2016年、AlphaGoとTensorFlowは、[人工知能（AI）]
(https://www.alibabacloud.com/product/machine-learning?spm=a2c65.11461447.0.0.40674933tlevBZ)の技術革命を学術界から産業界にもたらしました。 AIは、クラウドコンピューティングとコンピューティングパワーによって後押しされています。

#要求事項
長年の開発を経て、AIはインテリジェントなカスタマーサービス、機械翻訳、画像検索など、[多くのシナリオに適用されています](https://www.alibabacloud.com/product/machine-learning?spm=a2c65.11461447.0.0.40674933tlevBZ)。機械学習とAIには長い歴史があり、クラウドコンピューティングの普及と、それに伴うコンピューティングパワーの大幅な増加により、AIの適用が可能になっています。

![スクリーンショット 2021-04-30 10.57.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/6125f17e-cc2a-e5a0-a4fc-de9417db7e55.png)





2016年以降、[Kubernetes](https://www.alibabacloud.com/product/kubernetes?spm=a2c65.11461447.0.0.40674933tlevBZ)コミュニティには、TensorFlowをはじめとする機械学習フレームワークをKubernetesクラスタで動作させたいという要望がさまざまなチャネルから寄せられています。これらの要望に応えるためには、ジョブやその他のオフラインタスクの管理、機械学習に必要な異機種のデバイス、NVIDIA[グラフィックス・プロセッシング・ユニット](https://www.alibabacloud.com/product/gpu?spm=a2c65.11461447.0.0.40674933tlevBZ)（GPU）のサポートといった課題を解決しなければなりません。

Kubernetesを使ってGPUを管理することで、コストの削減と効率化を図ることができます。GPUはCPUよりもコストがかかります。オフプレミスのCPUは1時間あたり1人民元以下であるのに対し、オフプレミスのGPUは1時間あたり10～30人民元かかるため、GPUの利用率を向上させる必要があります。

Kubernetesを使ってGPUなどの異種リソースを管理することで、以下のことが実現できます。

- デプロイメントの高速化:コンテナを利用することで、機械学習のための複雑な環境を何度も導入する手間を省くことができます。
- クラスタリソースの利用率の向上：集中的なスケジューリングと割り当てにより、これを実現することができます。
- リソースへの排他的なアクセスを確保：異種のデバイスをコンテナで隔離し、干渉を防ぐことができます。

環境の準備にかかる時間を最小限にすることで、デプロイメントを加速することができ、コンテナイメージ技術により、デプロイメントプロセスを強固にし、再利用することができます。多くの機械学習フレームワークがコンテナイメージを提供しており、コンテナイメージを利用することで、GPUの利用率を向上させることができます。

また、時分割多重化により、GPU利用率を向上させることができます。ユーザーが必要に応じてリソースを申請し、使用後すぐにリソースを解放できるように、GPUを大量に集中的にスケジューリングするためには、Kubernetesが必要です。これにより、GPUプールの柔軟な利用が可能になります。

同一デバイス上で動作する異なるアプリケーションのプロセス間の干渉を防ぐためには、Dockerが提供するデバイス・アイソレーション機能が必要です。これにより、高い効率性、費用対効果、システムの安定性が確保されます。

#GPUのコンテナ化
KubernetesはGPUアプリケーションの実行に適しており、コンテナをスケジューリング単位としたコンテナスケジューリングプラットフォームでもあります。Kubernetesの使い方を学ぶ前に、コンテナ環境でGPUアプリケーションを実行する方法を学びましょう。

###1. GPUアプリケーションをコンテナ環境で動かす
コンテナ環境でGPUアプリケーションを実行することは複雑ではありません。これは2つのステップで行います。

- GPUをサポートするコンテナイメージを構築する
- このイメージをDockerで実行し、GPUデバイスと依存ライブラリをコンテナにマッピングする


###2. GPU コンテナイメージの準備
GPUコンテナイメージは、以下のいずれかの方法で準備できます。

- 深層学習用の公式コンテナイメージを使用する
Docker HubまたはAlibaba Cloud Container Registryから公式のGPUイメージを選択します。TensorFlow、Caffe、PyTorchなどの一般的な機械学習フレームワークに対応した標準的なイメージが用意されています。公式GPUイメージは、使いやすく、安全で、信頼性の高いものです。

- NVIDIA CUDAをベースにしたGPUコンテナイメージを構築します。
公式イメージがニーズを満たさない場合、たとえば、TensorFlowフレームワークにカスタム変更を加えた場合などは、カスタムTensorFlowイメージをコンパイルする必要があります。NVIDIAの公式イメージをベースに、カスタムイメージを作成することをお勧めします。

以下のコードはTensorFlowで書かれています。これは、CUDAイメージに基づいてカスタムGPUイメージを作成します。

![スクリーンショット 2021-04-30 10.58.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/d0aa18b0-ae73-8185-4411-054864676660.png)




###3. GPU コンテナイメージの仕組み
GPU コンテナイメージを構築する前に，GPU アプリケーションをホス トにインストールする方法を学ぶ必要があります．

下図の左部分に示すように，まず下層にNVIDIAハードウェアドライバがインストールされます。上位層にはCUDAツールライブラリがインストールされます。PyTorchやTensorFlowなどの機械学習フレームワークは最上位のレイヤーにインストールされます。

CUDAツールライブラリは、アプリケーションと密接に連携しています。アプリケーションのバージョンが変更されると、関連するCUDAのバージョンも更新されることがあります。NVIDIAドライバは比較的安定しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/a79b6be1-7f02-3a32-7fa7-8a77815399be.png)


前述の図の右部分は、NVIDIA GPU コンテナソリューションを示しています。このソリューションでは、NVIDIA ドライバがホストにインストールされ、CUDA ツールライブラリの上に位置するソフトウェアがコンテナイメージによって実装されています。NVIDIAドライバのリンクは、Mount Bindを通じてコンテナにマッピングされます。

新しいNVIDIAドライバをインストールすると、同一ノード上で異なるバージョンのCUDAイメージを動作させることができます。

###4. コンテナでGPUアプリケーションを実行
ここからは，GPU コンテナの仕組みを見ていきます。次の図は、Dockerを使ってGPUコンテナを実行する例です。



![スクリーンショット 2021-04-30 10.58.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/a8b7a237-c79a-ca1a-4423-7ff34d6de064.png)



ランタイムにホストデバイスとNVIDIAドライバライブラリをGPUコンテナにマッピングする必要があります。これは，一般的なコンテナの場合とは異なります。

前述の図の右側は，起動後のGPUコンテナのGPUコンフィギュレーションを示しています。前図の右上はデバイスマッピングの結果、右下はBindモードでコンテナにドライブライブラリがマッピングされた後の変化を示しています。

NVIDIA Dockerは一般的にGPUコンテナの実行に使用され、デバイスのマッピングとドライブライブラリのマッピングのプロセスを自動化します。デバイスのマウントは簡単ですが、GPUアプリケーションが依存するドライブライブラリは複雑です。

ディープラーニングや映像処理など、具体的なシナリオに応じて異なるドライブライブラリが使用されます。ドライブライブラリを使用するには、NVIDIA、特にNVIDIAコンテナについて理解している必要があります。

#KubernetesによるGPU管理
###1. GPU Kubernetesをデプロイ
KubernetesのノードにGPU機能を以下のように設定します。ここでは、CentOSのノードを例にしています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/f8c6ab34-5440-32ac-95fa-73454a46ec26.png)


前述の図のように、私たちは次のことをしなければなりません。

- 1、 NVIDIAドライバをインストール
NVIDIAドライバをインストールするには、カーネルのコンパイルが必要です。NVIDIAドライバをインストールする前に、GNU Compiler Collection（GCC）とカーネルのソースコードをインストールする必要があります。

- 2、 yumソースを使ってNVIDIA Docker 2をインストール
NVIDIA Docker 2がインストールされたら、Dockerをリロードします。Dockerのdaemon.jsonにあるデフォルトの起動エンジンをNVIDIAに置き換えます。docker info」コマンドを実行して、ランタイム中にNVIDIA runCが使用されているかどうかを確認します。

- 3、NVIDIA Device Pluginをインストール
NVIDIAのgit repoからデバイスプラグインのデプロイ宣言ファイルをダウンロードし、「kubectl create」コマンドを実行してプラグインをデプロイします。

ここではdeamonsetを使ってデバイスプラグインをデプロイしています。KubernetesノードでGPUアプリケーションのスケジューリングに失敗した場合、デバイスプラグインなどのモジュールを確認する必要があります。例えば、デバイスプラグインのログを見て、DockerのデフォルトのrunCがNVIDIA runCに設定されているか、NVIDIAドライバがインストールされているかなどを確認することができます。



###2. GPU Kubernetesデプロイの結果を確認
GPUノードがデプロイされたら、ノードステータス情報で以下のようなGPU情報を確認します。

- GPU名（この例ではnvidia.com/gpu）
- GPUの数：次の図では2で、ノードに2つのGPUがあることを示しています。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/ac4a831b-1d6a-f5b3-5a48-0e574f5bd591.png)


###3. KubernetesでGPU yamlサンプルを使う
KubernetesでGPUコンテナを使うのは簡単です。

Podリソース設定のlimit欄にnvidia.com/gpuを必要なGPUの数だけ設定します。以下の図では1に設定されており、その後、「kubectl create」コマンドを実行して対象のPodをデプロイします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/5df7cb0f-c1c4-84a1-0c0a-1c71e55bedb9.png)


###4. 結果を見る
デプロイが完了したら、コンテナにログオンして「nvidia-smi」コマンドを実行し、結果を確認します。T4 GPUがコンテナで使用されていることがわかります。2つのGPUのうち1つは、コンテナで使用されており、もう一方のGPUはコンテナに対して透過的で、GPUアイソレーション機能によりアクセスできません。

![スクリーンショット 2021-04-30 10.59.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/ee9f7069-9a0b-7ad0-a637-67cfeaa017ba.png)



#KubernetesにおけるGPUリソースの利用
###1. エクステンションによるGPUリソースの管理
Kubernetesでは，GPUリソースをプラグイン・エクステンションで管理していますが、これは2つの独立した内部メカニズムで実装されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/d8a92e43-2c5b-30dd-3091-cb6ad55b975d.png)

- Kubernetesでは、カスタムリソースを作成できるように拡張リソースを提供しています。拡張リソースは整数レベルで計測されるため、RDMA（Remote Direct Memory Access）、FPGA（Field Programmable Gate Array）、AMD GPUなどの一般的なモードを使用することで、異なる異機種のデバイスをサポートすることができます。この機能は、NVIDIA GPUに限定されるものではありません。
- Kubernetesは、サードパーティのデバイスプロバイダがデバイスをスケジュールし、ライフサイクル全体を管理できるよう、デバイスプラグインフレームワークを提供しています。デバイスプラグインフレームワークは、Kubernetesとデバイスプラグインモジュールを接続します。また、デバイス情報をKubernetesに報告し、スケジューリングのためにデバイスを選択します。

###2. 拡張リソースの報告
拡張リソースは、ノードレベルのAPIの一種であり、デバイスプラグインとは独立して使用されます。拡張リソースを報告するには、PATCH APIを使用してノードオブジェクトのステータスフィールドを更新します。PATCH操作は、シンプルなcurlコマンドを使用して実行されます。これにより、Kubernetesのスケジューラーは、1つのGPUを使用しているノードのGPUタイプを記録します。



![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/83f907a8-3334-2a68-fbd9-947a50a6f1ae.png)


デバイスプラグインを使用している場合、PATCHオペレーションは必要ありません。デバイスプラグインのプログラミングモデルを実装し、拡張リソースが報告されたときにデバイスプラグインがPATCHオペレーションを実行するようにするだけです。

### 3.デバイス・プラグインの仕組み
デバイス・プラグインのワークフローは2つのパートに分かれています。

- 起動時のリソース報告
- 使用中のスケジューリングと実行


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/d04367a9-22a1-281f-1fbb-0e7c24d0596d.png)


デバイスプラグインの開発は簡単です。2つのイベントメソッドが関係しています。

- ListAndWatchは、リソースの報告に使用され、ヘルスチェックのメカニズムを提供します。不健全なデバイスは、Kubernetesのunhealthy device IDリストに報告され、デバイスプラグインフレームワークがこのデバイスをスケジューリング可能なデバイスリストから削除するようにします。
- Allocateメソッドは、コンテナのデプロイ時にデバイスプラグインから呼び出されます。入力パラメータには、コンテナが使用するデバイスのIDが含まれます。返されるパラメータには、コンテナの起動に必要なデバイス、データボリューム、環境変数が含まれます。

###4. レポートとモニターリソース
各ハードウェア・デバイスは、関連するデバイス・プラグインによって管理されます。デバイス・プラグインは、クライアントとしてgRPCを介してkubeletのデバイス・プラグインマネージャに接続され、リッスンするUNIXソケットAPIのバージョンとデバイス名をkubeletに報告します。

次の図は、デバイスプラグインがリソースを報告するプロセスを示しています。このプロセスは4つのステップに分かれています。最初の3つのステップはノード上で行われ、4つ目のステップはkubeletとAPIサーバの間で行われます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/81a88c3c-922b-93c0-57b7-7756a8b03fc3.png)


- ステップ1：Kubernetesと対話するために、デバイスプラグインを登録します。1つのノードに複数のデバイスが存在する可能性があります。デバイスプラグインは、クライアントとして、以下の情報をkubletに報告します。


(1) GPUやRDMAなど、デバイスプラグインが管理するデバイスの名前
 (2) キューブレットがデバイスプラグインを呼び出すために、デバイスプラグインがリッスンするUNIXソケットのファイルパス 
(3) APIのバージョンであるインタラクションのプロトコル

- ステップ2： デバイス・プラグインは、gRPCサーバーを起動します。そして、デバイス・プラグインは、gRPC サーバを代行して、kubelet にサービスを提供します。リスニング・アドレスと API バージョンは、ステップ 1 で提供されます。


- ステップ3： gRPCサーバの起動後、kubeletはデバイス・プラグインのListAndWatchへの持続的な接続を確立し、デバイスIDの検出とデバイスの健全性の確認を行います。デバイス・プラグインは、デバイスが不健全である場合、kubeletに通知します。不健全なデバイスがアイドル状態の場合、kubelet はそのデバイスをスケジューリング可能なデバイスリストから削除します。不健全なデバイスがPodで使用されている場合、Podを殺すのは高リスクのアクションであるため、kubeletは何もしません。


- ステップ4： キューブレットは、これらのデバイスをノードのステータスに公開し、デバイスの数量をKubernetes APIサーバーに送信します。スケジューラは、この情報に基づいてスケジューリングを実施します。


キューブレットは、GPUの数量のみをKubernetes APIサーバーに報告します。キューブレットのデバイスプラグインマネージャは、GPU IDリストを保存し、GPU IDをデバイスに割り当てます。Kubernetesのグローバルスケジューラーは、GPU IDリストを見ることはなく、GPUの数量だけを見ます。

そのため、デバイスプラグインが使用されている場合、Kubernetesグローバルスケジューラは、GPUの数量のみに基づいてスケジューリングを実施します。同一ノード上の2つのGPUは、PCIe通信よりもNVLINK通信で効率的にデータを交換します。この場合、デバイスプラグインは、GPUアフィニティに基づくスケジューリングをサポートしません。

###5. Podのスケジューリングと実行

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/4f27492a-fb2b-1b8e-724f-1ee603251c1b.png)


PodがGPUを使用したい場合、GPUリソースと必要な数量をResource.limitにnvidia.com/gpu: 1のように宣言します。Kubernetesは必要なGPU数量を満たすノードを見つけ、そのノードに搭載されているGPUの数を1だけ減算し、Podとノードをバインドします。

結合が完了すると、ノードにマッチしたkubeletがコンテナを作成します。キューブレットは、Podのコンテナ要求で指定されたリソースがGPUであることを発見すると、内部デバイスプラグインマネージャを有効にして、GPU IDリストから利用可能なGPUを選択し、そのGPUをコンテナに割り当てます。

キューブレットは、デバイスプラグインにAllocateリクエストを送信します。このリクエストには，コンテナに割り当てるべきGPUを含むデバイスIDリストが含まれています．

Allocate リクエストを受け取ったデバイス・プラグインは，デバイス ID に関連するデバイス・パス，ドライバ・ディレクトリ，および環境変数を見つけ，Allocate レスポンスを通じて kubelet に情報を返します。

kubeletは、受け取ったデバイスパスとドライバディレクトリに基づいて、コンテナにGPUを割り当てます。そして、Dockerはkubeletの指示に従ってコンテナを作成します。作成されたコンテナにはGPUが搭載されています。最後に、必要なドライバディレクトリがマウントされます。以上で、KubernetesでGPUをPodに割り当てるプロセスが完了します。

#考え方と実践
###1. まとめ
今回は、DockerとKubernetesでGPUを利用する方法を学びました。

- GPUコンテナ化：DockerでGPUイメージを構築し、GPUコンテナを直接実行する方法
- [Kubernetes](https://www.alibabacloud.com/product/kubernetes?spm=a2c65.11461447.0.0.40674933tlevBZ)を使ったGPUリソースの管理：KubernetesでGPUのスケジューリングをサポートし、GPUコンテナの設定を検証する方法
- デバイスプラグイン：リソースの報告と監視の方法、Podのスケジュールと実行の方法
- 考え方：現在の欠陥とコミュニティでの一般的なデバイスプラグインについて

###2. デバイスプラグインの欠点
この最後のセクションでは、デバイスプラグインの評価を行います。

デバイスプラグインは、学術界や産業界の実際のシナリオを十分に考慮して設計されていません。GPUリソースのスケジューリングはkubeletのみで行います。

GPUリソースのスケジューリングには、グローバルスケジューラが必須です。しかし、Kubernetesのスケジューラは、GPUの数量のみを基準としたスケジューリングを実装しています。デバイスプラグインでは、2台のNVLK対応GPUを搭載したPodのように、GPUの数量以外の要素に基づいて異種デバイスをスケジューリングすることはできません。

デバイスプラグインは、クラスター内のデバイスのステータスに基づくグローバルなスケジューリングをサポートしていません。

デバイスプラグインは、Allocate APIおよびListAndWatch APIによって追加される拡張可能なパラメータをサポートしていません。また、デバイスプラグインは、APIの拡張によって複雑なデバイスのリソースをスケジューリングすることができません。

そのため、デバイスプラグインは限られたシナリオにしか適用できません。NVIDIAや他のベンダーが、Kubernetesのアップストリームコードをベースにしたフォークベースのソリューションを実装しているのはこのためです。

###3. コミュニティにおける異種リソーススケジューリングソリューション

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/67bf2919-b44d-8c59-c686-c023d66a22ed.png)


- 最も一般的に使用されているソリューションは、NVIDIAが開発したものです。
- Alibaba Cloudサービスチームは、共有GPUリソースをスケジューリングするための[GPU共有スケジューリングソリューション](https://www.alibabacloud.com/product/gpu?spm=a2c65.11461447.0.0.40674933tlevBZ)を開発しました。このソリューションをご利用いただき、改善にご協力いただけると幸いです。
- 他のベンダーは、RDMAとFPGAのスケジューリングソリューションを提供しています。
Alibaba Cloud Kubernetes製品についての詳細は、[https://www.alibabacloud.com/product/kubernetes](https://www.alibabacloud.com/product/kubernetes?spm=a2c65.11461447.0.0.40674933tlevBZ)

*本ブログは英語版からの翻訳です。オリジナルは[こちら](https://www.alibabacloud.com/blog/getting-started-with-kubernetes-%7C-gpu-management-and-device-plugin-implementation_596306)からご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです。*

*アリババクラウドは日本に２つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
[アリババクラウドジャパン公式ページ](https://www.alibabacloud.com/ja)*