Amazon Web Services 한국 블로그

AWS 서버리스 서비스를 사용하여 멀티테넌트 SaaS 솔루션 구축하기

이 글은 AWS 파트너 블로그의 Building a Multi-Tenant SaaS Solution Using AWS Serverless Services를 기반으로 서호성, 이의덕 AWS 파트너 솔루션즈 아키텍트가 한국어로 번역 및 편집하였습니다.

SaaS(서비스형 소프트웨어) 제공 모델로의 전환에는 비용 및 운영 효율성을 극대화하려는 요구가 수반됩니다. 이는 테넌트 활동을 예측하기 어려운 멀티 테넌트 환경에서 특히 어려울 수 있습니다. 테넌트 활동을 실제 리소스 소비와 연계하는 다양한 확장 전략의 조합을 찾는 것은 쉽지 않을 수 있습니다. 오늘 효과가 있는 전략이 내일은 효과가 없을 수도 있죠.

이러한 특성으로 인해 SaaS는 서버리스 모델에 매우 적합합니다. SaaS 아키텍처에서 서버라는 개념을 제거함으로써 조직은 매니지드 서비스를 통해 애플리케이션이 소비하는 정확한 수의 리소스를 제공하고 확장할 수 있습니다.

따라서 애플리케이션의 아키텍처 및 운영 절차가 단순화되므로 확장 정책을 지속적으로 추적하고 관리할 필요가 없습니다. 또한 관리형 서비스에 운영 책임을 더 많이 맡겨 운영 오버헤드와 복잡성을 줄일 수 있습니다.

이 게시물에서는 기능적인 멀티 테넌트 서버리스 SaaS 환경을 종합적으로 볼 수 있는 참조 솔루션을 살펴보겠습니다. 목표는 이 참조 솔루션을 만드는 데 사용된 아키텍처 및 설계 고려 사항을 살펴보는 것입니다.

여기에는 서버리스 모델을 이용하는 Amazon API Gateway, Amazon Cognito, AWS Lambda, Amazon DynamoDB, AWS CodePipeline, Amazon CloudWatch와 같은 Amazon Web Services(AWS) 서버리스 서비스를 활용하는 방법을 살펴보는 것도 포함됩니다.

레퍼런스 솔루션은 온보딩, 테넌트 격리, 데이터 파티셔닝, 테넌트 배포 파이프라인, 옵저버빌리티 등 멀티 테넌트 SaaS 솔루션을 구축하는 데 필요한 여러 구성 요소를 보여줍니다. 이 GitHub 리포지토리를 통해서 AWS 계정에서 참조 솔루션을 배포하고 확인해 볼 수 있습니다.

다양한 배포 모델 지원

이 서버리스 SaaS 참조 솔루션의 세부 사항을 살펴보기 전에 이 환경에서 지원하는 다른 배포 모델을 간략하게 설명하는 것이 도움이 될 듯 합니다. 솔루션에는 두 가지 배포 모델(사일로 및 풀)이 포함되어 있으며, 이러한 모델은 서버리스 SaaS 환경의 온보딩, 격리, Noizy Neighbor, 성능 및 계층화 프로필에 어떤 영향을 미치는지 조명합니다.

사일로 모델의 경우 각 테넌트는 고유한 인프라 리소스들을 가지고 있습니다. 반면 풀 모델에서는 모든 테넌트가 공통 스토리지 및 컴퓨팅 인프라를 공유합니다. 이러한 격리 모델에 대한 자세한 내용은 SaaS 테넌트 격리 전략 백서에서 확인할 수 있습니다.

이 서버리스 SaaS 솔루션은 티어를 사용하여 테넌트가 사용하는 모델을 결정합니다. 기본, 표준 및 프리미엄 티어의 테넌트는 공통 리소스 세트를 공유합니다. 플래티넘 티어의 테넌트는 전용 리소스를 보유하게 됩니다.

베이스라인 환경 배포

서버리스 SaaS 베이스라인 환경을 배포하기 위한 지침은 README 파일을 통해 확인할 수 있습니다.

설치 프로세스는 베이스라인 환경의 일부인 모든 리소스를 프로비저닝합니다. 이는 사일로 테넌트와 풀 테넌트 모두의 경우에 온보딩을 위해 필요한 인프라와 리소스를 나타냅니다. 이 베이스라인 인프라의 다이어그램은 다음과 같습니다.

SaaS-Multi-Tenant-Serverless-1

그림 1 – 베이스라인 배포 개념도

