EKSで生成AIソリューションのデプロイを検証し設定を確認する

こんにちは。サイオステクノロジーの塙です。

今回はEKS上でGPUを扱う生成AIソリューションのデプロイを試し、実際にGPUがどう使われてどう見えるのかを検証してみたいと思います。

概要

前回は、Kubernetes をベースとしたプラットフォームでGPUを扱っていくための手法について解説してみました。

KubernetesでGPUを扱うためにはどんな準備が必要となるのか、またどんな設定をすれば良いかをまとめています。

■前回の記事はこちら

KubernetesでGPUを使用する

 

前回までの記事は机上ベースでのまとめをしていたため、今回はEKSを用いて、実際にGPUの設定がどう見えるのかについてまとめてみました。

導入

EKS上での検証は以下の記事を参考にしています。

まずこの記事の内容を参考にして導入していきます。

 

■ 検証の概要

上記記事では、EKSに生成AIソリューションをデプロイします。

JARK Stack と呼ばれるモデルの構築と実行に利用出来るツールを用いており、アーキテクチャとしては、JupyterHub, Argo Workflows, Ray Serve, Kubernetes を指しています。(アーキテクチャの詳細は記事をご参考ください)

今回、それぞれのツールの詳細は割愛していますが、生成AIソリューションの構成時に使用するツール群の調査も追々行っていけたらと思います。

この時使用する生成AIは、Stable Diffusion というText-to-Imageの画像生成AIの拡散モデルを使用しています。

また、DreamBoothという特定の対象を事後学習させる学習方法を用いてトレーニングを行い、モデルを作成する一連の工程をEKSのPodで行っている形となります。

 

■ 前提情報

検証に必要な前提は以下となります。(バージョンは検証時のバージョン)

  • AWS CLI v2.11.13
  • kubectl
    • Client Version: v1.24.1
      Kustomize Version: v4.5.4
      Server Version: v1.29.5-eks-1de2ab1
  • Terraform v1.8.4
  • helm v3.13.1
  • Hugging Face のトークン
  • jq v1.6

 

検証準備

Aの記事の「Steps to deploy Stable Diffusion Model on Amazon EKS」から順次行っていきます。

1. data-on-eksのGitリポジトリをクローンします

$ git clone https://github.com/awslabs/data-on-eks.git

2.ブループリントをデプロイします

ai-ml/jark-stack/terraformのブループリントまで移動して、./install.sh スクリプトを実行して、terraformで生成AIソリューションを構築していきます。今回の構築用に不要なアドオンの削除、インスタンスタイプ、VPCネットワークなどを少し修正しデプロイを行います。デプロイは30分ほどかかるので完了するまで少し待ちます。

下記では、Hugging Faceのトークンを使用するためファイルのダミートークンを置き換える内容を加えています。

$ cd data-on-eks/ai-ml/jark-stack/terraform

