Trouble Shooting && 성능 개선

[Trouble Shooting] Redis Master-Replica 구조에서의 권한 문제

신민석 2025. 3. 10. 22:55

🎯 문제 상황


운영 서버에서 갑자기 다음과 같은 에러가 발생했습니다. (로그 모니터링은 AWS Cloud Watch 를 사용하고 있습니다.)

 

에러 메시지를 자세히 확인하면, "io.lettuce.core.RedisReadOnlyException: READONLY You can't write against a read only replica" 라는 문구를 볼 수 있는데, 이는 Master - Replica 구조에서 Replica Redis 서버에 새로운 데이터를 삽입할때 발생하는 버그 입니다.

 

그렇다면 먼저 마스터(Master) - 레플리카(Replica) 구조에 대해 알아야 합니다.

 

🎯 Master - Replica 구조란?


Master-Replica 구조는 데이터의 고가용성과 성능 향상을 위해 설계된 복제 시스템 입니다. 이를 통해 Redis 는 여러 서버에서 데이터를 분산하고, 읽기/쓰기 작업을 최적화하며 장애를 대비할 수 있습니다.

 

Master : 데이터를 읽고 쓰는 주 서버 입니다. 클라이언트는 Redis 에 데이터를 저장하거나 수정할 때 Master 서버에 요청을 보냅니다.

Replica : 읽기 전용 복제본으로 , Master 서버의 데이터를 복제하고 읽기 작업을 처리 합니다. 쓰기 작업은 처리하지 않습니다. 

 

Master-Replica 관계에서 Replica 서버는 Master 서버의 데이터를 실시간으로 복제합니다. 이로 인해 여러 Replica 서버가 있을 경우 읽기 부하를 분산시킬 수 있습니다.

 

이러한 Master - Replica 구조를 사용하는 이유를 정리하면 다음과 같습니다.

 

1. 성능 향상 : 읽기 부하를 분산해 Master 서버에 대한 부하를 줄이고, 시스템 성능을 향상 시킬 수 있습니다. 예를 들어 웹 애플리케이션에서 많은 읽기 작업이 발생하는 경우 Master 서버는 여전히 쓰기 작업을 처리하면서, 읽기 요청은 Replica 서버에서 처리하도록 분산할 수 있습니다.

 

2.데이터 안전성 : Master 서버가 장애를 일으켜도 데이터를 안전하게 보존시킬 수 있습니다. Replica 서버는 Master 서버의 모든 데이터를 복제하므로, 장애가 발생하더라고 Replica 서버를 새로운 MAster 로 승격시켜 데이터를 손실 없이 복구할 수 있습니다.

 

정리해보면, Replica Redis 서버에 새로운 데이터를 저장할때 발생한 문제 입니다. 저 같은 경우 집 게시물 정보를 캐싱 처리하고 있는데, 새로운 집 게시물을 등록할때 데이터를 삽입하고 이때 데이터를 삽입하는 서버가 Replica Redis 서버였기 때문에 발생한 문제였습니다.

🎯 해결 방법


다시 문제로 돌아와서, Master-Replica 구조의 Redis 에서 Replica Redis 서버에 새로운 데이터를 삽입할때 위와 같은 버그가 발생합니다. 로컬 환경에서 운영했을때는 단일 Redis 로 구현했고 배포할때도 별도의 Master-Replica 구조 를 설정하지 않았습니다. 그런데 왜 이와 같은 문제가 발생했을까 ?

 

정확하지는 않은데, Redis 설정 파일에서 replicaof 또는 slaveof 라는 명령어가 활성화 되어 있을 수 있기 때문인 것 같습니다. (아직 까지 정확한 원인은 찾지 못함)

 

Ec2 에 배포된 Redis 관련 설정을 확인해보면  role:slave 로 되어 있고 심지어 master_link_status:down 임을 확인할 수 있습니다. 이는 Redis 가 Replica(읽기 모드) 이고 연결된 Master 노드가 없음을 의미합니다. 즉, 읽기 전용 Redis 노드 가 문제임을 알 수 있었습니다.

 

 

 

[방법 1 실패 ] - Replica -> master 승격방법


현재 연결된 Redis 노드를 Master 로 승격하는 방법을 찾았습니다. 아래 명령어를 입력하면 Redis 노드는 Master 로 승격하게 됩니다.

 

 

✅ Master 승격 명령어

redis-cli
> slaveof no one

 

다시 확인해보면 ? role 이 master 로 변경됐음을 확인할 수 있습니다.

 

문제가 해결된 줄 알았으나 .. 다시 한번 똑같은 장애가 발생했습니다. 다시 자세히 알아보니 Replica로 다시 돌아가는 이유는 Redis의 설정이나 컨테이너의 재시작으로 인해 원래 상태가 복원될 수 있다고 합니다. 그래서 Redis 설정 파일인 redis.conf 에서의 설정도 변경했었는데 redis.conf 도 같이 원래 상태로 복원되어서 이 또한 근본적인 해결책이 되지 못했습니다.

 

 

[방법 2 성공 ] - Master-Slave 구조 설계


저는 서버를 배포할때 docker-compose 를 이용해 Redis 랑 애플리케이션 서버를 동시에 띄웠습니다. 물론 Master-Replica 구조를 설정하지 않았음에도 Master-Replica 구조로 배포되고 있었고 심지어 Master 가 없는 Replica 로 배포되고 있었습니다. 그래서 docker-compose 를 이용해서 Master-Replica 구조로 만들고, 애플리케이션에 연결된 Redis 를 Master 로 배포 했습니다.

 

https://tech-talks.tistory.com/entry/Redis-MasterSlave-%EA%B5%AC%EC%A1%B0%EC%99%80-Cluster-%EA%B5%AC%EC%A1%B0

 

