멀티테넌시 - TheOpenCloudEngine/uEngine-cloud GitHub Wiki

e-Shop 서비스의 멀티테넌시 요구사항

여러 조직에서 자체 조직의 e-Shop 을 멀티태넌시로 나누어 사용하고 싶은 경우 가능한 자원을 공유하여 사용할 수 있는 방법으로 싱글인스턴스-멀티테넌시가 있다. 테넌트 별로 Dedicate 한 자원을 제공하지 않고 공유된 자원을 사용하므로써 신규 테넌트 유입에 대한 부담이 적어 Free-tier 및 Evaluation 기간 등을 제공하기에 부담이 없는 아키텍처이다.

메타웍스4 의 멀티테넌시 프레임워크 기능

메타웍스4의 멀티테넌시 기능을 적용하여 데이터베이스와 파일 등의 저장공간을 분리하고 커스터마이징 기능을 제공 받을 수 있다:

  1. JWT 토큰과 OAuth2.0을 통한 테넌트 식별 (이를 위하여 OCE IAM 서버와 같이 적용해야 함)
  2. 데이터베이스의 컬럼 기반 테넌트 데이터 분리 저장 / 추출
  3. 파일 시스템의 접속 테넌트 별 저장 / 추출
  4. 메타데이터 설정 및 분산 노드 반영
  5. 메타데이터 설정 셀프서비스 UI
  6. 메타데이터 기반 기본 UI 컴포넌트 - 그리드, 폼
  7. 테넌트 셀프서비스 매시업

e-shop 서비스들의 데이터베이스 멀티테넌시화

e-shop 서비스를 구성하는 세부 마이크로 서비스들인 Customer 와 Order 서비스 중에서 우선 Order 서비스(Order.java와 Item.java) 에만 멀티테넌시 데이터베이스 처리 기능을 추가해보자.

엔티티 객체에 @Multitenant 애노테이션을 아래와 같이 Order.java 와 Item.java 에 추가해준다.


@Entity
@Table(name="order_table")
@Multitenant
@TenantDiscriminatorColumn(
        name = "TENANTID",
        contextProperty = "tenant-id"
)

그리고 둘의 Repository 선언을 MultitenantRepository 를 사용하도록 설정한

public interface OrderRepository extends MultitenantRepository<Order, Long> {
}

public interface ItemRepository extends MultitenantRepository<Item, String> {
}

** MultitenantRepository 가 활성화되려면 다음의 설정도 추가적으로 확인해야 한다:

  • Application.java 의 @EnableJpaRepsitories(repositoryBaseClass) 설정 그리고 Metaworks4BaseApplication 을 상속할 것.
@EnableJpaRepositories(repositoryBaseClass = MultitenantRepositoryImpl.class, basePackageClasses = {Order.class, Customer.class})

public class Application extends Metaworks4BaseApplication {
}
  • WebConfig.java 를 둘 것.
@EnableWebMvc
@Configuration
public class WebConfig extends Metaworks4WebConfig{
    public WebConfig(ApplicationContext context, ObjectFactory<ConversionService> conversionService) {
        super(context, conversionService);
    }

    @Override
    protected Storage storage() {
        return new LocalFileStorage();
    }
}
  • Token 인증 필터를 설치: token 인증 필터는 테넌트 유저의 인증과 함께 어느 테넌트 소속인지를 확인하는 중요한 역할을 한다. 따라서 꼭 토큰 필터가 설치 되었는지 확인한다:
@EnableWebMvc
@Configuration
public class WebConfig extends Metaworks4WebConfig{
    @Override
    protected Storage storage() {
        return new LocalFileStorage();
    }

    @Bean
    public TenantAwareFilter tenantAwareFilter(){
        TenantAwareFilter tenantAwareFilter = new TenantAwareFilter();
        tenantAwareFilter.setAllowAnonymousTenant(true);

        return tenantAwareFilter;
    }
}

  • 테스트를 위한 AnonymousTenant 를 true 로 두었다. 테스트 마다 header 에 access_token 을 줘서 테스트를 해야 하기 때문에 개발 동안에는 token 없이도 테스트를 가능하게 해준다. 물론, 멀티테넌시를 테스트해야 할때는 access_token을 반드시 줘야만 오류가 생기지 않는다.
  • IAM 없이 간단히 테스트를 하기 위한 옵션으로
tenantAwareFilter.setDevMode(true);

DevMode 옵션을 주게 되면, access_token 으로 실제 토큰을 발행하지 않고 사용자의 이메일 주소를 토큰 대신 입력하여 테스트할 수 있다.

테스트

예제에서 설치한 TenantAwareFilter 구현체는 IAM 토큰 유저네임 (이메일 주소로 이루어졌다고 가정) 내에서 도메인주소 (@이후 문자열) 를 테넌트 아이디로 식벼하도록 구현되어있다. 따라서 멀티테넌트 테스트를 위해서는 하나 이상의 다른 도메인 주소로 된 이메일주소로 IAM 에서 아이디를 발급받은 후, 다음의 REST 호출로 토큰을 발급받을 수 있다:

  1. 토큰의 발급 uengine.org 조직의 (테넌트의) 유저의 토큰
curl -X POST \
  http://my-iam-dev.pas-mini.io/oauth/access_token \
  -H 'cache-control: no-cache' \
  -H 'content-type: application/x-www-form-urlencoded' \
  -H 'postman-token: 96e89798-3e01-6c67-75fa-8eb82d70bd8b' \
  -d 'client_id=my-client-key&grant_type=password&client_secret=my-client-secret&username=darkgodarkgo%40gmail.com&password=gosu23546&scope=my-backend-1&token_type=JWT'

