작성일 :

어떻게 장애에도 서비스를 유지하는가

Part 1에서 로드 밸런싱으로 트래픽을 여러 서버에 분산하는 방법을, Part 2에서 DNS를 이용해 지리적으로 분산하는 방법을 살펴보았습니다.


서버를 여러 대로 늘리면 한 대에 장애가 발생해도 나머지가 트래픽을 처리할 수 있습니다. 하지만 트래픽을 나눠주는 로드 밸런서는 여전히 한 대입니다.


로드 밸런서 자체에 장애가 발생하면 어떻게 될까요?


가용성의 정의

장애 대응을 설계하려면, 먼저 “서비스가 얼마나 안정적인가”를 측정할 기준이 필요합니다.


가용성(Availability)은 전체 시간 중 시스템이 정상 동작한 시간의 비율입니다.


1
가용성 = 정상 운영 시간 / 전체 시간 × 100%


9의 개수

가용성은 흔히 숫자 9가 몇 개인지로 표현됩니다. 99.99%는 9가 네 개이므로 “Four Nines”입니다.


1
2
3
4
5
6
7
8
9
┌─────────────┬───────────────┬──────────────────────────┐
│   가용성    │  연간 다운타임 │           의미           │
├─────────────┼───────────────┼──────────────────────────┤
│   99%       │   3.65일      │   Two Nines              │
│   99.9%     │   8.76시간    │   Three Nines            │
│   99.99%    │   52.56분     │   Four Nines             │
│   99.999%   │   5.26분      │   Five Nines             │
│   99.9999%  │   31.5초      │   Six Nines              │
└─────────────┴───────────────┴──────────────────────────┘


99%와 99.99%는 0.99%밖에 차이가 나지 않아 보이지만, 연간 다운타임은 3.65일에서 약 52분으로 약 100배 줄어듭니다.


SLA (Service Level Agreement)

앞서 본 가용성 수치는 단순한 목표가 아닙니다. SLA(Service Level Agreement)는 서비스 제공자가 고객에게 보장하는 가용성 수준을 명시한 계약입니다. 충족하지 못하면 금전적 보상이 발생합니다.

예를 들어, AWS EC2는 월간 가용성 99.99%를 SLA로 보장합니다. 이 수치에 미달하면 AWS가 서비스 크레딧을 제공해야 합니다. 따라서 인프라 설계 시 목표 SLA를 먼저 정하고, 이에 맞는 장애 대비 수준을 결정합니다.


SLA를 맞추려면 하나의 구성 요소 장애가 전체 서비스를 중단시키는 지점, 즉 단일 장애점(SPOF: Single Point of Failure)을 제거해야 합니다.


이중화 패턴

SPOF를 제거하는 가장 기본적인 방법은 같은 역할을 하는 구성 요소를 두 개 이상 두는 것입니다. 이를 이중화(Redundancy)라고 합니다.

Active-Passive (Hot Standby)

하나의 시스템이 활성 상태로 트래픽을 처리하고, 다른 하나는 대기하며 상태를 동기화받습니다. Active만 데이터를 쓰고 Passive는 받기만 하므로, 동기화 방향이 한쪽이라 구현이 단순합니다.

1
2
3
4
5
6
7
8
9
10
11
정상 상태:
┌─────────────┐                    ┌─────────────┐
│   Active    │ ←── 트래픽 ──     │   Passive   │
│   (운영중)  │ ── 상태 동기화 ──► │   (대기중)  │
└─────────────┘                    └─────────────┘

장애 발생 시:
┌─────────────┐                    ┌─────────────┐
│   Failed    │                    │   Active    │ ←── 트래픽
│   (장애)    │                    │  (승격됨)   │
└─────────────┘                    └─────────────┘

Active에 장애가 발생하면 Passive가 승격되어 트래픽을 이어받습니다. 이 전환을 페일오버(Failover)라고 합니다.

구현이 단순한 대신 대가가 있습니다. Passive는 평소에 트래픽을 처리하지 않으므로 서버 두 대를 운영하면서 한 대만 일하는 셈이고, 페일오버 과정에서 짧은 중단이 발생할 수 있습니다.


Active-Active

