본문으로 건너뛰기

© 2026 Molayo

Zenn헤드라인2026. 05. 21. 10:42

SaaS로 AI Agent를 제공하려는 분들을 위한 Amazon Bedrock AgentCore 멀티테넌트 구현 - Runtime 편

요약

SaaS 형태로 AI Agent를 제공할 때 필수적인 멀티테넌트(Multi-tenant) 구현 전략을 Amazon Bedrock AgentCore Runtime을 중심으로 설명합니다. 테넌트별로 리소스를 분리하는 사이로 모델(Silo Model)과 리소스를 공유하는 풀 모델(Pool Model)의 특징, 선택 기준 및 구현 패턴을 다룹니다.

핵심 포인트

  • SaaS 애플리케이션 설계 시 컨트롤 플레인과 애플리케이션 플레인의 분리가 필요함
  • 사이로 모델은 테넌트별 독립적인 설정(예: 개별 ID 프로바이더)이 가능하지만 운영 복잡도가 높음
  • 풀 모델은 리소스를 공유하여 효율적이지만 테넌트 간 격리 설계가 중요함
  • 사이로 모델 구현 시 테넌트 요청을 적절히 전달할 라우팅 레이어 설계가 필수적임

SaaS로 AI Agent를 제공하려는 분들을 위한 Amazon Bedrock AgentCore 멀티테넌트 구현 - Runtime 편

처음 뵙겠습니다, AWS Japan에서 솔루션 아키텍트(Solution Architect)로 활동하고 있는 showish & fukumo_to 입니다.

「테넌트마다 리소스를 분리하는 것이 좋을까!?」, 「테넌트 격리(Tenant Isolation)는 어떻게 해야 하지!?」 등등, 멀티테넌트(Multi-tenant) 서비스나 시스템을 개발하면서 고민하시는 분들이 많지 않을까요?

본 기사에서는 Amazon Bedrock AgentCore Runtime을 주제로, 사이로 모델(Silo Model)과 풀 모델(Pool Model)이라는 두 가지 배포 모델을 축으로 하여 실무에서도 사용할 수 있는 지식을 전달해 드립니다.

이 기사를 통해 얻을 수 있는 것:

  • 사이로/풀 모델의 선택 기준과 AgentCore Runtime에서의 구현 패턴
  • 사이로 모델에서의 IAM 인증 및 JWT 인증 각각의 구현 코드
  • 세션 ID(Session ID)에 테넌트 정보를 포함하는 설계와 그 운영상의 의미
  • 구현 시 마주하기 쉬운 함정과 대처법

1. 멀티테넌트에서의 배포 모델

1.1 사이로 모델과 풀 모델

SaaS 구현에 있어서는 멀티테넌트 환경의 온보딩(Onboarding), 인증, 관리, 운영, 분석 등을 담당하는 컨트롤 플레인(Control Plane)과, 사용자에게 제공되는 기능을 포함하는 애플리케이션 플레인(Application Plane)의 두 가지 요소로 나뉩니다. 애플리케이션 플레인에는 몇 가지 배포 패턴이 존재합니다.

  • 테넌트마다 리소스를 점유하는 「사이로 모델 (Silo Model)"
  • 테넌트 간에 리소스를 공유하는 「풀 모델 (Pool Model)"
  • 레이어나 컴포넌트에 따라 이들을 조합한 「브릿지 모델 (Bridge Model)"
  • 특정 테넌트에는 사이로 모델로, 다른 테넌트 그룹에는 풀 모델로 제공하는 등의 하이브리드 모델

이러한 모델은 보안, 비용, 운영의 복잡성 등의 트레이드오프(Trade-off)를 고려하여 선택하게 됩니다.

1.2 Amazon Bedrock AgentCore Runtime에서의 배포 모델

AgentCore Runtime에서 앞서 언급한 모델들을 구현하려면 어떻게 해야 할까요?

Runtime 단독으로 생각하면 채택할 수 있는 모델은 사이로 모델 혹은 풀 모델뿐이므로, 각각에 대해 생각해 보겠습니다.

1.2.1 사이로 모델

Runtime은 「런타임 리소스 (Runtime Resource)」라는 단위로 리소스와 그에 연결된 설정을 생성합니다.

