vSphere Hypervisor(=ESXi) と NVIDIA GPU のパススルー 令和2年1月版

# はじめに
- 新年なので ESXi を構築しなおします。
- 最新の ESXi のバージョンは2019年08月20日の6.7です。 
- 機械学習等の為に GPU パススルーも実施します。


# VMware ESXi とは？
- VMware ESXi は、ベアメタルハイパーバイザの事です。OS が不要で、ハードウェアに直接インストールします。
- また、ESXi は、Elastic Sky X integrated の略称です。
- 有償の、VMware vSphere エディションの ESXi があります。
- 無償の、vSphere Hypervisor エディションの ESXi があります。
- もともと、ESX がありました。管理コンソール等を変更し、無償化した物が ESXi です。
- 現在は、ESXは廃止され、ESXiに一本化されています。


# 環境
- MSI MPG Z390 GAMING PLUS ATX
- MSI NVIDIA GeForce GT710 (管理用)
- MSI GeForce GTX 1650 AERO ITX 4G OC (GPUパススルー用)


# ESXiのダウンロード
- VMware の My VMware のアカウントを作成の上、下記からダウンロードします。
- https://my.vmware.com/jp/web/vmware/evalcenter?p=free-esxi6
- ライセンス情報
  - コンポーネント: VMware vSphere Hypervisor 6 License
  - ライセンス キー: XXXXX-XXXXX-XXXXX-XXXXX-XXXXX
- パッケージのダウンロード
  - VMware vSphere Hypervisor 6.7 Update 3- バイナリ
  - VMware vSphere Hypervisor (ESXi ISO) image (Includes VMware Tools)
  - 2019-08-20 | 6.7.0U3 | 314.66 MB | iso


# インストールUSBの作成
- Mac で実施した手順です。
- Mac のディスクユーティリティで USB を `MS-DOS (FAT)` でフォーマットします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68632/061cf17c-2de1-7a13-17fa-eb51980c4dfc.png)

- イメージをダブルクリックしマウントします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68632/35cbd11b-b25a-21e8-245f-449cea526cf1.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68632/9feccdad-3d16-d17d-bbbd-d2a5d83fe621.png)

- マウントした全ファイルを、USBにコピーします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68632/f48f9be5-fbe0-663e-b7a9-2c3caa298b43.png)

- USB内のファイルを修正します。これは、USB起動するために必要です。
- USBのフォルダへ移動します。

```
$ cd /Volumes/ESXI
```

- `APPEND -c boot.cfg` を `APPEND -c boot.cfg -p 1` へ変更する。

```
$ vim ISOLINUX.CFG
DEFAULT menu.c32
MENU TITLE ESXi-6.7.0-20190802001-standard Boot Menu
NOHALT 1
PROMPT 0
TIMEOUT 80
LABEL install
  KERNEL mboot.c32
  APPEND -c boot.cfg -p 1
  MENU LABEL ESXi-6.7.0-20190802001-standard ^Installer
LABEL hddboot
  LOCALBOOT 0x80
  MENU LABEL ^Boot from local disk

```

- ファイル名を変更する。

```
$ mv ISOLINUX.CFG SYSLINUX.CFG
```

- アンマウントします。


# マザーボード

## GPU
- MSI NVIDIA GeForce GT710 は、インストール用、その後の管理用に利用します。一番CPUに近いスロットです。
- MSI GeForce GTX 1650 AERO ITX 4G OC は、GPUパススルー用に利用します。
- GPUは、1つでもOKです。インストール用、管理用、GPUパススルー用で共用できます。
- ただし、GPUパススルーを無効から有効にすると、管理用としては使用出来なくなります。
- インストール後のVMの作成、管理等は、Webブラウザーから実施するので、多くの場合は問題ないでしょう。
- 管理用として利用する場合は、GPUパススルーを有効から無効にすればOKです。