대기 전용 시스템 없이, 모든 시스템이 활성 상태로 트래픽을 나눠 처리합니다.

1
2
3
4
5
6
7
8
9
10
11
정상 상태:
┌─────────────┐                    ┌─────────────┐
│   Active    │ ←── 트래픽 ──►   │   Active    │
│   (50%)     │ ◄── 상태 동기화 ──► │   (50%)     │
└─────────────┘                    └─────────────┘

장애 발생 시:
┌─────────────┐                    ┌─────────────┐
│   Failed    │                    │   Active    │ ←── 트래픽
│   (장애)    │                    │  (100%)     │
└─────────────┘                    └─────────────┘

양쪽 모두 이미 트래픽을 처리하고 있으므로, 한쪽에 장애가 발생하면 별도의 페일오버 없이 나머지가 트래픽을 흡수합니다. 리소스를 전부 활용할 수 있고 전환 중단도 없습니다.

다만 양쪽 모두 데이터를 쓰기 때문에, 같은 데이터를 동시에 수정하는 충돌이 발생할 수 있습니다. 예를 들어, 사용자 A의 요청이 서버 1에서 재고를 차감하는 동시에 사용자 B의 요청이 서버 2에서 같은 재고를 차감하면, 두 서버의 재고 값이 서로 달라집니다. Active-Passive에서는 쓰기가 한쪽에서만 일어나므로 이런 충돌이 없지만, Active-Active에서는 충돌을 해소하는 메커니즘이 추가로 필요합니다.


그렇다면 실무에서는 어떤 기준으로 패턴을 선택할까요? 핵심은 해당 구성 요소가 자체적으로 데이터를 변경하는지 여부입니다.

데이터베이스처럼 양쪽에서 같은 데이터를 수정하면 충돌이 발생하는 시스템은 Active-Passive가 자연스럽습니다.

반면, 웹 서버처럼 요청을 처리하되 자체적으로 상태를 저장하지 않는 시스템은 동기화 충돌이 없으므로 Active-Active로 리소스를 최대한 활용할 수 있습니다.


N+1, N+N

Active-Passive와 Active-Active가 “어떻게 전환하느냐”의 문제라면, N+1과 N+N은 “여분을 몇 대 두느냐”의 문제입니다.

N+1은 정상 운영에 필요한 N대에 1대를 추가합니다. 예를 들어, 트래픽 처리에 서버 3대가 필요하다면 4대를 구성합니다. 1대가 장애를 일으켜도 나머지 3대로 정상 운영이 가능합니다.

N+N(2N)은 필요 용량의 2배를 구성합니다. 서버 3대가 필요하면 6대를 배치하여, 절반이 동시에 장애를 일으켜도 서비스가 유지됩니다.

비용이 2배로 늘어나므로 대부분의 서비스에서는 N+1로 충분하고, 금융 거래처럼 동시 다발 장애까지 견뎌야 하는 시스템에서 N+N을 고려합니다.


VRRP와 HSRP

이중화 구성에서 Active에 장애가 발생하면 Passive가 승격됩니다. 그런데 Active와 Passive는 서로 다른 IP 주소를 가지고 있습니다. 클라이언트가 Active의 IP로 접속하고 있었다면, 승격된 Passive의 IP를 어떻게 알 수 있을까요?

가상 IP(Virtual IP)가 이 문제를 해결합니다. 실제 장비의 IP와 별도로 하나의 가상 IP를 두고, 이 IP가 항상 현재 Active인 장비를 가리키게 합니다. 클라이언트는 가상 IP로만 접속하므로, 뒤에서 어떤 장비가 Active인지 알 필요가 없습니다.

그런데 가상 IP가 “현재 Active를 가리킨다”는 것은 누가 관리할까요? 장애가 발생했을 때 가상 IP를 자동으로 새 Active에게 넘기는 프로토콜이 필요합니다. VRRPHSRP가 그 역할을 합니다.

VRRP (Virtual Router Redundancy Protocol)

