Project/대용량 트래픽 프로젝트

[프로젝트/구현] Redis Replication 마스터-슬레이브 구조를 통한 분산 처리 적용 과정

쉬지마 이굥진 2025. 5. 4. 01:25

이전 글

2025.05.01 - [Project/대용량 트래픽 프로젝트] - [기술적 의사결정] MSA 환경에서 배송 정보 임시 저장소로 Redis를 사용한 이유

 

[기술적 의사결정] MSA 환경에서 배송 정보 임시 저장소로 Redis를 사용한 이유

💡문제 상황우리 프로젝트에서는 주문을 생성할 때 배송 정보도 함께 입력받는 구조를 채택하고 있다. 현재 주문 처리 흐름은주문 ➡️ 재고 확인 ➡️ 결제 ➡️ 재고 차감 ➡️ 배송 ➡️ 주

developer-jinnie.tistory.com

 

이전 글에서는 배송 데이터 임시 저장소로 Redis를 선택해 사용하게 된 기술적 의사결정 과정을 기술해보았다. 이번 글에서는 해당 구조에서 오는 문제점을 인지하고 해결하는 과정을 공유해보려 한다.

 

💡문제 상황

현재 개발 중인 서비스에서는 배송 데이터를 임시로 저장하기 위해 Redis를 단일 인스턴스로 운용하고 있었다.  
하지만 Redis는 메모리 기반 저장소이기 때문에 장애 발생 시 데이터 유실의 위험이 존재하며, 단일 인스턴스에 모든 요청이 집중될 경우 성능 저하 및 서비스 가용성 문제가 발생할 수 있다. 


이를 방지하고자 Redis 서버를 복제 구조로 구성하여 읽기 부하를 분산하고, 장애 상황에도 데이터 손실을 최소화할 수 있는 구조를 마련할 필요가 있었다.

 

💡해결 과정

레디스 서버 장애를 대비하기 위해 Replication 방식을 통해 마스터-레플리카(slave) 구조를 구축해서 부하를 분산시키기로 했다.

  • 마스터: 데이터 쓰기 작업 담당
  • 레플리카(slave): 마스터로부터 데이터를 복제받아 읽기 작업을 담당

1. docker-compose 파일 작성

  redis_master:
    hostname: redis-master
    container_name: redis-master
    image: "bitnami/redis"
    environment:
      - REDIS_REPLICATION_MODE=master
      - ALLOW_EMPTY_PASSWORD=yes
    ports:
      - "6379:6379"
  redis_replica1:
    hostname: redis-replicas-1
    container_name: redis-replicas-1
    image: "bitnami:redis"
    environment:
      - REDIS_REPLICATION_MODE=slave
      - REDIS_MASTER_HOST=redis-master
      - ALLOW_EMPTY_PASSWORD=yes
    ports:
      - "5000:6379"
    depends_on: 
      - redis_master
  redis_replica2:
    hostname: redis-replicas-2
    container_name: redis-replicas-2
    image: "bitnami:redis"
    environment:
      - REDIS_REPLICATION_MODE=slave
      - REDIS_MASTER_HOST=redis-master
      - ALLOW_EMPTY_PASSWORD=yes
    ports:
      - "5001:6379"
    depends_on: 
      - redis_master
  • bitnami에서 배포하는 이미지들은 좀 더 쉽게 환경설정을 할 수 있도록 구성되어 있기 때문에 bitnami에서 이미지를 pull 해왔다.
  • redis.conf 파일을 직접 수정하지 않고도 replication 모드로 돌아가도록 설정하기 위해 environment 값에 replication mode를 주입한다.
  • master-slave가 연결될 수 있도록 EMPTY_PASSWORD 값을 허용해줘야 한다.
  • 마스터 레디스부터 먼저 실행될 수 있도록 depends_on 설정을 해준다.

도커 컴포즈 파일 작성 해주면 docker-compose up -d로 돌려주기!

docker-compose up -d

 

2. 테스트

도커 컴포즈 파일 실행 시켜줬다면 설정해 준 마스터-레플리카 레디스들이 잘 돌아가고 있는지 테스트를 통해 확인해주자.

docker ps

 

마스터 한 대, 레플리카 두 대가 잘 돌아가고 있음을 확인할 수 있다.

 

  • 읽기 테스트

  1. 두 개의 cmd 창을 켜놓고, 한 곳에선 마스터의 redis-cli, 한 곳에선 레플리카의 redis-cli로 들어가준다.
  2. 마스터에서 키를 설정해준다. (테스트에서는 aaa라고 설정)
  3. 레플리카에서 그 키를 get 하면, 마스터에서 설정한 값이 나온다.

 

  • 쓰기 테스트 - 레플리카에서 insert 한다면?

replica 옵션으로 master를 복제하고 있는 redis에서는 READ ONLY 옵션이 걸려있으므로 insert 할 수 없다는 에러가 뜬다. 즉 slave에 데이터를 추가하려고 하면 에러가 난다는 말! 알맞게 잘 떴다.

 

 

✔️마치며

이렇게 Redis replication을 실제로 구현해보고 테스트까지 진행해보았다. 이제 단일 인스턴스에 의존하던 구조에서 벗어나 읽기 부하를 분산하고, 장애 발생 시 복구 가능성을 확보함으로써 보다 견고한 서비스 환경을 구축했다. 특히 우리 서비스의 주문 과정에서 배송 데이터를 임시로 저장하는 Redis의 특성상 유실이 발생할 경우 사용자의 경험에 직접적인 영향을 미칠 수 있었기에, 사전에 이를 대비한 repication 구조 구현은 중요한 과제였다고 생각한다.


사실 Redis replication 구조에 대해 제대로 알아보기 전에는 master 서버가 종료되면 slave 서버가 자동으로 대체될 것으로 기대했으나, 실제로는 master 서버가 종료되고 레디스에 접근 시도 테스트를 해보니 500 에러가 발생했다. master 서버가 내려갔을 때 자동으로 slave 서버를 master 서버로 승격시키려면 Sentinel을 활용해야한다고 한다.

 

다음 글에서는 Sentinel이 정확히 뭔지, Sentinel을 직접 적용해서 Sentinel을 통해 master 레디스 장애 시에도 auto-failover가 가능하도록 하고 스프링으로 접근할 수 있도록 한 구현 과정을 공유해보겠다. (삽질 많이 함)

 

오늘도 땡큐포와칭!