Flask를 통해서 Multitenancy를 구현하는 방법에 대해서 이야기 해보고자 합니다. 제가 사용한 방법보다 더 효과적인 방법이 있을수 있으며, 이 방식은 그 중 하나의 방법 정도로 살펴봐 주시면 좋을듯 합니다. 이 문서는 몇개의 데이터 소스를 사전 등록하고 이 안에서 class routing을 tenant에 따라서 수행하는 방법을 다룹니다.
위 Flask-SQLAlchemy의 가이드 문서를 보면 Flask에 다중 데이터 소스를 등록할 수 있습니다. SQLALCHEMY_DATABASE_URI는 서비스에서 사용하는 공통 database로 사용하고, SQLALCHEMY_BINDS는 각 tenant별 데이터 소스를 등록합니다.
config.py에서의 데이터 소스
1 2 3 4 5 6 7
# MAIN Datasource SQLALCHEMY_DATABASE_URI = "postgresql:///{db_username}:{db_password}@{db_host}:5432/{db_name}" # Per Tenant Datasource SQLALCHEMY_BINDS = { "tenant1_ds": "postgresql:///{db_username}:{db_password}@{db_host}:5432/{db_name}", "tenant2_ds": "postgresql:///{db_username}:{db_password}@{db_host}:5432/{db_name}", "tenant3_ds": "postgresql:///{db_username}:{db_password}@{db_host}:5432/{db_name}",
Multitenancy Model Routing
Flask App기동시 Model Initialize 최초 수행하기
1 2 3
# Flask App 기동시 최초 1회 수행합니다. with app.app_context(): initialize_multi_tenant_model()
definitialize_multi_tenant_model(): """ Flask App Context에서 이 메소드를 실행하여 tenant별로 class를 생성하여 등록합니다. """ tenants= [tenant.value for tenant in tenants] for tenant in tenants: for base_model in base_models: # camel_to_snake는 base_model의 CamelCase를 Snake Case로 만드는 함수를 만드시면 됩니다. initialize_model(tenant, base_model, camel_to_snake(base_model.__name__))
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
definitialize_model(tenant_nm, base_cls, tbl_nm): """ 파라미터로 전달된 정보를 기반으로 Model을 생성하여 dict에 등록합니다. """ # tenant1_svc_in_bound_class_info -> Tenant1SvcInBoundClassInfo로 리턴하는 함수(string_transform)를 사용합니다. cls_nm = string_transform(tenant_nm + '_svc_' + tbl_nm ) Model = type(cls_nm, (base_cls,),{ # SQLALCHEMY_BINDS 에서 선언한 datasource와 일치하게 됩니다. '__bind_key__': tenant_nm + '_ds', # InBoundClassInfo의 경우 in_bound_class_info로 테이블이 생성되었다고 가정합니다. '__tablename__': tbl_nm, # 해당 datasource에서 schema 정보를 기술합니다. '__table_args__': {'schema': tenant_nm + '_ds'} }) # Tenant1SvcInBoundClassInfo, Tenant2SvcInBoundClassInfo, Tenant3SvcInBoundClassInfo라는 이름으로 모델을 dict에 등록합니다. flask_mdl_dict[cls_nm] = Model
1 2 3 4 5 6 7 8
defget_model_class_per_tenant(tenant, base_cls_nm): """ 해당 tenant + base model 정보를 기반으로 해당 tenant의 클래스를 리턴합니다. """ cls_nm = string_transform(tenant_nm + '_svc_' + tbl_nm ) model_cls = flask_mdl_dict.get(cls_nm) return model_cls
위와 같이 설정하고 나서 Flask의 서비스 클래스 등에서 다음과 같이 호출하여 db model을 사용하면 됩니다.