멀티 테넌트 LLM 앱: 공유 모델 위에서 고객을 격리하기
하나의 공유 Bedrock 모델, 여러 고객. 모델은 상태를 갖지 않으므로 격리는 여러분의 몫이다. 검색 범위를 정하고, 쿼터를 제한하고, 테넌트별 아이덴티티를 끝까지 전달하라.

여러 고객이 하나의 Bedrock 모델을 공유할 때, 모델은 어떤 격리도 제공하지 않으며 애초에 그럴 의도도 없었다. 모델은 상태를 갖지 않는 함수다. 같은 입력, 같은 동작, 누가 호출했는지에 대한 기억이 전혀 없다. 테넌트 간의 모든 경계는 모델 주변의 계층에서 직접 구축해야 하며, 모델로부터 기대할 수 없다. 팀들은 이를 거꾸로 이해해서 관리형 서비스가 알아서 분리해 줄 것이라 가정하고, 한 테넌트의 데이터, 비용, 부하가 다른 테넌트로 새어나가는 앱을 그대로 배포한다. 공유 모델 자체는 문제없다. 문제는 그 외의 모든 것을 함께 공유하는 것이다.
다행스러운 점은 모델이 상태를 갖지 않는다는 바로 그 특성이 멀티 테넌시를 다룰 만하게 만들어 준다는 것이다. 모델 안에 새어 나갈 테넌트별 상태 자체가 없다. 상태가 아예 없기 때문이다. 격리는 여러분이 이미 만들 줄 아는 세 가지 구체적인 경계로 압축된다. 테넌트의 요청이 어떤 데이터에 닿을 수 있는가, 공유 용량 중 얼마를 소비할 수 있는가, 그리고 어떤 아이덴티티로 실행되는가.
데이터 격리: 모델이 아니라 검색 범위를 정하라
RAG나 에이전트 앱에서 실제로 새어 나갈 위험이 있는 것은 가중치가 아니라 그 앞에 놓는 컨텍스트다. 테넌트 A의 질문이 테넌트 B의 문서를 검색해 온다면, 도움이 되는 답변으로 위장한 데이터 유출이 발생한 것이다. 그래서 테넌트 분리는 검색 계층에 존재해야 한다.
경계가 얼마나 엄격해야 하는지에 따라 실용적인 두 가지 형태가 있다.
- 테넌트별 Knowledge Base. 가장 확고한 경계다. 각 테넌트가 자신만의 Bedrock Knowledge Base를 가지므로, 쿼리는 오직 그 테넌트의 코퍼스에서만 검색할 수 있다. 추론하기는 가장 깔끔하지만, 테넌트 수가 늘어날수록 관리할 요소도 늘어난다.
- 공유 저장소, 테넌트 필터링. 하나의 벡터 저장소에 모든 쿼리에 필수적인 테넌트 ID 필터를 걸어 결과를 메타데이터로 범위 지정한다. 운영 비용은 저렴하지만, 이제 그 필터는 보안의 핵심 요소가 된다. 반드시 인증된 아이덴티티로부터 서버 측에서 적용되어야 하며, 클라이언트가 설정할 수 있는 값으로 적용해서는 안 된다.
피해야 할 실패는 요청 본문에 담겨 온 테넌트 ID로 필터링하는 것이다. 클라이언트가 자신의 테넌트를 지정할 수 있다면, 다른 사람의 테넌트도 지정할 수 있다. 테넌트는 인증된 주체로부터 도출하고, 클라이언트가 손댈 수 없는 곳에서 그 범위를 적용하라.
시끄러운 이웃: 공유 쿼터는 공유 장애다
Bedrock은 계정 및 모델 수준에서 분당 토큰 수를 기준으로 스로틀링한다. 그 숫자는 계정 내에서 해당 모델을 호출하는 모든 테넌트가 공유한다. 그래서 한 테넌트가 무거운 배치 작업을 돌리면 공유 토큰 예산을 소비하고, 자신은 아무 부하도 발생시키지 않은 다른 테넌트들이 스로틀링 오류를 겪기 시작한다. 모델은 논리적으로는 격리되어 있지만 운영적으로는 결합되어 있다.
여기서 격리란 공유 상한선이 대신 처리하기 전에 테넌트별로 미리 계량하고 제한하는 것을 의미한다.
- 테넌트별 토큰 예산. 테넌트별, 윈도우별로 소비된 토큰을 추적하고, 자신의 할당량을 초과한 테넌트는 거부하거나 대기시켜서, 그 급증이 공유 풀을 잡아먹지 못하게 한다.
- 공정한 큐잉. 모델 앞에 테넌트별로 상한이 있는 워커 풀을 두면, 한 테넌트의 버스트가 그 테넌트만의 지연으로 그치지, 모두의 장애로 번지지 않는다.
- 우선순위 등급. 테넌트가 서로 다른 서비스 수준에 대해 비용을 지불한다면, 이를 어드미션 컨트롤 단계에서 강제하라. 무료 등급의 배치 작업이 유료 테넌트의 인터랙티브 트래픽을 굶겨서는 안 된다.
아이덴티티: 테넌트 경계는 도구까지 닿아야 한다
격리는 검색에서 멈춰서는 안 된다. 에이전트가 도구를 호출하거나, 데이터베이스를 읽거나, 스토리지에 쓰는 순간, 테넌트 경계도 그 요청과 함께 이동해야 한다. 즉 요청은 그 테넌트로 범위가 지정된 아이덴티티 아래에서 실행되어야 하며, 그래야 모델이 잘못된 데이터를 요청하도록 유도되더라도 도구 뒤에 있는 자격 증명이 그것을 가져올 수 없다. 모델 계층은 아무것도 신뢰하지 않고, IAM 계층이 모든 것을 강제한다. 이것이 테넌트 단위로 적용되는 최소 권한 원칙이며, 프롬프트 인젝션 시도가 테넌트 간 데이터 유출로 번지지 않게 막아 주는 장치다.
결론
공유 모델이 모든 것을 공유하는 앱을 의미하지는 않는다. 모델은 상태를 갖지 않으므로 격리를 제공하지도 않고, 자체적으로 보호해야 할 것도 없다. 여러분의 몫은 그 주위에 있는 세 가지 경계다. 테넌트가 오직 자신의 데이터만 볼 수 있도록 검색 범위를 지정하고, 공유 토큰 쿼터에서 어떤 테넌트도 다른 테넌트를 굶기지 않도록 용량을 계량하고 제한하며, 프롬프트 계층이 속아 넘어갔을 때 마지막 방어선이 되도록 범위가 지정된 자격 증명이 도구까지 전달되게 테넌트별 아이덴티티를 유지하라. 이 세 가지를 갖추면 하나의 모델이 여러 고객에게 안전하게 서비스를 제공할 수 있다. 하나라도 빠뜨리면 모델은 기꺼이 엉뚱한 고객의 데이터를 엉뚱한 고객에게 내어줄 것이다.
다음으로 읽을 글
- IAM for LLM Apps: Least Privilege When the Caller Is a Model, 테넌트 경계를 도구까지 전달하는 범위가 지정된 아이덴티티에 관한 글이다.
- Cutting Amazon Bedrock Knowledge Base Costs by ~90% with pgvector, 공유 검색을 저렴하게 만드는 테넌트 필터링 벡터 저장소에 관한 글이다.
같은 문제의 플랫폼 및 멀티 계정 격리 측면은 ercan.cloud의 클라우드 필드 노트에서, 허브는 ercanermis.com에서 확인할 수 있다.
Ercan의 다른 글
같은 저자, 다른 영역의 사이트 두 개.