## BIOS
- 初期化グラフィックアダプターは、PEG等の PCI Express グラフィックカードにします。
- IGD等の Integrated Graphics Device(内臓グラフィックカード)は、無効にします。
- なぜ？って思われるかもしれませんが、これをやらないとGPUパススルーが出来ません。理由は、私もわかりません。。。

```
Settings\Advanced\Integrated Graphics Configuration
Initiate Graphic Adapter: IGD -> PEG
IGD Multi-Monitor: Enabled -> Disabled
```

- ブートは、UEFIにします。

```
Settings\Boot
Boot mode select: LEGACY+UEFI -> UEFI
```
 
- Intel VT-D を有効にします。

```
Overclocking\CPU Features
Intel VT-D Tech: Disabled -> Enabled
```


# ESXiのインストール

## USBブートとインストール
- 以下が設定の流れです。
- 注意する箇所は、インストールするディスクの選択箇所で、インストール用のUSBを選ばない用にしましょう。
- Welcome to the VMware ESXi 6.7.0 Installation
- End User License Agreement (EULA)
- Select a Disk to Install or Upgrade
- Confirm Disk Selection
- Please select a keyboard layout: Japanese
- Enter a root password
- Confirm Install
- Installation Complete

## ネットワーク設定
- 必要に応じて、ネットワークの設定を修正します。
- 私は、静的IPv4アドレスへ変更、IPv6は無効、DNSは手動設定、ホスト名は `esxi` にしました。
- IPv4 Configuration 
    - Set static IPv4 address and network configuration
        - IPv4 Address: 192.168.1.254
        - Subnet Mask: 255.255.255.0
        - Default Gateway: 192.168.1.1
- IPv6 Configuration
    - Disable IPv6 (restart required)
- DNS Configuration
    - Use the following DNS server addresses and hostname
    - Primary DNS Server: 192.168.1.1
    - Alternate DNS Server
    - Hostname: esxi


# ESXi Web UIで設定
- `https://192.168.1.254/` へアクセスします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68632/23472e01-15ed-0673-c68d-d585fff2ed04.png)

- ライセンスを割り当てます。割り当てない場合は、60日間のみ利用出来ます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68632/264b1c7b-b795-93e7-8096-87a434056070.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68632/fe5baa42-8bd0-b9ab-345b-4befb0845e2c.png)

- GPUパススルーを設定します。今回は、`NVIDIA Corporation TU117 [GeForce GTX 1650]` ですね。
- 自動的に `nVidia Corporation Audio device` も選択されます。
- 指示に従い、再起動します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68632/6a8e0c31-bfbd-0264-cdda-97e89492241e.png)


# Ubuntu等のisoファイルのアップロード
- データストアブラウザで、`iso` 等のフォルダを作成し、`Ubuntu` 等の `iso` イメージをアップロードします。
- 今回は、`Ubuntu Server 18.04.3 LTS` をアップロードしました。 

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68632/204a10db-20dc-17de-8a2b-036e54646197.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68632/228dd974-496d-3ef1-a941-e42f29084d51.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68632/2d71841b-4569-d6b1-ca76-cd5a87b6dc9e.png)


# 仮想マシンの作成
- 新規仮想マシンの作成をします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68632/f06c6de5-4357-ddc1-823a-73c902970b1c.png)

- 名前は `ubuntu-nvidia`、互換性 `ESXi 6.7 仮想マシン` ゲスト OS ファミリ `Linux` ゲスト OS のバージョン `Ubuntu Linux (64ビット)` にしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68632/44e20755-792b-0c5f-bc85-ce1b88a15bb8.png)

- ストレージを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68632/2d1cc290-dc45-aca7-7abe-7fbacdd84867.png)

- GPUパススルーを利用する場合は `すべてのゲスト メモリを予約(すべてロック)` を有効にします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68632/155f5cc2-4800-bea8-e3b6-ecae435b3aa4.png)

- ハードディスクは シンプロビジョニング にしておくと、実際に利用した分だけディスクが使われます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68632/4fc520cd-76c0-10dc-54a9-7b90399b1b52.png)

