AWS EC2でRayクラスター構築

# 概要

RayはPython用の分散実行フレームワーク。様々なライブラリと統合していて、容易に分散コンピューティングを導入できる（らしい）。

こちらを参考にしてAWS EC2でRayクラスターを構築する。その後、Rayクラスター上でJupyter notebookを立ち上げる。これでJupyter notebookを使ってRayを試すことができる。

https://docs.ray.io/en/latest/cluster/quickstart.html

# 前提
- Windows 10 Home
- WSLのUbuntuディストリビューション(Ubuntu20.04)がインストール済み
- AWSのIAMユーザアカウント作成済み（アクセスキーとシークレットキーが手元にある）

# 注意事項
本手順ではEC2を立ち上げるので料金が発生することに注意！

# 手順
1. Dockerコンテナを起動
1. AWS IAMユーザのクレデンシャル情報準備
1. Rayクラスターのコンフィグファイル作成
1. Rayクラスター(AWS EC2)の起動
1. Rayクラスター上でサンプルコード実行
1. AWS セキュリティグループでJupyter notebookのポートを許可
1. SSHでRayヘッドにアクセス
1. Jupyter notebookを起動
1. Jupyter notebookにアクセス
1. Rayクラスター上でデータ分析


## 1．Dockerコンテナを起動
WSL上にRayをインストールするとWSL環境がごちゃごちゃしてくるので、Rayコマンドを使用する環境をDockerコンテナで準備する。

適当なディレクトリを作る。

```bash
mkdir dockerfile
```

作成したディレクトリ配下に`Dockerfile`を作成する。

```bash
vi Dockerfile
```

Dockerfileの中身は以下の通り。

```Dockerfile
#Dockerイメージを指定
FROM ubuntu:20.04

#諸々のライブラリをインストール
RUN apt-get update
RUN apt-get install -y sudo
RUN sudo apt-get install -y curl
RUN sudo apt-get install -y unzip
RUN apt-get install -y python3 python3-pip
RUN pip3 install -U "ray[default]" boto3
WORKDIR /root
RUN curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
RUN unzip awscliv2.zip
RUN sudo ./aws/install
RUN sudo apt-get install -y vim
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get install -y ssh
RUN apt-get install -y rsync
ENV DEBIAN_FRONTEND=interactive

#Kubectlをインストール
ENV DEBIAN_FRONTEND=noninteractive
RUN curl -LO "https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl"
RUN chmod +x ./kubectl
RUN sudo mv ./kubectl /usr/local/bin/kubectl

```

DockerfileをもとにDockerイメージ(`ubuntu-ray:latest`)を作成する。

```bash
docker build -t ubuntu-ray:latest .
```

作成したDockerイメージをバックグラウンドで起動する。

```bash
docker run -itd -v /home/ceg/dockerfiles/mountdir:/root ubuntu-ray:latest
```

起動したDockerコンテナのコンテナIDを確認する。

```bash
docker ps
```

Dockerコンテナのbashに入る。

```bash
docker exec -it <コンテナID> /bin/bash
```

## 2. IAMユーザのクレデンシャル情報入力
`AWS Access Key ID`と`AWS Secret Access Key`が必要なため、事前にAWSのIAMユーザのを作成しておく。簡単に試すには、Admin権限を持ったIAMユーザを用意すればよい。最小限の権限に絞りたい場合には別記事[「EC2でRayクラスターを作成するのに必要な最小限のIAM権限」](https://qiita.com/TKsaku/items/b53128264d54868be420)を参照ください。

まずDockerコンテナのbashで以下を実行して`~/.aws`配下に`credentials`ファイルを作成する。

```bash
root@<コンテナID>:~# aws configure
```

対話式でAWSの`AWS Access Key ID`, `AWS Secret Access Key`, `Default region name`, `Default output format`を入力する。入力例は以下の通り。

```bash
AWS Access Key ID [None]: AxxxxxxxxxxxxxxxxxxxI
AWS Secret Access Key [None]: exxxxxxxxxxxxxxxxxxxxxxxx
Default region name [None]: us-west-2
Default output format [None]: json
```

`Default region name`, `Default output format`は任意で構わない。

`AWS Access Key ID`, `AWS Secret Access Key`は管理者権限をもつIAMユーザのIDとパスワードを入力する。シークレットキーはIAMユーザー作成時にのみ確認可能なので、わからない場合は新しくアカウント作成する必要がある。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619163/2609034c-6524-499b-9eb2-447f7c0e6742.png)

