Skip to content

Hetzner·Pulumi 기반 서버 리소스 관리: Trial 종료·유료 전환·독립(전용) 서버 기획서

목적: (1) 모든 서버는 Hetzner를 사용하며, prego-pulumi 레포에 연결된 Pulumi 솔루션이 기존 기획서·워크플로에 어떻게 반영되어 있는지 확인하고, (2) 무료 Trial 종료 후 유료 전환 또는 서비스 종료, (3) 처음부터 유료 패키지 선택 시 독립(전용) 서버 구성(Frappe·MariaDB·Redis 각 1대 = 테넌트당 3대)을 현재 설계에서 어떻게 다루는지 정리한 뒤, 서비스에 적합한 서버 리소스 관리 모델을 추천한다. 코드 생성 없음 — 기획·분석·추천만.

참조: prego-pulumi (README, main.py, config/region.py), tenant-onboard-resource-allocation-flow-plan.md, placement-test-data-and-simulation-dashboard.md, provision-tenant.yml, api-control-plane-tenant-lifecycle-analysis.md.


1. 현재 Pulumi(Hetzner) 구현 요약 (prego-pulumi)

항목내용
위치/Users/marco/prego-pulumi (별도 레포). Prego 워크플로에서 clone 후 pulumi stack select + pulumi up 호출.
프로바이더Hetzner Cloud (pulumi_hcloud), Cloudflare (DNS, R2, D1, KV).
스택리전별 1스택: sg, us, eu (또는 dev = sg). Pulumi Cloud 조직/프로젝트: opsfork-org/prego-pulumi.
리소스(스택당)App 서버 1대 + DB 서버 1대 + Redis 서버 1대 (고정). 방화벽, Placement Group(spread), 선택적 Volume.
출력server_ip(app), db_server_ip, redis_server_ip, management_dns(node-NN.pregoi.com).
역할멀티테넌트 공유: 동일 스택의 App 1대에 여러 테넌트(Frappe 사이트)가 적층됨. DB·Redis는 해당 리전 테넌트들이 공유.

정리: 현재 Pulumi는 “리전당 1세트(App 1 + DB 1 + Redis 1)” 만 생성하며, 테넌트 전용 독립 3대(1 tenant = App 1 + DB 1 + Redis 1) 구성은 미지원이다.


2. 기획서·워크플로에 Pulumi가 반영된 여부

2.1 반영되어 있는 부분

문서/구성요소Pulumi·Hetzner 반영 내용
tenant-onboard-resource-allocation-flow-plan.mdcreate_new_server 시 Pulumi로 새 서버 생성, R3 Pulumi 성공 후 노드 등록, 시나리오 A/B(메모리 포화·노드 증설)에서 Pulumi up 경로 언급. Hetzner는 문서에 “Hetzner 서버”로 명시.
provision-tenant.ymlcreate_new_server=true 시 prego-pulumi clone → pulumi stack select $stack (region=sg/us/eu) → pulumi up -y → server_ip, db_server_ip 출력 사용. Pulumi 성공 후 POST /internal/nodes 로 노드 등록.
placement.ts (R4)Enterprise → dedicated 노드만, 그 외 → shared. 노드는 Control Plane D1 nodes 에서 관리; “노드”의 물리 생성은 워크플로에서 Pulumi로 수행.
placement-test-data-and-simulation-dashboard.mdPlacement 결과(create_new_server, target_server_id)와 “서버 생성·Frappe·DB·Redis·적층” 검증을 다룸. 실제 서버 생성은 워크플로 → Pulumi 연동으로 이루어지므로, 해당 기획서는 Pulumi를 전제로 한 플로우와 호환되나, 문서 내에 “prego-pulumi”·“Hetzner” 문자열은 명시되어 있지 않음.
intelligent-automation-implementation-plan.md, IMPLEMENTATION_INDEXPulumi → Ansible → Zuplo → D1 전 구간 자동화, prego-pulumi 경로·스택(sg/us/eu) 참조.
saas-db-separation-and-scaling-plan.md, redis-separation-pulumi-ansible-plan.mdPulumi로 DB·Redis 서버 프로비저닝, Phase별 로드맵.