- CD/DVD で iso メディアを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68632/781c32fc-b4d4-57da-160f-c849981890ce.png)

- 新規PCIデバイスから GPUを設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68632/721abe2e-b0f8-404c-d7f9-9e33e3c54237.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68632/771dc1b7-debf-2c1f-be95-a14ca3a391b6.png)

- 起動オプションのファームウェアは `EFI` にしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68632/c435f414-1df8-5885-d31f-84224f6b62cb.png)

- 詳細の構成パラメータで構成の編集をします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68632/f70db82e-c48e-33dc-8177-3e41fa4ca0ea.png)

- 以下を設定します。これは、仮想マシン内のNVIDIAドライバが、仮想マシンで動いている事を検知出来ないようにする設定です。
    - キー: hypervisor.cpuid.v0
    - 値: FALSE

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68632/db5a2509-53b8-f4a2-b79c-71d368c28dcc.png)

- 仮想マシンを作成完了です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68632/597d93b4-e66a-831f-d110-47152edd1f41.png)


# Ubuntuのインストール
- こちらは、省略します。以下は、ユーザー名、ホスト名の設定箇所です。あと、`ssh` を有効にしています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68632/07093ede-e18f-ce42-fe8d-7bfd881cab0d.png)


# 仮想マシンへログイン
- ssh で仮想マシンへログインします。

```
$ ssh ubuntu@192.168.1.19
```

- GPUを確認します。NVIDIAなど確認できますね。

```
$ lspci
省略
03:00.0 VGA compatible controller: NVIDIA Corporation Device 1f82 (rev a1)
03:00.1 Audio device: NVIDIA Corporation Device 10fa (rev a1)
```

