こんにちは!TC3のAIチームの梅本(@mumeco_ml)です。LLM Advent Calendar 2023の25日目の記事です。

前回作成したLLMアプリではローカルLLMを用いてチャットをする特徴を実装しました。ここまでの内容でみなさんは自身の環境でAIチャットアプリを動作させることが出来たと思います。しかし、作ったアプリは知人や家族に自慢したいものですよね?ということで今回はクラウド上にアプリをデプロイしどこからでもアクセスできるようにしたいと思います。コード類はGithubリポジトリにもアップロードしているので、コーディングが面倒な方はこちらも活用ください。

はじめに

Streamlitアプリを公開する場合、実はStreamlit Cloudというプラットフォームを使うと一瞬でデプロイが出来てしまいます。それだけであれば公式のドキュメントを見れば一瞬で終わってしまうので、今回はクラウドプロバイダの1つであるGoogle Cloud Platform (GCP)のCloudRunを活用した方法をご紹介します。

事前準備

  • Python環境
    • 今回はPython 3.10を使いますが、3.11などでも多分大丈夫です
    • インストールしてない方は”python インストール OS名”で検索してください
  • Poetry(できれば)
    • ライブラリの仮想環境を作ります
    • インストールしてない場合は公式に方法が書いてあります
    • インストールが難しい場合はpoetryは使わずにpipを使ってください
  • Docker
    • イメージのビルドに必要
  • Google Cloud Platformアカウント
    • 各種GCPサービスを利用するのに必要
    • CloudRunやArtifact registryを使う際にAPIの有効化が必要かもしれないので適宜ブラウザで認証をしてください
  • gcloud CLIのインストール
    • gcloudコマンドを使うのでインストールしてください
    • 公式ドキュメントにインストール方法が書いてあります

クラウドアーキテクチャ

今回作るのは以下のようなアーキテクチャです。

  • Cloud Run
    • Webサーバーが動くフルマネージドサービスです。
    • 起動時間に課金がされるのですが、アクセスがあると自動的に立ち上がり、アクセスがないと自動的にシャットダウンされるのでとてもコスパの高いサービスです。
    • オートスケール等も出来るのである程度のサービスレベルまではkubernetesを使わなくてもいけると思います。
    • GPUが使えないのが惜しい
  • Artifact Registry
    • Cloud Runで起動するDocker imageを保存しておくサービスです。
  • Cloud Storage
    • Cloud Runではローカルファイルは毎回リセットされてしまいます。
    • GCSFuseというOSSを利用することで、Cloud RunにCloud Storageをマウントしてリセットされないファイルを作ることが出来ます。

Docker imageの作成

まずはDocker imageをビルドするためのDockerfileを作っていきましょう。以下のように実装します。

FROM python:3.10.13-slim-bookworm

RUN apt-get update && apt-get install -y curl gnupg2 lsb-release
RUN export GCSFUSE_REPO=gcsfuse-`lsb_release -c -s` && \
    echo "deb https://packages.cloud.google.com/apt $GCSFUSE_REPO main" | tee /etc/apt/sources.list.d/gcsfuse.list && \
    curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
RUN apt-get update && apt-get install -y gcsfuse

WORKDIR /app
RUN mkdir -p /app/data
RUN pip install poetry
RUN poetry config virtualenvs.in-project true

COPY . .
RUN poetry install

CMD ["poetry", "run", "streamlit", "run", "app.py", "--server.port", "8080"]
  • 3行目から7行目まではGCS Fuseを使うためのおまじないです
  • 9行目から15行目まででpoetryを用いてpython環境を作ります
  • 10行目のdataフォルダにこの後Cloud Storageをマウントします
  • 17行目では通常の起動コマンドに加えてポートを8080番に設定します。これはCloud Runのデフォルトのアクセスポートが8080のためです

それではローカルでテスト用のDocker imageをビルドして動作させてみましょう。docker imageの8080番をローカルの8080番にポートフォワーディングするので、アクセスはhttp://localhost:8080に行けば大丈夫です。

docker build -t test . && docker run --rm -p 8080:8080 test

すると正常にStreamlitのアプリが起動していることが分かります。

Artifact registryへのpush

次にArtifact registryへ今作ったimageをpushします。まずはgcloud CLIを認証させます。

gcloud auth login