## 3. Rayクラスターのコンフィグファイル作成

Rayクラスターの起動設定ファイル`config.yaml`を作成する。`cluster_name`が`minimal`だと最小構成のRayクラスターになる。

```yaml:config.yaml
# An unique identifier for the head node and workers of this cluster.
cluster_name: minimal

# Cloud-provider specific configuration.
provider:
    type: aws
    region: us-west-2
```

`ap-northeast-1`だと原因不明のエラーがでる。原因がわかるかた教えてください。

## 4. Rayクラスター(AWS EC2)の起動
作成した`config.yaml`をもとにRayクラスターを起動する。

```bash
ray up -y config.yaml
```

AWSコンソールでEC2が起動していることを確認する。

## 5．Rayクラスター上でサンプルコード実行
Rayクラスター上で動かすPythonファイル`script.py`を作成する。

```python:script.py
from collections import Counter
import socket
import time

import ray

ray.init()

print('''This cluster consists of
    {} nodes in total
    {} CPU resources in total
'''.format(len(ray.nodes()), ray.cluster_resources()['CPU']))

@ray.remote
def f():
    time.sleep(0.001)
    # Return IP address.
    return socket.gethostbyname(socket.gethostname())

object_ids = [f.remote() for _ in range(10000)]
ip_addresses = ray.get(object_ids)

print('Tasks executed')
for ip_address, num_tasks in Counter(ip_addresses).items():
    print('    {} tasks on {}'.format(num_tasks, ip_address))
```

`ray submit`コマンドで作成したRayクラスター`config.yaml`上で`script.py`を実行する。

```bash
ray submit config.yaml script.py
```

`script.py`を実行するとクラスターとして稼働しているノード数とCPU数が応答される。

```bash
This cluster consists of
    3 nodes in total
    6.0 CPU resources in total

Tasks executed
    3425 tasks on xxx.xxx.xxx.xxx
    3834 tasks on xxx.xxx.xxx.xxx
    2741 tasks on xxx.xxx.xxx.xxx
```

### socket.gaierrorエラー
ray.init()で以下のようなエラーが発生する場合がある。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619163/f2a9b6c5-03a3-c93b-043c-4823ca7f7340.png)

以下の通りsocket.gaierrorはRayバージョンが1.5より前だと発生するらしい。このエラーがでたらRayバージョンを上げる必要がある。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619163/e73e3efe-88c8-b21b-ec84-a1318800c0c1.png)


## 6. AWS セキュリティグループでJupyter notebookのポートを許可
EC2に割り当てられているセキュリティグループで、ポート8899を外部からアクセス可能に設定する。

## 7. SSHでRayヘッドにアクセス
RayクラスターのヘッドノードIPを取得する。

```bash
ray get-head-ip config.yaml
```

ヘッドノードにSSHでアクセスする。

```bash
ssh -o IdentitiesOnly=yes -i /root/.ssh/ray-autoscaler_1_us-west-2.pem ubuntu@＜クラスターヘッドのIP＞
```

あるいは下記コマンドでもヘッドノードにアクセスできる。

```bash
ray attach /root/config.yaml
```

## 8. Jupyter notebookを起動
Jupyter notebookを起動する。

```bash
jupyter notebook --ip=* --no-browser
```

**注意**
以下のようにアクセスできない場合がある。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619163/8f68bf08-fc42-64d4-a2de-2c8cb2ad4723.png)

もし下記パターン1/2のコマンドでJupyter notebookを起動している場合、Jupyter notebookがLocal host内部からしかアクセス許可していないことが考えられる。`jupyter notebook --ip=*`のようにアクセス元IPを全許可すればよい。

```bash:パターン1
jupyter notebook
```

```bash:パターン2
ray exec cluster.yaml --port-forward=8899 'source ~/anaconda3/bin/activate tensorflow_p36 && jupyter notebook --port=8899'
```

## 9. Jupyter notebookにアクセス
Chromeなど適当なブラウザでクラスターヘッド上に起動したJupyter notebookにアクセスする。tokenはJupyter notebook起動時にBash上に表示されたものを入力する。

```
http://＜クラスターヘッドのIP＞:8889/
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/619163/0dde4de9-92a3-3490-a94e-5a1368f831e0.png)

あとは適当にJupyter notebook上でJupyter notebookを利用する。

## x. Rayクラスター(AWS EC2)の削除
最後にAWS上に作成したクラスター（EC2）を削除する。忘れると課金され続けてしまうので要注意。

```bash
ray down -y config.yaml
```