본문으로 건너뛰기

고가용성(HA) 아키텍처 설계

현대 서비스에서 장애는 피할 수 없는 현실입니다. 서버 하드웨어 결함, 소프트웨어 버그, 네트워크 단절, 데이터센터 정전 등 수많은 원인이 서비스 중단을 야기합니다. 고가용성(High Availability, HA)은 이러한 장애 상황에서도 서비스를 지속적으로 제공할 수 있도록 시스템을 설계하는 방법론입니다. 이 챕터에서는 HA 개념부터 SPOF 분석, 이중화 전략, 실제 스택 구성까지 체계적으로 다룹니다.

고가용성(High Availability)이란?

고가용성은 시스템이 장애가 발생하더라도 서비스 중단 없이(또는 최소한의 중단으로) 계속 동작하는 능력을 의미합니다. 가용성(Availability)은 일반적으로 다음 공식으로 표현합니다.

가용성(%) = (서비스 가능 시간 / 전체 시간) × 100

예를 들어 연간 99.9% 가용성을 목표로 한다면 연간 허용 다운타임은 약 8.76시간입니다. 이 수치는 SLA(Service Level Agreement)로 명문화되어 서비스 제공자와 고객 간의 계약으로 관리됩니다.

가용성 목표별 허용 다운타임

가용성 등급연간 다운타임월간 다운타임주간 다운타임일간 다운타임
99% (Two Nines)87.6시간7.3시간1.68시간14.4분
99.9% (Three Nines)8.76시간43.8분10.1분1.44분
99.99% (Four Nines)52.6분4.38분1.01분8.6초
99.999% (Five Nines)5.26분26.3초6.05초0.86초

대부분의 상용 서비스는 99.9%~99.99% 수준을 목표로 합니다. 금융, 의료 같은 미션 크리티컬 서비스는 99.999% (Five Nines)를 요구하기도 합니다. Five Nines를 달성하려면 연간 다운타임이 5분 남짓에 불과하므로 극도로 정교한 설계가 필요합니다.

다운타임의 비용

서비스 중단은 단순한 불편함을 넘어 직접적인 비즈니스 손실로 이어집니다.

직접 비용: 매출 손실 (이커머스 플랫폼 1시간 장애 = 수억 원 손실), 위약금 지불 (SLA 위반), 복구 인건비

간접 비용: 브랜드 신뢰도 하락, 고객 이탈, 언론 보도로 인한 평판 손상, 규제 기관 제재

AWS는 2021년 대규모 장애로 수천 개의 서비스가 동시에 영향을 받았고, 메타의 2021년 6시간 장애는 약 6천억 원의 손실로 추정되었습니다. 이런 사례들이 HA 투자의 당위성을 증명합니다.

SPOF(Single Point of Failure) 분석

SPOF는 해당 컴포넌트가 장애를 일으키면 전체 시스템이 중단되는 단일 장애점입니다. HA 설계의 핵심은 모든 SPOF를 식별하고 제거하는 것입니다.

웹 서버 레이어 SPOF

단일 Nginx 서버만 운영하는 경우, Nginx 프로세스 크래시, 서버 하드웨어 고장, 네트워크 카드 불량 등이 발생하면 전체 트래픽이 차단됩니다.

[Before - SPOF 존재]
클라이언트 → Nginx(단일) → Tomcat

[After - SPOF 제거]
클라이언트 → VIP(Keepalived) → Nginx Master
→ Nginx Backup (대기)

WAS(Application Server) 레이어 SPOF

Tomcat 단일 인스턴스 운영은 배포 시 서비스 중단, JVM OutOfMemoryError, 애플리케이션 버그로 인한 프로세스 종료 등으로 SPOF가 됩니다.

DB 레이어 SPOF

DB는 가장 심각한 SPOF입니다. 데이터 무결성 요구사항 때문에 단순 이중화가 어렵고, 장애 시 데이터 손실 위험까지 있습니다.

[DB SPOF 해결 방법]
Primary DB ──(동기 복제)──▶ Standby DB

└── 장애 발생 시 Standby가 Primary로 승격

로드밸런서 레이어 SPOF

로드밸런서 자체가 SPOF가 되는 아이러니한 상황을 방지하려면 로드밸런서도 이중화해야 합니다. Keepalived + VRRP를 이용해 VIP(Virtual IP)를 공유하는 방식이 표준적입니다.

네트워크 레이어 SPOF