認証が出来たら、次にArtifact registryのレポジトリを作ります。今回は例としてllm-appという名前を使います。ロケーションはus-central1にしてますがすきに変えて大丈夫です。

gcloud artifacts repositories create llm-app --repository-format=docker --location=us-central1

次にこのArtifact registry向けにdocker buildしてpushします。GCPプロジェクト名を入れる必要があるので、各自の環境に合わせて変えてください。

docker build -t us-central1-docker.pkg.dev/{プロジェクト名}/llm-app/app .
docker push us-central1-docker.pkg.dev/{プロジェクト名}/llm-app/app:latest

Artifact registryへのpush時にpermission errorが出る場合は、この記事を参考に以下のコマンドで解決すると思います。

gcloud auth configure-docker us-central1-docker.pkg.dev

pushできたかどうかはArtifact registryのページにアクセスすると分かります。

Cloud Runへのデプロイ

ここまで出来たら後は超簡単です。以下のコマンドでCloud Runのサービスが登録できます。

gcloud run deploy ledgeai-app --image us-central1-docker.pkg.dev/{プロジェクト名}/llm-app/app:latest --platform=managed --project={プロジェクト名} \
    --region=us-central1 --allow-unauthenticated --memory=2Gi --cpu=1 --max-instances=1 --timeout=900 --port=8080

オプションの説明

  • image
    • 使うdocker imageのタグ。作成したArtifact registryのものを指定
  • platform
    • gkeやkubernetes等選べるが、今回の用途ではmanagedで大丈夫です。
  • project
    • GCPプロジェクト名
  • region
    • インスタンスが立つ場所の設定
  • allow-unauthenticated
    • サービスを全体公開する
  • cpu
    • インスタンスのcpu数
  • memory
    • インスタンスのメモリ
  • max-instances
    • オートスケールする場合の同時最大起動数。1にするとどんなにアクセスがあっても1インスタンスで頑張ります。
  • timeout
    • 処理のタイムアウト値

詳しくは公式ドキュメントを参照してください。

ここまでで、Cloud Runのページを見てみるとサービスが出来ていると思います。そして、そこからCloud RunのURLを押してみると作ったアプリにアクセスが出来ると思います。

GCS Fuseを用いたCloud Strageのマウント

Cloud Runでの欠点の1つにローカルファイルのリセットがあります。例えば今回のアプリの例で言うと、チャット履歴をファイル出力するようにしていてもCloud Runがシャットダウンしてしまうとそのファイルは消えてしまいます。サービスを作っていく場合はやはりファイルを出力して何かに使いたいと思いますので、その方法を説明します。

まず、以下のコマンドでマウントするCloud Strageのバケットを作成します。例のごとくバケット名はllm-appとしてますが、皆さんの環境に合わせて変えてください。

gcloud storage buckets create gs://llm-app --location us-central1

Dockerfileで既にGCS Fuseはインストールしているので、後は起動時にgcsfuseコマンドでマウントするだけです。start.shファイルを以下のように作成します。これだけで、/app/dataフォルダに保存したファイルが勝手にCloud Storageへ保存されるようになります。

# mount GCS Fuse
gcsfuse llm-app /app/data

# Run streamlit
poetry run streamlit run app.py --server.port 8080 

後はDockerfileをstart.shファイルを使うように書き換えます。新しいファイルを作ったので、再度buildからやり直してください。

# CMD ["poetry", "run", "streamlit", "run", "app.py", "--server.port", "8080"]
ENTRYPOINT ["bash", "start.sh"]

再度Cloud Runのコマンドを実行すると、勝手に新しいArtifact registryのイメージを使ってくれ、Cloud Runのサービスが更新されます。

gcloud run deploy ledgeai-app --image us-central1-docker.pkg.dev/{プロジェクト名}/llm-app/app:latest --platform=managed --project={プロジェクト名} \
    --region=us-central1 --allow-unauthenticated --memory=2Gi --cpu=1 --max-instances=1 --timeout=900 --port=8080

まとめ

今回は以前作成したAIチャットボットアプリをGCP上へデプロイする方法を説明しました。Streamlit Cloudを利用するのと比べ、GCP上の様々なサービスと連携しやすい点が強みかと思います。エラー等が出て出来ないなどありましたら、私のXへDM頂いても大丈夫ですし、Github上にissueを立てて頂いても大丈夫です。