ChatGPT APIを使ってちょっと便利なアプリケーションを開発すると、それを自分(またはクローズドな組織)用に使えるよう認証を掛けてデプロイしたくなります。今回はPythonで手軽にWebアプリケーションのデモを作れるStreamlitをCloud Runにデプロイし、Identity-Aware ProxyでGoogle認証を掛けてみました。
要件と利用技術
- 手軽に作れること
- Streamlit を利用
- Streamlitは特定のバージョン以降Cloud Runで動作しない問題がありましたが、2023年1月ごろに対処されました
- サーバレスで動作すること
- Google Cloud Cloud Run を利用
- アクセス制限ができること
- Google Cloud Identity-Aware Proxy(IAP) を利用
- Cloud RunへのIAP対応は2023年4月7日にGAしました
- 予め、AまたはAAAAレコードを設定できるドメインを用意しておく必要があります
環境
- python = "^3.10"
- streamlit = "^1.21.0"
- openai = "^0.27.4"
細かいバージョンなどを示すすべてのコードはGitHubにあります。
Poetry + Docker Composeによる環境構築
開発環境はDocker Composeを用いてPoetryを使いました。以下のようなDockerfile
とcompose.yml
で構築しました。マルチステージビルドの最適化などは割と中途半端なので、お好みで調整してください。
FROM python:3.10-slim AS builder
WORKDIR /app
ENV PYTHONPATH="/app:$PYTHONPATH"
RUN apt-get update && apt-get install -y \
build-essential \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
RUN pip install poetry
RUN poetry config virtualenvs.create false
COPY pyproject.toml ./
COPY poetry.lock ./
RUN poetry install --without dev
CMD ["poetry", "run", "streamlit", "run", "src/chatgpt_summarizer/main.py", "--server.port", "8080"]
FROM builder AS dev
RUN apt-get update && apt-get install -y \
git \
curl
RUN git config --global --add safe.directory /app
RUN poetry install
FROM builder AS prod
COPY ./src /app/src
services:
app:
build:
context: .
target: dev
volumes:
- .:/app
env_file:
- .env
ports:
- 8080:8080
OpenAIのAPIキーを発行して$ echo 'OPENAI_API_KEY=XXXXXXXX' > .env
しておきます。
実装
StreamlitでChatGPTのインタラクティブなチャットUIを実装する
まずは自分用に使うChatGPTアプリケーションを実装します。ここでは例として普通のチャットを実装しますが、お好みで様々な機能を実装してください。
StreamlitでChatGPTへの入力+出力を1回ずつ行うだけのアプリであれば簡単です。ですが普通のChatGPTのように可変長複数回のやりとりからなるチャットは、Streamlit上で無限ループを掛けながらUIを良い感じに生成する必要があるのと、ループ時に過去実行した箇所でChatGPT APIを毎回叩かないようにする点に注意が必要です。
session_stateを使う方法はよく見かけましたが、ここではStreamlitのCache機能を使って実装してみました。
stream_write()
関数については拙稿のStreamlitでChatGPTのStreamを表示するも参考にしてください。
import openai
import streamlit
# @see: https://qiita.com/suzuki_sh/items/64d84c417cba43cd6351
def stream_write(chunks, key=None):
result_area = streamlit.empty()
text = ''
for chunk in chunks:
next: str = chunk['choices'][0]['delta'].get('content', '') # type: ignore
text += next
if "。" in next:
text += "\n"
result_area.write(text, key=key)
return text
@streamlit.cache_data
def cached_chat(messages):
completion = openai.ChatCompletion.create(
model='gpt-3.5-turbo',
messages=messages,
stream=True,
)
text = stream_write(completion, key=f'output_{messages}')
return text
messages = []
chat_widget = streamlit.empty()
while True:
with chat_widget.container():
for message in messages:
streamlit.write(message['content'])
input_text = streamlit.text_input('入力', key=f'input_{messages}')
if len(input_text) == 0:
streamlit.stop()
messages.append({"role": "user", "content": input_text})
text = cached_chat(messages)
messages.append({"role": "assistant", "content": text})
StreamlitアプリをCloud Runにデプロイする
Google Cloudの利用準備はできているものとします。開発環境にDockerを使っていれば、Cloud Runにデプロイすることは簡単です。ビルドしたイメージを指定してgcloud run deploy
するだけです。
$ gcloud run deploy $SERVICE \
--image $IMAGE \
--project $PROJECT \
--region asia-northeast1 \
--ingress internal-and-cloud-load-balancing \
--max-instances=1 \
--allow-unauthenticated \
--set-env-vars "OPENAI_API_KEY=$OPENAI_API_KEY" \
--platform=managed
IAPを使う上で、以下が必要な点に注意してください。他はお好みで設定してください。
- 上り(内向き)を内部および Cloud Load Balancing に設定するために
--ingress internal-and-cloud-load-balancing
- allUsers に起動元ロールを付与するために
--allow-unauthenticated
デプロイが無事に完了し、発行されたURLを直接アクセスしてみて403 Forbiddenが表示されたら成功です。
Cloud RunでデプロイしたアプリにIAPを付ける
基本的にはGoogle Cloudの公式ドキュメントに沿って設定していきますが、既にGAしたはずなのに2023-04-23時点ではまだ「プレビュー」と書いてあったりとやや頼りないので、要点に絞って説明します。
Cloud RunでデプロイしたアプリにIAPを付けるには、HTTPS ロードバランサが必要です。以下のように設定します。
- ロードバランサ
- 「HTTP(S) ロード バランシング」を構成します
- フロントエンド
- プロトコルはHTTPS
- 今回IPv6を選択しましたが、IPv4でも行けると思います。後にDNSにレコードを登録する時、IPv4にしたならAレコード、IPv6ならAAAAレコードを設定することになります。
- 証明書
- バックエンドサービス
- エンドポイントグループ
ロードバランサがデプロイされるまでは結構時間がかかるので、待ちます。IPアドレスが発行されたら、DNSにAまたはAAAAレコードを設定しておきます。
ロードバランサが構成されたらIAP画面に移動し、先ほどのロードバランサを選択してIAPを有効にします。設定に不備があるとここでエラーが出ます。
IAPを有効にしたら、画面右側の「プリンシパルを追加」から、許可したいユーザを設定します。
今回私は自分用のGoogle Workspace ドメインを持っているのでそちらを指定しましたが、個人利用であればGmailアカウントを指定する感じが一般的だと思います。
ロールには「IAP-secured Web App User」を設定します。
設定したドメインにアクセスし、以下のような感じのGoogleログイン画面が表示されたら成功です。
ログインして、構築したStreamlitアプリが使えるか確かめてみましょう。
おわりに
Streamlitを使ってChatGPTアプリを開発し、Cloud Run IAPで自分だけがアクセスできるようにしてみました。Cloud Run自体は自分だけのアプリをデプロイして使うなら料金も安くて手軽に使える仕組みですが、IAPのためにロードバランサを用意しなければいけない点が惜しいなと思いました。