# 必要に応じて、不要なアドオンの削除、インスタンスタイプ、VPCネットワークなどを修正
..(snip)..
# 必要に応じて、Hugging face tokenの修正
# data-on-eks/ai-ml/jark-stack/terraform/variables.tf
variable "huggingface_token" {
  description = "Hugging Face Secret Token"
  type = string
  default = "DUMMY_TOKEN_REPLACE_ME"  ## hugging face token に置き換える
  sensitive = true

$ ./install.sh

3. 記事と同じように、Stable Diffusion モデルの調整を行っていきます

$ kubectl get svc proxy-public -n jupyterhub --output jsonpath='{.status.loadBalancer.ingress[0].hostname}
k8s-jupyterh-proxypub-xxx.elb.us-west-2.amazonaws.com

出力されたDNS ホスト名をWebブラウザ経由で開き、jupyterhubを起動します。

jupyterhub-values.yamlに記載されているユーザー名とパスワードを使用してログインします。(これにより新規のPodが立ち上がります)

# jupyter-xxx1 が立ち上がっていることを確認する
$ kubectl get pods -n jupyterhub
NAME READY STATUS RESTARTS AGE
continuous-image-puller-d4tqs 1/1 Running 0 90m
continuous-image-puller-m6ccv 1/1 Running 0 90m
hub-64f87f44dd-xlhd2 1/1 Running 0 90m
jupyter-admin1 1/1 Running 0 15m
proxy-8685586d98-wklvw 1/1 Running 0 90m

起動すると、Jupyter Notebook のコンソールにリダイレクトされるので、記事に従い Notebookで提供されているPythonを実行していきます。(Pythonの実行に関しては、ここでは割愛)

ここまでで今回の趣旨の準備段階が完了です。以降からデプロイ後の構成でGPUを確認していきます。

確認内容

それぞれいくつかの観点で設定の確認を行っていきます。

 

■ nvidia-device-plugin の確認

nvidia-device-plugin の確認を行うと、いくつかのリソースが動作していることが分かります。

$ kubectl get all -n nvidia-device-plugin
NAME READY STATUS RESTARTS AGE
pod/nvidia-device-plugin-gpu-feature-discovery-pkfff 1/1 Running 0 75m
pod/nvidia-device-plugin-node-feature-discovery-master-568b4977kb2j 1/1 Running 0 76m
pod/nvidia-device-plugin-node-feature-discovery-worker-8gddp 1/1 Running 1 (76m ago) 76m
pod/nvidia-device-plugin-node-feature-discovery-worker-xf9mp 1/1 Running 1 (76m ago) 76m
pod/nvidia-device-plugin-qwm44 1/1 Running 0 75m

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/nvidia-device-plugin-node-feature-discovery-master ClusterIP 10.100.136.70 <none> 8080/TCP 76m

NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
daemonset.apps/nvidia-device-plugin 1 1 1 1 1 <none> 76m
daemonset.apps/nvidia-device-plugin-gpu-feature-discovery 1 1 1 1 1 <none> 76m
daemonset.apps/nvidia-device-plugin-node-feature-discovery-worker 2 2 2 2 2 <none>

NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/nvidia-device-plugin-node-feature-discovery-master 1/1 1 1 76m

NAME DESIRED CURRENT READY AGE
replicaset.apps/nvidia-device-plugin-node-feature-discovery-master-568b497868 1 1 1 76m

今回用いたdata-on-eks では、daemonsetであるnvidia-device-pluginと、付随の機能として備わっているgpu feature discovery(以下、gfd), node feature discovery(以下、nfd)が動作する形となっています。

gfdとnfdはそれぞれ以下の機能を持つものとなっています。

  • gfd
    • nfdの一部であり、特にGPU関連の機能を検出するために用いるツールとなる
  • nfd 
    • Kubernetesの各ノードのハードウェア機能やカーネル機能などの属性を自動的に検出し、それらの情報をラベルとしてKubernetes APIに公開するツールとなる

つまり、gfdとnfdがノードの機能を検出しラベルを付けることで、nvidia-device-pluginがそれらの情報を用いてGPUリソースを管理し適切なPodに割り当てることをしています。

 

またnvicia-device-pluginのログを確認してみると、GRPCを開始し、Kubeletにnvidia.com/gpuのデバイスプラグインを登録していることが分かります。

$ kubectl logs daemonset.apps/nvidia-device-plugin -n nvidia-device-plugin
I0629 07:28:42.381694 1 server.go:165] Starting GRPC server for 'nvidia.com/gpu'
I0629 07:28:42.382522 1 server.go:117] Starting to serve 'nvidia.com/gpu' on /var/lib/kubelet/device-plugins/nvidia-gpu.sock
I0629 07:28:42.386109 1 server.go:125] Registered device plugin for 'nvidia.com/gpu' with Kubelet

※今回は対象外としていますが、NVIDIA GPU Operator には、gfdとNVIDIA デバイス プラグインの両方が含まれます。

 

■ Podから使用しているGPUの見え方の確認

