ECSの新機能Expressモードで、認証つきStreamlitアプリを簡単にAWSデプロイしよう! #AI – Qiita

11/22 20:40更新: Dockerfileを更新し忘れていたため反映しました🙏

Amazon ECSの新機能「エクスプレスモード」が登場しました。

ECRにコンテナイメージさえプッシュしておけば、面倒なネットワークやタスク定義などの準備をせずとも、簡単にECS周りのインフラをまとめて自動構築してくれます。

  • ECRへのイメージプッシュまでは自分でやっておく必要がある
  • 作成ウィザードで、詳細設定を変更せず進めるとデフォルトVPCにデプロイされる
  • 同じくデフォルトの場合、ALB・タスクともにパブリックサブネットに配置される
  • 実行環境はFargate。x86系アーキテクチャのためMacでビルドする際は注意
  • サービスを削除すると、自動作成されたリソースをまとめて掃除してくれる

これまでの課題

特にBedrockなどのAIチャットボットをStreamlitで開発したとき、WebSocketに対応したインフラを使う必要があるため、AWS App Runnerなどのお手軽デプロイが使えないのがこれまでの課題でした。

かといってECSを自分で構築するのは、特に初心者にとってはそれなりのハードルです。
App Runnerのようなサーバーレスではないため、デプロイ後はランニングコストが発生し続ける点には注意です。
(構成にもよりますが、ざっくり月額数千円〜ぐらいがミニマム?)

ECS Express Modeは、残念ながらDockerイメージの構築を自分でやる必要があります。
App RunnerやAmplifyのように、GitHubのリポジトリ紐付けにも対応してほしかった…!

このハンズオンでは、AWSバージニア北部リージョンのみを利用します。

ECRリポジトリを作成する

十分な権限をもつIAMユーザーでAWSアカウントにサインインし、reg と検索してAmazon ECRにアクセスします。

スクリーンショット 2025-11-22 15.00.51.png

トップページの「作成」ボタンから、新規プライベートリポジトリを作成します。
リポジトリ名は simple-streamlit-chatbot にして、他はデフォルトでOKです。

コンテナ構築用の開発環境を起動する

AWS CloudShellを使えないか頑張ったのですが、ディスクが1GBしかないので無理ゲーでした。大人しくローカルPCもしくはGitHub Codespacesなどを使うことにしましょう。

以下より、simple-streamlit-chatbot という名前で新規パブリックリポジトリを作成します。
Add README をオンにしておきましょう。

コードスペースを起動します。

スクリーンショット 2025-11-22 14.55.09.png

AWS CLIの最新版をインストールします。

# インストーラーをダウンロード
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"

# インストーラーを解凍
unzip awscliv2.zip

# インストーラーを実行
sudo ./aws/install

# 不要ファイルを削除
rm -rf awscliv2.zip aws

ターミナルから自分のAWSアカウントを操作できるよう、認証を行います。

AWS Region [us-east-1]: というプロンプトでリージョンを聞かれたら、Enterでデフォルトのまま進めます。

その後、表示されるURLにアクセスし、今回作業するAWSアカウントでブラウザ認証を行ったあと、検証コードをコピーして、コードスペースのターミナルに貼り付けます。

スクリーンショット 2025-11-22 15.02.27.png

Streamlitアプリを作成する

コードスペースの左サイドバーから、以下の新規Pythonファイルを作成します。

app.py

import asyncio
import streamlit as st
from strands import Agent
from strands.tools.mcp import MCPClient
from mcp import stdio_client, StdioServerParameters

# フロントエンドを描画
st.title("Strands MCPエージェント")
st.text("AWSナレッジMCPサーバーを使って、Nova Premierがあなたの質問に答えます!")
prompt = st.chat_input("質問を入力")

if prompt:
    # ユーザーのプロンプトを表示
    with st.chat_message("user"):
        st.markdown(prompt)

    # エージェントの応答を表示
    with st.chat_message("assistant"):
        with st.spinner("考え中…"):

            # MCPクライアント作成
            client = MCPClient(lambda: stdio_client(
                StdioServerParameters(
                    command="uvx",
                    args=["awslabs.aws-documentation-mcp-server@latest"])
                )
            )

            with client:
                # エージェント作成
                agent = Agent(
                    model="us.amazon.nova-premier-v1:0",
                    system_prompt="思考も回答も日本語で行ってください。",
                    tools=client.list_tools_sync()
                )

                # ストリーミング表示の準備
                container = st.container()
                state = {
                    "text_holder": container.empty(),
                    "buffer": "",
                    "shown_tools": set()
                }

                # Strandsをストリーミング実行する非同期関数を定義
                async def run_stream():
                    async for event in agent.stream_async(prompt):
                        current_tool = event.get('current_tool_use', {})
                        tool_id = current_tool.get('toolUseId')
                        tool_name = current_tool.get('name')

                        # ツール実行を検出して表示
                        if tool_id and tool_name and tool_id not in state["shown_tools"]:
                            state["shown_tools"].add(tool_id)
                            if state["buffer"]:
                                state["text_holder"].markdown(state["buffer"])
                                state["buffer"] = ""
                            container.info(f"🔧 **{tool_name}** ツールを実行中...")
                            state["text_holder"] = container.empty()

                        # テキストを抽出して表示
                        if event.get('data'):
                            state["buffer"] += event['data']
                            state["text_holder"].markdown(state["buffer"] + "")

                    # 最終表示
                    if state["buffer"]:
                        state["text_holder"].markdown(state["buffer"])

                # 非同期関数を実行
                asyncio.run(run_stream())

