11
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Bedrock AgentCore Runtime向けに環境変数を設定して、Gatewayへアクセス!

Last updated at Posted at 2025-07-23

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が、以下のような形です。

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: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は別スクリプトを用いて作成済みの前提です。

strands_agent.py
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の)天気情報を取得してくれました。
image.png

まとめ

Bedrock AgentCore Runtime での環境変数設定についての話でした。

  1. agentcore configure 後の Dockerfile 自動修正が最も確実な方法
  2. セキュリティ面を考慮して、Secrets ManagerやSystems Managerも用いる
  3. 個別にカスタマイズしたい場合は、スターターツールキットを使わない

スターターツールキットを用いることで自動作成される、.bedrock_agentcore.yamlというファイルがあります。

ここの中でEnvironmentを定義してあげることも考えて、agentcore configure実行後に同じように修正するような形で挑戦してみたのですが、上手くいきませんでした。

どうやらagentcore launch時にこのファイルが書き換えられてしまうようで、ラップされた一連処理の中に独自処理を入れるのは難しいため、今回はDockerfileを編集する方法を採用しています。

恐らくスターターツールキットを使わなければ、この方法でもいけると思います。

11
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?