v2 ECS 마이그레이션 전략 - 100-hours-a-week/KTB4-17th-wiki GitHub Wiki
전체 마이그레이션 전략
전체 마이그레이션은 한 번에 V2 VPC로 이전하지 않고, 두 단계로 나누어 진행한다.
1단계. V1 EC2 내부에서 Simple Broker → RabbitMQ 전환
현재 구조
- V1 Spring Container =
REST API+WebSocket+Spring Simple Broker - V1.5 Spring Container =
REST API+WebSocket+RabbitMQ - 현재 메시지 전송 구조
- Spring이 DB의 Outbox 테이블을 Polling
published = false인 메시지 발행- 발행 완료 후
published = true
1단계 전략
먼저 V2 VPC에 RabbitMQ를 구축하고, V1 VPC와 V2 VPC가 통신할 수 있도록 네트워크를 연결한다.
이후 기존 EC2 내부에 두 개의 Spring 컨테이너를 동시에 실행한다.
-
Blue: 기존 V1 코드
- REST + WebSocket
- Spring Simple Broker 사용
- Outbox의
published컬럼 사용
-
Green: V1.5 코드
- REST + WebSocket
- RabbitMQ 사용
- Outbox의
rabbit_published컬럼 사용
구조:
Shared DB
/ \
/ \
published rabbit_published
↓ ↓
Blue Container Green Container
↓ ↓
Simple Broker RabbitMQ
↓
V2 VPC
Blue/Green 공존 시 문제
Blue와 Green이 공존하는 동안 기존 WebSocket 연결은 Blue에 남아 있을 수 있다.
철수 ──→ Blue (Simple Broker)
영희 ──→ Green (RabbitMQ)
이 상태에서 하나의 published 컬럼만 사용하면 문제가 발생한다.
Blue가 Outbox 메시지를 먼저 읽어서 Simple Broker로 발행한 뒤
published = true로 변경하면 Green은 해당 메시지를 처리하지 않는다.
하지만 영희는 Green에 연결되어 있기 때문에 해당 메시지를 실시간으로 받을 수 없다.
Outbox 발행 상태 분리
이를 해결하기 위해 Outbox 발행 상태를 분리한다.
| published | rabbit_published |
|---|---|
| Simple Broker 발행 여부 | RabbitMQ 발행 여부 |
Blue와 Green은 각각 자신의 컬럼을 Polling하면서 모든 메시지를 발행한다.
Outbox
/ \
/ \
published rabbit_published
↓ ↓
Blue Green
↓ ↓
Simple Broker RabbitMQ
Blue → Green 트래픽 전환
이 상태에서 신규 트래픽을 Blue → Green으로 전환한다.
기존 Blue WebSocket 연결은 유지하면서 자연스럽게 Drain한다.
신규 HTTP 요청 → Green
신규 WS 연결 → Green
기존 WS 연결 → Blue에서 유지
Blue에 더 이상
- WebSocket 연결이 없고
- 처리 중인 요청도 없으면
Blue 컨테이너를 종료한다.
1단계 완료 상태
아직 VPC나 EC2 자체는 이전하지 않는다.
V1 VPC의 기존 EC2를 그대로 사용하지만, 메시징 구조는 RabbitMQ 기반으로 전환된 상태가 된다.
VPC V1
EC2
└─ Spring
├─ REST
├─ WebSocket
└─ RabbitMQ
↓
RabbitMQ
(V2 VPC)
즉,
기존
EC2
└─ Spring
└─ Simple Broker
1단계 완료
EC2
└─ Spring
└─ RabbitMQ
2단계. V1 EC2 → V2 VPC/ECS 이전
1단계에서 메시징 구조를 RabbitMQ로 통일한 이후, 실제 서버 및 VPC 마이그레이션을 진행한다.
V2 구조
V2 VPC에는 최종 운영 환경을 미리 구축한다.
V2 VPC
ALB
│
┌────────┴────────┐
│ │
/api /ws
│ │
↓ ↓
Spring ECS WebSocket ECS
│ │
└────────┬────────┘
↓
RabbitMQ
│
↓
Shared DB
Spring 서버와 WebSocket 서버는 분리되어 있지만, V1 EC2의 Spring 컨테이너와 동일한 RabbitMQ와 DB를 사용한다.
따라서 마이그레이션 과정에서 V1과 V2 사용자가 공존해도 된다.
철수
│
↓
V1 EC2
│
└──────────┐
│
↓
RabbitMQ
↑
│
┌──────────┘
│
V2 WebSocket ECS
↑
│
영희
철수는 기존 EC2 WebSocket에 연결되어 있고, 영희는 V2 ECS WebSocket 서버에 연결되어 있어도
두 서버가 동일한 RabbitMQ를 사용하기 때문에 서로 실시간 메시지를 전달할 수 있다.
V2 트래픽 전환
V2 환경 검증이 완료되면 CloudFront의 요청 경로를 기존 EC2에서 V2 ALB로 전환한다.
전환 전
CloudFront
│
├─ /api
└─ /ws
↓
EC2
│
↓
RabbitMQ
전환 후
CloudFront
│
↓
ALB
│
├─ /api → Spring ECS
│
└─ /ws → WebSocket ECS
│
↓
RabbitMQ
전환 이후 신규 요청과 기존 연결은 다음과 같이 처리된다.
신규 REST 요청
↓
V2 Spring ECS
신규 WebSocket 연결
↓
V2 WebSocket ECS
기존 WebSocket 연결
↓
V1 EC2에 그대로 유지
기존 EC2와 V2 ECS가 동일한 RabbitMQ와 DB를 사용하기 때문에 V1과 V2가 공존하는 동안에도 메시지를 전달할 수 있다.
기존 EC2 Connection Drain
CloudFront 전환 이후 신규 WebSocket 연결은 V2로 전달된다.
기존 EC2에 이미 연결되어 있던 WebSocket Connection은 기존 연결을 유지하면서 자연스럽게 Drain한다.
V1 EC2 WebSocket Connection
100
↓
50
↓
10
↓
0
기존 EC2에 더 이상
- WebSocket 연결이 없고
- 처리 중인 HTTP 요청이 없으면
EC2를 종료한다.
전체 흐름 요약
1단계 - 메시징 구조 전환
V1 EC2
Spring
├─ REST
├─ WebSocket
└─ Simple Broker
↓
Blue / Green 전환
↓
Spring
├─ REST
├─ WebSocket
└─ RabbitMQ
※ VPC와 EC2는 그대로 유지
2단계 - 인프라 이전
V1 VPC / EC2
Spring + WebSocket
│
│
└──── RabbitMQ
↑
│
V2 VPC / ECS │
Spring + WebSocket
V1과 V2가 동일한 RabbitMQ와 DB를 사용하는 상태에서 CloudFront의 트래픽을 V2로 전환한다.
최종 순서
V1
EC2 + Spring + Simple Broker
↓
V2 VPC에 RabbitMQ 구축
↓
V1 ↔ V2 VPC 네트워크 연결
↓
EC2 내부 Blue / Green 구성
↓
Simple Broker + RabbitMQ Dual Publish
(published / rabbit_published)
↓
Blue → Green 트래픽 전환
↓
Blue WebSocket Connection Drain
↓
Blue 종료
↓
V1 EC2가 RabbitMQ 기반으로 완전히 전환
↓
────────────────────────────────────
1단계 완료
────────────────────────────────────
↓
V2 VPC에 ECS 환경 구축 및 검증
↓
V1 EC2와 V2 ECS가
동일한 RabbitMQ + DB 사용
↓
CloudFront
EC2 → V2 ALB로 트래픽 전환
↓
신규 REST → V2 Spring ECS
신규 WS → V2 WebSocket ECS
↓
기존 EC2 WebSocket Connection Drain
↓
V1 EC2 Connection = 0
V1 EC2 처리 중 요청 = 0
↓
V1 EC2 종료
↓
────────────────────────────────────
2단계 완료
────────────────────────────────────
↓
V2 VPC / ECS + RabbitMQ
최종 구조로 마이그레이션 완료