✅ Master-Replica 구조 배포

networks:
  app-network:
    driver: bridge

services:
  redis-master:
    image: redis:7.0
    container_name: redis-master
    hostname: redis-master
    ports:
      - "6379:6379"
    networks:
      - app-network


  redis-slave-1:
    image: redis:7.0
    container_name: redis-slave-1
    hostname: redis-slave-1
    command: redis-server --replicaof redis-master 6379
    ports:
      - "6380:6379"
    depends_on:
      - redis-master
    networks:
      - app-network


  redis-slave-2:
    image: redis:7.0
    container_name: redis-slave-2
    hostname: redis-slave-2
    command: redis-server --replicaof redis-master 6379
    ports:
      - "6381:6379"
    depends_on:
      - redis-master
    networks:
      - app-network

  fl-api:
    image: minseok2000/cozy-api
    container_name: fl-api
    expose:
      - "8080"
    ports:
      - "8080:8080"
    env_file:
      - /home/ubuntu/.env
    environment:
      - REDIS_HOST=redis-master  # Redis Master 사용
    depends_on:
      - redis-master
    networks:
      - app-network

 

redis-master : 마스터 서버 입니다. 여기서 모든 쓰기 작업을 처리하고 클라이언트가 Redis 에 값을 쓸 때, 이 마스터 서버로 요청이 옵니다. redis-slave-1 과 redis-slave-2 는 이 마스터 서버를 복제합니다. 

 

redis-slave-1, redis-slave-2 : 슬레이브 서버 입니다. 마스터 서버의 데이터를 실시간으로 복제하고 읽기 작업을 처리 합니다. "redis-server --replicaof redis-master 6379" 명령어를 통해 마스터와 연결됩니다. 즉 이 명령어는 슬레이브 서버가 redis-master 를 복제하도록 설정합니다. 

 

fl-api 서버는 애플리케이션 서버 입니다. 원래는 애플리케이션 서버가 replica 서버였지만 master 서버로 연결되게끔 지정해줬습니다.

 

이후 docker-compose 를 실행해 서버를 구동시켜서 확인해보면 ?

 

role 이 master 임을 확인할 수 있고 연결된 slaves 또한 2개 임을 확인할 수 있습니다.

 

이로써 Master-Replica 구조로 Redis 를 재 배포해 문제를 해결할 수 있었습니다. 원래는 Redis 를 단일 구조로 배포하려 했으나.. 결과적으로는 캐싱 데이터를 읽고 쓸때 행위가 분산되어서 성능이 향상 될 수 있었습니다. 

 

 

[방법 3 성공 ] - 직접 Ec2 에 Redis 설치 


[방법 2] 에서 사용한 방법은 사용자가 많은 서비스에서 유리할 수 있으나, 현재 진행중인 프로젝트에서는 단일 Redis 노드로 충분히 운영이 가능하다 생각했습니다.

 

그래서 Redis 이미지를 사용하지 않고 직접 EC2 에 설치 하는 방법을 통해 배포 구조 자체를 변경해봤습니다. 작업 순서는 다음과 같습니다.

 

(1) EC2 에 Redis 설치

(2) Redis 외부 접속 허용

(3) EC2 인바운드 설정

 

✅ Redis 설치 

 

먼저 설치 방법 입니다. 아래 명령어를 입력해 설치를 진행해주세요.

sudo apt install redis-server -y

 

✅ conf 파일 수정

sudo nano /etc/redis/redis.conf

 

위 명령어를 입력해 conf 파일을 수정해야 합니다. 먼저 외부 접속에 대해 허용해야 합니다. 기본적으로 Redis 는 로컬호스트만 접근이 가능 하기 때문에 접근 IP 를 열어줘야 합니다.

bind 0.0.0.0

protected-mode no

 

bind 설정은 Redis 가 어떤 네트워크 인터페이스에서 요청을 받을지 결정하는 설정입니다. 일단은 테스트를 위해 0.0.0.0 으로 설정해 모든 요청에 열어뒀습니다. 

 

이는 해킹위험이 높으므로 테스트가 끝나면 본인 컴퓨터 IP 로 꼭! 수정해줘야 합니다. 

 

그리고, 기본적으로 Redis는 보호 모드가 활성화되어 있으며, 이 모드가 활성화되면 Redis는 외부 연결을 거부합니다. 즉, bind 0.0.0.0으로 모든 인터페이스에서 연결을 허용하더라도 보호 모드가 활성화되어 있으면 외부 연결을 받지 않습니다. 때문에 이 역시 테스트 환경에서만 no 로 설정한 뒤 실제 운영환경에서는 yes 로 변경한 뒤 Redis 에서 사용할 아이디와 비밀번호를 설정해야 합니다.

 

✅ EC2 인바운드 설정

 

마지막으로 EC2 인바운드 설정에서 Redis 포트를 열어줘야 합니다. 저는 6379 포트를 사용하고 있어 아래와 같이 설정했습니다. 해당 설정 역시 운영환경에서는 0.0.0.0 에서 본인의 IP 로 변경해야 합니다.

 

이제 마지막으로 Spring 설정에서 Redis Host 를 EC2 퍼블릭 IP 로 변경한 뒤 배포하면 다음과 같은 결과를 확인할 수 있습니다. 최종적으로 단일 Redis 노드가 정상적으로 배포 되었고 role 이 master 로 설정되면서, Relica 노드에 쓰기 작업을 하며 발생한 장애를 해결할 수 있었습니다.

 

 

 

이번 기회에 Master-Replica 구조에 대해 이해하고, 도커 이미지를 사용할때 내부 설정을 확인하며 안전하게 사용해야함을 경험했습니다.