11/22 20:40更新: Dockerfileを更新し忘れていたため反映しました🙏
Amazon ECSの新機能「エクスプレスモード」が登場しました。
ECRにコンテナイメージさえプッシュしておけば、面倒なネットワークやタスク定義などの準備をせずとも、簡単にECS周りのインフラをまとめて自動構築してくれます。
Expressモードのポイントまとめ
- ECRへのイメージプッシュまでは自分でやっておく必要がある
- 作成ウィザードで、詳細設定を変更せず進めるとデフォルトVPCにデプロイされる
- 同じくデフォルトの場合、ALB・タスクともにパブリックサブネットに配置される
- 実行環境はFargate。x86系アーキテクチャのためMacでビルドする際は注意
- サービスを削除すると、自動作成されたリソースをまとめて掃除してくれる
これまでの課題
特にBedrockなどのAIチャットボットをStreamlitで開発したとき、WebSocketに対応したインフラを使う必要があるため、AWS App Runnerなどのお手軽デプロイが使えないのがこれまでの課題でした。
かといってECSを自分で構築するのは、特に初心者にとってはそれなりのハードルです。
App Runnerのようなサーバーレスではないため、デプロイ後はランニングコストが発生し続ける点には注意です。
(構成にもよりますが、ざっくり月額数千円〜ぐらいがミニマム?)
1. Dockerイメージを作成する
ECS Express Modeは、残念ながらDockerイメージの構築を自分でやる必要があります。
App RunnerやAmplifyのように、GitHubのリポジトリ紐付けにも対応してほしかった…!
このハンズオンでは、AWSバージニア北部リージョンのみを利用します。
ECRリポジトリを作成する
十分な権限をもつIAMユーザーでAWSアカウントにサインインし、reg と検索してAmazon ECRにアクセスします。
トップページの「作成」ボタンから、新規プライベートリポジトリを作成します。
リポジトリ名は simple-streamlit-chatbot にして、他はデフォルトでOKです。
コンテナ構築用の開発環境を起動する
AWS CloudShellを使えないか頑張ったのですが、ディスクが1GBしかないので無理ゲーでした。大人しくローカルPCもしくはGitHub Codespacesなどを使うことにしましょう。
以下より、simple-streamlit-chatbot という名前で新規パブリックリポジトリを作成します。
Add README をオンにしておきましょう。
コードスペースを起動します。
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 login --remote
AWS Region [us-east-1]: というプロンプトでリージョンを聞かれたら、Enterでデフォルトのまま進めます。
その後、表示されるURLにアクセスし、今回作業するAWSアカウントでブラウザ認証を行ったあと、検証コードをコピーして、コードスペースのターミナルに貼り付けます。
Streamlitアプリを作成する
コードスペースの左サイドバーから、以下の新規Pythonファイルを作成します。
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も新規作成します。
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リポジトリを開くと、「プッシュコマンドを表示」ボタンから必要なコマンドを確認できます。
コードスペース上で、これらのコマンドをすべて順番に実行すれば、プッシュ完了です。
2. ECS Express Modeでデプロイする
ECSのコンソールにアクセスし、「今すぐ始める」ボタンからExpressモードのウィザードを開きます。
先ほど登録したイメージの latest タグを選択します。
2つのロールは、プルダウン内の「新しいロールの作成」を指定します。
(ecsTaskExecutionRoleとecsInfrastructureRoleForExpressServicesが自動作成されます)
「その他の設定」を展開し、タスクロールの「新しいロールの作成」をクリックします。
Bedrockにアクセス可能な権限を、ECSタスクに設定しましょう。
ステップ1では、ユースケースでECSタスクを選択して次に進みます。
許可ポリシーは AmazonBedrockFullAccess をチェックして進みます。
ecsTaskExecutionRoleWithBedrock と言う名前でロールを作成します。
ECS Expressモードを作成画面に戻り、タスクロール横の「更新」アイコンを押してから、作成したロールを指定します。
その後、「作成」ボタンを押すとインフラ諸々の構築が開始されます。
6分ぐらいでデプロイが完了します。
画面上部の「アプリケーションURL」から、Streamlitアプリにアクセスできます。このURLはこの後すぐ利用するので、コピーしておきましょう。
この時点ではアプリがインターネット公開されているので注意してください。後続の認証画面を実装せずに途中でやめてしまうと、不特定多数の人に使われてしまう恐れがあります。
3. Cognito認証を追加する
ユーザープールを作成する
Amazon Cognitoのメニューにアクセスし、「5分未満で無料で開始できます」をクリックします。
以下のみデフォルトから変更します。
- アプリケーションに名前を付ける:
simple-streamlit-chatbot - サインイン識別子のオプション: 「メールアドレス」にチェック
- サインアップのための必須属性: 「email」を選択
- リターンURL: 作ったECSアプリの「アプリケーションURL」+
/oauth2/idpresponseを入力(例:https://xxx.ecs.us-east-1.on.aws/oauth2/idpresponse)
ALBにCognito認証を設定する
ECS Expressモードで作成したサービスの「リソース」タブから、ALBリスナーへジャンプします。
優先度「1」のリスナールールを編集して、以下のルールを追加します。
- 事前ルーティンアクション: ユーザーを認証
- アイデンティティプロバイダー: Amazon Cognito
- ユーザープールID: いま作ったものを選択
- ユーザープールクライアントID:
simple-streamlit-chatbot - ターゲットグループ: 上記で確認したターゲットグループ名
後はそのまま進んで保存します。
セキュリティグループを修正する
認証完了後に、ALB経由でCognitoの認証エンドポイントにアクセスできるようにするため、セキュリティグループにアウトバウンドルールを追加する必要があります。
リスナールール上部の「ロードバランサー」をクリックして、ALBに移動します。
その後、「セキュリティ」タブからセキュリティグループIDをクリックします。
「アウトバウンドルール」タブに移動し、「アウトバウンドのルールを編集」します。
「ルールを追加」ボタンから、以下のルールを追加します。
- タイプ: HTTPS
- 送信先: Anywhare-IPv4
動作確認
これで、「アプリケーションURL」からStreamlitアプリに認証つきでアクセスできるようになります!
実際に使ってみてください。
アプリを消したいときは、マネコンからECSサービスを削除するだけで、Expressモードで作成された一連のリソースをまとめて削除してくれます。便利ですね!
おまけ
今回の手順でECSとCognitoに入門できたら、以下の書籍でもっと便利なAIエージェントを作ってデプロイしてみよう💪