사이로 모델을 선택하는 경우에는 이 런타임 리소스를 테넌트마다 생성하는 형태가 됩니다.

운영 및 관리의 복잡성이 높아지는 점, 각 테넌트별 설정을 과도하게 고유하게 만드는 것은 피해야 하지만, 요구사항에 따라 설정을 변경할 수 있다는 점 자체는 장점입니다. 예를 들어 테넌트마다 서로 다른 ID 프로바이더(Identity Provider)를 연결해야 하는 요구사항이 있는 경우에는 사이로 모델을 선택함으로써 심플하게 대응할 수 있습니다.

위 그림에서 보듯이, 사이로 모델에서는 테넌트마다 리소스가 분리되기 때문에 테넌트로부터의 요청을 적절하게 라우팅(Routing)하는 레이어가 필요합니다.

이 라우팅 레이어는 로드 밸런서(Load Balancer)나 DNS 등 인프라 레이어에서 구현할 수도 있고, 애플리케이션 컴포넌트로서 구현하는 것도 고려할 수 있습니다.

AWS 상에서의 구현에 관해서는, AWS에서의 SaaS 애플리케이션 테넌트 라우팅 전략이 참고가 됩니다.

1.2.2 풀 모델

풀 모델은 모든 테넌트가 런타임 리소스를 공유합니다.

런타임 리소스에 연결된 설정이 공통이 되며, 리소스 레벨에서 개별 요구사항에 대응하는 것은 어려워집니다. 관리 대상이 줄어든다는 점에서는 편해 보이지만, 특정 테넌트의 이용이 다른 테넌트에 영향을 주는 「노이지 네이버 (Noisy Neighbor)」 문제나, 테넌트 간의 데이터 액세스 등을 방지하기 위한 테넌트 격리 메커니즘을 확실히 마련해야 하는 등 운영의 허들은 높아집니다.

풀 모델 구현 (JWT 기반 인증, 테넌트 격리의 다층 방어, Token Vending Machine 등)은 본 기사에서 모두 다루기 어렵기 때문에, 다음 회차인 「Identity·인증 편」에서 모아서 전달해 드리겠습니다.

본 기사에서는 이후 내용으로 사이로 모델에서의 테넌트 격리와 세션 관리에 대해 Dive Deep 해 보겠습니다.

2. 사이로 모델에서의 테넌트 격리

2.1 Amazon Bedrock AgentCore Runtime에서의 테넌트 격리

Runtime은 세션 격리 (Session Isolation) 메커니즘을 제공하며, 요청 시 헤더에 포함된 값에 따라 실행 환경인 MicroVM이 격리됩니다.

프로토콜세션 헤더
HTTP / A2A / AG-UIX-Amzn-Bedrock-AgentCore-Runtime-Session-Id
MCPMcp-Session-Id

※ 헤더를 포함하지 않는 경우, 응답에 포함된 자동 생성된 값을 계속 사용함으로써 세션을 지속적으로 이용할 수 있습니다.

또 다른 핵심 요소는 런타임 리소스 (Runtime Resource) 설정에 있는 인바운드 인증 (Inbound Authentication)입니다.

인바운드 인증 유형:

  • IAM 권한 사용 — AWS 콘솔 로그인에 사용된 IAM Principal이 사용됩니다.
  • JSON Web Tokens (JWT) 사용 — JWT (OAuth 토큰 등)를 인바운드 인증으로 설정하여, 수신된 토큰의 서명(Signature)과 스코프(Scope)를 검증합니다.

사이로 모델 (Silo Model)에서는 리소스가 분리되어 있기 때문에 테넌트 격리가 비교적 단순합니다.

위 그림은 예시로 Amazon API Gateway를 경유하여 AWS Lambda가 실행되고, Lambda 함수 내에서 Amazon DynamoDB로부터 테넌트에 따른 런타임 리소스의 엔드포인트(Endpoint)를 가져와 Runtime을 실행하는 구조를 보여줍니다. 또한, 테넌트별 세션 정보 관리도 DynamoDB에서 수행합니다.

여기서 Runtime과 Identity가 연동된 인증에서는 IAM과 JWT 인증 방식 중 하나를 설정합니다. 두 패턴의 구현 방식에 대해 설명하겠습니다.