결론: 대부분의 기획서와 워크플로는 Pulumi(Hetzner)를 전제로 하며, create_new_server 시 prego-pulumi를 사용하는 구조가 반영되어 있다.

2.2 현재 제약(갭)

항목내용
스택당 서버 수Pulumi main.py는 스택당 App 1 + DB 1 + Redis 1 고정. “앱 서버만 1대 더” 추가하려면 새 스택(예: sg-node02) 또는 Pulumi 코드 변경(앱 서버 N대) 필요.
create_new_server 의미워크플로는 region별 기존 스택(sg/us/eu)에 대해 pulumi up 실행. 해당 스택이 이미 있으면 추가 서버가 생기지 않음(이미 리소스 존재). 즉 “첫 프로비저닝 시 3대 생성”에는 부합하나, “리전 내 2번째 앱 노드” 는 현재 구조로는 불가.
독립(전용) 3대“1 테넌트 = Frappe 1대 + MariaDB 1대 + Redis 1대” 전용 구성은 현재 Pulumi·기획서 어디에도 정의되어 있지 않음.

3. 시나리오 1: 무료 Trial 종료 후 유료 전환 또는 서비스 종료

3.1 요구 사항 정리

  • 무료 Trial: 약 3개월 사용 후
    • 유료 전환: 결제 시작 → 기존 테넌트 유지(동일 shared 노드 또는 업그레이드).
    • 서비스 종료: Trial 만료·미전환 시 접근 차단 또는 리소스 정리.

3.2 현재 설계 반영 여부

항목현재 반영갭·보완
Trial 기간tenants_master·billing에 trial_ends_at(또는 유사) 필드가 기획서·마이그레이션에 명시적으로 없음. Stripe의 trial_period_days·subscription trial_end 는 Stripe 측에 있음.정의 필요: D1에 trial_ends_at 또는 Stripe subscription과 매핑해 “Trial 만료일”을 Control Plane에서 조회 가능하게 할지 결정.
Trial → 유료 전환Stripe checkout.session.completed·customer.subscription.updated 로 유료 구독 시작 시 워크플로·Webhook 처리 가능. plan_tier 상향·갱신은 billing·tenants_master 반영으로 확장 가능.정의 필요: “Trial 만료 N일 전 알림”, “만료 시 자동 전환(결제 수단 있음) vs 미전환 시 제한” 정책을 기획서·Runbook에 명시.
Trial 종료·미전환(서비스 종료)api-control-plane-tenant-lifecycle-analysis: Suspended 상태, Grace Period(만료일+7일) 후 접근 차단. purge-job은 Pending_Deletion 30일 후 Hard Purge.: Trial 만료를 “결제 실패”와 동일한 Suspended·Grace 흐름으로 둘지, Trial_Expired 전용 상태로 둘지 정책 정의. “서비스 종료” 시 리소스(동일 shared 노드 상의 해당 테넌트만 제거) 정리 순서는 purge·Runbook에 맞춰 정의 가능.

정리: Trial 기간·만료일·전환/종료 정책은 부분적으로만 기획서에 반영되어 있다. trial_ends_at(또는 Stripe 연동), Trial 만료 시 전환 vs 종료 플로우, Suspended/Trial_Expired 상태를 명시하는 기획 보강이 필요하다.


4. 시나리오 2: 처음부터 유료 패키지 선택 — 독립(전용) 서버

4.1 요구 사항 정리

  • 독립(전용) 구성: 한 테넌트당 다른 테넌트와 서버를 공유하지 않음.
    • Frappe 1대 + MariaDB 1대 + Redis 1대 = 테넌트당 3대 필요.

4.2 현재 설계 반영 여부