스위치, NIC, 업링크 등 네트워크 장비도 SPOF가 될 수 있습니다. 본딩(NIC Bonding), 이중 스위치 구성, 다중 ISP 연결로 해결합니다.

Active-Active vs Active-Standby

HA 구성 방식은 크게 두 가지 패턴으로 나뉩니다.

Active-Active

모든 노드가 동시에 활성 상태로 트래픽을 처리합니다.

클라이언트


로드밸런서
┌─┴─┐
▼ ▼
노드A 노드B ← 둘 다 실제 요청 처리

장점:

  • 리소스를 100% 활용 (Standby 서버 낭비 없음)
  • 수평 확장(Scale-Out)이 용이
  • 한 노드 장애 시 나머지 노드가 전체 트래픽 처리

단점:

  • 세션 공유 메커니즘 필요 (세션 클러스터링 또는 stateless 설계)
  • DB 쓰기 충돌 방지를 위한 동기화 로직 복잡
  • 설정 및 운영 복잡도 높음

적합한 시나리오: 무상태(Stateless) REST API 서버, 콘텐츠 서빙 서버, 읽기 전용 DB 레플리카

Active-Standby

하나의 노드만 활성 상태로 트래픽을 처리하고, 나머지 노드는 대기 상태입니다.

클라이언트


로드밸런서
┌─┴─┐
▼ │
노드A 노드B ← 대기 중, 장애 시 자동 승격
(Active) (Standby)

장점:

  • 구성 단순 (세션 공유 불필요)
  • 데이터 일관성 유지 용이
  • 검증된 패턴, 운영 이해도 높음

단점:

  • Standby 서버 리소스 낭비
  • 페일오버 시 짧은 서비스 중단 발생 (수 초~수십 초)
  • Standby 서버 장애를 놓치기 쉬움

적합한 시나리오: DB Primary-Standby, 상태 유지가 필요한 세션 기반 서버, 소규모 인프라

비교 요약표

항목Active-ActiveActive-Standby
리소스 효율높음 (100% 활용)낮음 (Standby 낭비)
페일오버 속도즉각적수 초~수십 초
구성 복잡도높음낮음
세션 처리공유 스토리지 필요단순
비용추가 서버 없이 HA여분 서버 비용 발생
확장성우수제한적

HA 아키텍처 설계 원칙

중복화(Redundancy)

모든 단일 컴포넌트를 최소 2개 이상으로 구성합니다. N+1 원칙(필요한 수 + 1개 여분)이 기본이며, 중요 시스템은 N+2를 적용합니다.

N+1 원칙 예시:
- Nginx 서버 2대 (1대 장애 시 1대로 서비스)
- Tomcat 서버 3대 (1대 장애 시 2대로 서비스)
- DB 서버 2대 (Primary + Standby)

페일오버(Failover)

장애 감지 후 자동으로 트래픽을 정상 서버로 전환하는 메커니즘입니다. 수동 페일오버(Manual Failover)는 MTTR(Mean Time To Recovery)이 길어 HA 목표 달성이 어렵습니다.

자동 페일오버 흐름:

  1. 헬스체크 실패 감지 (예: 3회 연속 실패)
  2. 해당 서버를 로드밸런서 풀에서 제거
  3. 백업 서버 활성화 (Standby → Active 승격)
  4. 알림 발송 (PagerDuty, Slack 등)
  5. 원인 분석 후 수동 복구

자동 복구(Auto Recovery)

장애 서버를 자동으로 재시작하거나 교체하는 메커니즘입니다. systemd의 Restart=always, Kubernetes의 Pod 자동 재시작, AWS Auto Scaling의 인스턴스 교체 등이 대표적입니다.

# systemd 자동 재시작 설정 예시
[Service]
ExecStart=/opt/tomcat/bin/catalina.sh run
Restart=always
RestartSec=5
StartLimitInterval=60
StartLimitBurst=3

그레이스풀 셧다운(Graceful Shutdown)

장애나 배포 시 진행 중인 요청을 완료한 후 서버를 종료합니다. 갑작스러운 종료는 진행 중인 트랜잭션을 손상시킬 수 있습니다.

헬스체크 전략

헬스체크는 HA 시스템의 핵심 신경망입니다. 잘못된 헬스체크 설정은 정상 서버를 제거하거나 장애 서버를 방치하는 결과를 낳습니다.

L4 헬스체크 (TCP 레벨)

TCP 연결 성공 여부만 확인합니다. 빠르고 가벼우나 애플리케이션 실제 동작 여부는 확인 불가합니다.