VRRP는 RFC 5798로 정의된 표준 프로토콜로, 여러 라우터가 하나의 가상 IP를 공유하면서 장애 시 자동으로 전환합니다. 각 라우터에는 우선순위(Priority)가 부여되고, 우선순위가 가장 높은 라우터가 Master가 됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
       ┌───────────────┐
       │  가상 IP      │
       │ 192.168.1.1   │
       └───────┬───────┘
               │
        ┌──────┴──────┐
        │             │
        ▼             ▼
┌─────────────┐ ┌─────────────┐
│  라우터 A   │ │  라우터 B   │
│  .2 Master  │ │  .3 Backup  │
│ Priority:100│ │ Priority:90 │
└─────────────┘ └─────────────┘

Master는 가상 IP를 자신에게 연결하고, 주기적으로 Advertisement 메시지를 보내 자신이 살아 있음을 알립니다. Backup은 이 메시지를 기다리다가 일정 시간 동안 받지 못하면 Master에 장애가 발생한 것으로 판단하고 스스로 Master로 승격합니다.

1
2
3
4
5
6
7
8
정상 동작:
클라이언트 → 192.168.1.1 (가상 IP) → 라우터 A (Master)

라우터 A 장애:
  Backup이 Advertisement를 받지 못함 → Master로 승격
클라이언트 → 192.168.1.1 (가상 IP) → 라우터 B (새 Master)

클라이언트는 같은 IP로 접속하므로 전환을 인식하지 못함


HSRP (Hot Standby Router Protocol)

HSRP는 시스코가 자사 장비용으로 만든 프로토콜로, 가상 IP 공유와 장애 전환이라는 핵심 원리는 VRRP와 같습니다. 주요 차이는 다음과 같습니다.

  • 범용성: VRRP는 RFC 표준이라 벤더에 관계없이 사용할 수 있고, HSRP는 시스코 장비에서만 동작합니다.
  • 용어: VRRP는 Master/Backup, HSRP는 Active/Standby로 역할을 부릅니다.

시스코 장비로 구성된 네트워크라면 HSRP를, 멀티벤더 환경이라면 VRRP를 선택하는 것이 일반적입니다.


LB 자체의 고가용성

Part 1에서 로드 밸런서가 서버 앞에서 트래픽을 분산하는 구조를 살펴보았습니다. 서버는 여러 대로 이중화했지만, 모든 트래픽이 거쳐가는 로드 밸런서가 한 대라면 그 자체가 SPOF입니다.

1
2
3
4
5
6
7
8
9
10
11
사용자들
   │
   ▼
┌─────────────┐
│ 로드 밸런서 │ ← SPOF!
└──────┬──────┘
       │
   ┌───┼───┐
   │   │   │
   ▼   ▼   ▼
서버A 서버B 서버C  (이중화 완료)

로드 밸런서에 장애가 발생하면, 뒤의 서버가 아무리 많아도 전체 서비스가 중단됩니다. 앞서 살펴본 이중화 패턴과 VRRP를 로드 밸런서에도 적용해야 합니다.


LB 이중화

앞서 VRRP가 라우터의 가상 IP 전환을 자동화하는 것을 보았습니다. 같은 방식을 로드 밸런서에도 적용합니다.

두 로드 밸런서가 VRRP(또는 VRRP를 소프트웨어로 구현한 Keepalived)로 하나의 가상 IP를 공유합니다. 평상시에는 Master가 트래픽을 처리하고, Backup은 Master의 상태를 동기화받으며 대기합니다. Master에 장애가 발생하면 Backup이 가상 IP를 인수하여 트래픽을 이어받습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
사용자들
   │
   ▼
┌─────────────────────────────────────┐
│           가상 IP                    │
│        (VRRP/Keepalived)            │
└────────────────┬────────────────────┘
                 │
        ┌────────┴────────┐
        │                 │
        ▼                 ▼
┌─────────────┐     ┌─────────────┐
│ LB Master   │     │ LB Backup   │
│             │── 상태 동기화 ──►│ (대기중)    │
└──────┬──────┘     └──────┬──────┘
       │                   │
       └─────────┬─────────┘
                 │
          ┌──────┼──────┐
          │      │      │
          ▼      ▼      ▼
        서버A  서버B  서버C


Floating IP

