小規模日本語言語モデルをCloud Runにデプロイする実践ガイド
Descarty Team
2025-03-19

はじめに
この記事では、小規模日本語言語モデル「TinySwallow 1.5B」をCloud Runにデプロイして、コスト効率の良い実用的な LLM アプリケーションを構築する方法を詳細に解説します。大規模言語モデルの運用ではGPUコストが大きな課題となりますが、Cloud Runのサーバーレスアーキテクチャを活用することで、トラフィックがない時間帯のコストを最小限に抑えられます。
すぐにコードを確認したい方はこちら:
小規模言語モデルの実力と選定理由
最近の小規模言語モデルは驚くほど高性能で、特定のタスクにおいては大規模モデルに迫る実用性を持っています。本実装では「TinySwallow 1.5B」を採用しています。TinySwallow は Sakana AI が開発した小規模日本語モデルです。同様のアプローチはGemma-3やMistral-7Bなど他の小規模モデルでも応用可能ですが、日本語特化モデルとしてTinySwallowは非常に魅力的な選択肢です。
システムアーキテクチャの詳細
本システムは以下の2つの主要コンポーネントからなる分散アーキテクチャを採用しています:
-
フロントエンド/バックエンドサーバー (Go)
- ユーザーインターフェース提供
- API処理
- LLMサービスとの通信
-
LLMサービス (Ollama + TinySwallow)
- 言語モデルの実行
- 推論処理
この分離アーキテクチャには以下のメリットがあります:
- スケーラビリティ: 各コンポーネントが独立してスケールアウト可能
- リソース最適化: フロントエンドは最小リソースで運用し、LLM部分だけ必要に応じて高スペックインスタンスを割り当て
- 開発の柔軟性: モデル部分のみの更新や入れ替えが容易
フロントエンド技術スタック
フロントエンドは以下の技術で構築しています:
- Go標準ライブラリ
html/template
: テンプレートレンダリング - htmx: JavaScriptを最小限に抑えつつ、動的UIを実現
- CSS: シンプルで軽量なスタイリング
この構成では完全なSPA(Single Page Application)を構築せず、シンプルなHTTPリクエスト+部分的HTMLレンダリングで高速な体験を実現しています。
実装の詳細とベストプラクティス
Dockerを活用したモデルの効率的パッケージング
TinySwallowの効率的な配布と起動のために、事前にモデルを含めたDockerイメージを作成します。このアプローチにより、コンテナ起動時にモデルのダウンロードを待つ必要がなく、コールドスタート時間を大幅に短縮できます。
リージョン選定と GPU 活用のベストプラクティス
Cloud Run でGPUを利用する場合、リージョンの制約に注意が必要です:
- GPUサポートリージョン: 2025年3月時点で、
asia-northeast1
(東京)ではGPUを備えた Cloud Run が利用できません - 推奨リージョン:
asia-southeast1
(シンガポール)などGPUが利用可能なリージョンを選択 - CPUモード: TinySwallowのような小規模モデルはCPUでも動作するため、コスト優先ならCPUモードも検討
コールドスタート問題への対策
Cloud Runの特性上、インスタンスが0
台から増える際に発生するコールドスタートは避けられません。以下の対策を実装することで、ユーザー体験を向上させることができます:
- 最小インスタンス数設定
- 常に最低限のインスタンスを起動しておくことで、コールドスタートの影響を緩和
- 事前ウォームアップ:
- 定期的にヘルスチェックを実行し、インスタンスをウォームアップ
Goバックエンドの詳細設計
プロジェクト構造
バックエンドの設計は、クリーンアーキテクチャの考え方を取り入れています:
.
├── cmd/
│ └── coda/
│ └── main.go # エントリーポイント
├── internal/
│ ├── config/ # 設定管理
│ ├── frontend/ # UI関連
│ ├── infrastructure/ # インフラ関連
│ └── llm/ # LLM連携
├── Dockerfile
LLMサービスとの連携実装
Ollamaのような外部LLMサービスとの連携は、可読性と保守性を重視して設計しています。LLMクライアントインターフェースを定義し、Ollamaクライアントがこれを実装する形で、将来的に異なるLLMサービスへの切り替えも容易にしています。
エラーハンドリングとリトライ戦略
実運用では、LLMサービスへの接続が安定しない場合もあります。エラーハンドリングとリトライ戦略を実装することで、ロバスト性を高めています。バックオフを用いたリトライ機構により、一時的な接続問題を乗り越えることができます。
CI/CDパイプラインの構築
効率的な開発サイクルを実現するために、GitHub Actions と Google Cloud Build を組み合わせたCI/CDパイプラインを構築しています。テスト、ビルド、デプロイの各ステップを自動化することで、開発効率と品質を向上させています。
パフォーマンス最適化とチューニング
実運用時のパフォーマンスを最適化するための設定ポイントを解説します:
1. メモリとCPUの最適化
TinySwallow 1.5Bの場合、以下の設定で十分に動作します:
- CPU: 8 vCPU
- メモリ: 32GB
- GPU: NVIDIA L4 GPU x1
2. 推論パラメータの調整
Ollamaでのモデル実行時に、推論品質と速度のバランスを取るための主要パラメータとして、temperature、top_p、top_k、max_tokensなどがあります。用途に応じて適切に調整することで、最適な結果を得ることができます。
コスト試算
GPUを利用する際には、最低でも8つのvCPUと32GiBのメモリの割り当てが必要です。
具体的なコスト試算 (2025年3月時点)
例えば、1つのCPU付きの Cloud Run を 8 時間(28,800秒)稼働させた場合のコストは以下のようになります:
- GPUコスト: $0.0002796 × 28,800秒 = $8.05248
- CPUコスト: 8 vCPU × $0.00002160 × 28,800秒 = $4.97664
- メモリコスト: 32 GiB × $0.00000240 × 28,800秒 = $2.21184
合計コスト: $15.24096 ( 1 ドル 150 円の場合 約 2,286円 )
モニタリングと運用管理
効果的な運用のためのモニタリング戦略を整備することで、問題の早期発見と対応が可能になります:
ロギング戦略
構造化ログを実装することで、問題の診断と分析が容易になります。リクエストID、レイテンシ、トークン数などの情報を含めることで、パフォーマンスの追跡と最適化が可能になります。
セキュリティのベストプラクティス
セキュリティは常に最重要事項です。以下のポイントに特に注意してください:
1. サービス間通信の保護
TLS暗号化を使用して通信を保護します。常にHTTPSを使用し、適切な認証メカニズムを実装することで、通信の安全性を確保します。
2. プロンプトインジェクション対策
LLMへの入力は必ずサニタイズし、悪意あるプロンプトインジェクションを防止します。長さ制限などの基本的なサニタイズに加え、システムプロンプトとユーザー入力を明確に分離することで、セキュリティを強化します。
結論: GoでLLMを運用するメリットとデメリット
メリット
- 簡潔なコードベース: Goの明確な構文と標準ライブラリにより、保守性の高いコードを実現
- 高パフォーマンス: 低レイテンシでリソース効率の良い実行環境
- 優れた並行処理: Goroutineを活用した効率的なストリーミング処理
- デプロイの簡便さ: 単一バイナリで依存関係の問題を最小化
- スケーラビリティ: Cloud Runとの相性が良く、負荷に応じた自動スケール
デメリット
- ML関連ライブラリの少なさ: Python比で専門的ML処理用ライブラリが限定的
- 複雑なML処理の難しさ: ディープラーニング処理は実装が複雑になる傾向
- エコシステムの成熟度: LLM関連のGoエコシステムはまだ発展途上
実運用での教訓
- コスト最適化が重要: 小規模モデルでもサーバーレスアーキテクチャのメリットは大きい
- ユーザー体験を優先: コールドスタート対策など、技術的な課題を見える化して対処
- 段階的な改善: まずシンプルに実装し、データを元に最適化を進める
参考資料
本記事が、小規模言語モデルをCloud Runで効果的に運用するための参考になれば幸いです。質問やフィードバックがあれば、ぜひGitHubリポジトリのIssueまでお寄せください。