반응형
배포 전략과 무중단 배포
1. 재생성 전략 (Recreate)
- 재배포!
- 디플로이먼트와 관련된 모든 포드를 중지 → 새로운 버전의 포드를 생성
- 장점 : 쉽고 간편하다
- 단점 : 서비스 다운타임이 발생한다.
- 서비스에 영향이 있다.
- 기능 테스트 배포에 적합하다.
2. 롤링 업데이트 전략 (Rolling Update)
- Blue / Green 배포 방식과 유사
- 기존 버전의 애플리케이션을 점진적으로 새로운 버전으로 교체하는 배포 방식
- 트래픽을 기존 버전(V1.1)과 새 버전(V1.2)으로 분산하면서 배포
- 사실 사용자 경험 측면에서는 서버가 내려갔다는 것도 못 느끼지만, 아주 희박한 환경에서의 에러가 난다면?
- 기존 v1.1에 있던 사용자
- 로그아웃
- 심한 경우 500 에러
- [그런데 결제 중이었다면?]
- 실제 결제는 되었는데, 사용자는 v1.2로 옮겨가고 사이트에서는 재결제가 뜸
- → ALB에서 Connection Draining(연결 지연 종료) 기술
- 기존 V1.1에서 요청을 처리 중인 사용자가 있다면 해당 요청이 끝날 때까지 기다린다.
- 모든 연결이 종료된 후, 완전히 전환
- 세션이 끊길 때 대기하는 것
- 완벽한 해결일까?
- 기존 연결이 강제 종료되는 문제를 해결하지만, 세션 유지 문제는 해결하지 못함
- 세션 기반 애플리케이션 (로그인, 결제 등)은 해결되지 않음
- Connection Draining은 진행 중인 요청만 유지해줄 뿐, 새 요청까지 보장하지 않음
- 세션이 메모리에 저장되어 있다면, 서버 교체 시 세션이 사라질 가능성 있음
- 결제 진행 중일 때 사용자가 새로운 버전(V1.2)으로 이동하면 세션이 끊기고 재로그인 요구
- 웹소켓, 장기 연결(Long-lived connections)에 취약
- WebSocket 같은 경우, 클라이언트가 지속적으로 서버와 연결을 유지함
- Connection Draining이 끝난 후에는 새로운 서버(V1.2)로 강제 이동해야 함
- 완전한 무중단 배포가 어려움
- 기존 요청을 마친 후 서버를 종료하더라도, 세션을 공유하지 않으면 로그인 유지가 불가능
- Sticky Session을 설정하면 해결 가능하지만, 서버가 교체되면 Sticky Session도 의미가 없음
- 기존 v1.1에 있던 사용자
3. 실무 해결책
- ⇒ 실무 : Connection Draining + 세션 공유(redis 등) + Canary 배포 활용
- 세션 공유
- 세션을 서버에 저장하면 배포 시 세션이 사라지므로, 반드시 중앙 저장소 사용 필요
- Redis, Memcached 같은 세션 스토리지
- 레디스락..?
- Canary 배포
- 일부 사용자(예: 1~5%)만 새 버전으로 전환 후 안정성이 확인되면 전체 배포
- 롤백 가능
- 단점 : 리소스를 두 배로 사용… 비용 증가
- 이중 트랜잭션 처리 → DB + 메시지 큐(Kafka, SQS)를 활용하여 트랜잭션 상태 유지
- Idempotency Key 적용 → 중복 요청을 방지하여 결제가 두 번 이루어지지 않도록 함
- 트랜잭션 스토리지 사용 (DynamoDB, Kafka, SQS 등)
- 세션 공유
4. 결론
- ⇒ 그냥 이런 기능있네? 좋다! 도입하자!
- 가 아닌 "서비스" 레이어의 WorkFlow를 생각하는 것이 아키텍쳐 / 기술 선택에서 중요하다.
해결 방식 딥다이브
https://techblog.woowahan.com/15236/
https://oliveyoung.tech/2024-06-25/oliveyoung-order-payment-part4/
https://techblog.woowahan.com/4886/
실무에서 많이 쓰는 배포 방식
1. Canary Deployment (카나리아 배포)
- 일부 사용자(예: 1~5%)만 새 버전으로 전환 후 안정성이 확인되면 전체 배포
- 롤백 가능 → 결제 오류 발생 시 즉시 이전 버전으로 되돌릴 수 있음
- 진행 방법:
- Ingress 컨트롤러(Nginx, Traefik)나 서비스 메시(Istio, Linkerd)를 활용
- 트래픽을 5% → 10% → 50% → 100% 순서로 점진적 전환
## Istio 카나리아 배포 예제 (트래픽 90%: 기존 v1, 10%: 새로운 v2)
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: myapp
spec:
hosts:
- myapp.example.com
http:
- route:
- destination:
host: myapp-v1
weight: 90
- destination:
host: myapp-v2
weight: 10
2. Blue/Green Deployment
- 기존 버전(V1)과 새로운 버전(V2)을 동시에 운영
- 배포 완료 후 트래픽을 일괄적으로 새로운 버전(V2)로 스위칭
- 배포 중 결제 오류 발생 시 즉시 이전 버전(V1)로 롤백 가능
- 실무에서는 ALB나 Istio를 사용하여 Blue/Green 전환
## ALB Listener Rule을 사용하여 배포 전환 (AWS CLI)
aws elbv2 modify-listener --listener-arn <ALB_LISTENER_ARN> \
--default-actions '[{"Type":"forward","TargetGroupArn":"<NEW_TARGET_GROUP_ARN>"}]'
- "배포 단계에서 장애가 발생해도 기존 시스템은 그대로 유지됨"
- 하지만 리소스를 두 배로 사용해야 하므로 비용이 증가할 수 있음
결제 문제 해결을 위한 실무적 접근 방식
1. 세션 관리 & 트랜잭션 일관성 유지
- 세션을 서버에 저장하면 배포 시 세션이 사라지므로, 반드시 중앙 저장소 사용 필요
- Redis, Memcached 같은 세션 스토리지 활용
- JWT + Refresh Token 방식 사용 (Stateless한 방식)
- Sticky Session 적용은 단기적 해결책이지만, 서버가 내려가면 의미 없음
## Kubernetes에서 Redis 기반 세션 스토리지 설정
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
spec:
replicas: 1
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:latest
2. 결제 트랜잭션 안정성 확보
- 이중 트랜잭션 처리 → DB + 메시지 큐(Kafka, SQS)를 활용하여 트랜잭션 상태 유지
- Idempotency Key 적용 → 중복 요청을 방지하여 결제가 두 번 이루어지지 않도록 함
- 트랜잭션 스토리지 사용 (DynamoDB, Kafka, SQS 등)
## Idempotency Key를 사용한 결제 요청 처리 (Python)
import hashlib
def generate_idempotency_key(user_id, amount, timestamp):
return hashlib.sha256(f"{user_id}-{amount}-{timestamp}".encode()).hexdigest()
## 요청이 들어오면 중복 확인 후 처리
if not redis.exists(idempotency_key):
process_payment()
redis.set(idempotency_key, "processed", ex=3600) # 1시간 동안 저장
3. 배포 중인 사용자의 요청 보호
- ALB/NLB의 Connection Draining 활용 → 기존 연결을 즉시 끊지 않음
- Graceful Shutdown 적용 → 현재 진행 중인 요청이 끝날 때까지 기다린 후 종료
- WebSocket, gRPC 같은 장기 연결(LT connections) 보호
## Kubernetes에서 Graceful Shutdown 설정 (preStop Hook 사용)
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 3
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myapp:latest
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 5"] # 5초 동안 요청을 마칠 시간 부여
실무에서 많이 쓰는 결제 문제 해결 방식 정리
문제 | 해결 방법 |
---|---|
세션 유지 문제 | Redis/Memcached 같은 중앙 저장소 사용 |
배포 중 결제 요청 손실 | Kafka/SQS로 트랜잭션 이벤트 저장 후 재처리 |
중복 결제 | Idempotency Key 사용하여 중복 요청 방지 |
기존 사용자의 세션 끊김 | Sticky Session 적용 (단기 해결), Redis 기반 세션 유지 |
배포 중 트래픽 이동 | ALB Connection Draining + Graceful Shutdown |
롤백 필요 시 | Canary Deployment or Blue/Green Deployment |
결론
- 가장 많이 쓰이는 배포 방식 → Canary Deployment & Blue/Green Deployment
- 세션 & 트랜잭션 유지 필수 → Redis 기반 세션 저장 & Idempotency Key 활용
- 배포 중 결제 요청 손실 방지 → Kafka/SQS 사용하여 트랜잭션 상태 저장
- 배포 중 연결 보호 → Connection Draining + Graceful Shutdown 적용
- 실제 서비스에서는 여러 전략을 조합하여 안정성을 높임!
반응형