멀티 테넌트 서버리스 SaaS 솔루션을 실행하는 데 필요한 모든 애플리케이션, 서비스 및 인프라를 포괄적으로 표현하고 있음을 알 수 있습니다. 다음 섹션에서는 이 환경의 핵심 요소에 대해 자세히 다루겠습니다.

웹 애플리케이션

이 환경에서 백엔드 서비스와 상호 작용하는 세 가지 애플리케이션을 구축한 것을 볼 수 있습니다. Angular를 사용하여 이러한 애플리케이션을 구축했습니다.

위의 다이어그램에서 “SaaS provider admin console”은 SaaS 제공업체의 관리자가 사용하는 애플리케이션을 나타냅니다. “Landing/sign-up application”은 신규 테넌트가 직접 등록할 수 있는 공개 등록 페이지 역할을 합니다. “Sample SaaS commerce application”은 일반적인 전자 상거래 애플리케이션을 나타냅니다.

이 애플리케이션에는 SaaS 애플리케이션을 시뮬레이션하는 최소 기능이 일부 포함되어 있어 제품 및 주문을 생성/업데이트할 수 있습니다.

공유 서비스

이 서버리스 환경에는 애플리케이션의 온보딩, 테넌트 및 사용자 관리 측면을 담당하는 일련의 공유 서비스도 포함되어 있습니다.

“공유(shared)”라는 의미는, 이러한 공유 서비스가 애플리케이션 서비스와는 별개로 모든 테넌트에게 적용되는 기능을 제공하는 것은 SaaS 환경의 중요 요소임을 나타냅니다. 즉, 테넌트를 온보딩, 관리, 인증 및 설정하는 데 사용되는 작업과 데이터는 이러한 공유 서비스에서 처리됩니다.

애플리케이션 서비스

애플리케이션 서비스는 애플리케이션의 비즈니스 기능을 제공하는 마이크로서비스를 나타냅니다. 위에서 언급한 것처럼 이러한 애플리케이션 서비스의 배포 및 역할은 테넌트의 티어에 따라 달라집니다.

베이스라인 환경에 풀 모델을 선택한 티어의 테넌트가 사용하는 애플리케이션 서비스를 배포했습니다. 나중에 플래티넘 티어 테넌트를 온보딩하면 이 티어의 각 테넌트에 대해서 별도의 애플리케이션 서비스를 배포하는 것을 볼 수 있습니다.

멀티 테넌트 데이터 스토리지

SaaS 서비스를 위한 멀티 테넌트 애플리케이션 서비스는 Amazon DynamoDB를 사용하여 멀티 테넌트의 데이터를 저장하고 있습니다. 풀링된 테넌트의 경우 테넌트 데이터가 공유 DynamoDB 테이블에 함께 혼합되어 있는 풀링된 구조에 데이터를 저장하고 있습니다. 이 접근 방식을 사용하려면 DynamoDB 항목을 개별 테넌트와 연결하는 테넌트 파티셔닝 키를 도입해야 합니다.

이 접근 방식에 대한 자세한 내용은 이 블로그 게시물을 참조하십시오.

서버리스 마이크로서비스의 개념

서버리스 아키텍처를 더 잘 이해하려면 이 환경에서 마이크로서비스를 구성하는 방식에 대한 개념을 이해해야 합니다. 각 함수가 마이크로서비스가 될 수도 있지만, 논리적인 마이크로서비스를 나타내는 함수 모음을 갖는 것이 더 일반적입니다.

이 경우 마이크로서비스 경계는 하나 이상의 Lambda 함수로 연결된 Amazon API Gateway입니다. 예를 들어 주문 생성, 읽기, 업데이트 및 삭제를 위한 별도의 기능이 있는 주문 서비스를 상상해 보십시오. 이러한 함수는 모두 동일한 데이터에서 작동하므로 논리적인 마이크로서비스로 그룹화되어야 합니다.

아래 다이어그램은 이 개념을 개략적으로 표현한 것입니다. 이 백서를 참조하여 이 개념을 더 자세히 이해할 수도 있습니다.

SaaS-Multi-Tenant-Serverless-2

그림 2 – 서버리스 마이크로서비스

테넌트 등록 및 온보딩

이제 베이스라인 아키텍처를 이해했으니 테넌트가 이 환경에 어떻게 도입되는지 살펴보겠습니다. 테넌트는 가입 웹 애플리케이션을 사용하여 직접 등록할 수 있습니다. 테넌트의 등록 흐름은 테넌트가 가입할 때 선택하는 티어에 따라 약간 다릅니다.

다음 다이어그램은 테넌트 등록 흐름과 테넌트 등록 서비스가 다른 서비스를 활용하여 테넌트 등록을 조정하는 방법을 보여줍니다.