VRRP는 같은 물리 네트워크 안에서 장비들이 직접 통신하여 가상 IP를 이전합니다. 그런데 클라우드 환경에서는 네트워크를 클라우드 제공자가 관리하므로, 사용자가 이런 네트워크 수준의 제어를 할 수 없습니다.

대신 클라우드는 Floating IP를 제공합니다.

클라우드의 네트워크는 소프트웨어로 제어됩니다. “이 IP로 들어오는 트래픽은 이 인스턴스로 보내라”라는 라우팅 규칙을 클라우드 제공자가 소프트웨어로 관리하기 때문에, 이 규칙을 바꾸면 같은 IP가 다른 인스턴스를 가리키게 할 수 있습니다.

Floating IP는 이 원리를 이용합니다. 인스턴스를 생성할 때 부여되는 일반 IP와 달리, Floating IP는 어떤 인스턴스에도 묶이지 않은 채 독립적으로 존재합니다.

사용자가 이 IP를 원하는 인스턴스에 붙였다 떼었다 할 수 있으므로, 장애 시 API 한 번으로 같은 IP를 다른 인스턴스에 연결할 수 있습니다. AWS의 Elastic IP, GCP의 Static External IP 등이 이에 해당합니다.

1
2
3
4
5
6
7
정상:      Floating IP (203.0.113.10) → LB 인스턴스 A

장애 발생: 모니터링이 인스턴스 A 장애 감지
           → 클라우드 API 호출 → IP 재할당 (수초 내 완료)

전환 후:   Floating IP (203.0.113.10) → LB 인스턴스 B
           (인스턴스 A는 여전히 장애, B가 서비스 계속)

VRRP에서 장비가 자동으로 하던 가상 IP 전환을, 클라우드에서는 API 호출로 대신하는 것입니다.


상태 동기화

이중화 구성에서 페일오버가 일어나면, 새로 승격된 장비가 기존 연결을 이어받아야 합니다. 이를 위해서는 장비 간에 상태 동기화가 필요합니다.

세션 복제

Part 1에서 로드 밸런서가 클라이언트와 서버 간의 연결 정보를 세션 테이블로 관리하는 것을 보았습니다. Master에 장애가 발생하면 Backup이 승격되는데, 이때 세션 테이블이 없으면 기존 연결을 알 수 없어 모든 클라이언트가 재연결해야 합니다.

이를 방지하기 위해 Master의 세션 테이블을 Backup에 실시간으로 복제합니다.

1
2
3
4
5
6
7
8
9
10
LB Master               LB Backup
┌───────────────┐      ┌───────────────┐
│ 세션 테이블   │ ───► │ 세션 테이블   │
│ - 연결 1      │ 실시간│ - 연결 1      │
│ - 연결 2      │ 복제  │ - 연결 2      │
│ - 연결 3      │      │ - 연결 3      │
└───────────────┘      └───────────────┘

Master 장애 → Backup 승격:
세션 테이블이 이미 동기화되어 있으므로 기존 연결을 끊김 없이 이어받음


상태 동기화의 비용

세션 복제는 공짜가 아닙니다. 연결이 생길 때마다 Backup에도 전송해야 하므로 네트워크 대역폭을 소모하고, 복제가 완료될 때까지 응답이 약간 지연될 수 있습니다.

또한 복제가 완벽한 실시간이 아니라면, Master가 장애를 일으킨 시점과 마지막 복제 시점 사이에 생긴 세션은 유실됩니다. 동기화 주기를 짧게 하면 유실은 줄지만 대역폭 부담이 커지고, 길게 하면 유실 위험이 커지는 트레이드오프가 있습니다.

이러한 복잡성은 로드 밸런서가 세션 정보를 직접 들고 있기 때문에 발생합니다. 그렇다면 세션 정보를 로드 밸런서에 저장하지 않고, Redis 같은 외부 저장소에 두면 어떨까요?


Stateless 설계