準備段階で立ち上げた、jupyter-xxx1の内容を見てみます。確かにPodからGPUのLimits, Requests でGPUを要求していることが確認出来ます。

$ kubectl describe pod jupyter-admin1 -n jupyterhub
Containers:
  notebook:
  ..(snip)..
    Limits:
      nvidia.com/gpu: 1
    Requests:
      memory: 5368709120
      nvidia.com/gpu: 1

 

■ EKSのノードからのGPUの見え方の確認

1. kubernetes のコマンドからGPUの見え方の確認

kubectl からノードの状態を確認してみます。

Capacity, Allocatable から nvidia.com/gpu: 1 が確認できます。実際にPodからGPUを消費すると、Allocated resourcesのnvidia.com/gpuの値も更新されるようになります。

$ kubectl get node
NAME STATUS ROLES AGE VERSION
ip-100-64-105-40.us-west-2.compute.internal Ready <none> 56m v1.29.3-eks-ae9a62a
ip-100-64-150-241.us-west-2.compute.internal Ready <none> 56m v1.29.3-eks-ae9a62a

# GPU搭載のノードをdescribeで出力
$ kubectl describe node ip-100-64-105-40.us-west-2.compute.internal
..(snip)..
Capacity:
  cpu: 4
  ephemeral-storage: 104845292Ki
  hugepages-1Gi: 0
  hugepages-2Mi: 0
  memory: 16069056Ki
  nvidia.com/gpu: 1
  pods: 29
Allocatable:
  cpu: 3920m
  ephemeral-storage: 95551679124
  hugepages-1Gi: 0
  hugepages-2Mi: 0
  memory: 15378880Ki
  nvidia.com/gpu: 1
  pods: 29
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource Requests Limits
  -------- -------- ------
  cpu 180m (4%) 0 (0%)
  memory 5494538240 (34%) 768Mi (5%)
  ephemeral-storage 0 (0%) 0 (0%)
  hugepages-1Gi 0 (0%) 0 (0%)
  hugepages-2Mi 0 (0%) 0 (0%)
  nvidia.com/gpu 1 1
..(snip)..

 

2. ノード上からnvidia-smi, deviceQueryコマンドでGPUの見え方の確認

AWS のセッションマネージャーから該当のノードにログインして確認してみます。

nvidia-smiコマンドでGPUの状態を出力すると以下のような情報が確認出来ます。この時、検証準備で行ったPythonアプリケーションを動作させているため、PythonのプロセスがGPUを使用していることが分かります。

また、deviceQueryコマンドを用いてGPUの情報が確認出来ます。

sh-4.2$ nvidia-smi
Sat Jun 29 08:57:36 2024
+---------------------------------------------------------------------------------------+
| NVIDIA-SMI 535.183.01 Driver Version: 535.183.01 CUDA Version: 12.2 |
|-----------------------------------------+----------------------+----------------------+
| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|=========================================+======================+======================|
| 0 Tesla T4 On | 00000000:00:1E.0 Off | 0 |
| N/A 33C P0 32W / 70W | 14819MiB / 15360MiB | 21% Default |
| | | N/A |
+-----------------------------------------+----------------------+----------------------+

+---------------------------------------------------------------------------------------+
| Processes: |
| GPU GI CI PID Type Process name GPU Memory |
| ID ID Usage |
|=======================================================================================|
| 0 N/A N/A 90855 C /opt/conda/bin/python3.10 14816MiB |
+---------------------------------------------------------------------------------------+