SaaS-Multi-Tenant-Serverless-3

그림 3 – 테넌트 등록 흐름

가입 절차의 단계는 다음과 같습니다.

  1. 테넌트가 가입 세부 정보 및 티어 정보를 전송합니다. 등록 서비스에서 티어를 검토하고, 테넌트가 플래티넘 티어를 선택한 경우 사일로화된 모델(전용 리소스)에 온보딩되도록 플래그가 설정됩니다. 다른 티어를 선택하면 테넌트가 풀링된 모델(공유 리소스)에 온보딩되도록 플래그가 설정됩니다.
  2. 등록 서비스는 사용자 관리 서비스를 호출하여 새 테넌트의 어드민 사용자를 생성하며, Amazon Cognito를 ID Provider로 사용합니다. 플래티넘 티어 테넌트의 경우 각 테넌트에 대해 별도의 User Pool이 프로비저닝되는 것을 확인할 수 있습니다. 다른 티어는 공통 User Pool을 공유하지만 해당 단일 사용자 풀 내에서 서로 다른 Cognito Group이 할당됩니다. 테넌트 ID 및 사용자 역할은 Cognito에서 Custom Claim으로 저장됩니다.
  3. 등록 서비스는 테넌트 관리 서비스를 호출하여 테넌트 세부 정보를 저장합니다. 이 프로세스 과정에서 온보딩을 위해 선택한 테넌트 티어에 따라 API Key를 테넌트와 연결합니다.
  4. 마지막으로 테넌트 프로비저닝 서비스는 테넌트 파이프라인을 호출하여 플래티넘(사일로모델) 티어를 선택한 테넌트에 대한 각 테넌트별 인프라를 프로비저닝합니다.

테넌트 등록 서비스는 API Gateway 리소스 정책을 사용하여 사용자 관리, 테넌트 관리 및 프로비저닝 서비스에 대해 인증합니다.

테넌트 배포 파이프라인

이 솔루션에서는 AWS CodePipeline을 사용하여 애플리케이션 서비스(Product/Order 서비스)의 배포를 관리합니다.

이 파이프라인은 CI/CD 접근 방식으로 테넌트 인프라를 만들고 업데이트하는 역할을 합니다. 코드를 기본 브랜치에 Publish/Merge 하면 파이프라인 (그림 4 참조)이 자동으로 트리거되고, 소스를 빌드하고, 필요한 모든 단위 테스트를 수행하고, 모든 테넌트에 대한 서비스를 자동화된 방식으로 배포합니다.

SaaS-Multi-Tenant-Serverless-4

그림 4 – 테넌트 코드 파이프라인

이 배포 환경의 일환으로 TenantStackMapping 테이블이 도입되었음을 알 수 있습니다. 이 테이블은 파이프라인 환경의 핵심이며 테넌트 배포 방법을 제어하는 테넌트/티어 매핑을 제공합니다.

풀 테넌트(Basic, Standard, Premium 티어 테넌트)는 베이스라인 인프라 배포 과정에서 테이블에 풀링된 스택에 대한 항목이 생성됩니다. 그런 다음 사일로(플래티넘 티어) 테넌트가 온보딩되면 시스템은 이 테이블에 각 사일로 테넌트에 대한 항목을 생성합니다. CodePipeline은 이 테이블을 참조하여 필요에 따라 스택을 만들고 업데이트합니다.

아키텍처의 최종 모습

하나 이상의 플래티넘 티어 테넌트를 시스템에 프로비저닝하면 아키텍처에서 이러한 테넌트를 위한 새 인프라를 추가합니다 (그림 5 참조). 이제 플래티넘 티어 테넌트를 위한 사일로화된 인프라와 최초 프로비저닝 스크립트에 의해 배포되는 풀링된 인프라를 갖게 됩니다.

이 모델에서 각 플래티넘 티어 테넌트는 자신들의 테넌트를 위한 Cognito User Pool, 별도의 API 게이트웨이, 별도의 Lambda 함수 설치 및 전용의 DynamoDB 테이블 세트를 제공 받습니다.

SaaS-Multi-Tenant-Serverless-5

그림 5 – 최종 배포 공간

API 인증 및 테넌트 격리

서버리스 SaaS 레퍼런스 솔루션은 다양한 메커니즘을 활용하여 보안을 관리하고 테넌트 활동을 제어합니다. 그림 6 에서는 솔루션이 이러한 제어를 구현하기 위해 Lambda Authorizer, Amazon Cognito, 동적 Identity and Access Management(IAM) 정책 및 STS 서비스를 조합하는 것을 볼 수 있습니다.

