월 $160 GCP에서 무료 OCI로 서버 이전: 4 OCPU ARM의 성능은 얼마나 다를까


이전 글 요약: "병목은 CPU다"
지난 글에서 GCP VM(2 vCPU, 16GB RAM)의 성능을 한껏 튜닝했습니다. 커넥션 풀 조정, 불필요한 DISTINCT 제거, Nginx rate limit 완화. 할 수 있는 건 다 한 뒤에 k6로 300 VU 부하 테스트를 돌렸는데요.
| 지표 | 기본 설정 (Tomcat=30) | 스케일업 (Tomcat=80) |
|---|---|---|
| RPS | 35.2 | 36.8 |
| 중앙값 | 1.31s | 1.04s |
| p95 | 9.77s | 9.06s |
| 타임아웃 (>5s) | 28% | 25% |
스레드와 커넥션을 2.5배 올렸는데 개선은 5~20%. "진짜 병목은 2 vCPU다." 소프트웨어가 아니라 하드웨어가 한계였습니다.
그 글 마지막에 이렇게 적었습니다.
4 vCPU로 올리면 HikariCP도 max=20-25로 늘릴 수 있고, 동시접속 500명까지 쾌적하게 갈 수 있습니다.
하루 뒤에 진짜로 서버를 바꿨습니다. 다만 4 vCPU가 아니라 4 OCPU ARM으로, GCP가 아니라 Oracle OCI로.
왜 OCI인가
비용 문제
GCP e2-highmem-2는 월 ~$55. 실제로는 하루에 네트워크비용까지 모두 합쳐서 1만원정도가 나가고 있습니다. 한달이면 25~30만원정도입니다. 사이드 프로젝트 서버치고는 아깝습니다.
GCP e2-highmem-2 : 2 vCPU / 16GB / 50GB → ~$55/월 OCI A1.Flex (Free): 4 OCPU / 24GB / 200GB → $0/월
OCI의 Always Free Tier는 ARM VM에 4 OCPU / 24GB RAM / 200GB 스토리지를 무료로 줍니다. CPU 2배, RAM 1.5배, 스토리지 4배인데 가격은 0원. "무료"에는 함정이 있긴 한데(뒤에서 다룹니다), 숫자만 보면 안 옮길 이유가 없습니다.
이전 결론과의 연결
"병목은 CPU"라고 진단했으니, CPU를 2개에서 4개로 올리면 처리량도 대략 2배가 되지 않을까. ARM이라는 변수가 있긴 하지만, Java/Node.js/MySQL 워크로드에서 x86 대비 ARM이 느리다는 벤치마크도 딱히 없어서 해볼 만했습니다.
달라진 것, 같은 것
인프라 비교
┌─────── GCP VM (2 vCPU, 16GB, 서울) ──────┐ │ │ │ Nginx → Backend → MySQL Frontend │ │ 3GB 3GB 4GB 1.5GB │ │ │ │ x86 (amd64) / $55/월 │ └────────────────────────────────────────────┘ ↓ 마이그레이션 ↓ ┌──── OCI VM (4 OCPU ARM, 24GB, 춘천) ─────┐ │ │ │ Nginx → Backend → MySQL Frontend │ │ ~10MB ~800MB ~600MB ~150MB │ │ │ │ ARM (aarch64) / $0/월 │ │ 메모리 여유: 21GB+ (11% 사용) │ └────────────────────────────────────────────┘
변경 사항
| 항목 | GCP (이전) | OCI (현재) |
|---|---|---|
| CPU | 2 vCPU (x86) | 4 OCPU (ARM) |
| RAM | 16GB | 24GB |
| 디스크 | 50GB SSD | 200GB |
| 리전 | 서울 (asia-northeast3) | 춘천 (ap-chuncheon-1) |
| OS | (x86 Linux) | Ubuntu 24.04 (aarch64) |
| 배포 | Artifact Registry push → pull | SSH → git pull → docker build |
| 비용 | ~$55/월 | $0/월 |
안 바뀐 것
- Docker 구성: Nginx + Backend + Frontend + MySQL (4개 컨테이너)
- 소프트웨어 스택: Spring Boot, Next.js, MySQL 8.0
- 서버 설정: Tomcat=30, HikariCP=15, MySQL=30 (GCP에서 튜닝한 그대로)
- SSL: Let's Encrypt + certbot (Cloudflare DNS-01 challenge)
- DNS: Cloudflare DNS-only 모드
서버 설정을 그대로 가져간 게 포인트입니다. GCP에서 마지막으로 테스트한 보수적 설정(Tomcat=30)을 OCI에서도 동일하게 유지해서, 순수하게 하드웨어 차이만 비교할 수 있게 했습니다.
마이그레이션 과정에서 겪은 일들
결과표만 보면 깔끔한데, 실제 이전 과정은 좀 달랐습니다.
Always Free의 함정: capacity 부족
OCI Free Tier VM을 만들려고 하면 이 메시지를 만날 확률이 높습니다.
Out of host capacity.
무료 리소스는 서버 여유가 있을 때만 배정됩니다. 춘천 리전에서 CLI 자동 재시도 스크립트를 돌렸는데 계속 실패. 포기하고 유료 플랜으로 변경을 했습니다. 그런데 유료 플랜으로 바뀌는 것도 시간이 오래걸리는 것 같더라고요. (5시간이 지나도 유료플랜으로 안바뀜) 그래서, 유료 플랜으로 바뀌는 동안 스크립트를 돌렸는데 기쁘게도 성공했습니다!!
팁: 리전별, 시간대별로 capacity가 다르니 오전에 시도해보세요.
ARM 빌드: 의외로 문제없음
x86에서 ARM으로 바뀌면 뭔가 깨지지 않을까 걱정했는데, 사용 중인 Docker 이미지가 모두 공식적으로 ARM을 지원하고 있었습니다.
| 이미지 | ARM 호환 |
|---|---|
| nginx:1.25-alpine | 공식 지원 |
| mysql:8.0 | 공식 지원 |
| eclipse-temurin:21 (Java) | 공식 지원 |
| node:20-alpine | 공식 지원 |
VM에서 docker build를 하면 ARM 네이티브 이미지가 알아서 만들어집니다. 멀티플랫폼 빌드 같은 건 필요 없었고요. 백엔드 빌드 67초, 프론트엔드도 비슷한 수준. GCP보다 오히려 빨랐습니다.
배포 방식 간소화
GCP에서는 GitHub Actions → Docker 빌드 → Artifact Registry push → VM에서 pull이었는데, OCI에서는 레지스트리를 없애고 SSH 접속 → git pull → 로컬 빌드로 바꿨습니다.
[Before - GCP] GitHub Actions → docker build (x86) → push to Registry → SSH → docker pull [After - OCI] GitHub Actions → SSH → git pull → docker build (ARM native) → compose up
레지스트리 비용도 없고, 이미지 전송 시간도 없고, 빌드 캐시도 로컬에 남아서 효율적입니다.
k6 부하 테스트: 같은 설정, 다른 결과
테스트 조건
GCP에서 썼던 k6 시나리오를 그대로 돌렸습니다.
- 가상 사용자(VU): 50 → 100 → 150 → 200 → 300 (단계적 증가)
- 테스트 시간: 8분
- 엔드포인트: 메인/블로그/QnA/뉴스/수업 + API 4개 (9개 랜덤 방문)
- 요청 간 대기: 0.5~2.5초 (실제 사용자 패턴)
1차 테스트: GCP 설정 그대로 (Tomcat=30, HikariCP=15)
GCP에서 마지막으로 테스트한 보수적 설정을 OCI에서 그대로 돌렸습니다.
| 지표 | GCP (2 vCPU) | OCI (4 OCPU ARM) | 변화 |
|---|---|---|---|
| 총 요청 | 5,870 | 34,111 | 5.8x |
| RPS | 35.2 | 70.8 | 2x |
| 중앙값 | 1.31s | 0.052s | -96% |
| 평균 | 2.81s | 0.62s | -78% |
| p95 | 9.77s | 3.27s | -67% |
| 타임아웃 (>5s) | 27.7% | 0% | 제거 |
| HTTP 에러 | 0% | 0% | 동일 |
결과가 너무 달라서 처음에 테스트를 잘못 돌린 줄 알았습니다.
GCP에서 300 VU를 걸면 4명 중 1명이 5초 넘게 기다렸는데, OCI에서는 타임아웃이 0건. 중앙값도 1.31초에서 52밀리초로 내려갔습니다. 같은 소프트웨어, 같은 설정. 하드웨어만 바꿨을 뿐입니다.
왜 이렇게 차이가 큰가
"CPU 2배면 2배 빨라지겠지" 싶었는데, 실제로는 그 이상이었습니다.
GCP 2 vCPU에서의 병목: ┌────────────────────────────────────────┐ │ Tomcat 30 threads │ │ ├── 처리 중: CPU 2코어가 번갈아 처리 │ │ ├── 대기 큐: 270개 요청이 줄 서서 대기 │ │ └── 결과: 큐 대기 = 타임아웃 │ └────────────────────────────────────────┘ OCI 4 OCPU에서: ┌────────────────────────────────────────┐ │ Tomcat 30 threads │ │ ├── 처리 중: CPU 4코어가 여유 있게 처리│ │ ├── 대기 큐: 거의 비어 있음 │ │ └── 결과: 큐 대기 없음 = 타임아웃 제거 │ └────────────────────────────────────────┘
RPS가 2배(35→71)인 건 CPU 수 증가와 딱 맞아떨어집니다. 그런데 중앙값이 96% 줄어든 건 큐 대기 시간이 사라진 효과입니다. GCP에서는 요청이 처리 순서를 기다리느라 1초 넘게 줄을 섰는데, OCI에서는 도착하자마자 바로 처리가 됩니다.
메모리 여유도 한몫합니다. GCP에서는 16GB를 4개 컨테이너가 빡빡하게 나눠 썼는데, OCI는 24GB 중 2.3GB만 쓰고 있거든요(11%). JVM GC 압박이 줄면 응답 시간 편차도 줄어듭니다.
=== OCI 테스트 후 서버 상태 === Memory: 23GB 중 2.3GB 사용 (11%) HikariCP Active: 0 / Idle: 10 / Pending: 0 CPU Load: 1.22 (4코어 대비 여유)
스케일업: 4 OCPU에 맞게 설정 조정
1차 테스트에서 이미 좋은 결과가 나왔지만, 설정은 2 vCPU 시절 그대로입니다. 4 OCPU에 맞게 올리면 더 나아질까?
변경 내용
GCP에서의 교훈대로, 세 계층을 함께 올렸습니다.
| 설정 | Before (GCP 설정) | After (4 OCPU용) | 근거 |
|---|---|---|---|
TOMCAT_MAX_THREADS | 30 | 120 | 4 OCPU × 30 |
TOMCAT_MIN_SPARE | 5 | 10 | 워밍업 스레드 |
TOMCAT_ACCEPT_COUNT | 50 | 200 | 대기 큐 여유 |
DB_POOL_MAX | 15 | 30 | HikariCP 공식: (4×2)+1=9, 비동기 고려 30 |
DB_POOL_MIN_IDLE | 8 | 10 | 워밍업 커넥션 |
MySQL max_connections | 30 | 60 | HikariCP 30 + admin 여유 |
MySQL thread_cache_size | 16 | 32 | 커넥션 재사용 |
MySQL back_log | 64 | 128 | 대기 큐 |
HikariCP 공식대로라면 4 OCPU + SSD에서 최적 커넥션 수는 (4 × 2) + 1 = 9. 30으로 넉넉하게 잡은 건 비동기 Executor(levelExp, tagCount, notification, bannerClick)가 동시에 커넥션을 사용할 수 있기 때문입니다.
2차 테스트: 스케일업 후 300 VU
| 지표 | 1차 (Tomcat=30) | 2차 (Tomcat=120) | 변화 |
|---|---|---|---|
| 총 요청 | 34,111 | 34,613 | +1.5% |
| RPS | 70.8 | 71.9 | +1.6% |
| 중앙값 | 0.052s | 0.054s | 동일 |
| 평균 | 0.62s | 0.59s | -5% |
| p90 | 2.93s | 2.63s | -10% |
| p95 | 3.27s | 3.01s | -8% |
| max | 4.08s | 3.75s | -8% |
| 타임아웃 | 0% | 0% | 동일 |
| HTTP 에러 | 0% | 0% | 동일 |
GCP에서 봤던 패턴이 또 나왔습니다. 스레드를 4배 올렸는데 개선은 8~10%.
다만 GCP 때와 이유가 다릅니다.
GCP 스케일업이 안 먹힌 이유: CPU 2코어가 한계 → 스레드 올려도 CPU가 못 따라감 OCI 스케일업이 크게 안 먹히는 이유: 이미 여유가 넘침 → Tomcat 30개로도 충분히 처리됨 300 VU에서 병목이 없으므로 올릴 게 없음
GCP에서는 "CPU가 부족해서" 설정을 올려도 안 먹혔고, OCI에서는 "이미 충분해서" 올릴 필요가 없었던 겁니다. 같은 "개선 미미"인데 상황은 정반대.
동시에 몇 명까지 버틸 수 있나
OCI 기준 실측
| 동시 VU | 중앙값 | p95 | 타임아웃 | 판정 |
|---|---|---|---|---|
| ~50명 | ~50ms | ~500ms | 0% | 쾌적 |
| ~100명 | ~50ms | ~1.5s | 0% | 쾌적 |
| ~150명 | ~50ms | ~2.5s | 0% | 양호 |
| ~200명 | ~50ms | ~3s | 0% | 양호 |
| 300명 | 54ms | 3.01s | 0% | 수용 가능 |
GCP에서 300 VU는 "한계 초과"였는데, OCI에서는 "수용 가능". p95가 3초인 건 아쉽지만, 타임아웃 0에 에러 0이니 서비스가 죽지는 않습니다.
실 사용자 수로 환산
보수적 현실적 한계 서버 처리량 140 req/s 200 req/s 280 req/s 사용자당 요청률 0.3 req/s 0.2 req/s 0.15 req/s ───────── ───────── ────────── 동시 접속자 수 ~470명 ~1,000명 ~1,900명 응답 시간 <500ms <500ms 1초+ (저하)
| 시나리오 | 동시 접속 | 체감 |
|---|---|---|
| 쾌적한 운영 | 200-400명 | 모든 페이지 0.5초 이내 |
| 부하 상태 | 400-800명 | 대부분 정상, 간헐적 지연 |
| 한계 | 800명+ | 응답 지연 증가, 타임아웃 가능 |
GCP에서 "200명이면 쾌적"이었는데, OCI에서는 400명까지 쾌적 구간이 늘었습니다. 사이드 프로젝트에는 넉넉합니다.
전체 비교표
GCP vs OCI 300 VU 테스트 결과 (모든 라운드)
| 환경 | 설정 | RPS | 중앙값 | p95 | 타임아웃 | 에러 |
|---|---|---|---|---|---|---|
| GCP 2 vCPU | Tomcat=30 | 35.2 | 1.31s | 9.77s | 28% | 0% |
| GCP 2 vCPU | Tomcat=80 | 36.8 | 1.04s | 9.06s | 25% | 0% |
| OCI 4 OCPU | Tomcat=30 | 70.8 | 0.052s | 3.27s | 0% | 0% |
| OCI 4 OCPU | Tomcat=120 | 71.9 | 0.054s | 3.01s | 0% | 0% |
GCP Tomcat=80 (최고 설정) vs OCI Tomcat=30 (최저 설정) RPS: 36.8 → 70.8 (+93%) 중앙값: 1.04s → 0.052s (-95%) p95: 9.06s → 3.27s (-64%) 타임아웃: 25% → 0% (제거) 비용: $55/월 → $0/월 (-100%)
GCP의 최고 설정보다 OCI의 최저 설정이 모든 지표에서 더 좋습니다. 비용은 100% 줄었고요.
무료 서버의 리스크
좋은 얘기만 하면 공정하지 않으니, 겪었거나 우려되는 리스크도 정리합니다.
1. capacity 부족으로 VM 생성이 어려움
앞서 말한 대로 Free Tier VM은 서버 여유가 있을 때만 배정됩니다. 서비스가 죽어서 급하게 새 VM을 만들어야 하는데 "Out of host capacity"가 뜨면 답답할 수 있습니다.
2. SLA 없음
Always Free 리소스에는 공식 SLA가 없습니다. 유휴 자원을 회수한다는 정책은 없지만, 유료와 같은 보장도 없고요. 프로덕션에 100% 의존하기엔 좀 불안합니다.
3. 춘천 리전 레이턴시
GCP 서울 리전 대신 OCI 춘천 리전입니다. 서울-춘천은 ~80km라 가깝긴 한데, 서울 리전보다는 약간 느릴 수 있습니다. 체감 차이는 모르겠지만, 숫자로는 1-2ms 정도 있을 겁니다.
4. PAYG 전환 옵션
Free Tier가 불안하면 PAYG(Pay As You Go)로 전환할 수 있습니다. Always Free 포함분(4 OCPU/24GB)은 그대로 무료이고, 초과분만 과금.
무료 (현재): 4 OCPU / 24GB / 200GB = $0/월 PAYG 유료: 6 OCPU / 32GB / 300GB = ~$24/월 GCP (이전): 2 vCPU / 16GB / 50GB = ~$55/월
유료로 올려도 GCP 대비 56% 절감이고, SLA 99.9%가 붙습니다.
마무리
같은 설정에서 하드웨어만 바꿨더니 성능이 2~25배 좋아지고 비용은 0이 됐습니다.
| 항목 | GCP | OCI | 비고 |
|---|---|---|---|
| 월 비용 | $55 | $0 | -100% |
| 300 VU RPS | 36.8 | 71.9 | +95% |
| 300 VU p95 | 9.06s | 3.01s | -67% |
| 300 VU 타임아웃 | 25% | 0% | 제거 |
| 쾌적 동시접속 | ~200명 | ~400명 | 2배 |
| 메모리 여유 | 빡빡 | 21GB+ (89% 여유) | 여유 |
지난 글에서 "측정 → 분석 → 변경 → 측정" 사이클이 중요하다고 했는데, 이번에도 마찬가지입니다. 다만 바꾼 게 설정이 아니라 하드웨어였고, 효과는 비교가 안 됐습니다.
소프트웨어 튜닝에는 한계가 있습니다. 2 vCPU에서 스레드를 아무리 올려도 5~20% 개선에 그쳤는데, CPU를 2배로 올리니 처리량이 2배, 응답 시간은 25배 좋아졌습니다. 병목이 하드웨어일 때는 하드웨어를 바꿔야 합니다. 당연한 소리 같지만 "설정을 좀 더 만지면 되지 않을까" 하는 유혹에 빠지기 쉽거든요.
클라우드 간 가격 차이도 이 정도로 큽니다. 같은 워크로드를 GCP에서는 월 $55에, OCI에서는 무료로 돌릴 수 있습니다. SLA가 없다는 리스크는 있지만, 사이드 프로젝트에서는 감수할 만하고요. 정 불안하면 PAYG $24/월.
GCP의 장점(서울 리전, IAP 터널, 에코시스템)을 OCI가 다 대체하지는 못합니다. 하지만 "Docker 4개 컨테이너 돌리는 단일 VM" 수준이라면, OCI Free Tier가 비용 대비 성능에서 압도적입니다.
Oracle 감사합니다. 꾸벅






댓글
댓글을 작성하려면 이 필요합니다.