同様に、コンテナ化に必要なDockerfileも新規作成します。

Dockerfile

FROM python:3.13

# 作業ディレクトリ
WORKDIR /app

# 依存パッケージをインストール
RUN pip install uv mcp streamlit strands-agents strands-agents-tools

# アプリ本体をコピー
COPY . /app

# 公開ポート
EXPOSE 80

# Streamlitを起動
CMD ["streamlit", "run", "app.py", "--server.port=80", "--server.address=0.0.0.0"]

コンテナイメージを作成して登録する

先ほどのECRリポジトリを開くと、「プッシュコマンドを表示」ボタンから必要なコマンドを確認できます。

スクリーンショット 2025-11-22 15.56.44.png

コードスペース上で、これらのコマンドをすべて順番に実行すれば、プッシュ完了です。

ECSのコンソールにアクセスし、「今すぐ始める」ボタンからExpressモードのウィザードを開きます。

スクリーンショット 2025-11-22 16.03.36.png

先ほど登録したイメージの latest タグを選択します。

スクリーンショット 2025-11-22 16.04.57.png

2つのロールは、プルダウン内の「新しいロールの作成」を指定します。
ecsTaskExecutionRoleecsInfrastructureRoleForExpressServicesが自動作成されます)

「その他の設定」を展開し、タスクロールの「新しいロールの作成」をクリックします。
Bedrockにアクセス可能な権限を、ECSタスクに設定しましょう。

ステップ1では、ユースケースでECSタスクを選択して次に進みます。

スクリーンショット 2025-11-22 17.00.32.png

許可ポリシーは AmazonBedrockFullAccess をチェックして進みます。

スクリーンショット 2025-11-22 17.01.41.png

ecsTaskExecutionRoleWithBedrock と言う名前でロールを作成します。

ECS Expressモードを作成画面に戻り、タスクロール横の「更新」アイコンを押してから、作成したロールを指定します。

その後、「作成」ボタンを押すとインフラ諸々の構築が開始されます。

スクリーンショット 2025-11-22 16.07.36.png

6分ぐらいでデプロイが完了します。

画面上部の「アプリケーションURL」から、Streamlitアプリにアクセスできます。このURLはこの後すぐ利用するので、コピーしておきましょう。

この時点ではアプリがインターネット公開されているので注意してください。後続の認証画面を実装せずに途中でやめてしまうと、不特定多数の人に使われてしまう恐れがあります。

ユーザープールを作成する

Amazon Cognitoのメニューにアクセスし、「5分未満で無料で開始できます」をクリックします。

スクリーンショット 2025-11-22 17.07.32.png

以下のみデフォルトから変更します。

  • アプリケーションに名前を付ける: simple-streamlit-chatbot
  • サインイン識別子のオプション: 「メールアドレス」にチェック
  • サインアップのための必須属性: 「email」を選択
  • リターンURL: 作ったECSアプリの「アプリケーションURL」+ /oauth2/idpresponse を入力(例: https://xxx.ecs.us-east-1.on.aws/oauth2/idpresponse

ALBにCognito認証を設定する

ECS Expressモードで作成したサービスの「リソース」タブから、ALBリスナーへジャンプします。

スクリーンショット 2025-11-22 17.19.18.png

優先度「1」のリスナールールを編集して、以下のルールを追加します。

  • 事前ルーティンアクション: ユーザーを認証
  • アイデンティティプロバイダー: Amazon Cognito
  • ユーザープールID: いま作ったものを選択
  • ユーザープールクライアントID: simple-streamlit-chatbot
  • ターゲットグループ: 上記で確認したターゲットグループ名

後はそのまま進んで保存します。

セキュリティグループを修正する

認証完了後に、ALB経由でCognitoの認証エンドポイントにアクセスできるようにするため、セキュリティグループにアウトバウンドルールを追加する必要があります。

リスナールール上部の「ロードバランサー」をクリックして、ALBに移動します。
その後、「セキュリティ」タブからセキュリティグループIDをクリックします。

スクリーンショット 2025-11-22 18.13.26.png

「アウトバウンドルール」タブに移動し、「アウトバウンドのルールを編集」します。
「ルールを追加」ボタンから、以下のルールを追加します。

  • タイプ: HTTPS
  • 送信先: Anywhare-IPv4

動作確認

これで、「アプリケーションURL」からStreamlitアプリに認証つきでアクセスできるようになります!

スクリーンショット 2025-11-22 18.19.38.png

実際に使ってみてください。

アプリを消したいときは、マネコンからECSサービスを削除するだけで、Expressモードで作成された一連のリソースをまとめて削除してくれます。便利ですね!

今回の手順でECSとCognitoに入門できたら、以下の書籍でもっと便利なAIエージェントを作ってデプロイしてみよう💪




元の記事を確認する

関連記事