SaaS-Multi-Tenant-Serverless-6

그림 6 – 인증 흐름

이 흐름의 시작은 테넌트가 JWT 토큰을 발행하는 Amazon Cognito를 통해 인증하는 것으로 시작됩니다(1단계 및 2단계). 그런 다음 API 게이트웨이에서 처리되는 각 요청과 함께 이 JWT가 전달됩니다(3단계). API 게이트웨이에 도착하면 Lambda Authorizer를 사용하여 요청을 검증하고 승인합니다(4단계).

Authorizer 내에서 코드는 사용자의 역할에 따라 경로를 활성화/비활성화하여 해당 사용자에게 유효하지 않은 경로에 대한 액세스를 차단합니다(5단계). 또한 JWT의 테넌트 컨텍스트를 사용하여 테넌트의 범위가 지정된 보안 인증을 획득하고 이를 Lambda 함수로 전송하여 해당 함수가 액세스하는 리소스에 테넌트 범위의 액세스를 적용하는 데 사용됩니다(6단계).

테넌트 격리 정책 생성

인증 프로세스 과정에서 Lambda Authorizer는 해당 테넌트에만 적용되는 IAM 정책을 기반으로 단기 보안 인증을 생성합니다. 이러한 정책은 요청을 보낸 테넌트의 ID를 기반으로 Lambda Authorizer가 동적으로 생성합니다.

다음은 DynamoDB 테이블에 대한 테넌트 격리 정책의 예를 제공하는 JSON 코드입니다.

{
    "Effect": "Allow",
    "Action": [
        "dynamodb:UpdateItem",
        "dynamodb:GetItem",
        "dynamodb:PutItem",
        "dynamodb:DeleteItem",
        "dynamodb:Query"
    ],
    "Resource": [
        "arn:aws:dynamodb:{0}:{1}:table/Product-*".format(region, aws_account_id),                      
    ],
    "Condition": {
        "ForAllValues:StringLike": {
            "dynamodb:LeadingKeys": [
                "{0}-*".format(tenant_id)
            ]
        }
    }
}

이 JSON에서 “tenant_id” 변수는 들어오는 테넌트의 ID로 대체됩니다. 이렇게 하면 해당 테넌트에만 관련된 정책을 얻게 됩니다.

Lambda Authorizer는 이 IAM 정책을 사용하여 STS 서비스의 AssumeRole 메서드를 사용하여 세분화된 단기 보안 인증을 생성합니다. 그 결과 해당 테넌트의 데이터에만 액세스할 수 있는 비밀 액세스 키가 생성됩니다. Lambda Authorizer는 Lambda 컨텍스트의 일부로 다운스트림 Lambda 함수에 이러한 단기 보안 인증을 제공합니다.

이 프로세스의 효율성을 높이기 위해 Lambda Authorizer는 JWT 토큰을 기반으로 구성 가능한 기간 동안 보안 인증을 캐싱합니다.

테넌트 격리 적용

애플리케이션의 다양한 티어 및 배포 모델(사일로 및 풀)도 서버리스 SaaS 솔루션의 격리 스토리에 영향을 미칩니다. 풀링된 테넌트 티어는 Lambda Authorizer가 생성한 보안 인증을 사용하여 풀링된 애플리케이션 서비스에 필요한 세분화된 액세스 제어를 제공합니다.

사일로 테넌트에 대한 전략은 약간 다릅니다. 각 테넌트에 대해 별도의 DynamoDB 테이블을 프로비저닝하기 때문입니다. 이 경우 테넌트의 DynamoDB 테이블에 액세스할 때 범위가 지정된 보안 인증을 적용할 필요가 없습니다. 대신 사일로화된 Lambda 함수를 프로비저닝하는 동안 적용되는 테넌트 Execution Role이 해당 테넌트를 위해 프로비저닝된 특정 테이블만 액세스하도록 제한합니다.

다음 다이어그램은 이러한 변화를 보여줍니다. 그림 7 에서 격리 모델은 Authorizer가 제공하는 범위가 지정된 보안 인증을 사용하여 풀링된 DynamoDB 테이블의 DynamoDB 항목에 대한 액세스를 제한합니다.

SaaS-Multi-Tenant-Serverless-7

그림 7 – 테넌트 격리 – 풀링 모델

반면 그림 8에서는 Lambda 함수와 연결된 Execution Role을 사용하여 Amazon DynamoDB 테이블에 대한 액세스를 제어합니다.

SaaS-Multi-Tenant-Serverless-8
그림 8 – 테넌트 격리 – 사일로 모델

테넌트 네트워크 제한