세션 정보를 로드 밸런서나 서버가 직접 들고 있으면 장비 간 동기화가 필요합니다. 반대로, 세션 정보를 Redis 같은 외부 저장소에 두면 로드 밸런서와 서버는 상태를 갖지 않게 됩니다. 장비끼리 동기화할 것이 없으므로, 어떤 장비가 요청을 받든 외부 저장소에서 세션을 읽어오면 됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
┌─────────────────────────────────────────────────────────────┐
│                      Stateless 아키텍처                     │
│                                                             │
│    LB A (상태 없음)      LB B (상태 없음)                   │
│         │                     │                             │
│         └─────────┬───────────┘                             │
│                   │                                         │
│         ┌─────────┴─────────┐                              │
│         │                   │                              │
│    서버 A (상태 없음)   서버 B (상태 없음)                   │
│         │                   │                              │
│         └─────────┬─────────┘                              │
│                   │                                         │
│         ┌─────────┴─────────┐                              │
│         │                   │                              │
│   ┌─────┴─────┐       ┌─────┴─────┐                        │
│   │ Redis     │       │ 데이터베이스│                       │
│   │ (세션)    │       │ (데이터)  │                        │
│   └───────────┘       └───────────┘                        │
└─────────────────────────────────────────────────────────────┘

LB A에 장애가 발생해도 LB B가 그대로 이어받으면 됩니다. 세션 정보는 Redis에 있으므로 복제할 것이 없고, 장비를 추가할 때도 동기화 설정 없이 바로 투입할 수 있습니다.

다만 모든 요청이 외부 저장소를 거치므로 네트워크 요청이 한 단계 늘어나고, 외부 저장소 자체가 새로운 SPOF가 될 수 있으므로 Redis나 데이터베이스 역시 이중화가 필요합니다.

이중화 문제가 사라지는 것은 아닙니다. 핵심은 이중화해야 하는 대상이 줄어든다는 점입니다. Stateless 이전에는 LB와 서버 모두가 세션을 들고 있어서 노드 수만큼 동기화가 필요했습니다. Stateless 이후에는 LB와 서버가 아무리 늘어도 동기화할 것이 없고, 이중화는 Redis 한 곳에 집중됩니다. Redis는 복제를 위해 설계된 시스템이라 Sentinel, Cluster 같은 검증된 이중화 메커니즘을 기본 제공합니다.


실제 아키텍처 사례

지금까지 이중화, VRRP, Stateless 설계를 각각 살펴보았습니다. 실제 서비스에서는 이 개념들이 어떻게 조합될까요?

2-Tier 아키텍처

가장 기본적인 구성입니다. 하나의 로드 밸런서가 트래픽을 받아 뒤의 웹/앱 서버로 분산하고, 서버는 데이터베이스에 연결됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
인터넷
  │
  ▼
┌─────────────┐
│ L4/L7 LB    │ ← Active-Passive HA 구성 (VRRP/Keepalived)
└──────┬──────┘
       │
   ┌───┴───┐
   │       │
   ▼       ▼
 웹/앱   웹/앱  ← Stateless, 오토스케일링으로 수평 확장
 서버    서버
   │       │
   └───┬───┘
       │
       ▼
┌─────────────┐
│ 데이터베이스 │ ← Primary-Replica 이중화
└─────────────┘

각 계층에 앞서 다룬 개념이 적용되어 있습니다. 로드 밸런서는 VRRP로 Active-Passive HA를 구성하고, 웹/앱 서버는 Stateless로 설계하여 오토스케일링으로 자유롭게 늘리거나 줄이며, 데이터베이스는 Primary-Replica로 이중화합니다. 소규모에서 중규모 서비스에 적합한 구성입니다.


3-Tier 아키텍처

트래픽이 더 커지면, 하나의 로드 밸런서가 TCP 연결 분산과 요청 내용 해석을 모두 담당하기 어려워집니다. 3-Tier는 이 두 역할을 L4와 L7로 분리합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
인터넷
  │
  ▼
┌─────────────┐
│ L4 LB       │ ← IP/포트만 보고 빠르게 분산
└──────┬──────┘
       │
   ┌───┴───┐
   │       │
   ▼       ▼
┌─────┐ ┌─────┐
│L7 LB│ │L7 LB│ ← URL, 헤더, 쿠키를 해석하여 정밀 라우팅
└──┬──┘ └──┬──┘
   │       │
   └───┬───┘
       │
   ┌───┼───┐
   │   │   │
   ▼   ▼   ▼
  앱  앱  앱   ← Stateless 앱 서버
  서버 서버 서버
   │   │   │
   └───┼───┘
       │
       ▼
