Bedrockアプリに画面と認証を付けてインターネットに公開する一番簡単な手順 - Amazon Bedrock APIで始めるLLM超入門⑧

作成したBedrockアプリのデモを行う際に、インターネットに公開したくなる事もあると思うので、一番簡単そうな手順を整理してみます。
インターネットに公開するので申し訳程度の認証機能も付けます。

:::note
初稿ではnon-SSL(HTTP)だったのですがSSL(HTTPS)の手順に変更しました
:::

# アーキテクチャ図
画面はStreamlitで作成しCloud9上で稼働させます。
インターネットからのアクセスはエッジロケーションのCloudFrontにてSSL(HTTPS)で受け、AWSネットワークバックボーンをnon-SSL(HTTP)で通って、Elastic IPをアタッチしたCloud9上のStreamlitにアクセスします。
AWSネットワーク内はnon-SSLなのでプロダクション環境では望ましくない場合があるかもしれませんが、デモ程度であれば問題ないかなと思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3573242/732c9f71-b773-2134-5317-a0af2a82d846.png)

Bedrockへのモデルアクセスは許可済、リージョンはClaude2.1が使える前提です。

# 認証画面

こんな認証画面を付けます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3573242/4b2d1d5c-42d4-eb27-86d6-8fa6ddfeb841.png)

# Cloud9環境の作成

名前を適当に付けて全てデフォルトでCloud9環境を作成します。（Admin権限で…）
（デフォルト）タイムアウトが30分なので、30分経つと終了しお財布にもセキュリティにも安心です。30分では短すぎる場合は伸ばして作成してください。

EC2インスタンスが作成されたら、ElasticIPの割り当て・作成したCloud9のEC2に対する関連付けを行います。

# 必要ライブラリのインストール
LangChainとStreamlitを使います（LangChainは使わなくても良いんですが、サンプルプログラムをスッキリさせる為だけに使っています）。認証用にstreamlit_authenticatorを使います。
```
pip install -U boto3 langchain langchain-community streamlit streamlit_authenticator
```

今回久しぶりにlangchainを触っていて気付いたのですが、私が過去に作ったlangchainを使うサンプルプログラム、今のバージョンだときっと全部動かないですね。どこかで直さないといけません。

# プログラムの作成

認証機能は「付いていれば良い」の精神で、IDとパスワードは固定です。行儀が悪いですがプログラムにハードコーディングしています。
サンプルはIDが`admin`、パスワードが`pass`としていますが、これも行儀が悪いので適当に直してください。

凝れば色々出来そうなので詳しくは以下

https://github.com/mkhorasani/Streamlit-Authenticator

```Python:hogehoge.py
from langchain_community.llms import Bedrock
import streamlit as st
import streamlit_authenticator as sa

# admin/pass 固定で認証情報を作成
authenticator = sa.Authenticate(
    credentials={"usernames":{"admin":{"name":"admin","password":"pass"}}},
    cookie_name="streamlit_cookie",
    key="signature_key",
    cookie_expiry_days=1
)

# ログイン画面描画
authenticator.login()

if st.session_state["authentication_status"]: #ログイン成功
    # ログイン後画面の描画

    authenticator.logout() #ログアウトボタン
    st.title("LangChainからのBedrock呼出し") #タイトル
    input_text = st.text_input("このテキストをBedrockに送信します")
    send_button = st.button("送信")
    
    # 送信ボタン押下
    if send_button:
        llm = Bedrock(model_id="anthropic.claude-v2:1") #Claude2.1
        answer = llm.invoke(input_text)
        st.write(answer)

elif st.session_state["authentication_status"] is False: #ログイン失敗
    st.error('Username/password is incorrect')
elif st.session_state["authentication_status"] is None: #未入力
    st.warning('Please enter your username and password')
```

上記の「ログイン後画面の描画」の部分がログイン後の本体、「送信ボタン押下」の部分がBedrockアクセスを行っている箇所になるので、ここだけ各自の処理に差し替えてもらえば良いかなと思います。チャットなりRAGなりAgentsなりKnowledge baseなり。

# Streamlitから起動する

後でセキュリティグループに穴を開けるので、ポート番号はデフォルト（8501）で起動します。

```
python3 -m streamlit run hogehoge.py
```

:::note
Cloud9のプレビュー機能から見えれば充分な場合はポート番号8080を指定して起動してプレビュー機能で参照できます。
```
python3 -m streamlit run hogehoge.py --server.port 8080
```
:::

# セキュリティグループの穴開け
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3573242/afe77240-189f-1720-76c5-221171d1b7c2.png)

Cloud9のEC2インスタンスのセキュリティグループのインバウンドルールに対して、ポート：8501をプレフィックスリストの`com.amazonaws.global.cloudfront.origin-facing`に対して許可をし、CloudFrontからのポート番号8501（Streamlit）宛のアクセスのみ許可します。

# CloudFrontの作成
Cloud9のEC2インスタンスの`パブリック IPv4 DNS`をコピーしておきます。

CloudFrontのディストリビューションを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3573242/c7bc2d54-8086-97ee-d385-94c76d70ea6a.png)

Origin domainはEC2インスタンスの`パブリック IPv4 DNS`

プロトコルは`HTTPのみ`でPortは`8501`

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3573242/8af986bd-f95e-1e5b-0d9d-100d298a8324.png)

ビューワープロトコルポリシーは`HTTPS only`

キャッシュポリシーは`CachingDisabled`

オリジンリクエストポリシーは`AllViewer`

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3573242/b03e2195-d56f-2309-babe-7f8b229a0b9e.png)

WAFは適当に（適切に？）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3573242/5fe78536-ee70-b2ce-6008-cdb0de594f1c.png)

料金クラスはお好みで

で、作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3573242/30d2cba3-55e2-6dd5-40ae-cbe0927503b2.png)

作成後の画面にある`ディストリビューションドメイン名`をコピーします。

# 使ってみる

ドメインが有効になるまで数分かかります。

ブラウザから先ほどの`ディストリビューションドメイン名`にアクセスするとログイン画面が表示されます。HTTPSでアクセス出来ています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3573242/66b5e151-efac-842f-2c3d-7b77c4cacffb.png)

IDにadmin、パスワードにpassを入力しloginボタンを押下します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3573242/ed7a7112-e015-a2b9-d57f-f1b8ca2e4734.png)

ちょっと格好悪い動作の後に、本体部分が表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3573242/f1b86249-07d6-098f-4d1e-c9a74233ced6.png)

Bedrockへの問い合わせ内容を入力して送信ボタンを押下します。

:::note
インターネット上はSSLで通信されますので野生のハッカーは気にしなくて良いですが、AWSネットワーク内はnon-SSLで通信されますので、そこは認識しておいてください
（厳しいお会社様だとナニカに引っかかるかもしれません）
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3573242/bb42bfc3-af19-9dbe-0384-38bc45f0c7cf.png)

正しく動いている事が確認できました。
出力トークン数をデフォルトのままにしているので途中で切れていますね。

ログインするとCookieに保存されるので、Streamlitが起動していれば1日の間であれば再ログイン不要のはずです。

Logoutボタンを押下すると、ログアウトしてログイン画面に戻ります。

# デモの実施が終わったら

認証が緩いので無効にしておいた方が無難です。
CloudFrontディストリビューションを無効にしつつ、セキュリティグループのインバウンドルールを削除しましょう。
（再開するのは手間じゃないと思います）

https://qiita.com/cyberBOSE/items/fe66c2049aaa04568d61