SaaS 공급자는 시스템을 사용하는 테넌트의 각 유형(티어)에 서로 다른 환경을 제공하는 것이 일반적입니다. 네트워크 제한은 일반적으로 기본 티어 테넌트가 상위 티어 테넌트의 환경에 영향을 미치는 기능 사용에 제한을 가하는 정책을 이용하는 광범위한 티어링 전략의 일부입니다.

서버리스 SaaS 샘플 솔루션은 API Gateway를 통해 이러한 티어 기반 전략을 적용합니다. 이를 통해 API Key와 연결할 수 있는 Usage Plan을 생성하여 티어를 특정 계획에 바인딩할 수 있습니다. 이 환경의 경우에, 각 테넌트 티어(기본, 표준, 프리미엄 및 플래티넘)에 대해 하나의 Usage Plan이 있습니다. 각 Usage Plan에 별도의 API Key를 연결해 티어 기반 제한 한도를 구성했습니다.

시나리오에 따라 테넌트당 하나의 Usage Plan을 사용하는 것도 고려할 수 있습니다. 이는 일반적으로 테넌트 수가 제한되어 있는 경우에 해당됩니다.

Lambda 티어링 사용한 테넌트 옵저빌리티

일반적으로 SaaS 솔루션은 멀티 테넌시와 관련된 많은 세부 정보를 숨기는 라이브러리 또는 모듈을 도입하여 마이크로서비스를 최대한 간단하게 개발하려고 합니다. 예를 들어 로깅, 메트릭, 데이터 액세스 등 모두 테넌트 컨텍스트가 필요할 수 있으며 개발자가 각 서비스에서 이 테넌트 컨텍스트를 관리/액세스/적용하기 위한 일회성 코드를 만들도록 요구하고 싶지는 않습니다.

서버리스 환경에서는 이러한 일반적인 멀티테넌트 요구 사항을 해결하기 위해 재사용할 수 있는 공통 코드를 도입할 수 있는 자연스러운 메커니즘이 있습니다. 서버리스 SaaS 아키텍처는 Lambda Layer를 사용하여 로깅 및 지표 수집을 중앙 집중화합니다.

레퍼런스 솔루션에는 마이크로서비스의 일반적인 요구 사항 중 일부를 해결하기 위해 레이어가 도입되었습니다. 예를 들어 레이어는 JWT 토큰에서 테넌트 ID를 추출하는 데 사용됩니다. 또한 CloudWatch에 게시된 테넌트 인식 로그 및 지표를 기록하기 위해 레이어의 코드를 사용했습니다.

또한 레퍼런스 솔루션은 트레이싱을 위해 AWS X-Ray를 활용하고 X-Ray annotations을 활용하여 테넌트별로 트레이싱을 구분합니다. 마지막으로 CloudWatch Logs Insight를 사용하여 로그를 쿼리하고 이러한 지표를 요약할 수도 있습니다.

테넌트 비용

테넌트 ID를 기록하는 기능을 통해 테넌트, 특히 풀 모델에서 작동하는 테넌트의 사용량을 더 자세히 이해할 수 있습니다. 예를 들어, 이 블로그 게시물에서는 CloudWatch 로그 내의 테넌트 ID를 기반으로 테넌트당 비용을 결정하는 접근 방식을 살펴봅니다. AWS Application Cost Profiler(ACP)를 사용하여 테넌트 간에 공유되는 AWS 리소스에 대한 세분화된 비용 분석을 얻습니다.

사일로(플래티넘) 테넌트의 테넌트당 비용은 비용 할당 태그를 사용하여 비용 내역을 확인할 수 있으므로 비교적 간단합니다. 이는 사일로 테넌트의 리소스가 공유되지 않기 때문에 가능합니다.

결론

이 게시물에서는 AWS 서버리스 서비스를 사용하여 확장 가능하고 보안 가능한 SaaS 솔루션을 만드는 방법을 설명했습니다. AWS 서버리스 서비스 내의 다양한 기능을 SaaS 모범 사례에 매핑하는 방법에 대한 지침을 제공했습니다.

이 솔루션은 SaaS 여정의 시작점이자 기준점이 될 것입니다. 또한 GitHub 리포지토리에 이 레퍼런스 솔루션의 내부 작동에 대한 세부적인 정보를 제공하는 자세한 설명서가 제공됩니다. 아키텍처 및 구현을 심층적으로 살펴볼 때 유용하게 사용할 수 있습니다.

– Anubhav Sharma, Principal Solutions Architect, SaaS Factory at AWS
– Ujwal Bukka, Senior Partner Solutions Architect