항목현재 반영
Placement R4Enterprise → dedicated 노드만 선택. nodes.role = ‘dedicated’.물리 의미: “dedicated”는 현재 “다른 테넌트와 앱을 공유하지 않는 노드”를 의미하나, Pulumi는 노드 1대(앱만) 개념이지, 1 tenant = App 1 + DB 1 + Redis 1 세트를 만들지 않음.
Pulumi스택당 App 1 + DB 1 + Redis 1 (공유용 1세트). 테넌트 전용 3대 세트 생성 로직 없음.: 독립 구성은 테넌트별 별도 스택 또는 전용 스택 템플릿(tenant_id당 App 1 + DB 1 + Redis 1)이 필요.
워크플로create_new_server 시 리전 스택 1개만 사용. tenant_id·plan_tier에 따라 “전용 스택”을 선택하는 분기 없음.: plan_tier=enterprise(또는 “dedicated” 플래그)일 때 별도 Pulumi 스택(예: dedicated-{tenant_id} 또는 dedicated-{region}-{seq}) 실행 경로가 없음.
Ansible인벤토리: server_ip, db_server_ip, redis_server_ip. 1 tenant 1 app 인벤토리 구성은 가능하나, “이 테넌트만을 위한 DB·Redis 1대씩”은 해당 서버들만 인벤토리에 넣으면 됨.정합: Pulumi가 테넌트 전용 3대를 만들면, Ansible은 기존처럼 1 app + 1 db + 1 redis 대상으로 playbook 실행하면 됨.

결론: 독립(전용) 서버 구성(테넌트당 3대)현재 Pulumi·워크플로·기획서에 반영되어 있지 않다. R4 “dedicated”는 “dedicated 노드(앱 1대)” 수준이며, “1 tenant = 3 servers” 리소스 모델은 신규 정의가 필요하다.


5. 서버 리소스 관리 모델 추천

5.1 리소스 모델 구분

모델설명테넌트당 서버 수현재 구현
공유(Shared)여러 테넌트가 동일 App 노드(및 리전 공용 DB·Redis)에 적층.0 (리전당 1세트 공유)✅ Pulumi 스택당 1세트, Placement에서 shared 노드 배치.
독립(전용, Dedicated)1 테넌트가 Frappe 1대 + MariaDB 1대 + Redis 1대를 단독 사용.3❌ 미구현.

5.2 추천: 이원화 리소스 모델

공유 풀(Shared Pool)독립 풀(Dedicated Pool) 을 나누어 관리하는 것을 추천한다.

구분공유 풀 (Shared)독립 풀 (Dedicated)
대상Free, Basic, Professional, Trial(무료)Enterprise 또는 “독립 서버” 옵션 선택 고객
리소스리전당 N개 앱 노드 + 1(또는 소수) DB·Redis 공유.테넌트당 1스택: App 1 + DB 1 + Redis 1 (Hetzner 3대).
Placement기존 decidePlacement: region, role=shared, server_metrics 기준.“dedicated” 요청 시 노드 선택이 아니라 “전용 스택 프로비저닝” 플로우로 분기.
Pulumi기존과 동일 또는 리전당 앱 노드 다중화(스택 이름 확장 예: sg-node01, sg-node02).테넌트 전용 스택: 예) dedicated-{tenant_id_safe} 또는 dedicated-{region}-{seq}. 해당 스택에서 App 1 + DB 1 + Redis 1 생성.
워크플로create_new_server → 기존 region 스택(sg/us/eu) 또는 노드 추가 스택. target_server_id 있으면 기존 노드 사용.infra_mode=dedicated(또는 plan_tier=enterprise + dedicated_tenant=true) → 전용 스택 Pulumi up → 동일 스택에서 server_ip, db_server_ip, redis_server_ip 사용 → Ansible은 해당 3대만 대상.
노드 등록앱 노드만 nodes에 등록(host=app IP). DB·Redis는 region별 기본값 또는 R7.선택: 전용 세트를 “노드 1개”(app만)로 등록하거나, tenant_dedicated_resources 테이블에 tenant_id, app_host, db_host, redis_host 저장.