sh-4.2$ /usr/local/cuda-12.2/extras/demo_suite/deviceQuery
/usr/local/cuda-12.2/extras/demo_suite/deviceQuery Starting...
..(snip)..
Device 0: "Tesla T4"
  CUDA Driver Version / Runtime Version 12.2 / 12.2
  CUDA Capability Major/Minor version number: 7.5
  Total amount of global memory: 15102 MBytes (15835660288 bytes)
  (40) Multiprocessors, ( 64) CUDA Cores/MP: 2560 CUDA Cores
  GPU Max Clock rate: 1590 MHz (1.59 GHz)
  Memory Clock rate: 5001 Mhz
  Memory Bus Width: 256-bit
  L2 Cache Size: 4194304 bytes
  Maximum Texture Dimension Size (x,y,z) 1D=(131072), 2D=(131072, 65536), 3D=(16384, 16384, 16384)
  Maximum Layered 1D Texture Size, (num) layers 1D=(32768), 2048 layers
  Maximum Layered 2D Texture Size, (num) layers 2D=(32768, 32768), 2048 layers
  Total amount of constant memory: 65536 bytes
  Total amount of shared memory per block: 49152 bytes
  Total number of registers available per block: 65536
  Warp size: 32
  Maximum number of threads per multiprocessor: 1024
  Maximum number of threads per block: 1024
  Max dimension size of a thread block (x,y,z): (1024, 1024, 64)
  Max dimension size of a grid size (x,y,z): (2147483647, 65535, 65535)
  Maximum memory pitch: 2147483647 bytes
  Texture alignment: 512 bytes
  Concurrent copy and kernel execution: Yes with 3 copy engine(s)
  Run time limit on kernels: No
  Integrated GPU sharing Host Memory: No
  Support host page-locked memory mapping: Yes
  Alignment requirement for Surfaces: Yes
  Device has ECC support: Enabled
  Device supports Unified Addressing (UVA): Yes
  Device supports Compute Preemption: Yes
  Supports Cooperative Kernel Launch: Yes
  Supports MultiDevice Co-op Kernel Launch: Yes
  Device PCI Domain ID / Bus ID / location ID: 0 / 0 / 30
  Compute Mode:
    < Default (multiple host threads can use ::cudaSetDevice() with device simultaneously) >

deviceQuery, CUDA Driver = CUDART, CUDA Driver Version = 12.2, CUDA Runtime Version = 12.2, NumDevs = 1, Device0 = Tesla T4
Result = PASS

 

■ ノードグループからGPUの状態を確認

AWS コンソールからGPUをどう使用しているか確認してみます。

結論から言うと、AWSコンソールからはラベルで情報の確認が出来る程度でした。CUDAに関する情報、GPUに関する情報が見える程度です。

GPUへの共有アクセス方法を試す

デプロイ後の構成で、GPUへの共有アクセス方法も出来るか追加で試してみました。

GPUへの共有アクセス方法の設定は、以下の記事の内容を参考にして行っていきます。

 

■ Time Slicing の設定

今回は、前回の記事で紹介した方法の中からTime Slicingを試してみたいと思います。

今回の nvidia-device-plugin はhelmでデプロイされているため、helm をアップグレードする方法で試します。

まずリポジトリを追加します。

$ helm repo add nvdp https://nvidia.github.io/k8s-device-plugin
$ helm repo update

Time Slicingを有効にする前のノードで利用できるGPUの数を確認します。"nvidia.com/gpu": "1"とまだ利用出来る数は1つです。

$ kubectl get nodes -o json | jq -r '.items[] | select(.status.capacity."nvidia.com/gpu" != null) | {name: .metadata.name, capacity: .status.capacity}'
{
  "name": "ip-xxx.us-west-2.compute.internal",
  "capacity": {
  "cpu": "4",
  "ephemeral-storage": "104845292Ki",
  "hugepages-1Gi": "0",
  "hugepages-2Mi": "0",
  "memory": "16069060Ki",
  "nvidia.com/gpu": "1",
  "pods": "29"
  }
}

nvidia-device-plugin の helmに渡すvalues.yamlとConfigMap の設定を行い、helm upgrade を行います。

$ cat nvidia-device-plugin-helm-values.yaml
gfd:
  enabled: true
nfd:
  worker:
    tolerations:
    - key: nvidia.com/gpu
      operator: Exists
      effect: NoSchedule
    - operator: "Exists"

