English {#english}
SaaS Infrastructure — DB Separation and Usage-Based Scaling
Purpose: Define a strategy where Frappe is designed to use MariaDB on a separate DB server, Pulumi provisions DB servers based on usage monitoring, and Ansible performs installation and connectivity.
No code generation — planning, phases, and ownership only.
References: pulumi-ansible-step1-step2-plan.md, ansible-implementation-plan.md, intelligent-automation-implementation-plan.md.
1. Project Goals
| Goal | Description |
|---|---|
| Stable SaaS operations | Clear failure and scaling units by separating App and DB servers. |
| Minimize initial cost and complexity | Phase 1 starts with a single App server + single DB server. |
| Scalable architecture | Pulumi adds DB servers and Replicas based on usage monitoring; Ansible installs and connects. |
| Avoid DB bottlenecks | Define scaling criteria and move in steps to Phase 2 (Replica) and Phase 3 (sharding). |
2. Strategy Summary
| Phase | DB strategy | Owner | Purpose |
|---|---|---|---|
| Phase 1 | 1 App server + 1 DB server (single MariaDB) | Pulumi: provision both. Ansible: install MariaDB on DB server, install Frappe on App server and connect to remote DB. | Simplicity and cost. |
| Phase 2 | Primary DB + Replica DB | Pulumi: provision Replica based on usage/metrics. Ansible: install Replica and set up replication. | Read scaling and resilience. |
| Phase 3 | Multitenant sharding (or dedicated DB for large customers) | Pulumi: provision additional DB servers by metrics/policy. Ansible: install new DB and connect Frappe site routing. | Large-scale SaaS scaling. |
2.1 ⚠️ DB Server Count ≠ Customer (Tenant) Count
DB servers are not “one per customer.” One DB server serves thousands to tens of thousands of tenants in a shared multitenancy model.
| Phase | Description |
|---|---|
| Phase 1 | One DB server with a single MariaDB; only DB (schema) is per tenant. Hundreds to thousands of tenants share the same DB server. |
| Phase 2 | Two DB servers (Primary + Replica). Tenant count unchanged; Replica is for read distribution and failover, so server count becomes 2. |
| Phase 3 sharding | When one Primary is overloaded, split tenants into groups across a small number of DB servers. E.g. 100k tenants → 5–10 DB servers (10k–20k per server). Not 100k servers. |
| Dedicated DB for large customers | Option for some customers (e.g. Enterprise) to use a dedicated DB server; higher cost, applied selectively. |
Summary: Even with 100k customers, Phase 1–2 use 1–2 DB servers; Phase 3 uses a small number (e.g. 5–20). DB server count does not scale linearly with customer count.
2.2 Redis on Separate Server and Per-Tenant R2 Storage (Extension)
For 100+ tenants and 1,000+ concurrent users, the following are included as extension planning.
| Item | Content | Detail |
|---|---|---|
| Redis on separate server | Move Redis off the App server to a dedicated Redis server (or cluster) for cache/queue/socketio. Reduces impact of queue/cache spikes on App; enables future Redis HA (Sentinel). | saas-expanded-multitenancy-redis-storage-plan.md §3 |
| Per-tenant R2 storage | Isolate attachments, backups, archives per tenant in R2. Single bucket + tenant prefix (e.g. tenant_id=xxx/) or per-tenant bucket policy. On provisioning, create R2 prefix/bucket and reflect in site config. | Same plan §4 |
- Target architecture: App cluster → DB layer (separate server) + Redis layer (separate server) + Storage layer (R2, per-tenant).
- Order: After DB separation (Phase 1), apply Redis separation and R2 per-tenant storage design. Full HA, sharding, Redis HA: see saas-expanded-multitenancy-redis-storage-plan.md.
3. Architecture Roadmap
3.1 Phase 1 — Initial Operation (Single DB-Separated Setup)
Layout: App Server (Bench/Frappe, Redis, Workers) → DB Server (MariaDB single primary, remote).
Requirements: <1,000 concurrent users; <50 tenants. Frappe bench new-site uses --db-host, --db-port to point at the DB server; per-tenant DB (schema) on the same MariaDB.
Pulumi: Provision 1 App server and 1 DB server (Hetzner; firewall: 3306 on private network or App IP only); output app_server_ip, db_server_ip. Ansible: On DB server — install MariaDB, allow remote access, enable binlog for Phase 2. On App server — install Docker, Frappe bench, Redis, Workers; set db_host, db_port, db_root_password and use bench new-site --db-host etc. Private network for DB traffic.
3.2 Phase 2 — Higher Traffic (Primary + Replica)
Trigger: 1,500–2,000+ concurrent users; DB CPU >70% for 10 min; more read/report load; 70–100 tenants.
Layout: App servers → Primary DB; Replica DB (read-only) replicates from Primary; read/reports can use Replica.
Pulumi: Provision Replica server when metrics hit expansion criteria. Ansible: Primary already has binlog and replication user; Replica: MariaDB, CHANGE MASTER TO, START SLAVE, read-only. App or proxy can route read-only queries to Replica.
3.3 Phase 3 — Large-Scale (Multitenant Sharding)
Trigger: 100+ tenants; heavy load from large customers; 3,000+ concurrent users.
Strategy: Shard by DB (tenant groups A/B/C → different DB servers); optional dedicated DB for large/Enterprise customers. Pulumi provisions extra DB servers; Ansible installs and configures; tenant–DB mapping in Control Plane/D1; no cross-DB joins.
4–12. Pulumi Scope, Ansible Roles, Scaling Metrics, Sharding Principles, Complexity, Integration, Summary, Conclusion
- §4: Pulumi provisions App + DB servers in Phase 1; usage monitoring feeds expansion (Replica/shard). §5: Ansible groups
prego_db_servers(MariaDB, remote access, binlog) andprego_nodes(Frappe withdb_host/db_port/db_root_password);bench new-site --db-hostfor remote DB. §6: Primary/Replica Docker design (server-id, log-bin, replication user). §7: Scaling criteria (DB CPU >70%, query latency, connections, slow queries). §8: Sharding principles (tenant per DB, no cross-DB join). §9: Complexity by phase. §10: Links to pulumi-ansible, ansible-implementation, intelligent-automation, provision-tenant, saas-expanded-multitenancy. §11: Summary (remote DB, Pulumi+Ansible, Phase 1 binlog prep, private network; onboarding DB assignment, resource limits, log integration). §12: Conclusion — DB separation, Pulumi for provisioning and scaling, Ansible for install and connectivity.
Full detail for §§4–12 is in the Korean section below.
한국어 {#korean}
SaaS 인프라 기획서 — DB 분리·사용량 기반 확장
목적: Frappe를 별도 DB 서버에 설치된 MariaDB와 연동하도록 설계하고, Pulumi로 DB 서버를 사용량 모니터링에 따라 프로비저닝하며, Ansible으로 실제 설치·연동을 수행하는 전략을 정의한다.
코드 생성 없음 — 기획·단계·책임만 정리.
참조: pulumi-ansible-step1-step2-plan.md, ansible-implementation-plan.md, intelligent-automation-implementation-plan.md.
1. 프로젝트 목표
| 목표 | 설명 |
|---|---|
| 안정적인 SaaS 운영 구조 | App 서버와 DB 서버 분리로 장애·확장 단위를 명확히 함. |
| 초기 비용·복잡도 최소화 | Phase 1은 단일 App 서버 + 단일 DB 서버로 시작. |
| 확장 가능한 아키텍처 | 사용량 모니터링에 따라 Pulumi가 DB 서버·Replica를 추가하고, Ansible이 설치·연동. |
| DB 병목 사전 방지 | 확장 기준 지표를 정의하고, Phase 2(Replica)·Phase 3(샤딩)으로 단계적 전환. |
2. 핵심 전략 요약
| 단계 | DB 전략 | 담당 | 목적 |
|---|---|---|---|
| Phase 1 | App 서버 1대 + DB 서버 1대 (단일 MariaDB) | Pulumi: 두 서버 프로비저닝. Ansible: DB 서버에 MariaDB 설치, App 서버에 Frappe 설치 후 원격 DB 연동. | 단순성, 비용 절감. |
| Phase 2 | Primary DB + Replica DB | Pulumi: 사용량·지표에 따라 Replica 서버 프로비저닝. Ansible: Replica 설치·복제 연동. | 읽기 확장, 안정성. |
| Phase 3 | 멀티테넌트 샤딩(또는 대형 고객 전용 DB) | Pulumi: 지표·정책에 따라 추가 DB 서버 프로비저닝. Ansible: 새 DB 설치·Frappe site 라우팅 연동. | 대규모 SaaS 확장. |
2.1 ⚠️ DB 서버 수 ≠ 고객사(테넌트) 수 — 비용·규모 정리
DB 서버는 “고객사 1개당 1대”가 아닙니다. 한 DB 서버 한 대가 수천~수만 테넌트를 함께 담당하는 공유 멀티테넌시 구조입니다.
| 구분 | 설명 |
|---|---|
| Phase 1 | DB 서버 1대 안에 MariaDB 하나를 두고, 테넌트마다 DB(schema)만 분리. 수백~수천 테넌트가 동일 DB 서버를 공유. |
| Phase 2 | DB 서버 2대(Primary + Replica). 담당 테넌트 수는 그대로. Replica는 읽기 분산·장애 대응용이라 서버 수만 2로 늘어남. |
| Phase 3 샤딩 | 한 Primary가 감당하기 어려울 때 테넌트를 그룹으로 나누어 소수의 DB 서버에 분산. 예: 10만 테넌트 → DB 서버 5 |
| 대형 고객 전용 DB | Enterprise 등 일부 고객만 전용 DB 서버를 쓰는 옵션. 그만큼 비용이 들지만, 선택적 적용. |
요약: 10만 고객사라도 Phase 1~2에서는 DB 서버 1~2대, Phase 3에서도 소수(예: 5~20대) 수준으로 설계하면 됩니다. 고객사 수만큼 DB 서버가 선형으로 늘어나지는 않습니다.
2.2 Redis 별도 서버 분리 · 각 고객별 저장공간 R2 분리 (확장)
테넌트 100+ 및 동시 1,000+ 사용자 대응을 위해 아래 두 가지를 확장 기획으로 반영한다.
| 항목 | 내용 | 상세 기획서 |
|---|---|---|
| Redis 별도 서버 분리 | Redis를 App 서버에서 분리하여 전용 Redis 서버(또는 클러스터)에서 cache/queue/socketio 제공. Queue·Cache 폭주 시 App 영향 최소화, 향후 Redis HA(Sentinel) 확장. | saas-expanded-multitenancy-redis-storage-plan.md §3 |
| 각 고객(테넌트)별 저장공간 R2 분리 | 첨부파일·백업·아카이브를 테넌트 단위로 R2에서 격리. 단일 버킷 + 테넌트 prefix(예: tenant_id=xxx/) 또는 테넌트 전용 버킷 정책. 프로비저닝 시 R2 prefix/버킷 생성·site config 반영. | 동일 기획서 §4 |
- 목표 아키텍처: App Cluster → DB Layer(별도 서버) + Redis Layer(별도 서버) + Storage Layer(R2, 테넌트별 분리).
- 실행 순서: DB 분리(Phase 1) 완료 후 Redis 분리·R2 테넌트 저장공간 설계 적용. Full HA·샤딩·Redis HA는 saas-expanded-multitenancy-redis-storage-plan.md 참조.
3. 아키텍처 로드맵
3.1 Phase 1 — 초기 운영 (DB 분리형 단일 구성)
구성
[ App Server ] [ DB Server ] ├── Bench (Frappe) ──────► └── MariaDB (Single Primary) ├── Redis (원격 호스트로 연결) └── Workers요건
- 동시 사용자: 1,000 이하
- 테넌트: 50개 이하
- 연동 방식: Frappe
bench new-site시--db-host,--db-port등으로 DB 서버 지정. 동일 MariaDB 내에서 테넌트별 DB(schema)만 분리(Frappe Multi-tenancy).
Pulumi 역할
- App 서버 1대 프로비저닝 (기존과 유사: Hetzner Server, 방화벽, 관리용 DNS).
- DB 서버 1대 프로비저닝 추가: 별도 Hetzner Server, 방화벽(3306은 Private Network 또는 App 서버 IP만 허용), 동일 리전·Private Network 권장.
- 출력:
app_server_ip,db_server_ip(또는 기존server_ip를 App 전용으로 하고 DB용 추가). - 사용량 모니터링: Control Plane·server_metrics 수집 경로와 연동 가능하도록 스택/라벨 정리(Phase 2 트리거용).
Ansible 역할
- DB 서버 대상: MariaDB 설치(Docker 또는 네이티브), root 비밀번호 설정, 원격 접속 허용(예:
bind-address, App 서버 IP 허용), 필요 시 binlog 활성화(Phase 2 대비). 성능 튜닝: 8GB~16GB RAM 환경·Frappe 특화 my.cnf 파라미터는 mariadb-mycnf-optimization-plan.md 참조. - App 서버 대상: Docker, Frappe bench, Redis, Workers 설치. DB 연결 정보:
db_host={{ db_server_ip }},db_port,db_root_password등으로 별도 DB 서버 지정.bench new-site시--db-host등 사용. - 프로비저닝 워크플로: 인벤토리에 App 서버·DB 서버 그룹 분리, 또는 순차 play(먼저 DB 서버 설정, 다음 App 서버에 DB 호스트 변수 전달).
이유
- 다중 DB(Replica/샤딩)는 초기에는 과도한 복잡성. DB만 분리해도 운영·백업·확장 시 DB 서버만 스케일 업/아웃 가능.
- Private 네트워크 설계로 DB 트래픽이 공인 망을 타지 않도록 함.
3.2 Phase 2 — 트래픽 증가 (Primary + Replica)
트리거 조건 (사용량 모니터링 기반)
- 동시 사용자 1,500~2,000 이상
- DB CPU 70% 이상 지속(예: 10분)
- 리포트·읽기 쿼리 증가
- 테넌트 70~100개 근접
구성
┌─────────────────┐ │ Replica DB │ (읽기/리포트) │ (Read-only) │ └────────▲─────────┘ │ 복제[ App Servers ] ────────────►│ Primary DB (Bench → db_host) └─────────────────┘Pulumi 역할
- 사용량 모니터링 연동: D1 server_metrics·Hetzner 메트릭 또는 에이전트에서 DB 서버 CPU·연결 수·Slow Query 등 수집. 확장 기준 충족 시 Replica 서버 프로비저닝 트리거(수동 또는 decidePlacement 확장 로직).
- Replica용 Hetzner Server 1대 추가 프로비저닝, Private Network으로 Primary와 동일 VPC.
Ansible 역할
- Primary DB 서버: 이미 binlog·replication user 설정되어 있음(Phase 1에서 준비). 필요 시 Ansible로 replication user 재확인.
- Replica DB 서버: MariaDB 설치,
CHANGE MASTER TO(Primary 호스트·로그·위치),START SLAVE. Read-only 설정. - App 서버: 읽기 전용 쿼리만 Replica로 보내는 설정은 Frappe/설정 또는 프록시 레이어로 구현. Ansible은 필요 시 connection string·설정 파일 배포.
목적
- 읽기 트래픽 분산, 장애 시 Replica 승격(Failover), SLA 개선.
3.3 Phase 3 — 대규모 확장 (멀티테넌트 샤딩)
트리거 조건
- 테넌트 100개 이상
- 특정 대형 고객 DB 부하 집중
- 동시 사용자 3,000+ 이상
전략 요약
| 방식 | 설명 |
|---|---|
| DB 단위 샤딩 | Tenant Group A → DB Server 1, B → 2, C → 3. 테넌트별 DB 분산, 대형 고객 전용 DB 가능. |
| 대형 고객 전용 DB | SMB 테넌트 → Shared DB, Enterprise 테넌트 → Dedicated DB. |
Pulumi 역할
- 확장 지표·정책에 따라 추가 DB 서버 프로비저닝. 테넌트 배치(어느 DB에 넣을지)는 Control Plane·D1과 연동.
Ansible 역할
- 새 DB 서버에 MariaDB 설치·보안 설정.
- App 서버(또는 라우팅 레이어)에서 테넌트별 DB 호스트 매핑 반영. Cross-DB Join 금지 원칙 유지.
4. Pulumi — DB 서버 및 사용량 모니터링
4.1 Phase 1에서의 Pulumi 범위
| 리소스 | 설명 |
|---|---|
| App 서버 | 기존과 동일. Hetzner Server, 방화벽(22), 관리용 DNS. |
| DB 서버 | Hetzner Server 1대 추가. 이미지 Ubuntu 22.04/24.04. 방화벽: SSH(22), 3306은 Private Network 또는 App 서버 출신만 허용. 동일 location·Private Network 권장. |
| 출력 | app_server_ip(또는 server_ip), db_server_ip. Private Network IP 있으면 함께 출력. |
4.2 사용량 모니터링과의 연동
| 항목 | 설명 |
|---|---|
| 수집 경로 | Hetzner API 메트릭, 또는 DB 서버에 에이전트/Cron으로 CPU·메모리·연결 수·Slow Query 수집 → Control Plane API 또는 Worker → D1 server_metrics(또는 전용 db_metrics) 저장. |
| 확장 기준 지표 | DB CPU > 70% 지속 10분, 평균 쿼리 응답 > 100ms, Connection 수 80% 이상, Slow Query 증가 추세. (intelligent-automation §3.4, §3.5와 연동) |
| Pulumi 트리거 | 수동: 운영자가 지표 확인 후 Replica/샤드 서버 추가 시 pulumi up 또는 별도 스택. 자동: decidePlacement 확장 시 새 DB 서버 리소스 추가 후 Ansible/워크플로로 설치·연동. |
4.3 Phase 2/3에서의 Pulumi
- Replica 서버용 리소스 추가(스택 또는 모듈).
- 샤딩 시 DB 서버 N대 리소스, 테넌트–DB 매핑은 D1 등으로 관리.
5. Ansible — 설치 및 원격 DB 연동
5.1 인벤토리 구조 (Phase 1)
| 그룹 | 대상 | 역할 |
|---|---|---|
prego_db_servers | DB 서버 호스트(들) | MariaDB 설치, 원격 접속 허용, binlog(Phase 2 대비). |
prego_nodes (App) | App 서버 호스트(들) | Docker, Frappe bench, Redis, Workers. 변수: db_host, db_port, db_root_password(DB 서버 기준). |
5.2 DB 서버용 Ansible
| 작업 | 내용 |
|---|---|
| MariaDB 설치 | Docker 이미지(mariadb:10.6 등) 또는 네이티브 패키지. |
| 설정 | root 비밀번호, bind-address(0.0.0.0 또는 Private IP), App 서버 IP(또는 VPC 대역)만 3306 허용. |
| Phase 2 대비 | --server-id=1, --log-bin=mysql-bin, --binlog-format=ROW. Replication user 생성(REPLICATION SLAVE 권한). |
5.3 App 서버용 Ansible (Frappe ↔ 별도 DB)
| 작업 | 내용 |
|---|---|
| Frappe/bench 설치 | 기존 roles(docker, frappe_bench, frappe_site)와 동일하되, DB 연결을 로컬이 아닌 원격으로. |
| 연결 정보 | db_host: Pulumi 출력 db_server_ip(또는 인벤토리 변수). db_port: 3306. db_root_password: Secret/Vault. |
| bench new-site | bench new-site {{ site_name }} --db-host {{ db_host }} --db-port {{ db_port }} --db-root-password "{{ db_root_password }}" 등으로 별도 DB 서버에 DB 생성. |
| API Key | 기존과 동일. bench add-api-key 등. |
5.4 Phase 2 Replica 연동 (Ansible)
| 대상 | 작업 |
|---|---|
| Replica 서버 | MariaDB 설치, --server-id=2, --read-only=1, CHANGE MASTER TO(Primary 호스트·로그·위치), START SLAVE. |
| Primary | Replication user·권한은 Phase 1에서 준비 또는 Ansible로 확인. |
6. MariaDB Primary/Replica Docker 구성 참고 (설계만)
Phase 1에서 DB 서버에 Docker로 MariaDB를 띄울 경우, 향후 Replica 추가를 위해 아래 방향으로 설계한다. 코드 생성 없음, 예시 개념만.
- Primary:
--server-id=1,--log-bin=mysql-bin,--binlog-format=ROW. - Replica:
--server-id=2,--relay-log=relay-bin,--read-only=1. - 복제 설정: Primary에서
CREATE USER 'replica'@'%'+GRANT REPLICATION SLAVE; Replica에서CHANGE MASTER TO(MASTER_HOST=Primary IP 등),START SLAVE.
상세 docker-compose 예시는 운영 문서 또는 Ansible role defaults에서 다룬다.
7. 확장 기준 지표 정의
DB 확장 판단 기준 (모니터링 수집 항목과 매핑)
| 지표 | 기준 | 용도 |
|---|---|---|
| DB CPU | > 70% 지속 10분 이상 | Replica 또는 스케일 업 검토. |
| 평균 쿼리 응답시간 | > 100ms | 읽기 분산(Replica) 또는 인덱스·튜닝. |
| Connection 수 | 사용률 80% 이상 | max_connections 상향 또는 서버 추가. |
| Slow Query | 증가 추세 | 튜닝 또는 Replica로 읽기 이전. |
8. 샤딩 전략 설계 원칙 (Phase 3)
| 원칙 | 설명 |
|---|---|
| 테넌트 단위 분리 | Frappe는 DB(schema) 단위 분리 가능. 테넌트별로 다른 DB 호스트 지정 가능. |
| 대형 고객 전용 DB | Enterprise 전용 DB 서버 배치. |
| DB 서버별 부하 균형 | 테넌트 수·메트릭 기반으로 새 테넌트 배치 결정. |
| Cross-DB Join 금지 | 설계 단계에서 차단. 테넌트 간 조인은 애플리케이션 레벨에서 하지 않음. |
9. 복잡도 관리 전략
| 단계 | 복잡도 | 관리 전략 |
|---|---|---|
| Phase 1 (App + DB 1대씩) | 낮음 | 기본 모니터링, Ansible로 설치·연동 일원화. |
| Phase 2 (Primary + Replica) | 중간 | Failover 절차 문서화, Replica 지연 모니터링. |
| Phase 3 (샤딩) | 높음 | 테넌트–DB 매핑(D1), DB 라우팅 레이어 또는 Frappe site별 db_host 설정. |
10. 기존 기획서와의 연동
| 문서 | 연동 내용 |
|---|---|
| pulumi-ansible-step1-step2-plan.md | 1단계 Pulumi에서 App 서버 + DB 서버 두 대 출력. 2단계 Ansible에서 DB 서버 그룹과 App 서버 그룹 각각 역할 수행. |
| ansible-implementation-plan.md | 변수 추가: db_host, db_port(기본 3306). 인벤토리: prego_db_servers, prego_nodes 및 host 변수. DB 서버용 role(mariadb 또는 frappe_db_server), App 서버용 role에서 --db-host {{ db_host }} 반영. |
| intelligent-automation-implementation-plan.md | server_metrics에 DB 서버 메트릭 수집 추가. 확장 결정(decidePlacement 또는 전용 로직)에서 “Replica 필요”/“샤드 추가” 시 Pulumi·Ansible 순서로 반영. |
| provision-tenant.yml (워크플로) | resolve-server 출력에 db_server_ip 추가. 인벤토리에 db_host 전달. 확장 시 Redis 서버 IP, R2 테넌트 prefix/버킷 생성 단계. |
| saas-expanded-multitenancy-redis-storage-plan.md | Redis 별도 서버 분리, 각 고객별 저장공간 R2 분리, Full HA·샤딩·Redis HA 상세. 확장 SaaS(테넌트 100+) 목표 구조. |
11. 최종 전략 요약
지금 설계에서 반영할 것
- Frappe 설치 시 DB를 별도 DB 서버에 설치된 MariaDB와 연동하는 구조로 변경.
- Pulumi에서 App 서버 + DB 서버를 프로비저닝하고, 사용량 모니터링을 통해 Phase 2/3 확장(Replica, 샤드) 시 추가 DB 서버 구성.
- Ansible에서 DB 서버에 MariaDB 설치, App 서버에 Frappe 설치 후 원격 DB 연동(
db_host,db_port,db_root_password). - Phase 1: 단일 MariaDB(단, 별도 호스트). Replica 추가 가능하도록 binlog·replication user 준비.
- Private 네트워크 설계로 DB 트래픽 격리.
향후
- 동시 사용자 1,500~2,000·지표 충족 시 Replica 추가(Pulumi + Ansible).
- 테넌트 100+ 시 샤딩 설계 및 대형 고객 전용 DB 전략.
- Redis 별도 서버 분리 및 각 고객(테넌트)별 저장공간 R2 분리 적용 — saas-expanded-multitenancy-redis-storage-plan.md 참조.
확장성 관리 (온보딩·리소스·로그)
| 항목 | 전략 요약 |
|---|---|
| 온보딩 시 DB 할당 | 테넌트가 늘어나면 단일 DB 서버 한계를 줄이기 위해, 온보딩 파이프라인에 “현재 가장 한가한 DB 서버” 선택 로직을 둔다. server_metrics·tenant_count 기반으로 할당. provision-tenant 워크플로의 resolve-server 단계 또는 배치 결정 엔진과 연동. provision-tenant-workflow-design.md §7.1. |
| 리소스 격리 | 특정 테넌트의 무거운 작업이 다른 테넌트에 영향을 주지 않도록 Docker Compose 레벨에서 CPU·Memory 제한(deploy.resources.limits)을 적용. Cgroups 기반 격리. |
| 로그 통합 | 테넌트가 여러 노드에 분산되므로 Cloudflare Logpush 또는 ELK Stack 등으로 로그를 한곳에 모아 조회·알람·감사 체계를 갖춘다. provision-tenant-workflow-design.md §7.1. 상세 기획: cloudflare-logpush-observability-plan.md. |
12. 결론
- DB 분리: App 서버와 DB 서버를 분리하고, Frappe는 별도 DB 서버의 MariaDB와만 연동한다.
- Pulumi: DB 서버 프로비저닝 및 사용량 모니터링과 연동한 확장(Replica/샤드) 구성.
- Ansible: DB 서버 실제 설치(MariaDB), App 서버 실제 설치(Frappe)·원격 DB 연동을 담당한다.
- 초기 복잡도는 낮게 유지하고, 중기 안정성·장기 확장 가능성을 함께 확보하는 구조로 정리한다.