fix: 하네스 제거 및 keycloak 문서 보강
This commit is contained in:
@@ -1 +0,0 @@
|
||||
../../vault/30-knowledge/concepts/multi-tenancy-isolation-patterns.md
|
||||
@@ -0,0 +1,142 @@
|
||||
---
|
||||
title: Multi-tenancy Isolation 패턴 (Pool vs Silo vs Bridge)
|
||||
source_type: llm-generated
|
||||
status: draft
|
||||
confidence: medium
|
||||
tags: [multi-tenancy, saas, isolation]
|
||||
related_projects: [ca-skeleton]
|
||||
last_reviewed: 2026-05-22
|
||||
---
|
||||
|
||||
# Multi-tenancy Isolation 패턴 (Pool vs Silo vs Bridge)
|
||||
|
||||
> Layer: `wiki/concepts/` — 일반 개념. 실제 적용은 `wiki/projects/` 또는 raw 브랜치 노트 참조.
|
||||
|
||||
## Summary
|
||||
|
||||
Multi-tenancy isolation은 "여러 tenant가 같은 소프트웨어 인스턴스를 어느 수준까지 공유하는가"의 스펙트럼이다. AWS SaaS Lens는 이를 **Silo / Pool / Bridge** 3분류로 정리하고, Hibernate는 ORM 레벨에서 **DATABASE / SCHEMA / DISCRIMINATOR** 3 strategy로 공식 지원하며, Azure는 **Deployment Stamps** 패턴으로 hybrid를 다룬다. ca-tmpl은 **opt-in(`APP_TENANT_ENABLED=true` 시만 활성) + shared DB + `tenant_id` column(ULID) + JWT claim 우선 resolution** 조합을 baseline으로 채택한다. 이는 AWS Pool 모델 + Hibernate DISCRIMINATOR 전략에 해당하며, B2B 초기 단계(tenant 수 수십~수백 단위)에 isolation 비용 대비 운영 단순성을 우선한 의도적 선택이다. opt-in 설계의 의의는 single-tenant deployment에서는 tenant 로직 자체를 비활성화하여 skeleton의 적용 범위를 넓힌 점에 있다. **Migration trigger 3가지**는 (a) 규제(금융·의료) isolation 강제, (b) tenant 수 수백~수천 + 단일 row 수 수억 도달, (c) enterprise tier 등장으로 isolation을 가격에 반영해야 할 때다.
|
||||
|
||||
## Standard (공식 정의)
|
||||
|
||||
### AWS SaaS Tenant Isolation Strategies (Whitepaper) — Silo / Pool / Bridge
|
||||
|
||||
- **Silo**: tenant마다 별도 stack(compute/DB/network까지 분리). isolation 최강, 비용 최대.
|
||||
- **Pool**: 모든 tenant가 동일 infra와 schema를 공유, `tenant_id` 컬럼으로 row-level 구분.
|
||||
- **Bridge**: 일부 리소스는 silo, 일부는 pool. 예) DB는 silo, app server는 pool.
|
||||
- AWS는 "Authentication is not isolation. You must enforce isolation at the resource layer"라고 명시한다.
|
||||
|
||||
### Hibernate ORM Multi-tenancy — DATABASE / SCHEMA / DISCRIMINATOR
|
||||
|
||||
- **DATABASE**: tenant별 별도 데이터베이스.
|
||||
- **SCHEMA**: 동일 DB, tenant별 별도 schema.
|
||||
- **DISCRIMINATOR**: 동일 schema, `tenant_id` 컬럼. Hibernate 6부터 native 지원(이전엔 Filter로 우회).
|
||||
- 활성화는 `hibernate.tenant_identifier_resolver` + `hibernate.multi_tenant_connection_provider` 설정으로 수행. `CurrentTenantIdentifierResolver`가 ThreadLocal/SecurityContext에서 tenant를 결정.
|
||||
|
||||
### Azure Architecture Center — Deployment Stamps (Hybrid)
|
||||
|
||||
- Tenancy를 "fully shared → shared compute, isolated DB → isolated stamp → isolated subscription" **스펙트럼**으로 정의.
|
||||
- **Deployment Stamps**: 동일한 스택을 단위(stamp)로 복제하고, stamp 안에 N개 tenant를 pool. tier별로 stamp 크기와 isolation 수준을 다르게 둘 수 있음.
|
||||
- Microsoft는 "There's no single right approach to multitenancy"라고 명시 — 비즈니스 모델·규제·확장성·비용에 따라 모델이 달라진다.
|
||||
|
||||
### Tenant Resolution 방식 (isolation과 직교)
|
||||
|
||||
- **JWT claim**: token 서명 검증으로 위변조 방지. 가장 안전.
|
||||
- **Subdomain (`{tenant}.app.com`)**: UX 친화적, 단 wildcard DNS/TLS 필요.
|
||||
- **Custom header (`X-Tenant-Id`)**: 단순하나 외부 trust boundary에서 단독 신뢰 금지.
|
||||
- **Path (`/t/{tenant}/...`)**: routing 자연스럽지만 모든 client URL 변경.
|
||||
|
||||
## 한계 / 주의점
|
||||
|
||||
각 대안의 한계는 다음과 같다.
|
||||
|
||||
### shared DB + tenant_id (Pool / Hibernate DISCRIMINATOR)
|
||||
|
||||
- **Noisy neighbor**: hot tenant가 같은 인스턴스 전체에 영향.
|
||||
- **규제 isolation 불가**: application bug 한 줄로 cross-tenant leak 가능. HIPAA·FedRAMP·금융권은 storage 레벨 분리를 요구하는 경우가 있어 Pool로 충족 어려움.
|
||||
- **Index 비용**: tenant로 filter하는 모든 index에 `tenant_id`를 leading column으로 포함해야 plan이 효율적.
|
||||
- **Native query/JDBC bypass 위험**: JPQL 경로 외에서 tenant filter 누락 시 leak.
|
||||
|
||||
### Subdomain-based resolution
|
||||
|
||||
- **Wildcard DNS와 wildcard TLS 인증서** 필요. custom domain 지원 시 per-domain 인증서 자동화 추가.
|
||||
- Let's Encrypt rate limit은 "registered domain당 주 50개 인증서"로 보고되나 — 정확 수치와 적용 범위는 `needs-confirmation` (raw 발췌 기준).
|
||||
- **DNS propagation 지연**, **subdomain takeover 위험**(tenant 삭제 후 DNS record 미정리), **CORS/cookie domain 설정 복잡성**.
|
||||
- Local dev는 `lvh.me`/`nip.io`/hosts 수정 필요.
|
||||
|
||||
### JWT claim only
|
||||
|
||||
- claim 검증을 한 곳이라도 빠뜨리면 cross-tenant 위험.
|
||||
- token 재발급 없이 tenant 전환 불가 → admin/support 운영 동선 제약.
|
||||
- IdP와 강결합 → tenant 정보 변경 시 token rotation 정책 필요.
|
||||
|
||||
### Schema-per-tenant (Hibernate SCHEMA)
|
||||
|
||||
- Postgres metadata(`pg_class`, `pg_attribute`) overhead가 tenant 수 증가에 따라 누적.
|
||||
- Stripe/Citus 자료에 따르면 "수백~수천 tenant"에서 catalog bloat·autovacuum·plan cache miss가 문제로 보고됨 — 다만 정확한 임계 수치 인용은 `needs-confirmation`.
|
||||
- **Connection pooling 난이도**: `search_path` 전환이 plan cache를 무효화. HikariCP per tenant vs single pool 설계 선택 필요.
|
||||
- 마이그레이션이 tenant 수만큼 반복(Flyway `schemas` 옵션으로 일괄 처리 가능하나 추가/삭제 자동화 필요).
|
||||
|
||||
### Database-per-tenant (Silo)
|
||||
|
||||
- Isolation 가장 강함, **운영 비용 폭증**: 마이그레이션·백업·모니터링이 모두 tenant 수에 비례.
|
||||
- Connection pool이 (tenant 수 × pool size)로 폭발 → connection multiplexing(예: PgBouncer) 필수.
|
||||
- AWS 계정·서비스 limit에 부딪힐 수 있음.
|
||||
- 비용은 silo > bridge > pool 순.
|
||||
|
||||
### Hybrid (Azure Deployment Stamps / AWS Bridge)
|
||||
|
||||
- 두 가지 이상 모델을 동시 운영 → **운영 복잡도 최고**.
|
||||
- Tier 승급(pool → silo) 시 **데이터 이동 절차** 필요.
|
||||
- Routing layer + tenant catalog가 사실상 control plane이 되어, 가용성 single point가 되지 않도록 분산 필요.
|
||||
- 작은 팀에서 도입하면 ROI 부정. 일반적으로 product-market fit 이후 단계에서 검토.
|
||||
|
||||
### 공통 오해
|
||||
|
||||
- "Pool이면 무조건 싸다"는 거짓 — 노이즈/검증 비용이 일정 규모 이상에선 silo와 역전될 수 있음.
|
||||
- "Subdomain이면 자동 isolation" 거짓 — resolution과 isolation은 직교. subdomain은 routing일 뿐 storage 분리를 보장하지 않음.
|
||||
- "JWT claim만 있으면 안전" 거짓 — repository·query 레이어에서 tenant filter를 강제하지 않으면 claim의 의미가 없음.
|
||||
|
||||
## Project Application
|
||||
|
||||
- [[wiki/projects/ca-tmpl/multi-tenancy-isolation-patterns]] — ca-tmpl 의사결정 기록 (현재 `documented-only`, Phase C2 미진입). 실제 구현 여부는 project 문서 참조.
|
||||
ca-tmpl은 본 개념을 다음 위치에서 적용·문서화한다. concept 문서는 등급을 매기지 않으며, 검증 수준은 각 프로젝트/브랜치 노트에서 판정한다.
|
||||
|
||||
- [[raw/branch-notes/feature-tenant-context-policy]] — tenant resolution(JWT > header admin only > subdomain fallback) + isolation SSOT
|
||||
- [[raw/branch-notes/feature-repository-access-permission-contract]] — `CROSS_TENANT_ADMIN` capability, repository 레벨 tenant filter 강제 contract
|
||||
- [[raw/project-notes/ca-skeleton-operational-contract]] — §10 Repository Access Permission Contract, §29 Topic 6 Multi-tenancy Isolation
|
||||
|
||||
## Interview Questions
|
||||
|
||||
- AWS SaaS Lens의 Pool/Silo/Bridge는 무엇이 다르고, 어떤 상황에서 어떤 모델을 선택하나?
|
||||
- Tenant ID를 JWT claim과 HTTP header 중 어디서 읽어야 하며, 둘을 동시에 허용한다면 어떤 trust 기준을 두는가?
|
||||
- shared DB + tenant_id에서 schema-per-tenant 또는 db-per-tenant로 마이그레이션을 트리거하는 조건은 무엇인가?
|
||||
- Cross-tenant 침해를 막기 위해 어느 레이어(JWT 검증 / SecurityContext / repository / DB)에 어떤 방어가 필요한가?
|
||||
- Tenant 식별자에 ULID와 UUID 중 어느 쪽을 쓰는 게 적합하며, 각 선택의 trade-off는 무엇인가?
|
||||
|
||||
## Do Not Overclaim
|
||||
|
||||
- "shared DB + tenant_id가 항상 우월하다"고 말하지 말 것 — 규제 산업·data residency 요구가 있는 도메인에서는 Silo가 필수 또는 사실상 강제다.
|
||||
- Stripe/Citus의 schema-per-tenant 한계치(예: "정확히 N tenant에서 한계")는 **정확 인용 wording이 미완**이며 raw 자료는 `needs-confirmation` 상태다. 면접/이력서에서는 "수백~수천 단위에서 catalog overhead가 보고된다" 정도로 출처(Citus blog)와 함께만 언급할 것.
|
||||
- "Atlassian이 그렇게 하니까 best practice"라고 말하지 말 것 — company-tech-blog 사례는 관점·증거이지 공식 기준이 아니다.
|
||||
- "JWT claim만 검증하면 multi-tenant가 안전하다"는 단정 금지 — claim은 입구일 뿐 storage layer 강제가 별도로 필요하다.
|
||||
- ca-tmpl 적용 사실(예: ULID 채택 이유, capability 설계)은 본 concept 문서가 아니라 `wiki/projects/` 또는 branch-notes에서 검증 등급과 함께 진술할 것. "내가 했다"는 표현은 concept 레이어에 두지 않는다.
|
||||
|
||||
## Sources
|
||||
|
||||
- [AWS Whitepaper — SaaS Tenant Isolation Strategies](https://docs.aws.amazon.com/whitepapers/latest/saas-tenant-isolation-strategies/saas-tenant-isolation-strategies.html) — Silo/Pool/Bridge 분류 baseline
|
||||
- [Hibernate ORM User Guide — Multi-tenancy](https://docs.jboss.org/hibernate/orm/current/userguide/html_single/Hibernate_User_Guide.html#multitenacy) — DATABASE/SCHEMA/DISCRIMINATOR 공식 strategy
|
||||
- [Azure Architecture Center — Multitenant SaaS](https://learn.microsoft.com/en-us/azure/architecture/guide/multitenant/overview) — Deployment Stamps / hybrid spectrum
|
||||
- [Citus — Designing your SaaS DB for High Scalability](https://www.citusdata.com/blog/2016/10/03/designing-your-saas-database-for-high-scalability/) — schema vs shared schema 한계치 (company-tech-blog, needs-confirmation)
|
||||
- [Auth0 — Multi-tenant applications](https://auth0.com/docs/get-started/auth0-overview/create-tenants/multiple-tenants) — tenant resolution(subdomain/JWT/header) 비교
|
||||
- [Vercel — Multi-tenant Next.js Guide](https://vercel.com/guides/nextjs-multi-tenant-application) — subdomain routing 실무
|
||||
- [AWS APN Blog — Hybrid Tenant Isolation](https://aws.amazon.com/blogs/apn/) — tier-based hybrid 사례
|
||||
- [Atlassian Engineering — Cloud Architecture Guidelines](https://www.atlassian.com/engineering/cloud-architecture-and-guidelines) — shard 단위 isolation + tenant context propagation 사례
|
||||
- [[raw/official-docs/multitenancy-aws-saas-tenant-isolation-whitepaper]]
|
||||
- [[raw/official-docs/multitenancy-hibernate-user-guide]]
|
||||
- [[raw/official-docs/multitenancy-azure-architecture-patterns]]
|
||||
- [[raw/company-tech-blogs/multitenancy-stripe-citus-schema-per-tenant]]
|
||||
- [[raw/company-tech-blogs/multitenancy-auth0-tenant-resolution]]
|
||||
- [[raw/company-tech-blogs/multitenancy-subdomain-resolution-patterns]]
|
||||
- [[raw/company-tech-blogs/multitenancy-hybrid-pooled-siloed-mix]]
|
||||
- [[raw/company-tech-blogs/multitenancy-atlassian-tenant-context]]
|
||||
- [[raw/project-notes/ca-skeleton-operational-contract]] — §10, §29 Topic 6
|
||||
Reference in New Issue
Block a user