$ cat ndp_helm_upgrade.sh
helm upgrade -i nvidia-device-plugin nvdp/nvidia-device-plugin \
  --version=0.14.5 \
  --namespace nvidia-device-plugin \
  --values nvidia-device-plugin-helm-values.yaml \
  --set config.name=time-slicing-config

$ ./ndp_helm_upgrade.sh

Time Slicing を行う設定をConfigMapとして投入します。今回はレプリカ数を4にして設定しています。

$ cat time-slicing-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: time-slicing-config
  namespace: nvidia-device-plugin
data:
  any: |-
    version: v1
    flags:
      migStrategy: none
    sharing:
      timeSlicing:
        renameByDefault: false
        failRequestsGreaterThanOne: false
        resources:
          - name: nvidia.com/gpu
            replicas: 4

$ kubectl apply -f time-slicing-config.yaml

ノードで利用できるGPUの数を確認します。"nvidia.com/gpu": "4"と利用出来る数が増加したことを確認できます。

$ kubectl get nodes -o json | jq -r '.items[] | select(.status.capacity."nvidia.com/gpu" != null) | {name: .metadata.name, capacity: .status.capacity}'
{
  "name": "ip-xxx.us-west-2.compute.internal",
  "capacity": {
  "cpu": "4",
  "ephemeral-storage": "104845292Ki",
  "hugepages-1Gi": "0",
  "hugepages-2Mi": "0",
  "memory": "16069060Ki",
  "nvidia.com/gpu": "4",
  "pods": "29"
  }
}

 

■ Time Slicing の確認

それでは、サンプルのアプリケーションを実行して、増加させたGPUのレプリカ数をどう使用するのか確認してみます。

サンプルのアプリケーションは、参考にした記事にあるeks-gpu-sharing-demoを使用しています。今回はアプリケーションのレプリカ数は2に設定して適用しています。

$ kubectl create ns gpu-demonamespace/gpu-demo created
$ kubectl apply -f example-train.yaml
$ kubectl get pods -n gpu-demo
NAME READY STATUS RESTARTS AGE
tensorflow-cifar10-deployment-5df5f55756-k8vgp 1/1 Running 2 (3m50s ago) 6m57s
tensorflow-cifar10-deployment-5df5f55756-l4wfm 1/1 Running 2 (3m50s ago) 6m57s

sh-4.2$ nvidia-smi
Fri Jun 28 07:53:07 2024
+---------------------------------------------------------------------------------------+
| NVIDIA-SMI 535.183.01 Driver Version: 535.183.01 CUDA Version: 12.2 |
|-----------------------------------------+----------------------+----------------------+
| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|=========================================+======================+======================|
| 0 Tesla T4 On | 00000000:00:1E.0 Off | 0 |
| N/A 39C P0 43W / 70W | 14903MiB / 15360MiB | 16% Default |
| | | N/A |
+-----------------------------------------+----------------------+----------------------+

+---------------------------------------------------------------------------------------+
| Processes: |
| GPU GI CI PID Type Process name GPU Memory |
| ID ID Usage |
|=======================================================================================|
| 0 N/A N/A 77803 C python 14074MiB |
| 0 N/A N/A 78014 C python 826MiB |
+---------------------------------------------------------------------------------------+

nvidia-smiコマンドで確認すると確かに、pythonのプロセスが2つ動作していることが確認出来ています。

Time Slicingを使用して、GPUへの共有アクセスを提供することを簡単に確認しました。

 

まとめ

今回は、EKS上でGPUを扱う生成AIソリューションのデプロイを試し、実際にGPUがどう使われてどう見えるのかをまとめてみました。

実際に検証してみることで、机上ベースより詳細な情報を確認することが出来ました。

また発展として、MLOpsなどを効率的に回していくための様々なツール群を調査してみるのも面白いと考えています。

本書の記載が読者のお役に立てれば幸いです。

ご覧いただきありがとうございます! この投稿はお役に立ちましたか?

役に立った 役に立たなかった

0人がこの投稿は役に立ったと言っています。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です