포스트

멀티 인스턴스 환경에서 채팅 메시지가 유실되는 문제 해결

멀티 인스턴스 환경에서 채팅 메시지가 유실되는 문제 해결

문제 발견

카카오테크캠퍼스 최종 프로젝트에서 팀 채팅 기능을 WebSocket으로 구현했다. 로컬에서 테스트할 때는 아무 문제가 없었다.

그런데 AWS ECS에 배포하니까 오토스케일링으로 인스턴스가 2개 이상이 되면서 문제가 발생했다.

  • A 사용자가 보낸 메시지가 B 사용자에게 도착하지 않았다
  • 메시지 일부가 누락되는 경우가 있었다

원인 분석

WebSocket 연결은 특정 인스턴스와 맺어진다.

1
2
Client A가 Instance 1에 연결
Client B가 Instance 2에 연결

Client A가 메시지를 보내면 Instance 1에서 처리되는데, Client B는 Instance 2에 연결되어 있다. Instance 1 입장에서는 Client B가 어디 있는지 알 수가 없다.

unnamed.jpg

해결책 선택

인스턴스 간에 메시지를 동기화해야 했다. 여러 방법을 검토했다.

방법장점단점
DB 폴링구현이 간단하다실시간성이 떨어지고 DB에 부하가 간다
Kafka메시지 영속성이 있다이 케이스에선 도입 비용이 크다
Redis Pub/Sub실시간이고 간단하다메시지 영속성이 없다

채팅 메시지는 DB에도 별도로 기록하고 있어서 Pub/Sub의 메시지 영속성 문제는 영향이 제한적이었다. Redis Pub/Sub를 선택했다.

ADR 작성

이 결정을 팀에 공유하기 위해 ADR(Architecture Decision Record)을 작성했다.

핵심 결정 사항

  • 각 ECS 인스턴스는 Redis 채널을 구독한다
  • 메시지가 발생하면 채널에 publish한다
  • Redis는 메시지를 실시간으로 모든 구독 인스턴스에 전달한다
  • 세션은 각 인스턴스에서 관리하고, Redis는 오직 Pub/Sub 브로커 역할만 한다

리스크

  • Redis 장애가 나면 메시지 전달이 막혀서 채팅 기능 전체에 영향이 갈 수 있다
  • 트래픽이 급증하면 Redis Pub/Sub이 병목이 될 수 있다

unnamed (1).jpg

검증: JMeter 부하 테스트

구현만 해서는 안 되고, 실제 오토스케일링 상황에서 메시지가 유실되지 않는지 검증해야 했다.

ALB + Fargate + ECS 구조에서 JMeter로 동시에 100명이 채팅하는 상황을 시뮬레이션했다.

  1. 정상 상황: 인스턴스 2개로 고정했을 때, 테스트 시나리오에서 메시지 누락이 관측되지 않음을 확인
  2. 스케일 아웃: 트래픽 증가 후 인스턴스 3개로 확장했을 때, 신규 인스턴스 합류 뒤에도 메시지 수신이 이어지는지 확인
  3. 스케일 인: 트래픽 감소로 인스턴스가 줄어든 뒤에도, 남은 인스턴스에서 정상 수신 확인

정리

  • 단일 인스턴스에서 잘 되던 기능이, 멀티 인스턴스에서도 그대로 잘 된다는 보장은 없었다
  • 분산 환경 이슈는 재현이 어려워서, 스케일 아웃과 스케일 인까지 포함한 시나리오로 확인하는 과정이 필요했다
  • 선택 이유와 리스크는 말로만 공유하면 누락되기 쉬워서, ADR로 남겨두는 게 도움이 됐다

프로젝트: 카카오테크캠퍼스 3기 최종 프로젝트(대학생 맞춤형 일정 관리 서비스) 관련 링크

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.