┌─────────────┐
│ 데이터 계층  │
│(DB, Cache)  │
└─────────────┘

L4가 앞단에서 대용량 TCP 연결을 빠르게 나누고, L7이 그 뒤에서 요청 내용에 따라 적절한 서버를 선택합니다. 각 계층이 독립적이므로, 예를 들어 L7만 늘리거나 앱 서버만 늘리는 식으로 병목이 발생한 계층만 확장할 수 있습니다.


글로벌 분산 아키텍처

2-Tier와 3-Tier는 하나의 데이터센터 안에서의 구성입니다. 서비스가 전 세계 사용자를 대상으로 한다면, 여러 지역에 데이터센터를 두고 사용자를 가장 가까운 곳으로 안내해야 합니다.

Part 2에서 다룬 GSLB가 이 역할을 합니다. GSLB가 DNS 질의를 받아 사용자 위치에 따라 적절한 데이터센터의 IP를 응답하고, 각 데이터센터는 자체 LB, 서버, 데이터베이스를 갖춘 독립적인 구성을 유지합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
                     ┌─────────────┐
                     │   GSLB      │
                     │(DNS 기반)   │
                     └──────┬──────┘
                            │
        ┌───────────────────┼───────────────────┐
        │                   │                   │
        ▼                   ▼                   ▼
┌──────────────┐    ┌──────────────┐    ┌──────────────┐
│  아시아 DC   │    │  유럽 DC     │    │  북미 DC     │
│              │    │              │    │              │
│ ┌──────────┐ │    │ ┌──────────┐ │    │ ┌──────────┐ │
│ │   LB     │ │    │ │   LB     │ │    │ │   LB     │ │
│ └────┬─────┘ │    │ └────┬─────┘ │    │ └────┬─────┘ │
│      │       │    │      │       │    │      │       │
│   서버들     │    │   서버들     │    │   서버들     │
│              │    │              │    │              │
│ ┌──────────┐ │    │ ┌──────────┐ │    │ ┌──────────┐ │
│ │   DB     │◄├────┼─►│   DB     │◄├────┼─►│   DB     │ │
│ │ (복제)   │ │    │ │ (복제)   │ │    │ │ (복제)   │ │
│ └──────────┘ │    │ └──────────┘ │    │ └──────────┘ │
└──────────────┘    └──────────────┘    └──────────────┘

데이터센터 간 데이터베이스 복제가 필요합니다. 한국 사용자가 아시아 DC에서 작성한 데이터를 북미 DC에서도 읽을 수 있어야 하기 때문입니다. 이 복제 과정에서 앞서 다룬 Active-Active의 동시 쓰기 충돌 문제가 다시 등장하므로, 데이터센터 간 동기화 전략이 글로벌 분산 설계의 핵심 과제가 됩니다.


마무리: 장애는 막는 것이 아니라 대비하는 것

  • SLA는 목표 가용성을 정하고, 이에 맞는 장애 대비 수준을 결정하는 출발점입니다.
  • SPOF를 식별한 뒤, Active-Passive 또는 Active-Active로 이중화하여 단일 장애점을 제거합니다.
  • VRRP와 Floating IP는 페일오버 시 가상 IP를 자동으로 전환하여 클라이언트가 변경을 인식하지 못하게 합니다.
  • Stateless 설계는 장비 간 동기화 복잡성을 없애고, 이중화를 외부 저장소 한 곳에 집중시킵니다.

시스템은 반드시 장애가 발생합니다. 장애를 완전히 막을 수는 없지만, 장애가 발생했을 때 서비스가 계속되도록 설계할 수는 있습니다.


Part 1에서 트래픽 분산의 원리를, Part 2에서 DNS 기반 글로벌 분산을, 이 글에서 고가용성 아키텍처를 살펴보았습니다. 트래픽을 나누고, 지리적으로 분산하고, 장애에도 서비스를 유지하는 것이 현대 서비스 인프라의 기본 골격입니다.



관련 글

시리즈

Tags: Failover, HA, VRRP, 고가용성, 네트워크, 이중화

Categories: ,