概要
AWS EKS上でRayクラスターを作成してJupyter notebookを利用する手順を記載する。
EKSの作成には、AWSコンソールを利用する方法とeksctlを使う方法があるが、eksctlだとEKSを利用するのに必要なAWSリソースも同時に作成してくれるので今回はeksctlを使う。
EKSはEKS ClusterとEKS Nodegroupからなる。EKS ClusterがKubernetesクラスターのうちコントロールプレーンを担い、EKS Nodegroupがデータプレーン(=実際にコンテナ稼働するワーカーノード群)を担う。
参考
- EKS作成
- Helm
EKS作成後にRayクラスターをKubernetes上にデプロイする。Helmは、Kubernetesリソースのデプロイを支援するツール。
- Helm Chart
Ray公式が紹介しているものを利用
前提
- AWSアカウントを持っていること
- 踏み台サーバ(EC2)が作成済み
手順
- eksctlでEKS Cluster&EKS Nodegroup作成
- Rayクラスター作成
- Jupyter notebookへアクセス
0. Kubectlのインストール
curl -o kubectl https://amazon-eks.s3.us-west-2.amazonaws.com/1.21.2/2021-07-05/bin/linux/amd64/kubectl
curl -o kubectl.sha256 https://amazon-eks.s3.us-west-2.amazonaws.com/1.21.2/2021-07-05/bin/linux/amd64/kubectl.sha256
openssl sha1 -sha256 kubectl
chmod +x ./kubectl
mkdir -p $HOME/bin && cp ./kubectl $HOME/bin/kubectl && export PATH=$PATH:$HOME/bin
echo 'export PATH=$PATH:$HOME/bin' >> ~/.bashrc
kubectl version --short --client
1. EKS Cluster、EKS Nodegroupを作成
注意:踏み台サーバ(EC2)上でeksctlを実行する、すなわち踏み台サーバがAWSリソースやEKS Cluster/Nodegroupを作成するので、踏み台サーバのIAMロールに権限を付与しておいてあげる必要がある。最小限の権限がわからなかったので(調べたら出てきたが試してないので省く)AdministratorAccess
ポリシーを踏み台サーバのIAMロールに割り当てた。
まずは踏み台サーバにeksctlをインストールする。
$ curl --silent --location "https://github.com/weaveworks/eksctl/releases/latest/download/eksctl_$(uname -s)_amd64.tar.gz" | tar xz -C /tmp
$ sudo mv /tmp/eksctl /usr/local/bin
$ eksctl version
踏み台サーバからeksctl createでEKS Cluster, Nodegroupを作成する。sudo
を抜くとエラーになるので注意。
$ sudo eksctl create cluster \
--name ray-eks-cluster \
--region us-west-2 \
--nodegroup-name ray-nodegroup \
--node-type t3.large \
--nodes 3 \
--nodes-min 2 \
--nodes-max 4 \
--managed
これで一気に各種AWSリソースとEKS Cluster/Nodegroupを作成してくれる。eksctlはCloud Formationを利用してこれらを作成するので、リソースの作成状況はCloud Formationで確認できる。
リソースの作成が完了したら、kubectlでEKS Clusterを操作するために、kubeconfig
を作成する。
$ aws eks update-kubeconfig --region us-west-2 --name ray-eks-cluster
次のコマンドで以下の出力が得られればkubeconfig
が正しく設定されている。
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
svc/kubernetes ClusterIP 10.100.0.1 <none> 443/TCP 1m
以下のコマンドで接続しているクラスタを確認できる。
$ kubectl config get-contexts
2. Rayクラスター作成
踏み台サーバ上にHelmをインストールする。helm version
でバージョンが確認できれば正常にインストールされている。
$ curl -O https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3
$ bash ./get-helm-3
$ helm version
次にRay用のHelm Chartをダウンロードし、Kubernetesクラスター上にKuberntesリソース(Ray cluster)をデプロイする。
$ sudo apt-get install git
$ git clone https://github.com/ray-project/ray.git
$ cd ray/deploy/charts
$ sudo helm -n ray install example-cluster --create-namespace ./ray
Kubernetes NodeやPodが確認できる。
$ sudo kubectl get nodes
$ sudo kubectl get pods
$ sudo kubectl get pods -n ray
3. Jupyter notebookへアクセス
Jupyter notebookをHead node上で起動する予定。なので、Head nodeへアクセスする必要がある。アクセスの方法はいろいろあると思うが、3通りの方法を記載する。
- 同じVPC内に踏み台サーバ設置
- 既存のServiceをLoadBalancerに変更して外部公開
- 新たにLoadBalancer Serviceを作成して外部公開
1.については、同じVPC内にあるAWSリソースからEKSのPodにアクセスできることを利用している。
3-1. 同じVPC内に踏み台サーバ(EC2)を設置
以下に概要のみ記す。まずEKSクラスタとして起動されたEC2がどのVPCに入っているかAWSコンソールから確認する。次に、同じVPC内のパブリックサブネット内に踏み台サーバとしてEC2を起動する。(※eksctlにより各アベイラビリティゾーン毎にパブリックサブネットとプライベートサブネットが1つずつ作成されているはず)。手順概要は以上。起動したEC2上でブラウザを開いてJupyter notebookを触るので、踏み台サーバはGUI化しておく必要がある(手順省略)
3-2. 既存のServiceをLoadBalancerに変更
デフォルトでは以下のClusterIP Serviceが作成される。これをLoadBalancerに変更して外部からアクセス可能なURLを生成する。
$ kubectl get svc -n ray
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
example-cluster-ray-head ClusterIP 10.100.231.189 <none> 10001/TCP,8265/TCP,8000/TCP 74m
下記のコマンドでspec
配下のtype
の値をLoadBalancer
に変更する。
kubectl -n ray patch svc example-cluster-ray-head -p '{"spec": {"type": "LoadBalancer"}}'
再度Serviceを確認すると、EXTERNAL-IPが生成されていることがわかる。
$ kubectl get svc -n ray
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
example-cluster-ray-head LoadBalancer 10.100.231.189 a94bd10a573874295a1d41aa77f99957-1031258648.us-west-2.elb.amazonaws.com 10001:32634/TCP,8265:31708/TCP,8000:31710/TCP 76m
【!! 注意 !!】
EXTERNAL-IPが生成されても実際にルーティング処理できるようには結構時間がかかる気がする(全然数えてないけど10分くらい?)。
次に、example-cluster-ray-headのbashに入って(Head nodeに転送されるので実際にはHead nodeに入っている)、Jupyter notebookを起動する。
$ sudo kubectl -n ray exec -it service/example-cluster-ray-head -- bash
$ pip3 install notebook
$ jupyter notebook --ip=* --no-browser --port 8000
ブラウザでhttp://<EXTERNAL-IP>:8000
にアクセスする。Jupyter notebook起動時に表示されたtokenを入力してJupyter notebookにログインする。あとはJupyter notebookを使ってRayを試す。
http://a94bd10a573874295a1d41aa77f99957-1031258648.us-west-2.elb.amazonaws.com:8000/
【!! 注意 !!】
kubectl get svc -n ray
の結果では8000:31710/TCP
となっており8000番ポートを31710番に転送しているように見えるが、Jupyterを31710番ポートで起動してもなぜかアクセスできない(なにが原因なんだろ、、、)。8000番ポートで起動すればアクセスできる。
3-3. 新たにLoadBalancerを作成して外部公開
Jupyter notebookに外部からアクセスするためLoadBalancer Serviceを作成する。まずはLoadBalancer用のyamlを作成する。
既存のServiceを参考にするため既存のServiceをyamlで出力する。
$ sudo kubectl get service -o yaml -n ray
上記コマンドの出力結果を元にlb.yaml
を作成する。重要な点は
selector: cluster.ray.io/component: example-cluster-ray-head
として、LoadBalancerへのトラヒックがHead nodeにルーティングされるように設定しているところ。
apiVersion: v1
kind: Service
metadata:
name: example-cluster-ray-head-lb
namespace: ray
spec:
clusterIP: 10.100.231.167
clusterIPs:
- 10.100.231.167
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- name: client
port: 10001
protocol: TCP
targetPort: 10001
- name: dashboard
port: 8265
protocol: TCP
targetPort: 8265
- name: ray-serve
port: 8000
protocol: TCP
targetPort: 8000
selector:
cluster.ray.io/component: example-cluster-ray-head
sessionAffinity: None
type: LoadBalancer
作成したlb.yaml
をもとにLoad Balancerを作成する。
$ sudo kubectl create -f lb.yaml -n ray
Load Balancer serviceが作成されたことを確認する。
$ sudo kubectl get service -n ray
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
example-cluster-ray-head ClusterIP 10.100.150.175 <none> 10001/TCP,8265/TCP,8000/TCP 142m
example-cluster-ray-head-lb LoadBalancer 10.100.150.177 a932049cbef824477bfc553edba1d781-1349688053.us-west-2.elb.amazonaws.com 10001:31563/TCP,8265:31733/TCP,8000:30620/TCP 56s
【!! 注意 !!】
EXTERNAL-IPが生成されても実際にルーティング処理できるようには結構時間がかかる気がする(全然数えてないけど10分くらい?)。
example-cluster-ray-head (ClusterIP)のbashに入って、Jupyter notebookを起動する。指定するポートはexample-cluster-ray-head-lbの宛先ポート。
$ sudo kubectl -n ray exec -it service/example-cluster-ray-head -- bash
$ pip3 install notebook
$ jupyter notebook --ip=* --no-browser --port 8000
ブラウザでhttp://<EXTERNAL-IP>:8000
にアクセスする。Jupyter notebook起動時に表示されたtokenを入力してJupyter notebookにログインする。あとはJupyter notebookを使ってRayを試す。
http://a932049cbef824477bfc553edba1d781-1349688053.us-west-2.elb.amazonaws.com:8000/
【!! 注意 !!】
kubectl get svc -n ray
の結果では8000:31710/TCP
となっており8000番ポートを31710番に転送しているように見えるが、Jupyterを31710番ポートで起動してもなぜかアクセスできない(なにが原因なんだろ、、、)。8000番ポートで起動すればアクセスできる。
4. 削除
使い終わったら削除を忘れずに。
まずK8sリソースを削除。
# まずカスタムリソースrayClusterの「example-cluster」を削除
$ kubectl -n ray delete raycluster example-cluster
# 次に「example-cluster」をアンインストール
$ helm -n ray uninstall example-cluster
release "example-cluster" uninstalled
# 最後にネームスペースrayを削除
$ kubectl delete namespace ray
namespace "ray" deleted
さらにEKS clusterを削除。
$ sudo eksctl delete cluster \
--name ray-eks-cluster \
--wait
以上。