# NVIDA CUDA、Python、TensorFlow、Kerasのインストール
- 以前 [機械学習(Keras, PyTorch)と分散学習(Horovod)の環境構築シェルスクリプト](https://qiita.com/maeda_mikio/items/f71a1bedd7c8d2d89165) で紹介したものから一部を利用しています。
- 詳細は省略します。

## NVIDIA CUDA

```
$ sudo apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/7fa2af80.pub
$ wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/cuda-repo-ubuntu1804_10.0.130-1_amd64.deb
$ wget https://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1804/x86_64/nvidia-machine-learning-repo-ubuntu1804_1.0.0-1_amd64.deb
$ sudo dpkg -i cuda-repo-ubuntu1804_10.0.130-1_amd64.deb nvidia-machine-learning-repo-ubuntu1804_1.0.0-1_amd64.deb
$ sudo apt-mark hold cuda-repo-ubuntu1804 nvidia-machine-learning-repo-ubuntu1804
$ sudo apt update
$ sudo apt install -y --no-install-recommends nvidia-driver-430
$ sudo apt install -y --no-install-recommends cuda=10.0.130-1 libcudnn7=7.6.3.30-1+cuda10.0 libcudnn7-dev=7.6.3.30-1+cuda10.0
$ sudo apt-mark hold cuda libcudnn7 libcudnn7-dev
$ echo "#!/bin/sh -e\nnvidia-smi -pm 1\nexit 0" | sudo tee /etc/init.d/nvidia && sudo chmod 755 /etc/init.d/nvidia && sudo ln -s ../init.d/nvidia /etc/rc5.d/S01nvidia
```

## Python、Keras、TensorFlow
- KerasとTensorFlowはバージョン指定しました。

```
$ sudo apt install -y --no-install-recommends python3 python3-dev python3-pip python3-setuptools
$ sudo pip3 install keras==2.3.1 tensorflow-gpu==1.14.0
```

## リブート

```
$ sudo reboot
```

## nvidia-smi で GPU を確認
- GPU を確認します。

```
$ nvidia-smi
Thu Jan  9 15:37:09 2020
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 440.33.01    Driver Version: 440.33.01    CUDA Version: 10.2     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  GeForce GTX 1650    On   | 00000000:03:00.0 Off |                  N/A |
| 30%   38C    P0    15W /  75W |      0MiB /  3911MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID   Type   Process name                             Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+
```

- もし、下記を設定していない場合の、`nvidia-smi` の例です。
    - キー: hypervisor.cpuid.v0
    - 値: FALSE

```
$ nvidia-smi
Unable to determine the device handle for GPU 0000:03:00.0: Unknown Error
```


# Keras MNIST
- では、Keras で MNIST を動かしてみます。
- まず、GPU が使われているか確認したいので、`nvidia-smi` を起動します。
- 以下は、0.1秒毎に更新しながら、`nvidia-smi` を表示しています。

```
$ watch -n 0.1 nvidia-smi
Every 0.1s: nvidia-smi                                                                                                                                                                                                                   ubuntu-nvidia: Thu Jan  9 15:45:29 2020

Thu Jan  9 15:45:29 2020
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 440.33.01    Driver Version: 440.33.01    CUDA Version: 10.2     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  GeForce GTX 1650    On   | 00000000:03:00.0 Off |                  N/A |
| 30%   33C    P8     4W /  75W |      0MiB /  3911MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID   Type   Process name                             Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+
```

- 別の端末から ssh 接続し、MNISTをダウンロードします。

```
$ wget https://raw.githubusercontent.com/keras-team/keras/master/examples/mnist_cnn.py
```

- 実行します。`TF_FORCE_GPU_ALLOW_GROWTH` は、必要な分だけ、GPUのメモリを取得する変数です。

```
$ TF_FORCE_GPU_ALLOW_GROWTH=true python3 mnist_cnn.py
Epoch 1/12
60000/60000 [==============================] - 8s 127us/step - loss: 0.2635 - accuracy: 0.9196 - val_loss: 0.0583 - val_accuracy: 0.9823
省略
Epoch 12/12
60000/60000 [==============================] - 6s 104us/step - loss: 0.0234 - accuracy: 0.9927 - val_loss: 0.0254 - val_accuracy: 0.9913
Test loss: 0.02537108706822928
Test accuracy: 0.9912999868392944
```

- GPU も使われています。
- メモリは,777MiB 使ってますね。

```
Every 0.1s: nvidia-smi                                                                                                                                                                                                                   ubuntu-nvidia: Thu Jan  9 16:49:29 2020

Thu Jan  9 16:49:29 2020
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 440.33.01    Driver Version: 440.33.01    CUDA Version: 10.2     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  GeForce GTX 1650    On   | 00000000:03:00.0 Off |                  N/A |
| 30%   47C    P0    54W /  75W |    789MiB /  3911MiB |     82%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID   Type   Process name                             Usage      |
|=============================================================================|
|    0     12508      C   python3                                      777MiB |
+-----------------------------------------------------------------------------+
```

# EC2 p2.xlarge と GeForce GTX 1650 の比較
- 比較をしてみました。
- 実行時間は、若干 `GeForce GTX 1650` が速かったです。
- MNIST は 約800MiB 程度しかメモリを使わないので良いですが、最近はメモリを沢山使う場合が多いので、EC2の利用も必要かと思います。
- 簡単な検証は、ESXiで、本番の学習は EC2でと言う感じで、使い分ければ、お金の面でも安心かも。

|環境 |GPU |GPU メモリ |time |価格 |
|---|---|---|---|---|
|EC2 p2.xlarge |Tesla K80 |11441MiB |1m39.772s |約170円/時 (東京、オンデマンド)|
|ESXi |GeForce GTX 1650 |3911MiB |1m19.537s |約16,000円 |


# おわりに
- ESXi と GPU パススルー の環境構築をしました。
- EC2 の Tesla K80 と GeForce GTX 1650 で Keras MNIST を速度面で確認しました。
- 次回は、GeForce RTX 2080 Ti を Windows 10(ゲーム) と Ubuntu Server(機械学習) で切り替えながら利用したいと思います。