Skip to content

English {#english}

Resource Optimization — Safe Adoption Plan

Purpose: Analyze resource-reduction policies (Redis tuning, Gunicorn max-requests, CDN/Static offload, Docker resource limits, MariaDB/App optimization) and plan stepwise adoption of only those that do not negatively impact existing Prego.
No code generation — analysis, applicability, prerequisites, and application order only.

References: redis-separation-pulumi-ansible-plan.md, saas-db-separation-and-scaling-plan.md, prego-docker-implementation-plan.md, mariadb-mycnf-optimization-plan.md, pregoi-infrastructure-r2-api-update-plan.md.
One-page summary: resource-optimization-quick-reference.md.


1. Scope and principles

  • Noisy Neighbor: Prevent one tenant from monopolizing resources; use Docker cgroups (CPU/memory limits) and Control Plane–driven plan-based limits (Free/Pro/Enterprise).
  • Principles: Keep existing provisioning, multitenancy, Stripe, Control Plane, Ansible/Docker flow unchanged. Prefer config/tuning over structural change; apply in stages with verification; make changes rollbackable via Ansible vars and overrides.

2. Policy analysis (summary)

  • Redis: Structure (single Redis, DB 0/1/2/3) per redis-separation plan. Tuning only here: maxmemory, allkeys-lru; save/appendonly non-persistent for cache DB only (queue/socketio per ops policy). Apply: conf → Ansible/Redis role → restart.
  • Gunicorn: max_requests 1000, jitter 100, timeout 120. Apply: common_site_config or Procfile; no behavior change.
  • CDN/Static: Offload /assets (and optionally /files) to R2+CDN. Prerequisites: R2 upload path for site assets, assets_base_url support, Nginx redirect. Apply on staging first. Defer until prerequisites done.
  • Docker limits: Plan-based CPU/memory (Hard + Soft), memswap_limit = memory limit. Control Plane passes plan_limits to Ansible; OOM and CPU throttle monitoring. Apply: docker-compose/Ansible deploy.resources; plan_limits per §4.4.
  • MariaDB: Within scope of mariadb-mycnf-optimization-plan only. ProxySQL excluded for now.
  • Excluded / deferred: PyPy, SocketIO separate container. Optional: Zuplo Edge Cache (short TTL, read-only APIs).

3–7. Order, config, consistency, isolation, summary

  • §3 Application order: (1) Redis conf (2) Gunicorn (3) Docker limits (4) CDN/Static after prereqs (5) MariaDB my.cnf per existing plan. Optional: Edge Cache.
  • §4 Config: Redis common_site_config + redis.conf; Gunicorn vars; CDN steps (assets_base_url, Nginx 301, Cloudflare TTL); §4.4 plan_limits table (Free/Pro/Enterprise CPU, memory Hard/Soft), Ansible integration, OOM/throttle alerts.
  • §5 Consistency: Aligns with redis-separation, saas-db-separation, mariadb-mycnf, prego-docker, R2 plans; ProxySQL/D1 hybrid out of scope.
  • §6 Isolation: Edge (Zuplo rate limits) → App (Docker cgroups, plan_limits) → DB (sharding/separate servers). Control Plane supplies plan_limits to Ansible.
  • §7 Summary: Safe to apply now: Redis tuning (cache only non-persistent), Gunicorn, Docker plan limits + monitoring. After prereqs: CDN/Static. In-scope only: MariaDB my.cnf. Excluded: Redis migration, ProxySQL, PyPy, D1. Optional: Edge Cache, upgrade nudges.

Full tables (§1.1, §2.1–§2.7, §3, §4.1–§4.4, §5, §6) and exact wording are in the Korean section below.


한국어 {#korean}

리소스 사용량 절감 — 기존 시스템 영향 최소 적용 기획서

목적: 리소스 사용량을 줄이기 위한 정책들(Redis 통합·튜닝, Gunicorn max-requests, CDN/Static Offload, Docker 자원 제한, MariaDB/App 최적화 등)을 분석하고, 기존 Prego 시스템에 부정적 영향을 주지 않는 항목만 단계적으로 적용하기 위한 기획.
코드 생성 없음 — 분석·적용 가능 여부·선행 조건·적용 순서만 정의.

참조: redis-separation-pulumi-ansible-plan.md, saas-db-separation-and-scaling-plan.md, prego-docker-implementation-plan.md, mariadb-mycnf-optimization-plan.md, pregoi-infrastructure-r2-api-update-plan.md.
한 페이지 요약: resource-optimization-quick-reference.md.


1. 범위 및 원칙

1.1 Noisy Neighbor 방지·격리 목표 (SaaS 관점)

SaaS 환경에서 **한 테넌트가 자원을 독점해 다른 고객 서비스까지 느려지게 만드는 현상(Noisy Neighbor)**을 막기 위해, Docker 컨테이너 수준에서 CPU·메모리를 cgroups로 하드웨어적 격리하고, Control Plane(Ansible/Pulumi)이 패키지 등급(Free/Pro/Enterprise)에 따라 동적으로 제한값을 설정하는 것을 목표로 한다.

  • 장점: 특정 테넌트의 부하가 호스트 전체로 전파되지 않음. 플랜별 공정한 자원 배분·예측 가능한 성능.
원칙내용
기존 동작 유지프로비저닝·멀티테넌시·Stripe·Control Plane·Ansible/Docker 배포 흐름을 바꾸지 않음.
설정·튜닝 우선구조 변경(테넌트별 Redis → 단일 Redis 등)은 이미 별도 기획(redis-separation)에 있으면 그대로 따르고, 본 문서는 튜닝·옵션 추가만 다룸.
단계적 적용적용 순서를 정하고, 각 단계 후 동작 검증(health check, 로그인, API) 후 다음 단계 진행.
롤백 가능설정 변경은 Ansible 변수·common_site_config·docker-compose 오버라이드로 관리해, 문제 시 이전 값으로 복귀 가능하게 함.

2. 정책별 분석 및 적용 여부

2.1 Redis 통합·튜닝

항목제안기존 Prego영향·결론
구조테넌트별 별도 Redis → 단일 Redis, Key Prefix(또는 DB 번호)로 분리redis-separation-pulumi-ansible-plan.md에서 이미 단일 Redis·DB 0/1/2/3(cache/queue/socketio) 목표.구조 변경은 해당 기획서대로 진행. 본 문서에서는 구조 변경 없이 기존(또는 전환 후) 단일 Redis에 대한 튜닝만 다룸.
redis.confmaxmemory 2gb, maxmemory-policy allkeys-lru, save "", appendonly noRedis 서버는 Pulumi·Ansible으로 배포. conf는 Ansible role에서 관리.적용 시 주의: save ""·appendonly no영속성 없음. cache DB(1)에는 적용 가능; **queue(2)·socketio(3)**는 작업 유실 시 재시도 가능한지 운영 정책에 따름. 권장: cache용 DB만 비영속, queue/socketio는 appendonly yes 또는 운영 정책 유지.
common_site_configredis_cache/queue/socketio URL (redis:6379/0,1,2)redis-separation에서 이미 단일 호스트·DB 번호 설계.정합. URL은 Ansible·redis-separation 기획에 맞게 유지.

적용 가능(기존 시스템에 영향 없음)

  • Redis conf: maxmemory, maxmemory-policy allkeys-lru 적용. save ""·appendonly nocache DB에만 적용하거나, queue/socketio는 기존 정책 유지.
  • 적용 순서: redis.conf 수정 → Ansible/Redis role 반영 → Redis 재시작 → bench restart.

제외·별도 기획

  • 테넌트별 Redis → 단일 Redis 이전 자체: redis-separation Runbook·기획서에 따름.

2.2 Gunicorn max-requests

항목제안기존 Prego영향·결론
설정gunicorn_max_requests 1000, max_requests_jitter 100, timeout 120, workers 2Frappe/Docker 이미지·Ansible에서 gunicorn 실행. common_site_config 또는 Procfile.동작 변경 없음. Worker가 N회 요청 처리 후 재시작되어 메모리 초기화. jitter로 동시 재시작 방지.

적용 가능(기존 시스템에 영향 없음)

  • common_site_config.json 또는 Procfilegunicorn_max_requests, gunicorn_max_requests_jitter, gunicorn_timeout, gunicorn_workers 반영.
  • 값: max_requests 1000, jitter 100, timeout 120. workers 수는 Ansible 변수·플랜별로 유지.
  • 적용 순서: 설정 추가 → 이미지/Ansible 배포 → bench/컨테이너 재시작.

2.3 CDN/Static Offloading (R2 + CDN)

항목제안기존 Prego영향·결론
목표/assets, /files를 Frappe가 아닌 R2+CDN에서 서빙. App 서버 트래픽·메모리 감소prego-static-assets R2 버킷·static.pregoi.com(폰트 등) 이미 사용. Frappe 사이트/assets(빌드 에셋)·/files(업로드 파일)는 현재 App에서 서빙.구조적 변경. Frappe의 assets_base_url·Nginx 리다이렉트·R2에 사이트별 assets 업로드 파이프라인이 필요. 잘못 적용 시 404·빈 화면.

적용 가능(선택·선행 조건 필요)

  • 선행 조건: (1) Frappe 빌드 에셋을 R2에 업로드하는 경로(bench build 후 sync 또는 CI) 확보. (2) R2 버킷(또는 prego-static-assets 내 경로)에 사이트/테넌트별 prefix 정책. (3) serve_default_site·assets_base_url 지원 여부 확인(Frappe 버전).
  • 적용 범위: 우선 빌드 에셋(/assets)만 CDN으로 오프로드; /files(사용자 업로드)는 보안·경로 정책 검토 후 단계적.
  • Cloudflare 캐시: cdn.pregoi.com(또는 static.pregoi.com)/assets/* TTL 1달; API는 bypass.
  • 영향 최소화: 기존 URL이 동작하도록 Nginx에서 301 리다이렉트로 전환하면, 캐시 미스 시에도 R2로 fallback 가능.

제외(본 단계)

  • R2 업로드·assets_base_url 파이프라인 없이 Nginx만 리다이렉트하면 404 위험. 선행 작업 완료 후 적용.

2.4 Docker 자원 제한 (Soft/Hard·패키지별 동적 설정)

항목제안기존 Prego영향·결론
limits/reservations플랜별 CPU·Memory limits (Free 1GB, Pro 2GB, Enterprise 8GB 등)prego-docker·Ansible으로 컨테이너 배포. 현재 제한 없거나 role 기본값.추가만 하면 됨. 기존 동작은 유지; 한 테넌트가 호스트를 독점하는 것만 방지.
Soft vs HardHard Limit: 최대 사용 상한(초과 시 OOM·스로틀). Soft(Reservation): 최소 보장 메모리.장점: Soft로 최소 성능 보장, Hard로 Noisy Neighbor 상한 차단.
memswap_limit스왑 사용 상한을 메모리 limit과 동일하게 두어 스왑으로 인한 성능 저하·불확실성 방지장점: 컨테이너가 스왑에 과도 의존하지 않도록 예측 가능한 성능 유지.
Control Plane 연동테넌트 생성·업그레이드 시 패키지(plan)에 따라 Ansible에 plan_limits[user_plan] 전달 → 컨테이너 memory·memory_reservation·cpus·memswap_limit 동적 설정프로비저닝 시 Ansible에 tenant_id·site 등 전달.장점: 플랜 변경 시 재배포만으로 자원 한도 자동 반영.

적용 가능(기존 시스템에 영향 없음)

  • docker-compose 또는 Ansible community.docker.docker_container에서 memory(Hard), memory_reservation(Soft), cpus, memswap_limit(메모리 limit과 동일 권장) 설정.
  • 플랜별 수치: Ansible 변수 plan_limits[user_plan]로 분기. Free/Pro/Enterprise 예시는 §4.4 표 참조.
  • OOM 모니터링: docker events --filter event=oom 또는 Prometheus cAdvisor. OOM 사전 예방: container_memory_usage_bytes가 limit 대비 90% 도달 시 Alertmanager → Control Plane 알림(§16 연계).
  • 주의: limit을 너무 낮게 주면 정상 부하에서도 OOM 가능. Free 플랜은 최소 512MB~1GB 등 운영 정책에 맞게 설정.

Over-provisioning vs 격리 (운영 팁)

  • Shared 노드(Free/Pro): 실제 물리 자원보다 더 많은 컨테이너를 띄울 수 있되(Over-provisioning), memory_reservation으로 각 테넌트 최소 성능 보장.
  • Dedicated 노드(Enterprise): 다른 테넌트와 섞이지 않도록 Docker Node Label·Constraint로 전용 Hetzner 서버에만 배치하는 옵션을 둘 수 있음(saas-db-separation·Placement 정책과 연계).

실시간 모니터링·스로틀링 (장점)

  • OOM Kill 사전 방지: Prometheus가 container_memory_usage_bytes를 수집하고, limit 대비 90% 도달 시 Alertmanager로 알림 → 운영자 또는 Control Plane이 대응(재시작·업그레이드 유도).
  • CPU Throttling 감지: container_cpu_cfs_throttled_seconds_total 메트릭으로, CPU 제한 때문에 성능이 저하되는 테넌트를 식별 → 상위 플랜 업그레이드 유도 또는 limit 조정 검토.
  • 자동 업그레이드 유도(선택): 자원 부족이 잦은 테넌트에 Zuplo 등을 통해 상위 패키지 업그레이드 안내·리다이렉트 정책을 둘 수 있음(비즈니스 정책 범위).

2.5 MariaDB·ProxySQL·Buffer Pool

항목제안기존 Prego영향·결론
Connection Pooling (ProxySQL)테넌트당 커넥션 대신 풀링, 메모리 500MB→100MB 수준 절감saas-db-separation-and-scaling-plan.md, MariaDB 별도 서버. ProxySQL은 별도 도입 검토.구조·동작 변경. 연결 방식 변경으로 인한 타임아웃·락 이슈 가능.
table_definition_cache메타데이터 캐시 타이트하게mariadb-mycnf-optimization-plan.md에서 my.cnf 튜닝.기존 기획서에서 다룸. 본 문서에서는 my.cnf 범위 내만 권장; table_definition_cache는 조정 시 성능 모니터링 필요.

적용 가능(기존 시스템에 영향 없음)

  • MariaDB my.cnf 튜닝만: 이미 mariadb-mycnf-optimization-plan에 있으면 해당 계획 따름. 신규 변경은 buffer_pool·connection 수 등만 조정하고, table_definition_cache 등은 보수적 유지.

제외(본 단계)

  • ProxySQL 도입: 별도 기획·POC 후 적용.
  • D1(SQLite) 하이브리드: Frappe 코어 수정 필요, 장기 검토.

2.6 Frappe App 기타 (PyPy·SocketIO 분리)

항목제안영향·결론
PyPyCPython 대신 PyPy로 메모리·속도 개선라이브러리·C 확장 호환성 리스크. 제외 — 기존 시스템 영향 큼.
SocketIO 별도 컨테이너Node.js 등으로 분리해 App 부하 감소frappe-socketio는 이미 별도 프로세스. 별도 컨테이너로 분리 시 네트워크·배포 변경. 단계적 검토 — 우선 제외.

2.7 Edge Cache (Zuplo)

항목제안영향·결론
단순 조회·대시보드Frappe까지 가지 않고 Zuplo Edge Cache에서 응답API별 캐시 가능 여부·무효화 정책 정의 필요. 잘못 적용 시 stale 데이터. 선택 — 특정 읽기 전용 API만 TTL 짧게 적용 후 검토.

적용 가능(선택)

  • GET 특정 경로(예: 대시보드 집계)만 짧은 TTL(예: 60초) Edge Cache. 무효화 정책 명시. 본 문서에서는 우선순위 낮음으로 두고, 나중에 API 목록 정리 후 적용.

3. 적용 순서 요약 (기존 시스템 영향 없음 위주)

순서항목선행 조건비고
1Redis conf 튜닝Redis 서버 배포 완료(redis-separation 또는 현행).maxmemory, allkeys-lru. save/appendonly는 cache DB만 비영속 또는 유지.
2Gunicorn max-requests·jitter·timeoutcommon_site_config 또는 Procfile 배포 경로 확보.설정 추가 후 재시작.
3Docker 자원 제한Ansible·compose에서 deploy.resources 적용 가능.플랜별 변수화. OOM 모니터링 추가.
4CDN/Static OffloadR2에 Frappe 사이트 assets 업로드 경로·assets_base_url 지원 확인.Nginx 301 + Cloudflare 캐시. 적용 시 Staging에서 먼저 검증.
5MariaDB my.cnfmariadb-mycnf-optimization-plan 참조.기존 기획 범위 내만.
(선택)Edge Cache (Zuplo)캐시할 API 목록·TTL·무효화 정책.읽기 전용·짧은 TTL부터.

4. 우선순위별 구체 설정 요약 (참고)

아래는 설계·Ansible/이미지 반영 시 참고용. 코드/설정 파일 생성은 별도 작업.

4.1 Redis (단일 인스턴스·DB 번호)

  • common_site_config.json: redis_cache → redis://host:6379/1, redis_queue → /2, redis_socketio → /3. (redis-separation 기획과 동일.)
  • redis.conf: maxmemory 2gb, maxmemory-policy allkeys-lru. save ""·appendonly no는 캐시 DB만 또는 운영 정책에 따라 queue/socketio는 유지.

4.2 Gunicorn

  • common_site_config.json: gunicorn_workers, gunicorn_max_requests(1000), gunicorn_max_requests_jitter(100), gunicorn_timeout(120).
  • 또는 Procfile: —max-requests 1000 —max-requests-jitter 100 —timeout 120.

4.3 CDN/Static (R2)

  • Step 1: common_site_config에 serve_default_site false, assets_base_url https://cdn.pregoi.com (또는 static.pregoi.com).
  • Step 2: Nginx에서 /assets (및 선택 /files) → 301 to CDN URL.
  • Step 3: Cloudflare 캐시 규칙 — cdn./assets/ TTL 1달; API bypass.

4.4 Docker 자원 제한 (패키지별·Soft/Hard)

패키지별 자원 할당 예시

패키지CPU (Core)메모리 (Hard Limit)메모리 (Reservation/Soft)
Free0.5512MB ~ 1GB*256MB ~ 512MB
Pro1.52GB1GB
Enterprise4.08GB4GB

* Free는 운영 정책에 따라 512MB(경량) 또는 1GB(권장 최소) 선택.

  • Ansible 연동: Control Plane이 테넌트 생성·업그레이드 시 user_plan(또는 plan_tier)을 Ansible 변수로 전달. plan_limits[user_plan].mem_limit, plan_limits[user_plan].mem_soft, plan_limits[user_plan].cpu_limit으로 docker_containermemory, memory_reservation, cpus 설정. memswap_limit은 메모리 Hard limit과 동일하게 두어 스왑으로 인한 성능 저하 방지.
  • OOM 감지: docker events 또는 cAdvisor → Prometheus. 사전 알림: container_memory_usage_bytes / limit ≥ 90% 시 Alertmanager → §16 Observability 연계.
  • CPU 스로틀 감지: container_cpu_cfs_throttled_seconds_total 수집 → 제한에 자주 걸리는 테넌트 식별.

5. 기존 기획서와의 정합성

기존 기획서본 문서와의 관계
redis-separation-pulumi-ansible-planRedis 구조(단일·DB 번호)는 그대로 따름. 본 문서는 conf 튜닝만 추가.
saas-db-separation-and-scaling-planDB 서버 분리·Phase 유지. ProxySQL·D1 하이브리드는 본 문서에서 제외.
mariadb-mycnf-optimization-planmy.cnf 튜닝은 해당 기획 범위. 본 문서는 “기존 계획 내에서만” 적용.
prego-docker-implementation-plan이미지·compose에 Gunicorn 설정·자원 제한 반영 시 해당 기획과 통합.
pregoi-infrastructure-r2-api-update-planR2·static.pregoi.com 이미 사용. Frappe 사이트 assets CDN은 추가 도메인/경로 정책만 정합.

6. 격리 계층 요약 (Noisy Neighbor 방지)

자원 격리는 계층별로 적용하면 효과가 크다.

계층수단역할
EdgeZuploAPI 호출 횟수(Rate Limit)·테넌트별 제한으로 진입 단계에서 과부하 차단.
App ServerDocker (cgroups)CPU·메모리 Hard/Soft 제한으로 컨테이너 간 물리적 격리. 패키지별 동적 설정.
DatabaseSharding·별도 서버saas-db-separation 등으로 데이터·I/O 간섭 차단.

Control Plane은 프로비저닝·플랜 변경 시 Ansible에 plan_limits를 전달하여 App 계층 자원 제한을 동적으로 적용한다.


7. 요약

  • 즉시 적용 가능(기존 동작 유지): Redis conf(maxmemory, allkeys-lru, cache DB만 비영속 선택), Gunicorn max-requests·jitter·timeout, Docker 플랜별 자원 제한(Soft/Hard·memswap)·OOM·CPU 스로틀 모니터링. Control Plane이 plan에 따라 Ansible에 plan_limits 전달.
  • 선행 조건 후 적용: CDN/Static Offload(R2 업로드·assets_base_url·Nginx 리다이렉트).
  • 기존 기획 범위 내만: MariaDB my.cnf.
  • 제외·장기 검토: Redis 구조 이전(별도 기획), ProxySQL, PyPy, D1 하이브리드, SocketIO 별도 컨테이너. 선택: Zuplo Edge Cache(API별·짧은 TTL), 자원 부족 시 상위 플랜 업그레이드 유도 정책.

이 순서와 범위로 적용하면 리소스 사용량을 줄이면서도 Noisy Neighbor를 억제하고 기존 시스템 동작에 부정적 영향을 주지 않는다.

Help