2.1.1 사이로 모델에서의 인증 방식 - JWT

사이로 모델에서는 런타임 리소스가 분리되어 있으므로 IdP (Identity Provider)도 테넌트별로 나누는 구성이 자연스러워 보이지만, 실제 운영에서는 공통 IdP를 사용하는 케이스도 많습니다. 사용자 관리나 로그인 경험을 일원화하고 싶은 반면, JWT로부터 테넌트를 식별하는 메커니즘이 별도로 필요하다는 트레이드오프 (Trade-off)가 존재합니다.

위 그림에서 보여준 샘플 아키텍처 (Sample Architecture)에서, JWT는 클라이언트의 요청 시 헤더에 실려 Lambda 함수로 전달됩니다.

Lambda 함수 내에서는 JWT 검증을 수행하고, 토큰 내의 속성 정보 (Attribute Information)를 바탕으로 DynamoDB에 저장된 라우팅 대상(Routing Destination)을 가져옵니다.

Runtime에서 JWT 인증을 이용할 경우, Runtime 엔드포인트는 boto3와 같은 AWS SDK를 이용한 호출이 불가능하며, HTTP 클라이언트를 사용하여 HTTP 요청을 보내야 합니다.

# JWT 인증 시에는 boto3가 아닌 HTTP 요청을 보냄
import urllib.parse
import requests
...

Runtime 측에서도 JWT 검증을 수행하여 적절한 인가 (Authorization)가 실행되지만, 만약 Lambda 함수의 로직이 잘못될 경우 다른 테넌트의 리소스에 접근할 위험이 있으므로 주의가 필요합니다.

2.1.2 사이로 모델에서의 인증 방식 - IAM

앞서 설명한 아키텍처 그림을 바탕으로 설명을 이어가겠습니다.

IAM 인증을 이용할 경우, 요청 측이 사용하는 IAM Role을 통해 엔드포인트에 대한 접근이 허용되어 있는지가 핵심입니다.

다만, 이 경우에도 Lambda는 어떤 테넌트로부터의 접근인지 판단할 재료, 즉 테넌트 컨텍스트 (Tenant Context)가 필요합니다. 이는 ID Token일 수도 있고, Cookie 등을 이용한 세션 정보 등 여러 가지 방법이 있을 수 있습니다.

Lambda가 Runtime 엔드포인트에 접근할 때, 모든 테넌트의 리소스에 대한 권한을 갖는 방법으로는 다음과 같은 방법들이 떠오를 것입니다.

  • Lambda에 어태치 (Attach)하는 IAM Role로 모든 테넌트를 커버한다.
  • 테넌트별 IAM Role을 준비하고, Lambda는 해당 역할들에 대한 Assume Role 권한을 갖는다.

이러한 방법들이 가장 먼저 생각날 것입니다.

첫 번째 방법은 크로스 테넌트 액세스 (Cross-tenant access)의 가능성을 높이기 때문에 가급적 채택하고 싶지 않으며, 테넌트가 추가될 때마다 IAM Role에 연결된 정책 (Policy)을 수정해야 합니다. 자동화를 한다고 하더라도, Policy의 문자 수 크기 제한이 우려됩니다.

두 번째 방법 역시 테넌트 수만큼 IAM Role을 생성하여 관리해야 한다는 점과 쿼터 (Quota)가 걱정됩니다.

이러한 상황에서 이용할 수 있는 접근 방식에 IAM의 동적 생성 (Dynamic Generation)이 있습니다.

자세한 내용은 '동적으로 생성된 IAM 정책으로 SaaS 테넌트를 격리하기'를 읽어주시길 바라며, 개요를 설명하겠습니다.

위 그림과 같이, 테넌트에 공통되는 요소만을 기술한 IAM 정책을 연결한 테넌트 간 공통 IAM Role을 준비해 둡니다. Lambda에 어태치 (Attach)하는 IAM Role에는 해당 테넌트 공통 IAM Role을 수임 (Assume Role)할 수 있는 권한을 부여해 둡니다.

