この資料は VCP Hands-on 「201-GPUインスタンスの利用.ipynb」 の内容をJupyterNotebook環境のない状態でも閲覧できるように用意した自習用資料です。各ステップの終わりに、そのステップをJupyterNotebookで実行した様子を見ることができる動画がついています。
全体を実行している様子はハンズオンの動画 でみることができます。
GPUが利用できるEC2インスタンスタイプ(Amazon)、VMサイズ(Microsoft Azure)を指定する必要があります。
GPUを利用するためには、GPUドライバ、ライブラリなどを準備する必要があります。
VCPではGPUドライバなどをVCノードの構成に合わせた形でセットアップしたものを事前に用意してあります。そのため通常のVCノードを利用する場合にspec指定を行ったのと同様の手順で、いくつかのパラメータを specに指定するだけでGPUを利用することが出来ます。
GPUを利用しない場合(下図左側)と、GPUを利用する場合(下図右側)のVCノードの構成を以下に示します。

GPUを利用しない場合との違いを以下に示します。
nvidia ドライバはカーネルモジュールになっているため、VMのカーネルバージョンに強く依存しています。そのためnvidiaドライバはBaseコンテナではなくOS(マシンイメージ)にインストールしています。
この Notebook では以下の操作を行います。
GPUを利用するために必要となるパラメータを specに指定して、VCノードを起動する。
VCP SDKを利用するにはVC Controllerのアクセストークンが必要となります。次のセルを実行すると入力枠が表示されるのでアクセストークンの値を入力してください。
アクセストークン入力後に Enter キーを押すことで入力が完了します。
from getpass import getpass
vcc_access_token = getpass()
VCP SDKの初期化を行います。
from common import logsetting
from vcpsdk.vcpsdk import VcpSDK
# VCの管理オブジェクトの作成
vcp = VcpSDK(
vcc_access_token, # VCCのアクセストークン
)
上のセルの実行結果がエラーとなり以下のようなメッセージが表示されている場合は、入力されたアクセストークンに誤りがあります。
2018-09-XX XX:XX:XX,XXX - ERROR - config vc failed: http_status(403)
2018-09-XX XX:XX:XX,XXX - ERROR - 2018/XX/XX XX:XX:XX UTC: VCPAuthException: xxxxxxx:token lookup is failed: permission denied
この場合はアクセストークンの入力からやり直してください。
GPUを利用するVCノードのspecを指定します。
プロバイダと flavorを指定して specオブジェクトを取得します。ここでは以下の値を指定します。
awsgpuflavor に gpu を指定することで、GPUが利用できるインスタンスタイプが選択されます。
spec = vcp.get_spec(
'aws', # プロバイダ
'gpu' # flavor
)
GPUを利用するための nvidiaドライバや、GPUコンテナを実行するためのランタイム(nvidia-docker2)を準備したVMイメージ、Baseコンテナイメージを spec の設定に追加します。
# Baseコンテナイメージの設定
spec.image = 'vcp/gpu:1.6-nvidia-384'
# 仮想マシンイメージの設定
spec.cloud_image = 'niivcp-gpu-20190925'
VCノードにsshでログインするためには事前に公開鍵認証の鍵を登録する必要があります。そのための設定をここで行います。
VCノードに登録する公開鍵認証の公開鍵のパスを次のセルで指定してください。
import os
ssh_public_key = os.path.expanduser('~/.ssh/id_rsa.pub')
指定した公開鍵を spec に設定します。
spec.set_ssh_pubkey(ssh_public_key)
後でVCノードにSSHでログインする際に秘密鍵も必要になるので、ここで設定しておきます。次のセルで秘密鍵のパスを指定してください。
ssh_private_key = os.path.expanduser('~/.ssh/id_rsa')
公開鍵と秘密鍵が正しいペアであることをチェックします。次のセルを実行してエラーにならないことを確認してください。
!grep -q "$(ssh-keygen -y -f {ssh_private_key})" {ssh_public_key}
ここまで spec に設定した内容を確認してみます。
print(spec)
VCノードの起動と、VCノードを操作するためのAnsible設定を行います。
Unitの作成とVCノードの起動を行います。
まずはUnitGroupを作成します。UnitGroupの名前は handson201とします。
unit_group = vcp.create_ugroup(
'handson201' # UnitGroupの名前
)
VCノードを起動する前のUnitとVCノードの状態を確認しておきます。
from IPython.display import display
# Unitの一覧を DataFrame で表示する
display(unit_group.df_units())
# VCノードの一覧を DataFrame で表示する
display(unit_group.df_nodes())
Unitの作成とVCノードの起動を行います。
処理が完了するまで2分~7分程度かかります。
# Unitの作成(同時に VCノードが作成される)
unit_group.create_unit(
'gpu', # Unit名
spec
)
起動したUnit, VCノードの一覧を表示します。
# Unitの一覧を DataFrame で表示する
display(unit_group.df_units())
# VCノードの一覧を DataFrame で表示する
display(unit_group.df_nodes())
※ VCノード作成のセルの実行は時間がかかるため、この動画では編集し短くしています
起動した VC ノードに SSHでログイン出来ることを確認します。
まず、VCノードのIPアドレスを確認します。
ip_address = unit_group.find_ip_addresses(node_state='RUNNING').pop()
print(ip_address)
ping による疎通確認を行います。
!ping -c 4 {ip_address}
VCノードにSSHでログインする準備としてVCノードのホストキーを ~/.ssh/known_hosts に登録します。
!touch ~/.ssh/known_hosts
# ~/.ssh/known_hosts から古いホストキーを削除する
!ssh-keygen -R {ip_address}
# ~/.ssh/known_hostsにVCノードを登録する
!ssh-keyscan -H {ip_address} >> ~/.ssh/known_hosts
VCノードにSSHでログインをしてコマンドを実行してみます。
!ssh root@{ip_address} ls -la
NVIDIA GPUではGPUデバイスを管理、監視するためのコマンドnvidia-smiが提供されています。ここでは、起動したVCノードのGPUに関する状態を取得するためにnvidia-smiコマンドを実行してみます。
nvidia-smi コマンドを利用するとGPU名やドライババージョン、GPUメモリ使用量、GPU使用率、GPUの温度などのGPUに関する情報を取得することができます。nvidia-smiコマンドが表示するサマリ情報の例を以下に示します。
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 384.130 Driver Version: 384.130 |
|-------------------------------+----------------------+----------------------+
| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |
|===============================+======================+======================|
| 0 Tesla K80 Off | 00000000:00:1E.0 Off | 0 |
| N/A 51C P0 59W / 149W | 0MiB / 11439MiB | 96% Default |
+-------------------------------+----------------------+----------------------+
+-----------------------------------------------------------------------------+
| Processes: GPU Memory |
| GPU PID Type Process name Usage |
|=============================================================================|
| No running processes found |
+-----------------------------------------------------------------------------+
nvidia-smiコマンドはCUDAコンテナの中で実行するので、まずコンテナイメージの取得を行います。
コンテナイメージの取得に1~3分程度かかります。
!ssh root@{ip_address} docker pull nvidia/cuda:8.0-cudnn5-runtime-ubuntu16.04
実際にCUDAコンテナからnvidia-smi コマンドを実行してGPUに関するサマリ情報が表示させてみます。
コンテナを実行する際に
docker runコマンドの引数に--runtime=nvidiaオプションを追加しています。このオプションを指定することで、コンテナ内からGPUを利用するための処理が nvidia-docker2 によって実行されています。
!ssh root@{ip_address} docker run --runtime=nvidia --rm \
nvidia/cuda:8.0-cudnn5-runtime-ubuntu16.04 nvidia-smi
GPUに関する詳細情報を取得するために --queryオプションを指定して nvidia-smi コマンドを実行してみます。
nvidia-smiコマンドの他のオプションについては nvidia-smi documentationを参照してください。
!ssh root@{ip_address} docker run --runtime=nvidia --rm \
nvidia/cuda:8.0-cudnn5-runtime-ubuntu16.04 nvidia-smi --query
※ コンテナイメージ取得のセルの実行は時間がかかるため、この動画では編集し短くしています
起動時のオプションなどを記述した docker-compose.ymlファイルをGPU環境に配置します。
OpenPoseのコンテナイメージを作成するためのDockerfileとコンテナを起動する際のオプションなどを記述した docker-compose.yml をGPU環境に配置します。
# プライベートレジストリのアドレスを取得
registry = vcp.vcc_info()['docker_registry']['official']
!scp -r openpose/ root@{ip_address}:.
!ssh root@{ip_address} sed -i -e '/image:/s/{{{{registry}}}}/{registry}/' openpose/docker-compose.yml
VCPのプライベートレジストリに既にビルドしたOpenPoseコンテナのイメージが格納されている場合は、次のセルを実行するとイメージの取得が行われます。イメージの取得には1~3分程度かかります。
!ssh root@{ip_address} "cd openpose && docker-compose pull"
事前にプライベートレジストリにイメージが格納されていない場合は次のセルのコメントを外して、イメージのビルドを行ってください。 イメージのビルドには7~8分程度かかります。
プライベートレジストリからイメージを取得した場合は、ビルドを行う必要はありません。
# import tempfile
# log_file = tempfile.mkstemp()
# !ssh root@{ip_address} "cd openpose && docker-compose build" > {log_file[1]} 2>&1
OpenPoseのコンテナを起動します。
!ssh root@{ip_address} 'cd openpose && docker-compose up -d'
コンテナが起動されたことを確認します。次のセルを実行してStateが Up と表示されていればコンテナの起動に成功しています。
!ssh root@{ip_address} 'cd openpose && docker-compose ps'
※ コンテナイメージ取得のセルの実行は時間がかかるため、この動画では編集し短くしています
OpenPoseのサンプル画像を処理してみます。
まず処理前のファイルを格納するディレクトリを作成します。
result_dir = './openpose_result'
!mkdir -p {result_dir}
処理前のファイルを取得します。
!ssh root@{ip_address} \
'cd openpose && docker cp openpose:/root/openpose/examples/media .'
!scp -r root@{ip_address}:openpose/media {result_dir}
処理前の画像を1つ表示させてみます。
from IPython.display import Image
Image(filename='openpose_result/media/COCO_val2014_000000000192.jpg')
処理前の画像のリンクから他の画像を確認することもできます。
OpenPoseで画像の処理を行います。
!ssh root@{ip_address} 'docker exec -t openpose \
build/examples/openpose/openpose.bin --display 0 \
--image_dir examples/media --write_images /root/result \
--write_images_format jpg'
処理後のファイルを取得します。
!scp -r root@{ip_address}:openpose/result {result_dir}
OpenPoseで処理した画像を1枚表示させてみます。
Image(filename='openpose_result/result/COCO_val2014_000000000192_rendered.jpg')
処理結果のリンクから他の処理結果の画像を確認することもできます。
処理前の動画を確認します。
OpenPoseのサンプル動画を処理してみます。
処理には30秒程度かかります。
!ssh root@{ip_address} 'docker exec -t openpose \
build/examples/openpose/openpose.bin --display 0 \
--video /root/openpose/examples/media/video.avi --write_video /root/result/result.avi'
処理結果のファイルを取得します。
!scp -r root@{ip_address}:openpose/result {result_dir}
処理後の動画を確認します。
ログインするためのユーザ、パスワードは
admin/adminです。
インターネット上の画像を処理してみます。
処理対象となる画像の URL のリストを設定してください。
image_urls = [
# (例)
# 'http://www.example.com/sample.jpg',
]
処理対象の画像ファイルをOpenPoseコンテナに配置します。
for url in image_urls:
!ssh root@{ip_address} 'cd openpose/data && curl -O {url}'
OpenPoseの処理を実行します。
if len(image_urls) > 0:
!ssh root@{ip_address} 'docker exec -t openpose \
build/examples/openpose/openpose.bin --display 0 \
--image_dir /root/data/ --write_images /root/result2'
処理結果のファイルを取得します。
!scp -r root@{ip_address}:openpose/result2 {result_dir}
処理結果を確認します。
ビルドしたOpenPoseのイメージをプライベートレジストリに格納する場合は、次のセルのコメントを削除して実行してください。
完了するまで2分程度かかります。
# !ssh root@{ip_address} 'cd openpose && docker-compose push'
全てのリソースを削除します。
処理が完了するまで1分~2分程度かかります。
unit_group.cleanup()
ビルドのログがある場合は削除します。
if 'log_file' in locals():
!rm -f {log_file[1]}
取得した画像ファイルなどを削除します。
!rm -rf {result_dir}
※ リソース削除のセルの実行は時間がかかるため、この動画では編集し短くしています