노드 스케일링 패턴
체인에 무관하게 RPC 서비스·퍼블릭 인프라를 확장할 때 쓰이는 공통 패턴입니다. 체인별 클라이언트 세부는 각 chains/<id>/ 가이드를 참고하세요.
1. 단일 노드 vs. 수평 확장
| 요구 | 구성 |
|---|---|
| 개발·테스트 | 단일 노드 (Docker 또는 호스트) |
| 프로덕션 RPC 1~100 RPS | 단일 노드 + reverse proxy(nginx/Caddy) |
| 100 RPS+ | 2~N개의 RPC 노드 + 로드 밸런서 + 헬스체크 |
| 대량 historical 조회 | Full/Pruned 노드 + Archive 노드 분리 |
flowchart TB
Client((Client))
CDN[Cloudflare / AWS GA]
LB[nginx / HAProxy]
subgraph Read["Read 풀 (round-robin)"]
R1[Full #1]
R2[Full #2]
R3[Full #3]
end
Archive[Archive Node]
Client --> CDN --> LB
LB -->|eth_blockNumber, 최근 블록| Read
LB -->|getBlockByNumber old| Archive
LB -->|sendTransaction sticky| R1
Prometheus[Prometheus 헬스체크] -. scrape .- Read
LB -. 제외 .- Prometheus
2. Reverse Proxy (nginx 예시)
# /etc/nginx/conf.d/rpc.conf
upstream rpc_pool {
least_conn;
server 10.0.0.11:8545 max_fails=3 fail_timeout=30s;
server 10.0.0.12:8545 max_fails=3 fail_timeout=30s;
server 10.0.0.13:8545 max_fails=3 fail_timeout=30s;
}
server {
listen 443 ssl http2;
server_name rpc.example.com;
ssl_certificate /etc/letsencrypt/live/rpc.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/rpc.example.com/privkey.pem;
# 간단한 IP 기반 rate limit
limit_req_zone $binary_remote_addr zone=rpc:10m rate=10r/s;
limit_req zone=rpc burst=30 nodelay;
location / {
proxy_pass http://rpc_pool;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header Connection "";
proxy_connect_timeout 5s;
proxy_read_timeout 30s;
proxy_next_upstream error timeout http_502 http_504;
}
# 메서드 allowlist (Lua 없이 Nginx map)
# 실제 운영 시 OpenResty + Lua로 JSON-RPC 본문 검사 권장
}
헬스체크
각 upstream 노드에 별도 /health 엔드포인트를 두거나, nginx_upstream_check_module을 사용해 동기화 상태 기준으로 제외시킵니다. 동기화 지연을 판단하는 쿼리 예:
- EVM:
eth_syncing== false &ð_blockNumber>= threshold - Solana:
getSlot차이 < 200 - Cosmos:
GET /status→sync_info.catching_up == false
3. 읽기/쓰기 분리
트랜잭션 전파(write) 경로와 조회(read) 경로의 요구가 다릅니다.
- Write(
sendTransaction): sticky session·같은 노드 유지 → mempool 반영 지연 최소화 - Read(
getBlockByNumber등): round-robin 로드밸런싱·aggressive 캐싱
nginx map을 사용한 간단한 분기:
map $request_method $is_write {
default 0;
POST 1;
}
# POST + JSON-RPC 본문 기준 분기는 OpenResty + Lua로
4. WebSocket 노드
WebSocket RPC(wss://)는 세션이 장시간 유지되므로 로드밸런서에서 sticky 구성과 connection limit이 필수입니다.
- nginx:
ip_hash또는hash $remote_addr consistent; - Cloudflare Spectrum: TCP/WebSocket 지원, 글로벌 Anycast
5. 캐시 레이어
체인 상태는 빠르게 변하지만 과거 블록/트랜잭션은 immutable 합니다.
| 데이터 | 캐시 TTL | 캐시 키 |
|---|---|---|
| 과거 블록 해시·본문 | 영구 | getBlockByHash/getBlock + height |
| 영구 트랜잭션 receipt | 영구 | getTransactionReceipt:<txhash> |
| 현재 블록 번호 | 500 ms~1 s | eth_blockNumber |
| 계정 잔고 | 1~5 s | eth_getBalance:<addr>:<block> |
Redis 또는 LFU 인메모리 캐시를 nginx proxy_cache 앞단에 두는 방식이 일반적입니다.
6. 글로벌 배포
- Anycast / Multi-region RPC: 사용자에게 가까운 리전으로 자동 라우팅 (Cloudflare, AWS Global Accelerator)
- 읽기 복제본: 리전별 Full 노드 여러 대 + 중앙 Archive 노드 1~2대
- 쓰기 경로 일원화: 트랜잭션 브로드캐스트는 체인의 P2P 네트워크가 처리하므로 로드밸런서 걱정 없음
7. 비용 대비 성능 팁
- 체인 스냅샷 활용: 수 TB 재동기화보다 스냅샷 복원이 압도적으로 빠름
- Pruning 설정: 스토리지 비용을 2~3배 줄임
- 메트릭 기반 오토스케일: 1시간 RPS/피크 기준으로 노드 수 자동 조정 (HPA)
- 노드 운영 비용 vs. 외부 RPC: 1M RPS 이상 수요에는 자체 운영이 저렴, 100 RPS 이하면 Alchemy/Infura 등이 저렴