Bedrock AgentCore Runtime で環境変数を設定したくなったので、試してみました。
7/24追記
コメントで追加情報教えていただきました!
@moritalous さんありがとうございます!
平文になっちゃうんで良い方法ではない気もしますが、、
CreateAgentRuntime APIで環境変数はセットできたと思います。
マネコンからも設定できた気がします(折りたたまれてますが)
シークレットマネージャとの連携が追加されるといいですよね!
7/26更新
シークレットなどを保存したい場合は @moritalous さんの以下記事を参考に、AgentCore Identityをご活用ください!(記事の循環参照失礼します)
状況確認
AgentCore Runtimeの制約
AgentCore Runtimeはコンテナ実行環境となります。
構築したエージェントをRuntime上で動かすには、
エージェントを構築したコードを元にDocker Imageなどを作成する
↓
作製したDocker ImageをECRにプッシュ
↓
Docker ImageがRuntime上へデプロイされて動く
という流れが必要です。
というところで、Runtime上にデプロイした後、環境変数を良しなに参照して欲しい時が少なからずあると思います。
考えられる解決策は以下2つです。
- 解決策1.Dockerfileを作成した際に環境変数を渡す
- 解決策2.SSMパラメータストアやSecrets Managerを使う
この2つを実際にやってみます。サンプルコードはこちらです。
プロジェクト構成はこんな感じです。
project/
├── agent/
│ ├── strands_agent.py # エージェントコード
│ ├── requirements.txt # 依存関係
│ ├── Dockerfile # 自動作成されるDockerfile
│ └── .env # 環境変数
├── deploy/
│ └── deploy_runtime.py # エージェントデプロイスクリプト
└── gateway
├── lambda # エージェント用ツール
│ └── deploy_runtime.py
└── setup_gateway.py # ゲートウェイ作成スクリプト
詳しい実行手順などはGitHubを参照いただければと思いますが、以下操作は事前に行っておきます。
# 1. Lambdaツールのデプロイ
cd deploy/
uv run deploy_lambda.py
# 以下ツールが使用可能
# - get_weather - 天気情報の取得
# - get_time - 現在時刻の取得
# 2. Gatewayのセットアップ
cd ../gateway/
# Lambda ARNを環境変数に設定
echo LAMBDA_TOOLS_ARN=arn:aws:lambda:us-east-1:123456789012:function:agent-tools >> .env
uv run setup_gateway.py
そして、エージェントをRuntimeへデプロイするための手順において、環境変数をどう設定するか、のお話です。
# 3. Runtimeへのデプロイ
cd ../deploy/
uv run deploy_runtime.py
解決策1.Dockerfile内で環境変数設定
まずはDockerfile内で環境変数を設定する方法です。
デプロイスクリプトの修正
agentcore configure
実行後に Dockerfile を自動修正するコードを作成しました(Claudeが)。
以下のサンプルコードは、AgentCore RuntimeからAgentCore Gatewayを経由してLambda関数ツールを使う際に、GatewayのURLと接続用アクセストークンを環境変数に設定するようなものになっています。
.envファイルを参照して、そのキーと値を環境変数としてDockerfile内に記述します。
def modify_dockerfile_with_env_vars():
"""agentcore configure後にDockerfileを修正して環境変数を追加"""
agent_dir = Path('../agent')
dockerfile_path = agent_dir / 'Dockerfile'
gateway_url = os.environ.get('GATEWAY_MCP_URL')
access_token = os.environ.get('GATEWAY_ACCESS_TOKEN')
if not gateway_url or not access_token:
print("Warning: Environment variables not found")
return
# Dockerfileを読み込み
with open(dockerfile_path, 'r') as f:
dockerfile_content = f.read()
# 環境変数ブロックを追加
env_vars_block = f"""
# Gateway configuration environment variables
ENV GATEWAY_MCP_URL={gateway_url}
ENV GATEWAY_ACCESS_TOKEN={access_token}
"""
# AWS_REGION設定の後に挿入
if 'ENV AWS_DEFAULT_REGION=us-east-1' in dockerfile_content:
dockerfile_content = dockerfile_content.replace(
'ENV AWS_DEFAULT_REGION=us-east-1',
f'ENV AWS_DEFAULT_REGION=us-east-1{env_vars_block}'
)
# Dockerfileを書き戻し
with open(dockerfile_path, 'w') as f:
f.write(dockerfile_content)
print("✓ Modified Dockerfile with environment variables")
上記の結果できあがるDockerfileが、以下のような形です。
FROM public.ecr.aws/docker/library/python:3.13-slim
WORKDIR /app
# Copy entire project (respecting .dockerignore)
COPY . .
# Install from requirements file
RUN python -m pip install --no-cache-dir -r requirements.txt
# Set AWS region environment variable
ENV AWS_REGION=us-east-1
ENV AWS_DEFAULT_REGION=us-east-1
# Gateway configuration environment variables
ENV GATEWAY_MCP_URL=https://<gateway-id>.gateway.bedrock-agentcore.us-east-1.amazonaws.com/mcp
ENV GATEWAY_ACCESS_TOKEN=<access_tokenベタ書き>
# Signal that this is running in Docker for host binding logic
ENV DOCKER_CONTAINER=1
RUN python -m pip install aws_opentelemetry_distro_genai_beta>=0.1.2
# Create non-root user
RUN useradd -m -u 1000 bedrock_agentcore
USER bedrock_agentcore
EXPOSE 8080
# Use the full module path
CMD ["opentelemetry-instrument", "python", "-m", "strands_agent"]
そしてデプロイフローにおいて、Dockerfile作成後に上記処理を呼び出すように追加しました。
def main():
# 1. IAM ロール作成
role_arn = create_runtime_role()
# 2. agentcore configure 実行
configure_agent_runtime(role_arn)
# 3. 重要: configure後にDockerfileを修正
modify_dockerfile_with_env_vars()
# 4. agentcore launch実行(修正されたDockerfileを使用)
agent_arn = launch_agent_runtime()
補足:Dockerfile修正処理実行時の注意点
なぜこんな形式にしているかというと、agentcore configure
コマンドは実行のたびに Dockerfile を再生成するためです。
つまり、agentcore configure
コマンドを実行する前にDockerfileを修正しても、実行すると上書きされてしまうということです。
# 手動でDockerfileを編集
echo "ENV MY_VAR=value" >> Dockerfile
# しかし、configureを実行すると...
agentcore configure --entrypoint my_agent.py -er arn:aws:iam::123:role/MyRole
# Dockerfileが完全に再作成され、手動の変更が消失!
また、デプロイのために実行するagentcore launch
コマンドには以下の制約があります。
-
--env
オプションが存在しない - ビルド時の
--build-arg
を直接指定できない - Docker ビルドプロセスを直接制御できない
処理がまるっとラップされているがゆえの弊害ですね。
個別にビルドなどを実行する場合↓は(恐らく)色々やりようがあると思いますが、スターターツールキットを使う場合は注意が必要です。
環境設定ファイル
今回はRuntimeからGatewayに接続するための情報を環境変数として設定してみました。
# agent/.env (エージェント用)
GATEWAY_MCP_URL=https://gateway-id.gateway.bedrock-agentcore.us-east-1.amazonaws.com/mcp
GATEWAY_ACCESS_TOKEN=****************
エージェントコード
※後ほど、軽く中身について解説しています。
# agent/strands_agent.py
from strands import Agent
from strands.models import BedrockModel
from strands.tools.mcp.mcp_client import MCPClient
from bedrock_agentcore.runtime import BedrockAgentCoreApp
import os
# .envファイル読み込み(ローカル開発用)
try:
from dotenv import load_dotenv
load_dotenv()
except ImportError:
pass
app = BedrockAgentCoreApp()
@app.entrypoint
def invoke(payload):
"""エージェント実行のエントリーポイント"""
# 環境変数から設定取得
mcp_url = os.environ.get('GATEWAY_MCP_URL')
access_token = os.environ.get('GATEWAY_ACCESS_TOKEN')
if not mcp_url or not access_token:
return {"error": "Gateway configuration missing"}
# MCP クライアント初期化
mcp_client = MCPClient(lambda: create_streamable_http_transport(mcp_url, access_token))
# エージェント実行
with mcp_client:
tools = get_full_tools_list(mcp_client)
agent = Agent(model=BedrockModel(), tools=tools)
response = agent(payload.get("prompt", "Hello"))
return {"result": str(response)}
if __name__ == "__main__":
app.run()
大きな課題:セキュリティ
ここまで読んでいただいておわかりの方がいるかも知れませんが、アクセストークンがベタ書きなのはセキュリティ的にまずいです。
せめてSecrets ManagerやSystems Managerのパラメータストアを使いたいですよね。
# ❌ 危険:アクセストークンがDockerイメージに埋め込まれる
ENV GATEWAY_ACCESS_TOKEN=abcdefgh12345...
解決策2.Secrets Managerを使う
ということで、Dockerfile内で環境変数を設定する際に、Secrets Managerを使用することにします。
Dockerfile内にARN書くのも微妙な気がしますが…ベタ書きよりはマシかなと…(他に良い設定方法あったら教えて下さい)
# せめて Secrets Manager を使用するべき(Dockerfile内にARN書いていいかは微妙)
ENV GATEWAY_ACCESS_TOKEN_SECRET_ARN=arn:aws:secretsmanager:us-east-1:123:secret:gateway-token
また、これもDockerfileに直接記述するのではなく、agentcore configure
実行後に追記するよう修正を加えておきます。
def modify_dockerfile_with_env_vars():
"""agentcore configure後にDockerfileを修正して環境変数を追加"""
agent_dir = Path('../agent')
dockerfile_path = agent_dir / 'Dockerfile'
gateway_url = os.environ.get('GATEWAY_MCP_URL')
# Dockerfileを読み込み
with open(dockerfile_path, 'r') as f:
dockerfile_content = f.read()
# 既に環境変数が設定されているかチェック(Secrets Manager ARNが設定されているか確認)
if 'GATEWAY_MCP_URL' in dockerfile_content and 'GATEWAY_ACCESS_TOKEN_SECRET_ARN' in dockerfile_content:
print("✓ Environment variables already properly configured in Dockerfile")
return
# 既存のSecrets ManagerからARNを取得
secret_name = "agentcore-gateway-access-token"
secret_arn = None
try:
region_name = os.environ.get('AWS_DEFAULT_REGION', 'us-east-1')
client = boto3.client('secretsmanager', region_name=region_name)
response = client.describe_secret(SecretId=secret_name)
secret_arn = response['ARN']
print(f"✓ Found existing secret ARN: {secret_arn}")
except ClientError as e:
if e.response['Error']['Code'] == 'ResourceNotFoundException':
print(f"Warning: Secret '{secret_name}' not found in Secrets Manager")
else:
print(f"Error accessing Secrets Manager: {str(e)}")
# AWS_REGION設定の後に環境変数を挿入
if secret_arn:
env_vars_block = f"""
# Gateway configuration environment variables
ENV GATEWAY_MCP_URL={gateway_url}
ENV GATEWAY_ACCESS_TOKEN_SECRET_ARN={secret_arn}
"""
最終的なDockerfileが以下のようになります。URLは一旦そのままです。
(URLわかってもアクセストークン無いとツール実行できないので、まぁ一旦ええか…の精神です)
```Dockerfile:Dockerfile
FROM public.ecr.aws/docker/library/python:3.13-slim
WORKDIR /app
# Copy entire project (respecting .dockerignore)
COPY . .
# Install from requirements file
RUN python -m pip install --no-cache-dir -r requirements.txt
# Set AWS region environment variable
ENV AWS_REGION=us-east-1
ENV AWS_DEFAULT_REGION=us-east-1
# Gateway configuration environment variables
ENV GATEWAY_MCP_URL=https://<gateway-id>.gateway.bedrock-agentcore.us-east-1.amazonaws.com/mcp
ENV GATEWAY_ACCESS_TOKEN_SECRET_ARN=arn:aws:secretsmanager:us-east-1:123:secret:gateway-token
# Signal that this is running in Docker for host binding logic
ENV DOCKER_CONTAINER=1
RUN python -m pip install aws_opentelemetry_distro_genai_beta>=0.1.2
# Create non-root user
RUN useradd -m -u 1000 bedrock_agentcore
USER bedrock_agentcore
EXPOSE 8080
# Use the full module path
CMD ["opentelemetry-instrument", "python", "-m", "strands_agent"]
エージェント側では、boto3でSecrets Managerの情報を取得しつつ、Gatewayのアクセスを実行するような処理を記述します。
※Gatewayは別スクリプトを用いて作成済みの前提です。
from strands import Agent
from strands.models import BedrockModel
from strands.tools.mcp.mcp_client import MCPClient
from mcp.client.streamable_http import streamablehttp_client
from bedrock_agentcore.runtime import BedrockAgentCoreApp
import os
import boto3
from botocore.exceptions import ClientError
app = BedrockAgentCoreApp()
def get_secret(secret_name: str, region_name: str = None) -> str:
"""Secrets Managerから秘密情報を取得"""
if not region_name:
region_name = os.environ.get('AWS_DEFAULT_REGION', 'us-east-1')
# Create a Secrets Manager client
session = boto3.session.Session()
client = session.client(
service_name='secretsmanager',
region_name=region_name
)
try:
get_secret_value_response = client.get_secret_value(SecretId=secret_name)
return get_secret_value_response['SecretString']
except ClientError as e:
print(f"Error retrieving secret {secret_name}: {str(e)}")
raise e
# MCPクライアントがGatewayと通信するためのHTTPトランスポート層を作成する関数
def create_streamable_http_transport(mcp_url: str, access_token: str):
return streamablehttp_client(mcp_url, headers={"Authorization": f"Bearer {access_token}"})
# MCPサーバーからすべてのツール情報を取得する関数
def get_full_tools_list(client):
more_tools = True
tools = []
pagination_token = None
while more_tools:
tmp_tools = client.list_tools_sync(pagination_token=pagination_token)
tools.extend(tmp_tools)
if tmp_tools.pagination_token is None:
more_tools = False
else:
more_tools = True
pagination_token = tmp_tools.pagination_token
return tools
@app.entrypoint
def invoke(payload):
"""エージェント呼び出しのエントリーポイント"""
# Gateway MCP URLを環境変数から取得
mcp_url = os.environ.get('GATEWAY_MCP_URL')
if not mcp_url:
return {"error": "Gateway configuration missing. Please set GATEWAY_MCP_URL environment variable."}
# アクセストークンをSecrets Managerから取得
try:
# 環境変数でSecrets Manager ARNが指定されている場合はそれを使用
secret_name = os.environ.get('GATEWAY_ACCESS_TOKEN_SECRET_ARN') or os.environ.get('GATEWAY_ACCESS_TOKEN_SECRET_NAME')
if secret_name:
access_token = get_secret(secret_name)
else:
# フォールバック:環境変数から直接取得(開発用)
access_token = os.environ.get('GATEWAY_ACCESS_TOKEN')
if not access_token:
return {"error": "Gateway access token missing. Please set GATEWAY_ACCESS_TOKEN_SECRET_ARN or GATEWAY_ACCESS_TOKEN environment variable."}
except Exception as e:
return {"error": f"Failed to retrieve access token: {str(e)}"}
# モデルの設定
bedrockmodel = BedrockModel(
inference_profile_id="anthropic.claude-3-7-sonnet-20250219-v1:0",
temperature=0.7
)
# MCPクライアントの初期化
mcp_client = MCPClient(lambda: create_streamable_http_transport(mcp_url, access_token))
try:
with mcp_client:
tools = get_full_tools_list(mcp_client)
agent = Agent(model=bedrockmodel, tools=tools)
# ペイロードからプロンプトを取得
user_message = payload.get("prompt", "Hello")
# エージェントを実行
response = agent(user_message)
# レスポンスを文字列に変換
if hasattr(response, 'message'):
result = response.message
else:
result = str(response)
return {"result": result}
except Exception as e:
print(f"Error during agent execution: {str(e)}")
return {"error": f"Agent execution failed: {str(e)}"}
if __name__ == "__main__":
app.run()
これでエージェントに必要な認証情報を持つことができ、Gatewayを経由してツールを実行できるようになります。
参考までに実行結果は以下の通りです。きちんとツールを参照して、(Mockの)天気情報を取得してくれました。
まとめ
Bedrock AgentCore Runtime での環境変数設定についての話でした。
-
agentcore configure
後の Dockerfile 自動修正が最も確実な方法 - セキュリティ面を考慮して、Secrets ManagerやSystems Managerも用いる
- 個別にカスタマイズしたい場合は、スターターツールキットを使わない
スターターツールキットを用いることで自動作成される、.bedrock_agentcore.yaml
というファイルがあります。
ここの中でEnvironmentを定義してあげることも考えて、agentcore configure
実行後に同じように修正するような形で挑戦してみたのですが、上手くいきませんでした。
どうやらagentcore launch
時にこのファイルが書き換えられてしまうようで、ラップされた一連処理の中に独自処理を入れるのは難しいため、今回はDockerfileを編集する方法を採用しています。
恐らくスターターツールキットを使わなければ、この方法でもいけると思います。