GPUクラウドサービス「RunPod」を試す
公式
安くて使いやすいという感じで、ちらほら聞くので。サーバレスっぽい。
公式ドキュメント
Get startedに従って進める。
アカウント作成&ファンド購入
アカウントを作成
利用規約に同意
ダッシュボードが表示される。右上にファンド、いわゆるクレジットが表示されている。どうやら$0.10だけもらえるみたい。で、これをクリックするか、左の「Billing」をクリック。
ここでファンドの購入を行う。クレジットカード以外に暗号通貨でも支払えるみたい。
とりあえず$10にしてみる。
Link(Stripe)の支払い画面が出てくるので支払いを行う。支払いが完了するとダッシュボードに戻るので、一応「Billing」を見てみる。
追加されている。
これで準備OK。
リソースの種類
で、ここからリソースを作成するのだが、どうやらリソースには2種類ある様子。
o3-miniに上記の内容から比較表にまとめてもらった
項目 | Serverless | Pod |
---|---|---|
アーキテクチャ | リクエストごとにオンデマンドで起動・終了するサーバーレス環境。 | 常時稼働するコンテナインスタンス。 |
利用用途 | ・AI推論(大量の短時間リクエスト対応) ・AI学習(最大12時間程度のタスク) ・その他オンデマンドな計算タスク |
・長期稼働が必要なアプリケーション ・高度なカスタマイズが必要な環境 |
スケーリング | オートスケール (ワーカーが0~100まで動的に割当可能) |
オートスケールなし。 必要に応じて複数のPodを手動or設定で管理 |
課金モデル | 利用時間(秒単位)に応じた課金 | インスタンス(Pod)の稼働時間や割当リソースに応じた課金 |
デプロイ方法 | ・Quick Deploy(既製のエンドポイント利用) ・Handler Functions(自前の関数実行) ・vLLM Endpoint(Hugging Face モデル指定) |
Docker イメージを利用して、カスタム設定(起動コマンド、環境変数、ポート公開等)で起動 |
起動時間 | 短いコールドスタート(例:Stable Diffusionでは3秒のコールドスタート+5秒実行) | コンテナの起動時間は一般的なDockerコンテナ起動時間となる |
ストレージ | 主に一時的な計算環境として利用され、永続ストレージの概念は基本的に無し(必要に応じたWebhook等) | ・コンテナボリューム(OSや一時ストレージ) ・ディスクボリューム(永続ストレージ) ・ネットワークストレージ(Pod間で共有可能) |
モニタリング・デバッグ | GPU、CPU、メモリ等の各種メトリクス、ログ、SSHやWebターミナルを利用可能 | Podごとに環境設定やアクセス方法が異なり、標準的なコンテナ管理・デバッグツールが利用可能 |
AWSでいうと、ServerlessがLambdaで、Podは・・・なんだろう、コンテナには違いないんだけどEC2っぽさがあるというか、常時稼働しているってところが一番の違いかなという気がした。
Serverless
概要
RunPodは、AI推論、トレーニング、一般的な計算のためのServerless GPUおよびCPUコンピューティングを提供しており、ユーザーは使用したコンピューティングリソースに対して秒単位で支払いを行うことができます。
この柔軟なプラットフォームは動的にスケールするように設計されており、最小規模から最大規模までのAIワークロードの計算ニーズに応えます。以下の方法を使用できます:
- Quick Deploy: 人気のあるAIモデルの事前構築されたカスタムエンドポイントを迅速にデプロイします。
- Handler Functions: 独自の関数を持ち込み、クラウド上で実行します。
- vLLM Endpoint: Hugging Faceのモデルを指定してクラウド上で実行します。
なぜRunPod Serverlessを選ぶのか?
以下の理由から、RunPod Serverlessインスタンスを選択すべきです:
- AI推論: 毎日数百万の推論リクエストを処理し、数十億のリクエストにもスケールできるため、機械学習の推論タスクに最適なソリューションとなります。これにより、ユーザーは低コストで機械学習の推論をスケールすることが可能です。
- AIトレーニング: 最大12時間かかる機械学習のトレーニングタスクにも対応します。リクエストごとにGPUを起動し、タスクが完了したらスケールダウンするため、AIトレーニングのニーズに柔軟に対応できます。
- オートスケール: Secure Cloudプラットフォーム上でワーカーを0から100まで動的にスケールでき、これは高可用性でグローバルに分散されています。これにより、ユーザーは必要な時に正確な計算リソースを得ることができます。
- コンテナサポート: 任意のDockerコンテナをRunPodに持ち込むことができます。パブリックおよびプライベートなイメージリポジトリの両方がサポートされており、ユーザーは自分好みに環境を構成することが可能です。
- 3秒のコールドスタート: コールドスタート時間を短縮するために、RunPodはワーカーを事前にウォームアップします。総開始時間はランタイムによって異なりますが、Stable Diffusionの場合、総開始時間は3秒のコールドスタートと5秒のランタイムとなります。
- メトリクスとデバッグ: デバッグにおいて透明性は非常に重要です。RunPodはGPU、CPU、メモリなどのメトリクスへのアクセスを提供し、ユーザーが自分の計算ワークロードを理解するのに役立ちます。ログやSSHを通じたワーカーの完全なデバッグ機能も利用可能で、ウェブターミナルによりさらにアクセスしやすくなっています。
- Webhook: ユーザーはWebhookを活用して、リクエスト完了と同時にデータ出力を受け取ることができます。データはユーザーのWebhook APIに直接プッシュされ、結果に即座にアクセスできます。
RunPod Serverlessは、単にAI推論およびトレーニングのためのものではありません。
さまざまなその他のユースケースにも最適です。
レンダリング、分子動力学、またはその他の計算タスクなど、ニーズに合ったタスクに自由にご利用ください。RunPod Serverlessとのやり取り方法
RunPodはServerless Podとやり取りするためのEndpoint Idを生成します。
Endpoint IdをEndpoint URLに渡し、操作を指定してください。このEndpoint URLは次のようになります:
https://api.runpod.ai/v2/{endpoint_id}/{operation}
api.runpod.ai
: RunPodにアクセスするための基本URL。v2
: APIのバージョン。endpoint_id
: Serverless EndpointのID。operation
: Serverless Endpointで実行する操作。
- 有効なオプション:
run
|runsync
|status
|cancel
|health
|purge-queue
詳細については、Invoke jobs を参照してください。
とりあえずServerlessから触ってみる。ServerlessのGet startedに従って進める
まずRunPodのAPIキーを作成。
「Account」→「Settings」→「API Keys」で「Create API Key」
APIキー作成のダイアログが表示される。下のタブでAPIキーの権限を設定できる。「RESTRICTED」だと細かく設定できる様子。
まだちょっと良くわかっていないので、今回は一旦「ALL」にする。APIキー名を入力して「Create」。
APIキーが作成された。作成されたAPIキーはこのタイミングでしか表示されないようなので、適切なところに保管のこと。
次にサーバレスのアプリケーションのコードを書いていく。今回はローカルのMacでやる。
作業ディレクトリ作成
mkdir runpod-test && cd runpod-test
Python仮想環境を作成。自分はmiseを使うが、適宜。
mise use python@3.12
cat << 'EOS' >> mise.toml
[env]
_.python.venv = { path = ".venv", create = true }
EOS
RunPodのSDKをインストール
pip install runpod
ハンドラファイルrp_handler.py
を作成する。
import runpod
import time
def handler(event):
input = event['input']
instruction = input.get('instruction')
seconds = input.get('seconds', 0)
# タスクのプレースホルダー: 必要に応じて書き換える
time.sleep(seconds)
result = "画像を作成しました!"
return result
if __name__ == '__main__':
runpod.serverless.start({'handler': handler})
次に、テスト用の入力ファイルtest_input.json
を作成。
{
"input": {
"instruction": "なにか画像を生成して。",
"seconds": 15
}
}
一旦ローカルで実行。テスト用の入力ファイルを使って実際に実行してくれる。
python rp_handler.py
--- Starting Serverless Worker | Version 1.7.7 ---
INFO | Using test_input.json as job input.
DEBUG | Retrieved local job: {'input': {'instruction': 'なにか画像を生成して。', 'seconds': 15}, 'id': 'local_test'}
INFO | local_test | Started.
DEBUG | local_test | Handler output: 作成しました!
DEBUG | local_test | run_job return: {'output': '作成しました!'}
INFO | Job local_test completed successfully.
INFO | Job result: {'output': '作成しました!'}
INFO | Local testing complete, exiting.
これをDockerイメージにする。Dockerfileを作成。
FROM python:3.10-slim
WORKDIR /
RUN pip install --no-cache-dir runpod
COPY rp_handler.py /
# コンテナを起動
CMD ["python3", "-u", "rp_handler.py"]
ビルド。MacなどAMD64以外のアーキテクチャでビルドする場合は--platform linux/amd64
が必要。
docker build --platform linux/amd64 --tag kun432/runpod-test:0.1 .
Docker Hubにpush。
docker push kun432/runpod-test:0.1
これをRunPodにデプロイする。RunPodのダッシュボードから「Serverless」→「New Endpoint」
「Docker Image」を選択。
Dockerイメージ名を入力。今回はDocker Hubにパブリックなイメージとしてpushしているのでクレデンシャル等は不要だと思うし、イメージ名だけでOK。テンプレートはよく使うデプロイ設定をあらかじめ登録しておくと手間が省けるらしいが、今回はそのまま進める。
エンドポイントの設定画面が表示される。ここでGPUを選んだりワーカーの挙動などを設定できる様子。とりあえず以下だけ設定、その他はデフォルトにして進めてみる。ちなみに各設定項目についてはここに記載がある。
- エンドポイント名
- GPU: 16GBを選択
- 最大ワーカー数: 1
エンドポイントが作成された。エンドポイントの状態・メトリクス・ログなどはここで見れるみたい。あと、CLI等でのアクセスの例なども記載されている。
しばらくすると以下が「Initializing」から「Ready」になったので、これでアクセスができるように鳴ったと思われる。
curlでアクセスしてみる。同期の場合はrunsync
を使う。
RUNPOD_API_KEY="XXXXXXXXXX"
RUNPOD_ENDPOINT_ID="XXXXXXXXXX"
curl -X POST "https://api.runpod.ai/v2/$RUNPOD_ENDPOINT_ID/runsync" \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $RUNPOD_API_KEY" \
-d '{"input":{"instruction":"画像を作成して。","seconds":5 }}' \
| jq -r .
{
"delayTime": 1311,
"executionTime": 5057,
"id": "sync-a0b2b048-8583-4fae-b38c-25a6455e8d48-e1",
"output": "作成しました!",
"status": "COMPLETED",
"workerId": "1yxjyk2n9nxhid"
}
run
は非同期になる。
curl -X POST "https://api.runpod.ai/v2/$RUNPOD_ENDPOINT_ID/run" \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $RUNPOD_API_KEY" \
-d '{"input":{"instruction":"画像を作成して。","seconds":15 }}' \
| jq -r .
キューイングされ、キューIDが返される。
{
"id": "4a67d404-c07c-4d8d-86b8-77a231790d4c-e1",
"status": "IN_QUEUE"
}
キューIDのステータスを確認。
curl -X GET https://api.runpod.ai/v2/$RUNPOD_ENDPOINT_ID/status/4a67d404-c07c-4d8d-86b8-77a231790d4c-e1 \
-H "Authorization: Bearer $RUNPOD_API_KEY" \
| jq -r .
「実行中」となっている
{
"delayTime": 1034,
"id": "4a67d404-c07c-4d8d-86b8-77a231790d4c-e1",
"status": "IN_PROGRESS",
"workerId": "1yxjyk2n9nxhid"
}
しばらくして再度リクエストするとステータスが「完了」になり、結果が返ってきているのがわかる。
{
"delayTime": 1034,
"executionTime": 15178,
"id": "4a67d404-c07c-4d8d-86b8-77a231790d4c-e1",
"output": "作成しました!",
"status": "COMPLETED",
"workerId": "1yxjyk2n9nxhid"
}
ダッシュボードの「Request」タブでもリクエストのテストができる。非同期の場合は定期的に結果の確認もしてくれる。
エンドポイントが不要になったらここで削除できる。
GitHubレポジトリからデプロイすることもできる。
上で作成したコードのディレクトリを元にレポジトリを作成する。今はこうなっている。
tree -a -L 1
.
├── .venv
├── Dockerfile
├── mise.toml
├── rp_handler.py
└── test_input.json
2 directories, 4 files
.gitignoreを追加する。各種OSとPythonの環境で余計なファイルは無視する。Python仮想環境に使用しているmiseのファイルも不要なので追加。
wget https://www.toptal.com/developers/gitignore/api/python,windows,macos,linux -O .gitignore
echo "mise.toml" >> .gitignore
作成した.gitignoreの内容
# Created by https://www.toptal.com/developers/gitignore/api/python,windows,macos,linux
# Edit at https://www.toptal.com/developers/gitignore?templates=python,windows,macos,linux
### Linux ###
*~
# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### macOS Patch ###
# iCloud generated files
*.icloud
### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
### Python Patch ###
# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
poetry.toml
# ruff
.ruff_cache/
# LSP config files
pyrightconfig.json
### Windows ###
# Windows thumbnail cache files
Thumbs.db
Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
# End of https://www.toptal.com/developers/gitignore/api/python,windows,macos,linux
mise.toml
gitで初期化
git init
登録対象のファイル一覧
git status
On branch main
No commits yet
Untracked files:
(use "git add <file>..." to include in what will be committed)
.gitignore
Dockerfile
rp_handler.py
test_input.json
コミット
git add .
git commit -m "initial commit"
レポジトリを作成してpush
gh repo create runpod-test --source=. --public --push
レポジトリができた。
RunPodとGitHubの認証連携を行う。RunPodのダッシュボードで「Account」→「Settings」→「Connections」にあるGitHubのところの「Connect」をクリック。
GitHubでRunPodからのアクセスを許可するか?を聞かれる。以下のどちらかを選択して承認する。
-
All repositories(すべてのレポジトリ)
- 現在および将来のすべてのレポジトリに対して読み取り権を与える
- パブリックなレポジトリも含まれる
-
Only select repositories(選択したレポジトリのみ)
- 選択したレポジトリにのみ読み取り権を与える
- パブリックなレポジトリも含まれる
今回は先ほど作成したレポジトリだけを承認した。
RunPod側でもアカウントがリンクされている。
Serverlessで新しいエンドポイントを作成するところで「GitHub Repo」を選択。
指定したレポジトリ(+パブリックなレポジトリも含む)の一覧が表示されるので、先程のレポジトリを選択。
ブランチとDockerfileのパスを指定して進める
エンドポイントの設定画面では上でやったのと同じように実施。レポジトリからだとエンドポイント名が自動で入るね。
こんな感じで、レポジトリからクローンしてワーカーがビルドされる。
ビルドが無事完了して、ワーカーの状態も「Ready」になった。
ダッシュボードのテストでも動作を確認。レポジトリからのデプロイが確認できた。
ちなみにコミットで更新したらどうなるのか?
(snip)
result = "作成しました!"
(snip)
(snip)
result = "画像を作成したよー!"
(snip)
git add .
git commit -m "更新"
git push
変更をpushすると、以下のようにビルドが実行される。
ビルドが完了するとワーカーに新しく反映される。
テストでも変更内容が反映されているのがわかる。
「Releases」タブで変更の履歴が見える。
なお、この場合もコンテナイメージは作成されているが、RunPod内のコンテナレジストリで管理されているらしい。ただし、それを確認するすべはなさそう。
以下にレポジトリのディレクトリ構成やGitHub Actionsを使ったCIなどについても記載がある。
ワーカーのテンプレートレポジトリも用意されている。
Serverless の基本的なところはこんな感じ。その他については以下。
あらかじめ用意されたモデルの場合はQuick Deploysを使えば少ない設定でデプロイできる。
vLLMワーカーを使うと、HuggingFaceのモデルのほとんどをモデル名で指定するだけで、OpenAI互換APIとして動かすことができるらしい。
ハンドラーについて
その他Serverlessを使ったアプリ開発のいろいろ
エンドポイントへのリクエストについて
Pod
概要
Podはコンテナインスタンスを実行しています。
Docker Hub、GitHub Container Registry、Amazon Elastic Container Registry、またはその他の互換性のあるレジストリなど、コンテナレジストリからインスタンスをプルできます。Podのコンポーネントと構成の理解
Podは、動的に生成された識別子が割り当てられた、ハードウェアにアクセスするためにあなたが作成したサーバーコンテナです。
たとえば、2s56cp0pof1rmt
はそのインスタンスを識別します。Podは、オペレーティングシステムと一時ストレージを含むコンテナボリューム、永続ストレージ用のディスクボリューム、Ubuntu Linuxコンテナ、割り当てられたvCPUとシステムRAM、特定のワークロード向けのオプションのGPUまたはCPU、簡単なソフトウェアアクセスのための事前構成済みテンプレート、およびウェブアクセス用のプロキシ接続から構成されます。
各Podは以下のさまざまなコンポーネントを包含しています:
- オペレーティングシステムと一時ストレージを収容するコンテナボリューム。
- このストレージは揮発性であり、Podが停止または再起動されると失われます。
- ハードディスクのように、Podのリース期間中保持される永続ストレージ用のディスクボリューム。
- このストレージは永続的であり、Podが停止または再起動されても利用可能です。
- マシン間で移動可能なボリュームに似たネットワークストレージ。
- ネットワークストレージを使用する場合、Podのみを削除できます。
- Ubuntu Linuxコンテナ。これは、Ubuntu上で実行可能なほぼすべてのソフトウェアを実行する能力を持ちます。
- コンテナおよびその実行するプロセスに専用の割り当てられたvCPUとシステムRAM。
- CUDAやAI/MLタスクなど、特定のワークロード向けに最適化されたオプションのGPUまたはCPU。ただし、コンテナを起動するために必須ではありません。
- Pod作成時にソフトウェアと設定のインストールを自動化する事前構成済みテンプレートで、各種パッケージへワンクリックで簡単にアクセスできます。
- コンテナ上の任意のオープンポートへの接続を可能にするウェブアクセス用のプロキシ接続。
- たとえば、
https://[pod-id]-[port number].proxy.runpod.net
またはhttps://2s56cp0pof1rmt-7860.proxy.runpod.net/
など。はじめるには、Podの選択を参照し、その後Podの管理の手順を確認してください。
詳しく知る
テンプレートから始めることで、すぐに実行中のPodにアクセスできます。さらにカスタマイズする場合は、以下の項目を構成できます:
- GPUの種類と数量
- システムディスクサイズ
- スタートコマンド
- 環境変数
- HTTP/TCPポートの公開
- 永続ストレージオプション
ドキュメントだとこの辺に従って進めれば良さそう。
とりあえずやってみる。
ダッシュボードから「Pods」→「Deploy」
Podのインスタンスタイプを選択。一番安いのは以下っぽいので、これを選択。
- RTX 2000 Ada
- 料金: $0.28/時間
- VRAM: 16GB
- RAM: 31GB
- vCPU: 6
で少し上の方にあるのが気になったので確認しておく。
- RunPodには2つのクラウドがある
- Secure Cloud
- RunPodのパートナー企業が運営するデータセンター上で稼働
- 冗長性・セキュリティ・レスポンスタイム・障害対応等について信頼性が高い
- 機密性を求める企業向けにはこちら
- Community Cloud
- RunPodの分散プラットフォーム上で、(おそらく)コンピューティング環境を提供可能な個人と利用者をP2Pでつなぐものと思われる。
- RunPodが招待し審査をパスした提供者のみが参加可能。
- Secure Cloudと比較すると信頼性は高くないかもしれないが、一定の品質と手頃な価格で利用できる(らしい)
- Secure Cloud
-
グローバルネットワーキング
- アカウント内の複数のPod間の通信を可能にする仮想ネットワークを有効にできる
- 各PodにはプライベートIPが割り当てられ、DNSで相互に参照可能。
- 一部のインスタンスタイプ・一部のリージョンでのみ利用可能
- ネットワークボリューム
- Podが利用可能なストレージには複数のタイプがある
- コンテナボリューム
- PodのOSを保存、また一時的なストレージとして使える
- Podにローカル接続されているため高速
- Podが停止・再起動で失われる
- 容量は選択したインスタンスタイプで決まる
- ディスクボリューム
- Podが利用している間保持される永続ストレージ
- Podが停止・再起動しても失われない
- 容量を選択可能
- 永続性はあるが。コンテナボリュームよりも読み取り・書き込みでやや性能が下がる
- ネットワークストレージ
- ネットワークで共有可能な永続ストレージ
- 複数のPodで共有・別のPodに付け替え等が可能
- Podとストレージは別管理になるため、ストレージを残しつつPodだけ削除することが可能
- コンテナボリューム
- Podが利用可能なストレージには複数のタイプがある
- リージョン
- 複数のリージョンが提供されている。
- 日本から近いのは
US-CA-1
かUS-OR-1
かな?
- 複数のリージョンが提供されている。
上記の条件がすべて使えるというわけではなくて、これに対応したインスタンスを指定できる、という感じね。
で、インスタンスタイプを選択すると、デプロイの設定になる。ここでは、
- Podの名前
- Podのテンプレート
- 各種ライブラリの複数バージョンごと、などで、あらかじめ用意された多数のテンプレートから選択することが可能
- 自分でテンプレートを作成して、それを利用することも可能
- GPU数
- インスタンスの利用料金に関する設定
- オンデマンド
- 期間契約(一定期間分の支払いで割引がある)
- スポット(予期せず停止される可能性があるため割引がある)
- その他
- ボリューム暗号化
- SSHアクセス(事前に鍵の登録が別途必要)
- Jupyter Notebookの起動有無
を指定して、Podを起動できる。
Pod名を入力、今回はオンデマンドでやってみる。それ以外はデフォルトのままにした。
Podが起動した。接続してみる。
Podへの接続方法が複数表示される
- RunPodが提供するプロキシ経由で、JupyterLabにHTTP(S)で接続
- ダッシュボードで動作するWebターミナルで接続
- ローカルからSSHで接続(事前に鍵の登録が必要)
とりあえずJupyter Labで開いてみる。
GPUが確認できた。
次にWebターミナル。「Start」をクリックする。
この時、最初はなぜかうまくいかなかったのだが、何度か試しているうち以下のように起動した。ターミナルを開いてみる。
こんな感じで使える。ただレスポンスはあまり良くないので、ターミナルを使うならば、鍵を登録しておいてローカルから接続したほうがいいとは思う。
なお、ドキュメントには
Podのテンプレートによっては、Webターミナルに接続する機能が提供されます。
とあるので対応していないテンプレートもあるのかもしれない。
Podへの接続については以下のドキュメントを参照。
Podが不要になったら、停止の上、削除。
気になったので調べてみたけど、一番安いRTX 2000 Adaが使えるリージョンは、
- EUR-IS-1(多分アイスランド)
- EU-RO-1(多分ルーマニア)
だけだった。ただ、今後も同じかどうかはわからない。需要等によって変わる可能性は(価格も含めて)ありそう。
トレーニング等で使う場合にはあまり気にしなくて良いかもだけど、アプリケーション実行環境として使う場合、選択したインスタンスがどこのリージョンで動作しているか、によってはレイテンシーが出てくる可能性があるかもしれない。特に日本からアクセスする場合には。アジアのリージョンはなさそうだし。
テンプレート
テンプレートは以下で構成される。
-コンテナインスタンス内で使用するDockerイメージ
- 上記に各種設定を追加
Podのところでやったけども、Podのインスタンスタイプはコンテナインスタンスとしてのハードウェア等を定義したもので、それに対して、Pod内のOSやソフトウェアなどをあらかじめ定義したものがテンプレートにあたるのだろうと思う。
デプロイのたびに必要な設定やセットアップなどは、あらかじめテンプレートにしておくことで、繰り返しデプロイする場合などに効率的に使える。
テンプレートにはいくつかの種類がある。
-
マネージドテンプレート
- オフィシャルテンプレートとも言う。
- RunPodが公式に作成してメンテしているもの。
-
カスタムテンプレート
- ユーザが自分で作ったテンプレート。
- テンプレートを公開することもできるし、自分だけでプライベートに使うこともできる。
- 公開・共有されているのがコミュニティテンプレート
- 自分だけのプライベートなものがプライベートテンプレート
ここではカスタムなテンプレートを作成していく。
まずアプリケーションのDockerイメージを作成する。今回はFastAPIの「Hello、World」を使う。
作業ディレクトリを作成
mkdir runpod-sample-fastapi-docker && cd runpod-sample-fastapi-docker
Python仮想環境を作成。自分はmiseを使うが、適宜。
mise use python@3.12
cat << 'EOS' >> mise.toml
[env]
_.python.venv = { path = ".venv", create = true }
EOS
パッケージインストール
pip install fastapi uvicorn
スクリプトを作成
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def root():
return {"message": "こんにちは!RunPod!"}
テストで実行して動作することを確認
uvicorn main:app --host 0.0.0.0 --port 8000
curl http://[上記を起動したマシンのIPアドレス]:8000/
Dockerfileを作成
FROM python:3.12-slim
WORKDIR /app
RUN pip install --no-cache-dir fastapi uvicorn
COPY main.py .
EXPOSE 8000
CMD ["uvicorn","main:app","--host","0.0.0.0","--port","8000"]
ビルド
DOCKER_BUILDKIT=1 docker build \
--progress=plain \
--platform linux/amd64 \
-t kun432/fastapi-hello-world:0.1 \
.
Docker Hubにpush
docker push kun432/fastapi-hello-world:0.1
ではテンプレートを作成する。「Templates」→「New Template」をクリック。
テンプレートの設定を行う。
- テンプレート名を適宜入力。
- テンプレートの種類は「Pod」を選択。なお、「Serverless」を選ぶとServerless向けのテンプレートも作れる。
- 「Visibility」で、コミュニティテンプレートにするかプライベートテンプレートにするかを指定できる。今回は「Private」
- コンテナイメージにDocker Hubのイメージ名を入力。今回もパブリックイメージなのでクレデンシャルは不要。
- 一番下でコンテナにアクセスするためのポートを指定。コンテナは8000番で待ち受けているのでそれで。
その他は適宜設定。これでテンプレートが登録される。
このテンプレートを使用してPodを起動する。
インスタンスタイプを指定後、テンプレートを変更。
先ほど作成したテンプレートが表示されているので、これを選択。
テンプレートが変更されたのを確認して、Pod名を入力、その他適宜選択してPodを作成。
Podが作成され稼働したら、接続してみる。
アクセスできた。
なお、PodのURLは
https://[PodのID]-[ポート番号].proxy.runpod.net
となる。curlでもアクセスしてみる。
curl https://XXXXXXXXXX-8000.proxy.runpod.net/ | jq -r .
{
"message": "こんにちは!RunPod!"
}
ドキュメントを色々眺めていて気になったところ。
- Podへのアクセスには認証をかけることがどうやらできなさそう。
- 一応、PodのIDはランダムな文字列だし、ポートも適宜変更すれば、ある程度当たりをつけにくくすることはできなくはないけども・・・
- Podへの最大接続時間は100秒。
- RunPodのプロキシはCloudlareで動作しているらしく、ここの上限が100秒らしい。
- 100秒以内に応答が帰らないと524で接続が閉じられる。
あとポート周りの設定はいろいろあるみたい
SDK
ざっと見たところ、
- RunPod上のServerlessワーカーやPod,エンドポイントなどの操作を行うことができる。これらは主にGraphQLを使う
- エンドポイントに接続するクライアントSDKは複数の言語に対応している
という感じっぽい。
ふと思ったことなのだけど、プライベートなDockerイメージのレジストリを使う場合は「Settings」→「Container Registry Auth」にクレデンシャルを登録するのだが、
設定画面はこんな感じ。
ピンポイントな話だけど、AWS ECRの場合、認証トークンが定期的(12時間)に書き換わる前提になるので、この画面だと無理だよなー、と思って調べてみたら、GraphQLでトークンを定期的に書き換えることができるみたい。
GraphQLが運用管理に使えるのは結構助かるケースがありそう。
まとめ
RunPodのユースケースでありそうなのは、
- GPU高い、なるべく安く使いたい
- Colaboratoryだと自由度が低いし、定額じゃなくてスポットで使いたい
とかになるのかな?
個人的にはServerlessは、LLMのエンドポイント作るのにとても使い勝手が良さそうな印象を持った。PodはColaboratory的使い方にはいいと思うんだけど、常駐するAPIサーバとして使うには認証などでひと手間必要かなぁというところ。
でも、主要クラウドサービスに対して価格的には競争力ある印象なので、お手軽に使えそうである。
他にもいくつかありそう。
参考)
Active (Min) Workers
active workersを1人以上に設定すると、「常時起動」のワーカーが確保され、コールドスタートによる遅延なしにジョブリクエストに応答できるようになります。
デフォルト:0
LambdaでいうところのProvisioned Concurrencyみたいなものかな。30%割引とあるけど、コスト計算的にはどうなるんだろうか?Serverlessの一番やすいものは $0.00016/秒のようなので、それから考えると
- active workers: 1で起動したワーカー
- 1時間フル稼働すると仮定
- $0.00016*3600秒=$0.576/時間
- 30%ディスカウント
- $0.576 * 0.7 = $0.4032
- 1時間フル稼働すると仮定
という感じかな。これがミニマムで、同時リクエストがあった場合には別のワーカーが処理するので、そちらは起動秒数分で加算される、って感じかなぁ・・・
やっぱりPodに認証機能がほしいところ・・・