Lambda 함수 내에서는 테넌트 컨텍스트 (Tenant Context)를 바탕으로 테넌트의 엔드포인트 (Endpoint)를 가져오고, 해당 엔드포인트로 권한의 스코프 (Scope)를 좁히는 정책 내용을 코드 내에서 구성합니다.

AssumeRole

API 실행 시에는 테넌트 공통 Role과 동적으로 구성한 정책을 전달함으로써, 정책 간의 AND 연산이 이루어져 특정 테넌트에 스코프를 좁힌 액세스 권한을 동적으로 발행할 수 있습니다.

# AssumeRole 시 session policy를 전달하여 권한을 제한하는 예
scoped_policy = {
"Version": "2012-10-17",
...

이 방법을 채택함으로써 테넌트 수만큼의 IAM Role을 관리하지 않고도 확장 가능한 구현을 할 수 있습니다. 동일한 개념은 풀 모델 (Pool Model)에서의 DynamoDB 항목 레벨 테넌트 격리에서도 중요합니다 (다음 회차 이후에서 자세히 다룹니다).

2.1.3 사이로 모델 (Silo Model)에서의 Runtime 세션 관리

Runtime은 세션 격리 메커니즘이 제공되며, 요청 시 헤더에 포함한 값에 따라 실행 환경인 MicroVM이 격리됩니다. 앞서 언급한 바와 같이, HTTP/A2A/AG-UI에서는 X-Amzn-Bedrock-AgentCore-Runtime-Session-Id를, MCP에서는 Mcp-Session-Id 헤더를 사용합니다.

여기서 중요한 점은, MicroVM의 격리 단위는 '세션'이지 '테넌트'나 '사용자'가 아니라는 것입니다. AgentCore는 세션과 사용자의 매핑을 강제하지 않으므로, 어떤 입도(Granularity)로 세션을 끊을지는 호출 측의 설계에 달려 있습니다.

사이로 모델에서는 Runtime 리소스 자체가 테넌트별로 나뉘어 있기 때문에, 세션 레벨에서의 테넌트 격리는 이미 보장되어 있습니다. 남은 논점은 **'동일 테넌트 내에서 사용자나 비즈니스 컨텍스트 (채팅 대화, 서포트 티켓 등)를 어떻게 연결할 것인가'**입니다.

여기서 의식해야 할 점은, Runtime 측이 관리하는 세션과 애플리케이션 측에서 다루고 싶은 세션은 입도와 수명(Lifecycle)이 다르다는 점입니다. 정리하면 다음 두 축이 됩니다.

세션 ID격리 단위수명용도
Runtime 세션 IDMicroVM기본 15분 idle timeout / 최대 8시간에이전트 실행 환경의 격리
비즈니스 세션 ID대화·티켓·태스크 등업무 요구사항에 따라 다름 (수 시간~영구적)사용자 경험으로서의 대화 연속성

Runtime 세션은 공식 문서에 따르면 idle timeout의 기본값이 15분이며, 최대 라이프사이클 (Lifecycle)은 8시간입니다. 즉, 고객 지원 채팅처럼 '사용자가 다음 날 다시 말을 거는' 유스케이스에서는 Runtime 세션만으로는 대화의 연속성을 보장할 수 없습니다.

이 두 층의 세션 ID를 어디에 영속화할지는 유스케이스나 채택하고 있는 프레임워크에 따라 몇 가지 선택지가 있습니다.

  • DynamoDB와 같은 KVS에 비즈니스 세션과 Runtime 세션의 매핑을 보유
  • Strands Agents SDKFileSessionManager

등을 사용하여, 파일 스토리지(로컬 / S3 등)에 대화 상태별로 저장합니다. -
AgentCore Memory에 사용자 단위의 장기 기억을 모으고, 비즈니스 세션 측은 최소한의 메타데이터만 관리합니다.

구현 예시로서, DynamoDB에 매핑을 가지는 경우의 스키마는 다음과 같습니다.

속성설명
business_session_id (PK)ticket-12345비즈니스적으로 영속하는 식별자
tenant_idtenant_acme라우팅 및 감사(Audit)용
runtime_session_idtenant_acme:user-123:a1b2c3...Runtime 호출 시의 헤더 값
last_access_time2026-01-15T10:30:00Zidle timeout을 넘었는지 여부 판정
runtime_arnarn:aws:...사이로(Silo) 모델에서의 라우팅 대상
세션 ID 자체에 테넌트 정보를 포함하기

또 다른, 실무적으로 매우 효과적인 관행(Practice)으로, 세션 ID 안에 테넌트 ID를 포함하는 것이 있습니다.

# 권장: 테넌트·사용자·고유 ID를 연결
runtime_session_id = f"{tenant_id}:{user_id}:{uuid.uuid4()}"
# 예: "tenant_acme:user-123:a1b2c3d4-e5f6-7890-abcd-ef1234567890"

이는 AgentCore Memory의 actorId 설계({tenant_id}:{user_id} 형식)와도 일치시키는 개념이며, 다음과 같은 장점이 있습니다.

  • CloudWatch Logs / X-Ray에서 테넌트 단위의 그룹화가 용이session.id를 접두사(prefix) 검색하는 것만으로 테넌트별 트레이스(Trace) 추출이 가능합니다.
  • 실수로 세션 ID를 넘나들더라도, 로그를 보면 크로스테넌트(Cross-tenant)임을 즉시 알아챌 수 있습니다.
  • Observability 편에서 후술할 trace_attributes와 정합성을 맞추기 쉽습니다.
과거 대화를 재개할 때의 플로우

이중 세션 ID와 영속화 레이어를 조합하면, 과거 대화의 재개는 다음과 같은 흐름이 됩니다.

  • 사용자가 과거의 티켓(business_session_id)을 엽니다.
  • 서버 측에서 business_session_id를 키로 하여 runtime_session_idlast_access_time을 가져옵니다.
  • last_access_time이 idle timeout(기본 15분) 이내라면, 동일한 runtime_session_id로 호출하여 실행 컨텍스트(Execution Context)를 지속합니다.
  • idle timeout을 초과했다면, 새로운 runtime_session_id를 발행한 후, 과거의 대화 이력을 AgentCore Memory나 Strands Agents SDK의 Session Manager 등으로부터 읽어 들여 에이전트에게 전달합니다.

이 설계를 통해 Runtime 세션의 라이프사이클과 사용자 경험으로서의 대화 연속성을 서로 다른 관심사(Separation of Concerns)로 다룰 수 있게 됩니다.

세션 ID의 라이프사이클 설계

AgentCore Runtime은 세션 ID 단위로 실행 컨텍스트를 관리하기 때문에, 동일한 세션 ID로 호출하는 동안에는 대화 상태가 그대로 유지됩니다. 반대로 새로운 세션 ID로 호출하면 별도의 실행 컨텍스트로 취급됩니다.

이 특성을 활용하기 위해, 비즈니스 세션(티켓 ID 등)과 Runtime 세션을 위와 같이 나누어 두면, last_access_time이 idle timeout(기본 15분)을 초과했을 때만 새로운 Runtime 세션 ID를 발행하는 등의 라이프사이클 제어가 가능해집니다. 대화의 연속성이 필요한 동안에는 세션 ID를 유지함으로써 사용자 경험을 해치지 않을 수 있습니다.

3. 다음 예고

다음 시간에는 구현 난이도가 더 높은 풀 모델(Pool Model)에서의 인증과 테넌트 격리를 다룹니다.

  • Amazon Cognito와 Pre Token Generation Lambda V2를 통한 JWT (JSON Web Token)로의 테넌트 정보 주입
  • TenantMetadata 테이블을 통한 사용자 ↔ 테넌트 매핑
  • Token Vending Machine 패턴을 통한 DynamoDB 항목 수준(Item-level)의 테넌트 격리
  • Runtime Inbound Auth의 JWT Authorizer 설정
  • 다층 방어 (JWT 검증 → 애플리케이션 계층 → IAM 계층)

핸즈온으로 직접 실습해보고 싶은 분들을 위해

본 기사의 내용은 아래의 AWS Workshop을 통해 실제로 직접 실습하며 배울 수 있습니다. Lab 1에서 간단한 에이전트를 Runtime에 배포하고, Lab 3 이후부터 풀 모델(Pool Model)에서의 테넌트 격리를 단계적으로 구현해 나가는 구성으로 되어 있습니다.

Discussion

AI 자동 생성 콘텐츠

본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0