# Nginx 스트림 모듈 TCP 헬스체크
upstream tomcat_backend {
server 192.168.1.10:8080;
server 192.168.1.11:8080;
}

L7 헬스체크 (HTTP 레벨)

실제 HTTP 요청을 보내 응답 코드와 내용을 검증합니다. 애플리케이션의 실제 상태를 정확하게 파악할 수 있습니다.

# Nginx upstream 헬스체크 (nginx_upstream_check_module)
upstream tomcat_backend {
server 192.168.1.10:8080;
server 192.168.1.11:8080;

check interval=3000 rise=2 fall=3 timeout=1000 type=http;
check_http_send "GET /health HTTP/1.0\r\n\r\n";
check_http_expect_alive http_2xx http_3xx;
}

헬스체크 엔드포인트 설계

// Spring Boot 헬스체크 컨트롤러
@RestController
public class HealthController {

@Autowired
private DataSource dataSource;

@GetMapping("/health")
public ResponseEntity<Map<String, String>> health() {
Map<String, String> status = new HashMap<>();
try {
// DB 연결 확인
dataSource.getConnection().isValid(1);
status.put("status", "UP");
status.put("db", "connected");
return ResponseEntity.ok(status);
} catch (Exception e) {
status.put("status", "DOWN");
status.put("db", "disconnected");
return ResponseEntity.status(503).body(status);
}
}
}

실제 HA 스택 예시

Keepalived + Nginx + Tomcat + PostgreSQL HA 구성

                    ┌─────────────────────────────────┐
│ VIP: 192.168.1.100 │
│ (Keepalived VRRP 관리) │
└────────────┬────────────────────┘

┌──────────────────┴──────────────────┐
▼ ▼
┌─────────────────────┐ ┌─────────────────────┐
│ Nginx Master │ │ Nginx Backup │
│ 192.168.1.10 │ │ 192.168.1.11 │
│ (VRRP Priority 100) │ │ (VRRP Priority 90) │
└──────────┬──────────┘ └─────────────────────┘
│ (장애 시 VIP 이전)

┌──────────────────────────────────────────────┐
│ Nginx Upstream (로드밸런싱) │
└────────────┬─────────────────┬───────────────┘
▼ ▼
┌────────────────┐ ┌────────────────┐
│ Tomcat Node 1 │ │ Tomcat Node 2 │
│ 192.168.1.20 │ │ 192.168.1.21 │
│ :8080 │ │ :8080 │
└────────┬───────┘ └───────┬────────┘
└─────────┬─────────┘

┌──────────────────────────────────────────────┐
│ PostgreSQL Primary-Standby │
│ Primary: 192.168.1.30 ──▶ Standby: 192.168.1.31 │
└──────────────────────────────────────────────┘

이 구성에서 각 레이어의 SPOF가 모두 제거됩니다. Nginx Master 장애 시 Keepalived가 VIP를 Backup으로 자동 이전하고, Tomcat 노드 장애 시 Nginx upstream에서 자동으로 제외하며, PostgreSQL Primary 장애 시 Standby가 Primary로 승격됩니다.

고가용성 설계 시 주의사항

Split-Brain 문제: Active-Active 구성에서 네트워크 파티션 발생 시 양쪽 노드가 모두 자신이 Primary라고 판단하는 상황입니다. Quorum 기반 의사결정, Fencing 메커니즘으로 방지합니다.

Thundering Herd: 장애 복구 후 모든 요청이 한 번에 몰리는 현상입니다. 점진적 트래픽 전환, 캐시 워밍업으로 완화합니다.

운영 복잡도: HA 구성은 운영 복잡도를 높입니다. 팀의 역량과 서비스 규모에 맞는 수준을 선택하세요.

전문가 팁

  • 가용성 수치보다 MTTR(복구 시간)을 줄이는 데 집중하세요. 장애는 반드시 발생합니다.
  • 카오스 엔지니어링(Chaos Engineering)으로 정기적으로 장애를 시뮬레이션하고 복구 절차를 검증하세요.
  • 헬스체크 엔드포인트는 외부 의존성(DB, 캐시) 연결 상태까지 포함해 설계하세요.
  • 자동 페일오버는 반드시 알림과 함께 구성해 운영팀이 인지할 수 있게 하세요.
  • SLA 수치보다 실제 사용자 경험 기반의 가용성 지표(합성 모니터링, RUM)를 추적하세요.