Files
chocomae/doc/plan/db/09-aws-db-separation.md
T

618 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AWS에서 MariaDB 분리 검토 가이드
> 웹 서버와 DB 서버를 분리할 때의 비용, 성능, 구현 고려사항 분석
> - 작성일: 2026-09-15 / 최종 수정: 2026-09-16 (2장에 CPU 크레딧 원인 추가)
> - 현재 환경: AWS EC2 t3a.medium (Apache + PHP + Phaser + MariaDB 함께 운영)
> - 문제: 동시접속 150~200명 초과 시 10초 멈춤
> - 이 문서는 **의사결정 가이드**입니다. DB 분리는 선택사항이며, 우선순위는 **안1~5 적용 후**입니다.
>
> ⚠️ **2026-09-16 갱신:** 초판의 2장은 10초 멈춤의 원인을 "DB와 PHP의 리소스 경쟁" 두 가지로만
> 설명했으나, **인스턴스 CPU 크레딧 소진**이라는 세 번째 원인이 빠져 있었습니다.
> 2026-04-14 인스턴스 업그레이드의 실제 사유가 이것입니다. 2-0절에 추가했습니다.
> 관련 실측: [260916-load-and-capacity-check/01-web-tier-analysis.md](260916-load-and-capacity-check/01-web-tier-analysis.md)
---
## 1. 현재 상황 요약
| 항목 | 현재 상태 |
|---|---|
| **인스턴스** | AWS EC2 t3a.medium (2 vCPU, 4GB RAM) |
| **운영 비용** | $0.094/시간 ≈ **월 $67** |
| **실행 환경** | Docker: Apache + PHP + Phaser (웹) + MariaDB (DB) **동시 운영** |
| **병목 현상** | 동시접속 150~200명 초과 → 10초 멈춤 (CPU/메모리 부족) |
| **단일 실패점** | 인스턴스 1개 → 장애 시 모든 서비스 다운 |
---
## 2. 원인 분석: 10초 멈춤은 왜?
10초 멈춤의 후보 원인은 **세 가지**입니다. 초판은 2-1(리소스 경쟁)만 다뤘습니다.
| # | 원인 | 계층 | 현재 상태 (2026-09-16 실측) |
|---|---|---|---|
| 2-0 | **인스턴스 CPU 크레딧 소진** | 인프라 | ✅ 해소 — 재현 안 됨 |
| 2-1 | DB·PHP 리소스 경쟁 | 애플리케이션 | ✅ 대폭 개선 (인덱스 적용) |
| 2-2 | Apache 워커 고갈 | 웹 서버 | ✅ 여유 — 피크 busy 204 / 400 (51%) |
### 2-0. 인스턴스 CPU 크레딧 소진 (2026-04-14 장애의 실제 원인)
t 계열(`t2`/`t3`/`t3a`)은 **버스터블** 인스턴스입니다. 베이스라인을 넘는 CPU 사용은 적립된
크레딧으로 충당하고, 크레딧이 바닥나면 **Standard 모드에서는 베이스라인까지 강제로 조여집니다.**
```
동시 접속 증가
→ Apache MaxRequestWorkers 가 감당 못 함
→ 서버 waiting 과 CPU steal 급등
→ CPU 크레딧 소진
→ 베이스라인으로 스로틀링 → 약 10초간 무응답
```
게스트 OS 안에서는 이 스로틀링이 `vmstat``st`(steal) 컬럼 급등으로 나타나고,
CloudWatch 에서는 `CPUCreditBalance` 가 0에 붙는 것으로 나타납니다.
**이것이 2026-04-14 인스턴스 업그레이드를 하게 된 직접적인 사유입니다.**
**중요한 점은 이 원인이 DB 분리로 해결되지 않는다는 것입니다.** DB를 RDS로 빼내도
웹 서버 인스턴스의 크레딧 구조는 그대로 남습니다.
#### 현재 상태: 재현되지 않음 ✅
피크 시간대(12:50~15:00) vmstat 779개 샘플 실측입니다.
원본: [260916-load-and-capacity-check/result/peaktime_steal_summary.txt](260916-load-and-capacity-check/result/peaktime_steal_summary.txt)
| | 9/14 (인덱스 개선 전) | 9/15 (개선 전) | 9/16 (개선 후) |
|---|---|---|---|
| steal 평균 | 0.003% | 0.021% | 0.010% |
| steal 최대 | 1% | 2% | 1% |
| us+sy 평균 | 13.50% | 9.48% | **2.79%** |
| 베이스라인(20%) 초과 샘플 | 109개 (14.0%) | 65개 (8.3%) | **1개 (0.1%)** |
`t3a.medium` 의 크레딧 베이스라인은 인스턴스 전체 CPU의 **20%** 이고, 이는 `vmstat`
`us+sy` 20% 와 같은 값입니다. 인덱스 개선 전에도 피크 평균이 13.5% 로 베이스라인 미만이었으므로
**피크 시간에도 크레딧은 순증하고 있었습니다.** 개선 후에는 2.79% 로 사실상 무관해졌습니다.
t2.micro 시절과 비교하면 여유가 십수 배 늘어난 것이 원인입니다.
| | t2.micro (2026-04 이전) | t3a.medium (현재) |
|---|---|---|
| vCPU / 메모리 | 1 / 1GB | 2 / 4GB |
| 크레딧 베이스라인 | 10% | 20% |
| 시간당 크레딧 (최대 적립) | 6 (144) | 24 (576) |
> 미확인: 이 인스턴스의 크레딧 모드가 **Unlimited 인지 Standard 인지** 확인이 필요합니다.
> T3 계열 기본값은 Unlimited(스로틀 대신 초과분 과금)지만, t2 에서 마이그레이션한 경우
> Standard 로 넘어왔을 수 있습니다. Standard 라면 위 장애 경로가 이론상 아직 살아 있습니다.
> 확인: AWS 콘솔 → EC2 → 인스턴스 → 크레딧 사양, 또는 CloudWatch `CPUSurplusCreditsCharged`.
### 2-1. 리소스 경쟁 구조
```
┌─────────────────────────────────────┐
│ t3a.medium (2 vCPU, 4GB) │
│ ├─ Apache (PHP) │
│ │ ├─ 요청 처리 │
│ │ └─ DB 쿼리 (네트워크 기다림) │
│ │ │
│ └─ MariaDB │
│ ├─ 쿼리 실행 │
│ └─ 인덱스 스캔 (CPU/메모리 사용)│
│ │
│ 리소스: CPU ◐◐ 메모리 ◐◐ │
└─────────────────────────────────────┘
```
동시접속 150~200명:
- 각 요청이 DB 쿼리 실행 → MariaDB가 리소스 사용량 증가
- PHP도 동시에 요청 처리 → 둘이 2 vCPU를 놓고 경쟁
- 결과: 한쪽이 기다리는 동안 다른 쪽이 처리 → **10초 멈춤**
### 2-2. 실제 병목이 어디인가?
**지난 분석 (01-07 문서)에서 발견:**
- DB 문제 多: 인덱스 부족, 함수로 감싼 조건(`DATE()`, `HOUR()` 등), N+1 쿼리
- PHP도 함께: 동시 요청 처리에 CPU 필요
**결론: DB와 PHP 둘 다 병목일 가능성 높음**
→ DB만 분리해서는 부분적 개선만 가능
#### 2026-09-16 갱신: DB 측은 해소됨 ✅
9/15 인덱스·쿼리 개선 적용 후 피크 CPU가 크게 떨어졌습니다.
| 지표 | 9/15 (개선 전) | 9/16 (개선 후) | 변화 |
|---|---|---|---|
| 피크 us+sy 평균 | 9.49% | 2.80% | 3.4배 ↓ |
| 기록 1,000건당 CPU | 379.8초 | 100.3초 | 3.8배 ↓ |
| 디스크 읽기 bi 평균 | 60.5 | 17.4 | 3.5배 ↓ |
출처: [260915-improve-production/result/peaktime_vmstat_summary.txt](260915-improve-production/result/peaktime_vmstat_summary.txt)
### 2-3. Apache 워커 고갈 (웹 계층)
prefork MPM 에서는 워커(자식 프로세스)가 **요청 처리 중**만이 아니라 **TCP 연결이 열려 있는
내내** 점유됩니다. `KeepAliveTimeout` 동안 아무 일도 안 하면서 연결만 붙잡습니다.
실측에서 busy 워커의 70~80%가 이 KeepAlive 대기 상태였습니다.
현재 설정과 실부하입니다.
| 항목 | 값 |
|---|---|
| MPM / PHP | prefork / mod_php (PHP-FPM 아님) |
| `MaxRequestWorkers` / `ServerLimit` | 400 / 400 |
| `KeepAliveTimeout` | 5초 |
| HTTP/2 | 미적용 (브라우저가 origin당 연결 최대 6개) |
| 평균 요청률 | 3.55 req/s |
| 평균 응답 시간 | 15.4 ms |
| Apache CPU 점유 | 코어 1개의 **0.42%** |
| Apache 메모리 (PSS 합계) | 14프로세스 **29MB** |
| `max_connections` / `Max_used_connections` | 151 / **27** |
#### 2026-09-17 피크 측정 결과 ✅
수업 시간(12:50~15:00) 780샘플 실측입니다.
| 항목 | 값 | 상한 대비 |
|---|---|---|
| busy 워커 최대 | **204** | `MaxRequestWorkers` 400의 51% |
| 요청률 최대 | 132 req/s | 4일 평균(3.55)의 37배 |
| `K`(KeepAlive 대기) | busy 의 **69%** | |
| `Max_used_connections` | 27 | `max_connections` 151의 18% |
| us+sy 최대 / steal 최대 | 9% / **0%** | 크레딧 베이스라인 초과 0샘플 |
**웹 계층도 상한 대비 여유입니다.** 워커 수요는 `busy ≈ 1.74 × req/s` 로, 400 포화점은
약 230 req/s 입니다(실측 피크의 1.74배).
`MaxRequestWorkers 400` 은 근거 있는 설정이며 **변경 불필요**합니다.
상세: [260916-load-and-capacity-check/01-web-tier-analysis.md](260916-load-and-capacity-check/01-web-tier-analysis.md)
---
## 3. 비용 분석: 분리하면 저렴할까?
### 3-1. 시나리오별 월 비용 비교
| 시나리오 | 웹 서버 | DB 서버 | 월 비용 | 변화 | 평가 |
|---|---|---|---|---|---|
| **현재** (분리 안 함) | t3a.medium | (없음) | **$67** | — | — |
| **시나리오 A** | t3a.small | RDS micro | $48 | ↓10% | 절감 (웹 성능↓ 위험) |
| **시나리오 B** | t3a.small | RDS small | $58 | ↓13% | 절감 (안정성↑) |
| **시나리오 C** | t3a.medium | RDS micro | $79 | ↑18% | 증가 (안정성↑) |
| **시나리오 D** | t3a.medium | RDS small | $92 | ↑37% | 증가 (고가용성) |
**추가 비용 (모든 시나리오):**
- RDS 자동 백업: +$1~3/월
- 멀티 AZ (선택): +$50~80/월
- EC2 ↔ RDS 데이터 전송: 무료 (같은 VPC 내)
### 3-2. 비용 절감 결론
**대부분 비용 증가 또는 현상 유지**
- t3a.small으로 다운사이징 시 10% 절감 가능 but **웹 성능 저하 위험**
- 안정성을 위해 t3a.medium 유지 필요 → 비용 18~37% 증가
- **결론: 비용 절감 기대 어려움**
---
## 4. 성능 분석: 10초 멈춤이 해결될까?
### 4-1. DB 분리의 성능 효과
**시나리오 A: DB가 유일한 병목이었다면**
```
분리 전:
동시접속 (150~200) → PHP 대기 → DB 느림 → 멈춤 10초
분리 후:
동시접속 (150~200) → PHP (빠름) + DB 서버 (독립) → 개선
예상: 5~7초 감소 ⭐⭐⭐ (큰 효과)
```
**시나리오 B: PHP도 병목이었다면 (가능성 높음)**
```
분리 전:
동시접속 (150~200) → PHP 과부하 + DB 과부하 → 멈춤 10초
분리 후:
동시접속 (150~200) → PHP 여전히 과부하 + DB 서버 (독립) → 부분 개선
예상: 2~3초 감소만 ⭐ (효과 제한적)
```
### 4-2. 실제로 DB만 분리하면 충분한가?
**지난 분석(01-07)에서 발견된 쿼리 문제:**
- 인덱스 없이 120만 행 전체 스캔 (0.93초)
- `DATE()`, `HOUR()` 함수로 인덱스 무효화
- N+1 쿼리: 앱 10개 = DB 쿼리 10번
- 동시접속 150명 × 이런 쿼리 = CPU 폭증
**결론:** DB 분리만으로는 **완전 해결 불가**. 인덱스+쿼리 최적화(안1) 필수.
#### 2026-09-16 갱신
안1은 9/15 운영 적용을 마쳤고, 효과가 확인됐습니다(2-2절). 그리고 2-0절에서 드러났듯
**10초 멈춤의 원인 중 하나는 애초에 DB 계층이 아니라 인스턴스 CPU 크레딧이었습니다.**
세 원인을 DB 분리가 각각 어떻게 다루는지 정리하면 이렇습니다.
| 원인 | DB 분리로 해결되나? | 실제 해결 수단 |
|---|---|---|
| 2-0 CPU 크레딧 소진 | ❌ 전혀. 웹 인스턴스 구조는 그대로 | 인스턴스 사양 상향 (2026-04-14 완료) |
| 2-1 DB·PHP 리소스 경쟁 | ⭕ 부분적 | 인덱스·쿼리 개선 (2026-09-15 완료) |
| 2-2 Apache 워커 고갈 | ❌ 전혀. 웹 계층 문제 | MPM·KeepAlive 튜닝 (미측정) |
**세 원인 중 DB 분리가 유효한 것은 하나뿐이고, 그 하나는 이미 비용 0원으로 해결됐습니다.**
---
## 5. 권장 순서: 안1~5를 먼저 적용하세요
### 5-1. 왜 DB 분리 전에 안1~5를 해야 하나?
| 단계 | 작업 | 비용 | 구현 시간 | 성능 효과 | 필수도 |
|---|---|---|---|---|---|
| **1단계** | 안1: 인덱스+쿼리 | $0 | 2~3시간 | **18배 향상** | ⭐⭐⭐ |
| **2단계** | 안5 일부: N+1, SQL Injection | $0 | 3~4시간 | **2~3배 향상** | ⭐⭐⭐ |
| **3단계** | 안2/안3: 집계, 아카이빙 | $0 | 1~2주 | 지속적 개선 | ⭐⭐ |
| **미래** | DB 분리 (선택사항) | $600~1000/연간 | 2~3일 | 부분 개선 | ⭐ |
**안1 적용의 예상 효과:**
```
현재 (느린 쿼리):
┌─ 동시접속 150명
│ └─ 각각 0.93초 쿼리 × 5~10번
│ = 평균 5~10초 지연
└─ 인스턴스 꽉 찼음 → 10초 멈춤
안1 적용 후:
┌─ 동시접속 150명
│ └─ 각각 0.05초 쿼리 × 5~10번
│ = 평균 0.25~0.5초 지연
└─ 여유 있음 → 멈춤 사라짐
```
### 5-2. 체계적 진행 계획
```
1주차: 안1 (인덱스+쿼리) 스테이징 테스트
→ 효과 확인 (EXPLAIN 전/후, 성능 측정)
→ 운영 적용
1~2주: 안5 일부 (N+1, SQL Injection) 적용
→ 추가 개선
3주 이상: 안2/안3 검토
→ 안1~2로도 충분하면 멈춤
필요하면: DB 분리 재검토
(아마 불필요할 가능성 높음)
```
---
## 6. DB 분리 옵션 분석
### 6-1. 옵션 A: AWS RDS (관리형, 권장)
**구성:**
- 웹 서버: t3a.small ($0.047/시간)
- DB 서버: RDS db.t3.micro ($0.017/시간) 또는 db.t3.small ($0.034/시간)
**장점:**
```
✅ 자동 백업 (7일, 비용 포함)
✅ 자동 패치 (보안 업데이트 자동 적용)
✅ 멀티 AZ 옵션 (장애 자동 복구)
✅ CloudWatch 모니터링 (무료)
✅ 성능 인사이트 (데이터베이스 부하 가시화)
```
**단점:**
```
❌ 비용 증가 (월 $48~92)
❌ 커스터마이징 제한 (파라미터 일부 수정 불가)
❌ DB 직접 접근 제한 (일부 admin 작업 불가)
❌ 마이그레이션 다운타임 (1~2시간)
```
**RDS 선택 기준:**
| 선택 | 상황 | 비용 | 성능 |
|---|---|---|---|
| **db.t3.micro** | 안1~2 적용 후 여유 충분 | $0.017/시간 | 충분 |
| **db.t3.small** | 안정성 우선 | $0.034/시간 | 넉넉함 |
### 6-2. 옵션 B: 별도 EC2 인스턴스
**구성:**
- 웹 서버: t3a.small ($0.047/시간)
- DB 서버: EC2 t3a.small ($0.047/시간)
**장점:**
```
✅ 비용 낮음 (월 $67, 현재와 유사)
✅ 완전한 제어 (모든 설정 수정 가능)
✅ Synology 복제본과 동일 구조 (운영 경험 재사용)
```
**단점:**
```
❌ 직접 백업/관리 필요
❌ 자동 장애 복구 없음 (인스턴스 다운 → 수동 재시작)
❌ 보안 그룹 + 네트워크 설정 복잡
❌ 모니터링 스크립트 직접 작성/관리
```
### 6-3. 옵션 C: 분리하지 않음 (권장)
**상황:**
- 안1 (인덱스+쿼리)로 10초 멈춤 해결됨
- 안5 (N+1)로 동시접속 처리량 2~3배 향상
- 결과: 150~200명 동시접속도 무리 없음
**장점:**
```
✅ 추가 비용 0원
✅ 구현 복잡도 낮음 (현재 구조 유지)
✅ 운영 단순함 (Docker 1개 관리)
```
**단점:**
```
❌ 단일 실패점 (인스턴스 다운 = 전체 서비스 다운)
→ 해결책: 08번 문서의 Synology 복제본 운영
```
---
## 7. 의사결정 프레임워크
### 7-1. 지금 바로 해야 할 것
**✅ 즉시 (필수):**
1. **안1 적용** (인덱스+쿼리 개선)
```bash
# 스테이징 DB에서 테스트
# 1. 현재 쿼리 EXPLAIN 분석
# 2. 인덱스 추가
# 3. 쿼리 조건 개선
# 4. EXPLAIN 재확인 (인덱스 사용 확인)
# 5. 성능 측정 (느린 쿼리 로그)
```
- 예상 시간: 2~3시간
- 예상 효과: **10초 멈춤 → 무시할 수 있는 수준**
2. **안5 일부 적용** (N+1 제거)
```bash
# SQL Injection 보안 수정 (3개 엔드포인트)
# N+1 쿼리 제거 (앱 목록 조회)
```
- 예상 시간: 3~4시간
- 예상 효과: 추가 2~3배 성능 향상
### 7-2. 안1~2 적용 후 재평가
**체크리스트:**
```
☐ 안1 운영 적용 완료
☐ 1주일 모니터링 (slow query log, CPU 사용률)
→ 동시접속 150~200명 시 응답시간 확인
→ 10초 멈춤 재발 확인
결과:
☐ YES: 문제 해결됨 → DB 분리 불필요, Synology 복제만 운영
☐ NO: 문제 지속 → 안5 추가 적용
☐ 안5까지 적용 완료
☐ 2주 모니터링
결과:
☐ YES: 문제 해결됨 → DB 분리 불필요
☐ NO: 여전히 느림 → 다음 중 선택:
(1) 안2/안3 적용 (구조 개선, 1~2주)
(2) DB 분리 검토 (RDS, 2~3일)
```
### 7-3. DB 분리 결정 체크리스트
**분리가 필요하면:**
```
☐ 안1~5 모두 적용했으나 여전히 느림
☐ AWS 비용 증가를 감수할 수 있음
☐ 마이그레이션 다운타임 (1~2시간) 감수 가능
선택: RDS vs EC2
☐ 관리의 편의성 우선 → RDS 선택
☐ 비용 최소화 우선 → EC2 선택
```
---
## 8. DB 분리 구현 절차 (참고용)
### 8-1. 사전 준비
```bash
# 1. 현재 DB 전체 백업 (필수!)
mariadb-dump -h chocomae.jinaju.com -u backup -p \
--single-transaction \
chocomae > /backup/chocomae_before_separation_$(date +%Y%m%d).sql
# 2. 백업 파일 크기 확인 (보통 수백 MB)
ls -lh /backup/chocomae_before_separation_*.sql
# 3. 백업 정합성 확인
mariadb < /backup/chocomae_before_separation_*.sql chocomae -e "SELECT COUNT(*) FROM best_record;"
```
### 8-2. RDS 생성 (AWS Console)
```
1. RDS 대시보드 → "데이터베이스 생성"
2. 엔진: MariaDB 10.11
3. 인스턴스 클래스: db.t3.micro (비용) 또는 db.t3.small (안정)
4. 스토리지: 20GB (현재 데이터 + 여유)
5. 보안 그룹: 웹 서버 EC2 인스턴스만 접근 허용 (포트 3306)
6. 마스터 사용자 이름: admin
7. 마스터 암호: 강력한 비밀번호 (생성 후 AWS Secrets Manager 저장)
8. 백업: 7일 (기본값)
9. 멀티 AZ: 아니오 (비용 증가, 필요시 나중에)
```
### 8-3. 데이터 마이그레이션
```bash
# 1. RDS 엔드포인트 확인 (예: chocomae-db.c12345.us-east-1.rds.amazonaws.com)
RDS_ENDPOINT="chocomae-db.c12345.us-east-1.rds.amazonaws.com"
# 2. RDS에서 mariadb 데이터베이스 생성
mariadb -h $RDS_ENDPOINT -u admin -p -e "CREATE DATABASE chocomae CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;"
# 3. 스키마 복원 (구조만)
mariadb -h $RDS_ENDPOINT -u admin -p chocomae < src/web/sql/make_db.sql
mariadb -h $RDS_ENDPOINT -u admin -p chocomae < src/web/sql/insert_app.sql
# 4. 데이터 복원 (백업 파일에서)
mariadb -h $RDS_ENDPOINT -u admin -p chocomae < /backup/chocomae_before_separation_*.sql
# 5. 데이터 정합성 확인
mariadb -h $RDS_ENDPOINT -u admin -p chocomae -e "SELECT COUNT(*) FROM best_record;"
# 기존과 동일해야 함
```
### 8-4. 애플리케이션 연결 변경
```php
// src/web/server/setup/NA_service_db_setting.php 수정
// 변경 전:
// $hostName = "localhost"; // 또는 "mysql" (Docker)
// 변경 후:
// $hostName = "chocomae-db.c12345.us-east-1.rds.amazonaws.com";
// $userName = "admin";
// $userPassword = "RDS에서_생성한_암호";
```
### 8-5. 테스트
```bash
# 1. 스테이징 환경에서 먼저 테스트 (RDS + 로컬 웹 서버)
php src/web/server/player/get_login_key.php # 기본 쿼리 테스트
# 2. 성능 테스트
ab -n 1000 -c 50 https://staging.chocomae.com/ # 50 동시접속
# 3. 운영 서버 웹 서버 설정 변경 (배포 훅 수정)
# → EC2의 Docker 컨테이너가 RDS를 가리키도록
# 4. 운영 환경 테스트 (트래픽 낮은 시간)
```
### 8-6. 롤백 계획
```bash
# 문제 발생 시 원래 상태로 복원
# 1. 웹 서버 DB 연결 정보 되돌리기
# (localhost 또는 mysql로 변경)
# 2. Docker의 MariaDB 컨테이너 재시작
docker restart chocomae-mariadb
# 3. 운영 중단 시간: 10~15분
```
---
## 9. 체크리스트
### 완료 (2026-09-15 ~ 09-16)
- [x] 안1 (인덱스+쿼리) 문서 읽기: `02-option1-index-and-query-rewrite.md`
- [x] 스테이징 DB에서 안1 테스트 (EXPLAIN 전/후) — [260915-improve-stage](260915-improve-stage/)
- [x] 성능 향상 확인 후 운영 적용 (9/15 23:00) — [260915-improve-production](260915-improve-production/07-post-analysis.md)
- [x] 안5 일부 (N+1, SQL Injection) 문서 읽기: `06-option5-application-layer.md`
- [x] 추가 성능 향상 측정 — 피크 CPU 3.4배 감소, 기록 1,000건당 CPU 3.8배 감소
- [x] CPU 크레딧·steal 재검증 — 재현 안 됨 (2-0절)
- [x] 웹 계층(Apache) 평상시 부하 측정 — CPU 0.42%, 메모리 29MB (2-3절)
- [x] 2026-09-17 피크 모니터링 (워커 버스트 판정) — busy 최대 204/400, 여유 확인
- [x] `MaxRequestWorkers` 판정 — 400 적정, 변경 불필요
### 지금 바로 할 것
- [ ] 없음 (필수 변경 사항 없음)
### 선택 / 남은 확인
- [ ] 트래픽 증가 전망 시: `KeepAliveTimeout` 5→2, `MaxConnectionsPerChild` 0→10000
- [ ] `MinSpareServers` 현재값 확인 (idle=0 샘플 5.0%)
- [ ] EC2 크레딧 모드 확인 (Unlimited / Standard)
- [ ] 외부에서 `/server-status` 접근 차단 확인
→ 상세: [260916-load-and-capacity-check/01-web-tier-analysis.md](260916-load-and-capacity-check/01-web-tier-analysis.md)
### 그 이후 재평가
- [ ] 10초 멈춤 현상 재발 확인
- [ ] 필요 시: 안2/안3 검토
- [ ] 필요 시: DB 분리 검토 (이때 이 문서 다시 읽기)
### DB 분리 결정 시
- [ ] 현재 DB 전체 백업 (필수)
- [ ] RDS 또는 EC2 선택
- [ ] 마이그레이션 계획 (다운타임 계획)
- [ ] 스테이징에서 먼저 테스트
- [ ] 모니터링 및 롤백 계획 수립
---
## 10. 최종 결론
| 질문 | 답변 | 근거 |
|---|---|---|
| **비용이 절감될까?** | ❌ 아니오. 증가할 가능성. | 대부분의 조합이 월 비용 증가 |
| **CPU 부하가 줄까?** | ⚠️ 부분적. | 세 원인 중 하나에만 유효 (4-2절) |
| **10초 멈춤이 해결될까?** | ❌ 아니오. | 원인 2-0(크레딧)·2-2(워커)는 웹 계층. DB 분리와 무관 |
| **지금 해야 할 일?** | ✅ 없음 | 2026-09-17 측정으로 세 원인 모두 해소 확인 |
| **DB 분리 필요한가?** | ❌ 불필요. | 아래 근거 참조 |
**2026-09-16 기준 최종 권장: DB 분리하지 마세요.**
초판은 "아마 불필요"였으나, 실측이 쌓이면서 근거가 명확해졌습니다.
```
① 크레딧 원인 (2-0) → 2026-04-14 인스턴스 상향으로 해소. steal 최대 2%, 재현 안 됨
② DB 원인 (2-1) → 2026-09-15 인덱스·쿼리 개선으로 해소. 피크 CPU 3.4배 감소
③ 웹 계층 (2-2) → 2026-09-17 피크 실측. busy 204/400(51%), steal 0%, us+sy 최대 9%
→ 세 원인이 모두 해소됐고, DB 분리가 유효했던 것은 ② 하나뿐이며 이미 비용 0원으로 해결됨
→ 지금 DB를 분리할 이유가 없음
```
**다음 할 일:**
```
1. 필수 변경 사항 없음. 현 구성 유지.
2. 트래픽이 1.7배 이상 늘어날 전망이면 (비용 0원, 무중단)
→ KeepAliveTimeout 5초 → 2초 : 워커 여유 1.74배 → 3.1배
→ MaxConnectionsPerChild 0 → 10000 : 누수 누적 방지
상세: 260916-load-and-capacity-check/01-web-tier-analysis.md 6장
3. 위로도 부족해지면 안2/안3 검토
→ DB 분리는 그 다음의 마지막 선택지
```
> 이 문서는 **DB 분리 시점이 오면 다시 읽는 참고 자료**로 보존합니다.
> 6~8장의 비용 비교·마이그레이션 절차는 그대로 유효합니다.