5.3 Trial 종료 반영 추천

항목추천
데이터tenants_master 또는 billing에 trial_ends_at (또는 Stripe subscription trial_end와 동기화).
정책Trial 만료 X일 전 알림(이메일·앱 내). 만료 시: (A) 결제 수단 있음 → 유료 전환 플로우, (B) 없음 → Suspended 또는 Trial_Expired 전환, Grace(예: 7일) 후 접근 차단.
리소스Trial·Free는 공유 풀만 사용. 전환 시 유료 플랜이 shared면 동일 노드 유지; dedicated 선택 시 독립 풀 프로비저닝(마이그레이션 또는 신규 3대) 기획 필요.

5.4 Pulumi 측 추천 (개념)

  • 공유 풀:
    • 현행 유지: 리전당 1스택(1 App + 1 DB + 1 Redis)으로 첫 create_new_server 처리.
    • 확장 시: 리전 내 앱 노드 추가가 필요하면 스택 이름 규칙 확장(예: sg → sg-node01, sg-node02)하여 스택당 App 1대만 두거나, Pulumi 코드에서 app_server_count 등 변수로 앱 서버 다대 생성.
  • 독립 풀:
    • 테넌트 전용 스택 1개 = App 1 + DB 1 + Redis 1.
    • 스택 이름: dedicated-{tenant_id} (Hetzner 리소스명 충돌 방지용 sanitize) 또는 ded-{region}-{short_id}.
    • 워크플로에서 infra_mode=dedicated(또는 전용 플래그)일 때만 이 경로 실행; 기존 shared 경로와 분리.

6. 기획서 반영 체크리스트 (요약)

#항목현재권장
1기획서에 Pulumi·Hetzner 명시대부분 반영됨. placement-test-data 문서에는 “prego-pulumi”·“Hetzner” 문구 없음.placement-test-data-and-simulation-dashboard.md 참조에 “prego-pulumi(Hetzner)” 추가.
2Trial 기간·만료미정의.trial_ends_at(또는 Stripe 연동), Trial 만료 시 전환/종료 정책 기획서에 추가.
3Trial → 유료 / 미전환 종료lifecycle에 Suspended·Grace 있음. Trial 전용 상태·플로우는 미명시.Trial_Expired 또는 Suspended 활용, Grace 후 제한 순서 Runbook·§13에 명시.
4독립(전용) 3대 구성미반영.“독립 풀” 리소스 모델·테넌트당 3대, Pulumi 전용 스택·워크플로 분기 기획서에 신규 절 추가.
5create_new_server 확장현재는 리전 1스택만.공유 풀 앱 노드 다대화 시 스택 명명 규칙 또는 Pulumi 변수 확장; 독립 풀은 전용 스택 경로 분리.

7. 정리

  • Pulumi(Hetzner) 반영: 대부분의 기획서와 provision-tenant 워크플로는 prego-pulumi를 사용하며, create_new_server 시 리전별 1스택(App 1 + DB 1 + Redis 1) 생성·노드 등록까지 반영되어 있다. Placement 테스트 기획서는 해당 플로우와 호환되나, 문서에 “prego-pulumi”·“Hetzner”를 한 줄 명시하면 좋다.
  • Trial 종료: 무료 Trial(~3개월) 종료 후 유료 전환 또는 서비스 종료부분적으로만 반영됨. trial_ends_at, Trial 만료 시 전환/종료 정책, Suspended·Grace와의 관계를 기획서·Runbook에 명시하는 것이 좋다.
  • 독립(전용) 서버: “1 테넌트 = Frappe 1대 + MariaDB 1대 + Redis 1대” 구성은 현재 설계에 없음. 서버 리소스 관리 모델공유 풀(기존)독립 풀(테넌트당 3대, Pulumi 전용 스택) 을 이원화하고, Trial·유료·전용을 이 모델에 맞춰 정책과 워크플로를 확장하는 것을 추천한다.
Help