機械学習基盤をAWSに構築してみた


本業はwebエンジニアなので、機械学習的なお作法は知らずにとりあえずAWS上に機械学習をできる環境を立ち上げようと思ってまとめてみました。

機械学習用サーバ（以下、GPUサーバ）はプライベートサブネットに置くことにしました。
※踏み台を乗っ取られたら意味ないじゃんという説もあります…。

また機械学習をしたいのでGPUサーバには**Jupyter Notebook**と**TensorFlow**を入れる予定です。先にセキュリティグループのポート設定だけはしておこうと思います。GPUサーバにTensorFlowやCUDA・cuDNNをインストールする方法は後日書きます。

# **構成**

## **基本構成**
- GPUサーバ：１台（GPUインスタンス）
- 踏み台サーバ：１台（Bastianインスタンス）
- クラウドストレージ：１個（S3-Bucket）… マウントして使うことにした

![NW.gif](https://qiita-image-store.s3.amazonaws.com/0/163518/4c63daf1-0380-82db-f491-0aae2061e34c.gif)

※S3のマウントは[こちら](https://qiita.com/shicky_1121/items/e19b50308c79e5a6081e)

# **構築手順**



## VPCの作成

- 1 個のパブリックサブネットを持つ VPCを選択する
- これを選択すると「Bastian-Subnet」をVPCと同時に作成することができる
- RouteTableもVPCと同時に作成される
- このタイミングでPablic用のInternet Gateway はattach されている

|IPv4 CIDR ブロック|VPC名|アベイラビリティゾーン|
|:--:|:--:|:--:|
|10.0.0.0/16|GPU-VPC|us-east-1a|

|名前タグ|VPC|アベイラビリティゾーン|IPv4 CIDR|Pablic/Private|
|:--:|:--:|:--:|:--:|:--:|
|Bastian-Subnet|GPU-VPC|us-east-1a|10.0.0.0/24|Pablic|

※少しでも安くしたかったのでアベイラビリティゾーンをバージニア北部にしました。
　やはり若干、SSHが遅いですが慣れれば気にならないかなと思います。

## Subnetの作成

- GPUサーバ用のSubnetを作成する（※GPU用はVPC作成時に同時作成されている）
- Pablic/PrivateはInternet Gatewayの設定をRouteTableで定義することで決定する

|名前タグ|VPC|アベイラビリティゾーン|IPv4 CIDR|Pablic/Private|
|:--:|:--:|:--:|:--:|:--:|
|GPU-Subnet1|GPU-VPC|us-east-1a|10.0.1.0/24|Private|

※GPU用のサブネットは拡張することを想定

![図9.gif](https://qiita-image-store.s3.amazonaws.com/0/163518/0d0d45ec-409e-6c24-52ad-75fa7fc42621.gif)

## Internet Gateway の確認 & 設定

- VPC作成時に自動作成されるので今回は割愛

## Route Table の確認 & 設定

Bastian-SubnetにはRoute Tableは設定されているので、そのままインターネットに接続することができる。

![図7.gif](https://qiita-image-store.s3.amazonaws.com/0/163518/ec2649bf-f1e7-21dd-1505-8221f6d4c71d.gif)

----

## Security Group の作成

GPUサーバ用と踏み台サーバ用の２つを作成する。設定は以下の通り。

**踏み台サーバ用のセキュリティグループ**


|セキュリティグループ名|VPC|説明|
|:--:|:--:|:--|
|Bastian-Security|GPU-VPC|Use for Bastian|

**インバウンドルール** （※アウトバウンドは全て）

|タイプ|ポート範囲|ソース（種類）|ソース（IP/SG）|説明|
|:--|:--|:--|:--|:--|
|SSH|22|カスタム|各接続元のグローバルIP|SSH@|
|カスタムTCPルール|6006|カスタム|各接続元のグローバルIP|TesnsorBoard|
|カスタムTCPルール|19999|カスタム|各接続元のグローバルIP|JupyterNotebook|

 - SSHでの接続：22
 - JuptyerNotebook：19999（ポート番号は任意）
 - TensorBoarf：6006（ポート番号はデフォルト）


**GPUサーバ用のセキュリティグループ**

|セキュリティグループ名|VPC|説明|
|:--:|:--:|:--|
|GPU-Security|GPU-VPC|Use for GPU|

**インバウンドルール** （※アウトバウンドは全て）

|タイプ|ポート範囲|ソース（種類）|ソース（IP/SG）|説明|
|:--|:--|:--|:--|:--|
|SSH|22|カスタム|Bastian-Security|SSH from Bastian|
|カスタムTCP|6066|カスタム|Bastian-Security|TesnsorBoard from Bastian|
|SSH|19999|カスタム|Bastian-Security|JupyterNotebook from Bastian|

- SSHでの接続：22
- JuptyerNotebook：19999（踏み台サーバと同じにする）
- TensorBoarf：6006（デフォルト）


## 踏み台サーバの作成

- **STEP1**　AMIの選択

クイックスタートで「Ubuntu Server 16.04 」を選択する。

![図6.gif](https://qiita-image-store.s3.amazonaws.com/0/163518/dd50156f-5d9b-5838-2b00-d7467abda13f.gif)

- **STEP2** インスタンスタイプの作成

GPUインスタンス/p2.microを選択する。（踏み台だけの用途なのでインスタンスサイズはt2.microでOK）

![図5.gif](https://qiita-image-store.s3.amazonaws.com/0/163518/f0dad2ab-caca-4d14-5bf6-2f393a7c121e.gif)

- **STEP3** インスタンスの設定

<kbd>ネットワーク</kbd>には作成したVPCを選択し、<kbd>サブネット</kbd>にはVPCと同時に作成したパブリックサブネットを選択する。<kbd>IAMロール</kbd>は今は選ばない（後で設定できる）。また<kbd>削除保護の有効化</kbd>にはチェックを入れておく。

![図4.gif](https://qiita-image-store.s3.amazonaws.com/0/163518/c5d71961-8dc7-e7ca-582d-80732007ebf1.gif)

- **STEP4** ストレージの追加

ストレージの<kbd>サイズ</kbd>は8GBのままで変更する必要はない。


- **STEP5** インスタンスのタグ付け

後で区別できるように<kbd>タグ</kbd>を付ける。ここでは<kbd>キー</kbd>を「Name」「Client」「Owner」の３つとしてそれぞれ任意の<kbd>値</kbd>を入れる。


- **STEP6** セキュリティグループの設定

既存のセキュリティグループから踏み台サーバ用のセキュリティグループBastian-Security を選択する。
（接続元に応じてBastian-Securityのインバウンドを変更する必要がある）

![図2.gif](https://qiita-image-store.s3.amazonaws.com/0/163518/28fc68ef-afb9-392f-d218-7bea67042e18.gif)

- **STEP7** 確認

STEP6で<kbd>確認と作成</kbd>を選択して問題なさそうならば<kbd>作成</kbd>をクリックすると以下のポップアップが表示されるので既存のキーペアから選択して<kbd>インスタンスの作成</kbd>をクリックするとインスタンスの作成が開始される。

![図1.gif](https://qiita-image-store.s3.amazonaws.com/0/163518/e38842b1-191b-4b26-7f04-bcb20c4a6da3.gif)

## Elastic IPの割り当て

踏み台サーバは外部に接続するのでElastic IPを割り当てておくと少しラクかも。

![図8.gif](https://qiita-image-store.s3.amazonaws.com/0/163518/bb313936-1d31-28f8-f43a-c3b182cc17a5.gif)

これで踏み台サーバの準備は完了です。

## GPUサーバの作成
基本的には踏み台サーバの作り方と同じ。

- **STEP1**　AMIの選択

クイックスタートで「Ubuntu Server 16.04 」を選択する。

-  **STEP2**　インスタンスタイプの作成

インスタンスタイプはGPUインスタンス/p2.xlargeを選択する。
（p2.xlargeはNVIDIA Tesla K80 GPUを搭載している）

- **STEP3** インスタンスの設定

<kbd>ネットワーク</kbd>には踏み台サーバと同じVPCを選択し、<kbd>サブネット</kbd>はGPUサーバ用に作成したGPU-Subnet1を選択する。<kbd>IAMロール</kbd>は今は選ばない（後で設定できる）。また<kbd>削除保護の有効化</kbd>にはチェックを入れておく。

- **STEP4** ストレージの追加

ストレージの<kbd>サイズ</kbd>を8GB→20GBへ変更する。（余裕を持つなら30GB）

![図3.gif](https://qiita-image-store.s3.amazonaws.com/0/163518/b7795da7-ae2f-748f-600e-38c2eb0ca339.gif)

- **STEP5** インスタンスのタグ付け

後で区別できるように<kbd>タグ</kbd>を付ける。ここでは<kbd>キー</kbd>を「Name」「Client」「Owner」の３つとしてそれぞれ任意の<kbd>値</kbd>を入れる。

- **STEP6** セキュリティグループの設定

既存のセキュリティグループからGPUサーバ用のセキュリティグループGPU-Security を選択する。

- **STEP7** 確認

STEP6で<kbd>確認と作成</kbd>を選択して問題なさそうならば<kbd>作成</kbd>をクリックすると以下のポップアップが表示されるので既存のキーペアから選択して<kbd>インスタンスの作成</kbd>をクリックするとインスタンスの作成が開始される。

----

これでGPUサーバと踏み台サーバを構築できたので、あとはSSHで接続すれば完了。

# GPUサーバにSSH接続する

踏み台サーバからGPUサーバへ簡単にSSH接続するために踏み台サーバの以下のディレクトリにkeyファイルを格納する。

```shell-session
/home/ubuntu/.ssh
```

パーミッションを変更して参照できるようにする。

```shell-session
$ chmod 600 GPU-Key.pem
```

<kbd>.ssh/config</kbd>ファイルへ以下の内容を記載する。

```shell-session
# GPU-Srver host
# Hostnameは内部IP
Host GPU
  Hostname 10.0.0.24
  User ubuntu
  IdentityFile /home/ubuntu/.ssh/GPU-Key.pem
```

※踏み台サーバにはグローバルIPで接続するが、GPUサーバは同じVPCにあるので内部IPで接続する。また内部IPはインスタンスを再起動しても変わらないので、confファイルに記載して問題ない。

configファイルが無い場合は以下のコマンドで作成する

```shell-session
$ cd /home/ubuntu/.ssh
$ touch config
```

この設定をすると

```shell-session
$ ssh GPU
```
だけで踏み台サーバから簡単にGPUサーバにSSH接続することができる。

# まとめ

今回とりあえず環境を作ってみましたが、ここからGPUサーバにSSH接続して必要なもの（CUDA、NVIDIAドライバ、cuDNNなど）色々と入れていかなければならないので、そっちの方が大変でした。

また、ここではGPUインスタンスをプライベートサブネットに置いているので、実際にGPUサーバに諸々をインストールする際にはRoute Tableを設定してパブリックサブネットにした方が何かと便利かと思います。