{
   "access_token" : "eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJteS1pYW0iLCJjb250ZXh0Ijp7ImNsaWVudEtleSI6Im15LWNsaWVudC1rZXkiLCJzY29wZXMiOlsibXktYmFja2VuZC0xIl0sInR5cGUiOiJ1c2VyIiwidXNlck5hbWUiOiJkYXJrZ29kYXJrZ29AZ21haWwuY29tIiwidXNlciI6eyJ1c2VyTmFtZSI6ImRhcmtnb2Rhcmtnb0BnbWFpbC5jb20iLCJtZXRhRGF0YSI6eyJuYW1lIjoi67CV7Iq57ZWEIiwiZW1haWwiOiJkYXJrZ29kYXJrZ29AZ21haWwuY29tIn0sInJlZ0RhdGUiOjE1MTYyNjg2ODE2MzksInVwZERhdGUiOjE1MTYyNjg2ODE2Mzl9LCJyZWZyZXNoVG9rZW4iOiJkZjEwNDlmYS1mOWViLTQ4ZDktYTk1Mi1iNDg4ZjMxOWU4YjMifSwiY2xhaW0iOnt9LCJleHAiOjE1MTYyNzI1MDksImlhdCI6MTUxNjI2ODkwOX0.atSSi3stodqZ37ygzMQF3XNNjCOM9KYmjE4xMpuNyfo",
   "refresh_token" : "df1049fa-f9eb-48d9-a952-b488f319e8b3",
   "token_type" : "JWT",
   "expires_in" : 3600
}

opence.org 조직의 (테넌트의) 유저의 토큰

curl -X POST \
  http://my-iam-dev.pas-mini.io/oauth/access_token \
  -H 'cache-control: no-cache' \
  -H 'content-type: application/x-www-form-urlencoded' \
  -H 'postman-token: 96e89798-3e01-6c67-75fa-8eb82d70bd8b' \
  -d 'client_id=my-client-key&grant_type=password&client_secret=my-client-secret&username=jyjang%40uengine.org&password=defaultpass6&scope=my-backend-1&token_type=JWT'
  1. 발급 받은 두개의 다른 테넌트의 토큰을 입력하여 주문 정보를 입력 (기존의 http 호출 명령어와 같고, 끝 부분에 access_token 헤더만 추가하여 호출하면 된다.
http localhost:8080 
access_token:eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJteS1pYW0iLCJjb250ZXh0Ijp7ImNsaWVudEtleSI6Im15LWNsaWVudC1rZXkiLCJzY29wZXMiOlsibXktYmFja2VuZC0xIl0sInR5cGUiOiJ1c2VyIiwidXNlck5hbWUiOiJkYXJrZ29kYXJrZ29AZ21haWwuY29tIiwidXNlciI6eyJ1c2VyTmFtZSI6ImRhcmtnb2Rhcmtnb0BnbWFpbC5jb20iLCJtZXRhRGF0YSI6eyJuYW1lIjoi67CV7Iq57ZWEIiwiZW1haWwiOiJkYXJrZ29kYXJrZ29AZ21haWwuY29tIn0sInJlZ0RhdGUiOjE1MTYyNjg2ODE2MzksInVwZERhdGUiOjE1MTYyNjg2ODE2Mzl9LCJyZWZyZXNoVG9rZW4iOiJkZjEwNDlmYS1mOWViLTQ4ZDktYTk1Mi1iNDg4ZjMxOWU4YjMifSwiY2xhaW0iOnt9LCJleHAiOjE1MTYyNzI1MDksImlhdCI6MTUxNjI2ODkwOX0.atSSi3stodqZ37ygzMQF3XNNjCOM9KYmjE4xMpuNyfo
  1. 마찬가지 두개의 다른 토큰을 입력하여 주문 내역을 조회
http localhost:8080/orders access_token:access_token:eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJteS1pYW0iLCJjb250ZXh0Ijp7ImNsaWVudEtleSI6Im15LWNsaWVudC1rZXkiLCJzY29wZXMiOlsibXktYmFja2VuZC0xIl0sInR5cGUiOiJ1c2VyIiwidXNlck5hbWUiOiJkYXJrZ29kYXJrZ29AZ21haWwuY29tIiwidXNlciI6eyJ1c2VyTmFtZSI6ImRhcmtnb2Rhcmtnb0BnbWFpbC5jb20iLCJtZXRhRGF0YSI6eyJuYW1lIjoi67CV7Iq57ZWEIiwiZW1haWwiOiJkYXJrZ29kYXJrZ29AZ21haWwuY29tIn0sInJlZ0RhdGUiOjE1MTYyNjg2ODE2MzksInVwZERhdGUiOjE1MTYyNjg2ODE2Mzl9LCJyZWZyZXNoVG9rZW4iOiJkZjEwNDlmYS1mOWViLTQ4ZDktYTk1Mi1iNDg4ZjMxOWU4YjMifSwiY2xhaW0iOnt9LCJleHAiOjE1MTYyNzI1MDksImlhdCI6MTUxNjI2ODkwOX0.atSSi3stodqZ37ygzMQF3XNNjCOM9KYmjE4xMpuNyfo
  1. 두 데이터가 분리되어 저장되고 인출됨을 알 수 있다.

실제 데이터베이스 레이아웃은?

  1. 메타웍스4의 기본 멀티테넌시 레이아웃은 Single schema, multi-tenancy 로, 하나의 테이블에 여러 테넌트의 데이터를 컬럼 구분자를 통하여 적재하고 where 절로 다시 걸러내어 인출하는 방식이다. 테넌트 구분분