# Anvil 부하테스트 및 성능 최적화 보고서 **작성일**: 2026-01-10 **환경**: Incus jp1 → k8s (k3s) → anvil namespace --- # Part 1. 핵심 요약 ## 1. 아키텍처 ``` ┌─────────────────────────────────────────┐ │ k8s (k3s, max-pods=200) │ │ │ [Client] ──▶ Cloudflare ─┼─▶ Traefik (x3) │ (권장) │ │ │ │ ▼ │ │ Nginx (x2~15, HPA) │ │ │ │ │ ▼ │ │ PHP-FPM (x5~30, HPA) ─▶ Redis │ │ │ (Session) │ │ ▼ │ │ ProxySQL (Cache 256MB) │ │ │ │ └───────┼─────────────────────────────────┘ │ ┌────────────┴────────────┐ ▼ ▼ ┌─────────────┐ ┌─────────────┐ │ MariaDB-1 │ │ MariaDB-2 │ │ (Primary) │──Repl───▶ │ (Replica) │ └─────────────┘ └─────────────┘ ``` --- ## 2. 최종 성능 결과 ### 2.1 Gnuboard5 (실제 애플리케이션) | 항목 | Origin Only | + Cloudflare (예상) | |------|-------------|---------------------| | **최대 RPS** | **505.9** | **2,000+** | | 성공률 | 100% | 100% | | 평균 응답 | 390ms | ~50ms (캐시 HIT) | ### 2.2 simple-db.php (단순 쿼리) | 항목 | 값 | |------|-----| | **최대 RPS** | **3,466** | | 성공률 | 100% | | 평균 응답 | 56ms | ### 2.3 예상 동시 접속자 | 시나리오 | Gnuboard5 | |----------|-----------| | Origin Only | ~1,700명 | | **Cloudflare 70% 캐시** | **~5,600명** | | **Cloudflare 80% 캐시** | **~8,500명** | --- ## 3. 최적화 적용 현황 | 영역 | 최적화 | 효과 | 상태 | |------|--------|------|------| | ProxySQL | Query Cache 256MB | DB 부하 99% 감소 | ✅ | | ProxySQL | Read Replica 활용 | +21% RPS | ✅ | | PHP | OPcache 256MB | PHP 컴파일 캐시 | ✅ | | PHP | Redis 세션 | 세션 공유/속도 | ✅ | | HPA | minReplicas 설정 | Cold Start 방지 | ✅ | | MariaDB | Buffer Pool 512MB | 99.998% 히트율 | ✅ | | **CDN** | **Cloudflare** | **+300% 예상** | ⏳ 권장 | --- ## 4. 핵심 설정값 ### 4.1 HPA 설정 | Deployment | Min | Max | CPU Target | |------------|-----|-----|------------| | nginx | 2 | 15 | 70% | | php-fpm | 5 | 30 | 70% | ### 4.2 리소스 설정 | 구성요소 | 설정 | 값 | |----------|------|-----| | MariaDB | max_connections | 500 | | ProxySQL | Query Cache | 256MB, TTL 5초 | | ProxySQL | Read Replica | hostgroup 1 | | PHP | OPcache | 256MB, 20K files | | PHP | Session | Redis | | k3s | max-pods | 200 | ### 4.3 업로드 제한 | 구성요소 | 값 | |----------|-----| | Nginx | 100MB | | PHP | 100MB | --- ## 5. 운영 체크리스트 ### 필수 확인 사항 - [ ] HPA minReplicas 설정 확인 (nginx: 2, php-fpm: 5) - [ ] ProxySQL Read Replica 설정 확인 (hostgroup 1) - [ ] ProxySQL PVC 마운트 확인 (설정 영구 저장) - [ ] OPcache 활성화 확인 - [ ] Redis 세션 연결 확인 - [ ] Cloudflare 적용 (권장) ### 모니터링 명령어 ```bash # k9s 실행 k9s # HPA 상태 kubectl get hpa -n anvil # Pod 리소스 kubectl top pods -n anvil # ProxySQL 캐시 상태 kubectl exec -n anvil deploy/proxysql -- mysql -h127.0.0.1 -P6032 -uadmin -padmin \ -e "SELECT * FROM stats.stats_mysql_global WHERE Variable_Name LIKE '%Query_Cache%';" ``` --- ## 6. 변경 이력 | 일시 | 변경 내용 | 효과 | |------|----------|------| | 2026-01-09 | MariaDB max_connections 151→500 | 연결 여유 확보 | | 2026-01-09 | nginx/php-fpm CPU 2코어 | 처리량 증가 | | 2026-01-09 | ProxySQL Query Cache 256MB | +17% RPS | | 2026-01-09 | k3s max-pods 200 | Pod 확장성 | | 2026-01-09 | OPcache 최적화 | PHP 성능 향상 | | 2026-01-09 | HPA minReplicas 설정 | +6% RPS | | 2026-01-09 | ProxySQL Read Replica | +21% RPS | | 2026-01-09 | Real IP 설정 | 클라이언트 IP 추적 | | 2026-01-09 | 파일 업로드 100MB | 대용량 파일 지원 | | 2026-01-10 | 최종 문서 정리 | - | --- # Part 2. 상세 설정 (참고) ## A. ProxySQL 설정 ### Query Cache 설정 ```sql -- ProxySQL Admin (포트 6032) 접속 SET mysql-query_cache_size_MB = 256; LOAD MYSQL VARIABLES TO RUNTIME; SAVE MYSQL VARIABLES TO DISK; -- SELECT 쿼리 캐시 + Read Replica 라우팅 INSERT INTO mysql_query_rules (rule_id, active, match_digest, cache_ttl, destination_hostgroup, apply) VALUES (100, 1, '^SELECT', 5000, 1, 1); LOAD MYSQL QUERY RULES TO RUNTIME; SAVE MYSQL QUERY RULES TO DISK; ``` ### Hostgroup 구성 | Hostgroup | 서버 | 용도 | |-----------|------|------| | 0 | Primary (10.253.101.133) | Write | | 1 | Primary + Replica | Read (캐시 + 부하분산) | ### 캐시 동작 흐름 ``` 요청 ──▶ ProxySQL │ ├─ Cache HIT (99.5%) ──▶ 즉시 응답 (0.1ms) │ └─ Cache MISS (0.5%) ──▶ MariaDB (Primary/Replica) │ └──▶ 캐시 저장 ──▶ 응답 ``` --- ## B. PHP 설정 ### OPcache (opcache.ini) ```ini opcache.enable=1 opcache.memory_consumption=256 opcache.max_accelerated_files=20000 opcache.validate_timestamps=0 opcache.revalidate_freq=0 opcache.fast_shutdown=1 opcache.interned_strings_buffer=16 ``` ### 세션 (session.ini) ```ini session.save_handler = redis session.save_path = "tcp://redis:6379" ``` ### 업로드 (upload.ini) ```ini upload_max_filesize = 100M post_max_size = 100M max_file_uploads = 20 max_execution_time = 300 max_input_time = 300 ``` --- ## C. Nginx 설정 ### nginx.conf ```nginx worker_processes auto; events { worker_connections 1024; use epoll; multi_accept on; } http { sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; # Real IP set_real_ip_from 0.0.0.0/0; real_ip_header X-Forwarded-For; real_ip_recursive on; # Gzip gzip on; gzip_vary on; gzip_min_length 1024; gzip_types text/plain text/css application/json application/javascript; } ``` ### default.conf (주요 부분) ```nginx upstream php-upstream { server php-fpm:9000; keepalive 32; } server { client_max_body_size 100m; location ~ \.php$ { fastcgi_pass php-upstream; fastcgi_keep_conn on; fastcgi_buffer_size 32k; fastcgi_buffers 16 16k; } } ``` --- ## D. Traefik Real IP 설정 ```yaml apiVersion: helm.cattle.io/v1 kind: HelmChartConfig metadata: name: traefik namespace: kube-system spec: valuesContent: |- additionalArguments: - "--entryPoints.web.forwardedHeaders.insecure=true" - "--entryPoints.websecure.forwardedHeaders.insecure=true" ``` --- ## E. HPA 설정 ### Cold Start 방지 ```bash kubectl patch hpa nginx-hpa -n anvil -p '{"spec":{"minReplicas":2}}' kubectl patch hpa php-fpm-hpa -n anvil -p '{"spec":{"minReplicas":5}}' ``` --- ## F. k3s 설정 ### max-pods 증가 ```bash # /etc/rancher/k3s/config.yaml kubelet-arg: - "max-pods=200" ``` --- ## G. MariaDB 상태 (참고) | 지표 | 값 | 평가 | |------|-----|------| | Buffer Pool Size | 512MB | 적정 | | Buffer Pool Hit Rate | 99.998% | 최상 | | max_connections | 500 | 적정 | | Slow Queries | 0 | 최상 | --- ## H. Cloudflare 설정 가이드 (권장) ### Cache Rules 설정 **Rule 1: 정적 파일** ``` If: File extension = css, js, jpg, png, gif, ico, woff2 Then: Cache (Edge TTL: 1 month) ``` **Rule 2: 게시판 페이지 (비로그인)** ``` If: URI contains "/gb5/" AND Cookie NOT contains "ck_mb_id" Then: Cache (Edge TTL: 5 minutes) ``` **Rule 3: 관리자/글쓰기 제외** ``` If: URI contains "/adm" OR "/bbs/write" OR Cookie contains "ck_mb_id" Then: Bypass cache ``` ### 예상 효과 | 캐시 히트율 | 유효 RPS | |------------|----------| | 50% | ~1,000 | | 70% | ~1,700 | | 80% | ~2,500 | --- ## I. 성능 튜닝 히스토리 | 설정 | RPS | 성공률 | 비고 | |------|-----|--------|------| | 초기 상태 | ~150 | 93% | Cold Start | | + CPU 2코어 | ~370 | 99% | - | | + minReplicas | 419 | 99.98% | +6% | | **+ Read Replica** | **505.9** | **100%** | **+21%** | --- ## J. 부하 테스트 명령어 ```bash # Gnuboard5 테스트 hey -n 20000 -c 200 -t 20 -host "gnu.anvil.it.com" "http://10.253.103.124/gb5/" # simple-db.php 테스트 hey -n 20000 -c 200 -t 20 -host "gnu.anvil.it.com" "http://10.253.103.124/simple-db.php" # ProxySQL 캐시 히트율 확인 kubectl exec -n anvil deploy/proxysql -- mysql -h127.0.0.1 -P6032 -uadmin -padmin \ -e "SELECT * FROM stats.stats_mysql_global WHERE Variable_Name LIKE '%Query_Cache%';" ``` --- ## K. k9s 단축키 | 키 | 기능 | |----|------| | `:ns` | 네임스페이스 선택 | | `:pod` | Pod 목록 | | `:hpa` | HPA 목록 | | `l` | 로그 보기 | | `s` | Shell 접속 | | `d` | Describe | | `/` | 필터링 |