gony-dev 님의 블로그

[Redis] Connection Mode-1 본문

DBMS

[Redis] Connection Mode-1

minarinamu 2024. 12. 6. 15:07

Redis는 통상적으로 4가지의 모드가 존재한다.
1. Redis Standalone(default)
2. Master/Slave
3. Redis Sentinel
4. Redis Cluster
각각의 모드는 뚜렷한 특징을 띄며, 모드를 조합하여 시스템의 요구사항을 만족시킬 수 있다.

1. Redis Standalone

  • 우리가 흔히 프로젝트를 할 때 사용하는 모드로, 하나의 redis 서버만을 다룬다.
@Bean
public LettuceConnectionFactory redisConnectionFactory() {
    RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();

    return new LettuceConnectionFactory(config);
}

2. Master/Slave

  • 서버에 안전하게 데이터를 저장하게 하기 위한 모드이다.
  • 읽기 성능을 향상시키며 redis 서버가 다운되더라도, 백업이 가능하다!
  • Master
    • 데이터를 읽고 쓰는 모든 요청을 처리하는 메인 서버이다.
    • 데이터 쓰기 작업 발생 시 데이터를 자체 업데이트하고, 그와 동시에 하나 이상의 Slave 서버에게 복제한다.
  • Slave
    • 읽기 작업을 처리하는 서버이다.
    • 복제 서버가 데이터를 제공하기에 Master에 가해지는 부하을 분산시킨다.
    • Master가 제공하는 데이터를 주기적으로 반영한다.
  • Replication
    • Master와 Slave 간의 데이터 복제 과정을 의미하며 비동기적으로 실시된다.
    • Master가 데이터를 쓰면 곧바로 Slave에 변경 사항이 전달된다.

application.yml

data:
  redis:
    master:
      host: localhost
      port: 6379
    slaves:
      - host: localhost
        port: 7000
      - host: localhost
        port: 7001

 

RedisProperty.class

@Getter
@NoArgsConstructor
@ConfigurationProperties(prefix = "spring.redis")
@Configuration
public class RedisProperty {

    private String host;
    private Integer port;
    private RedisProperty master;
    private List<RedisProperty> slaves;
}

 

RedisConfig.class

@Bean
public LettuceConnectionFactory redisConnectionFactory() {
    LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
            .readFrom(ReadFrom.REPLICA_PREFERRED)
            .build();

    RedisStaticMasterReplicaConfiguration slaveConfig = new RedisStaticMasterReplicaConfiguration(
            redisProperty.getMaster().getHost(), redisProperty.getMaster().getPort()
    );

    redisProperty.getSlaves().forEach(slave -> slaveConfig.addNode(slave.getHost(), slave.getPort()));

    return new LettuceConnectionFactory(slaveConfig, clientConfig);
}

 

Test

  • docker를 실행하여 master redis에서 key1에 대한 값을 써보고 읽어보았다.
    정상적으로 처리되는 것을 확인할 수 있다!
  • 마찬가지로 slave redis에서도 동일한 명령어를 사용해 보자!

  • 데이터를 넣을 수 없었다! 자연스러운 결과이다. slave redis는 오로지 데이터를 읽기 위함이므로 insert가 불가능하기 때문이다.

결론

  • 실습을 통해 docker에서 master와 slave redis를 만들어 테스트해보았다.
  • Redis Master-Slave 구조를 통해 Slave는 읽기 전용으로 Master의 부하를 줄이며,
    비동기적 작동 방식으로 Slave redis에 데이터가 복제되는 것을 확인할 수 있었다!
  • 하지만 만일 Master가 죽으면 어떻게 될까? 이를 해결하기 위해서는 앞으로 나올 모드들을 파헤쳐 보는 것이 중요하다!

3. Redis Sentinel

  • 고가용성 Redis를 처리하기 위한 모드이다.
  • 자동 장애 복구를 수행하며 마스터-슬레이브 방식의 단점을 해결할 수 있다!
    • 마스터-슬레이브 모드에서 마스터에 장애가 발생하면 슬레이브 중 하나를 승격하여 새로운 마스터로 지정한다.
    • Sentinel 모드는 어플리케이션이 새로운 마스터를 자동으로 인식할 수 있게 돕는 역할을 한다.
  • 이 부분은 도커를 이용하여 장애 조치가 원활히 이루어지는지 알아보겠다.

docker-compose.yml

version: '3.8'

services:
  redis-master:
    image: redis:latest
    container_name: "redis-master"
    ports:
      - "6379:6379"
    command: [ "redis-server" ]
    volumes:
      - master-data:/data
    networks:
      - redis-net

  redis-slave1:
    image: redis:latest
    command: redis-server --slaveof redis-master 6379
    links:
      - redis-master
    container_name: "redis-slave1"
    networks:
      - redis-net

  redis-slave2:
    image: redis:latest
    command: redis-server --slaveof redis-master 6379
    links:
      - redis-master
    container_name: "redis-slave2"
    networks:
      - redis-net

  sentinel-1:
    build: sentinel
    ports:
      - "5000:26379"
    env_file:
      - .env
    depends_on:
      - redis-master
      - redis-slave1
      - redis-slave2
    container_name: "sentinel1"
    networks:
      - redis-net

  sentinel-2:
    build: sentinel
    ports:
      - "5001:26379"
    env_file:
      - .env
    depends_on:
      - redis-master
      - redis-slave1
      - redis-slave2
    container_name: "sentinel2"
    networks:
      - redis-net

  sentinel-3:
    build: sentinel
    ports:
      - "5002:26379"
    env_file:
      - .env
    depends_on:
      - redis-master
      - redis-slave1
      - redis-slave2
    container_name: "sentinel3"
    networks:
      - redis-net

volumes:
  master-data:
  slave1-data:
  slave2-data:
networks:
  redis-net:
    driver: bridge

 

sentinel

  • 올바른 환경 변수와 권한, 그리고 sentinel 명령을 달아주기 위해 sentinel이라는 폴더를 생성하여 빌드한다.
  • 구조는 다음과 같다.
|
|_ _ _ Dockerfile
|_ _ _ sentinel.conf
|_ _ _ sentinel-entrypoint.sh

 

Dockerfile

  • sentinel.conf 파일을 추가하고, 각 디렉터리에 권한을 부여한다.
  • 최종적으로 sentinel-entirypoint.sh를 실행한다.
FROM redis:latest

EXPOSE 26379

ADD sentinel.conf /etc/redis/sentinel.conf

RUN mkdir -p /var/lib/redis /etc/redis && \
    chmod 777 /var/lib/redis /etc/redis && \
    chown redis:redis /etc/redis/sentinel.conf /var/lib/redis /etc/redis \

COPY sentinel-entrypoint.sh /usr/local/bin/

RUN chmod +x /usr/local/bin/sentinel-entrypoint.sh

ENTRYPOINT ["sentinel-entrypoint.sh"]

 

sentinel-entirypoint.sh

  • 해당 파일 내에서는 기존에 저장해놓은 환경변수를 sentinel.conf에다가 집어넣는다.
  • 그리고 /etc/redis에 대한 권한을 부여 후, 실행한다.
#!/bin/sh

# Replace environment variables in the configuration file
sed -i "s/\$SENTINEL_QUORUM/$SENTINEL_QUORUM/g" /etc/redis/sentinel.conf
sed -i "s/\$SENTINEL_DOWN_AFTER/$SENTINEL_DOWN_AFTER/g" /etc/redis/sentinel.conf
sed -i "s/\$SENTINEL_FAILOVER/$SENTINEL_FAILOVER/g" /etc/redis/sentinel.conf


chown -R redis:redis /etc/redis
# Run the Redis Sentinel with the updated configuration
exec docker-entrypoint.sh redis-server /etc/redis/sentinel.conf --sentinel

 

sentinel.conf

  • log가 보이는 정도를 debug로 설정하고, 각 sentinel에 대한 명령을 수행한다.
  • 가장 중요하다고 생각하는, 내가 그동안 되지 않았던 원인인.. 이 'sentinel resolve-hostnames yes'은 여러 서버가 도메인 이름을 통해 동적으로 IP를 할당받는 환경에서 유용하게 사용할 수 있다. 반드시 사용하기 바란다...
loglevel debug

port 26379
sentinel resolve-hostnames yes
sentinel monitor mymaster redis-master 6379 $SENTINEL_QUORUM
sentinel down-after-milliseconds mymaster $SENTINEL_DOWN_AFTER
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster $SENTINEL_FAILOVER
bind 0.0.0.0

 

그리고 docker-compose.yml를 실행하면 다음과 같이 컨테이너가 만들어진다.

redis-master의 정보를 보면, slave 노드가 2개 연결되어 있는 것을 확인할 수가 있다.

sentinel도 "info replication"을 사용하면 마스터 노드를 포함하여 다른 sentinel이 잘 등록되어 있는지 확인할 수 있다!

그렇게 만들어진 노드들로 장애 조치가 이루어지는지 확인하기 위해서는 redis-master의 연결을 끊어야 한다.

끊게 되면 sentinel들이 투표를 통해 다른 slave 노드를 마스터로 승격 시키게 된다.

마스터 노드를 slave1 주소인 172.19.0.3으로 변경하는 것을 볼 수 있다!
투표를 통해 승격한 redis-slave1 노드!!

결론

  • sentinel 모드를 이용하여 master/slave 모드에 대해 master 노드에 장애가 생겼을 시 다른 slave 노드를 sentinel들의 투표를 통해 승격시키는 과정을 실습해보았다!
  • 사실 실습하는 과정에서 환경변수에 대한 설정이 그닥 필요없었던 것 같았다.
    그리고 왜인지 모르게 Dockerfile의 sentinel의 쓰기 권한이 제대로 설정되지 않아 disk에 변경 사항이 반영이 되지 않아 일일이 권한을 설정해주어야했던 번거로움이 있었다.(만일 제가 잘못짰다면 알려주세요..ㅜㅠㅜ)
  • 이렇게 sentinel은 장애 조치가 가능하여 redis 내의 데이터가 사라지는 것을 백업을 통해 해결할 수 있다!
  • 하지만 sentinel + master/slave 간의 조합의 단점 또한 존재한다.
    1. Sentinel 자체가 다운되면 장애 복구가 이루어지지 않는다.
    2. 마스터와 슬레이브 간 동기화가 지연되면 읽기 작업 수행 시 최신 데이터 읽기를 수행할 수 없다.
    3. failover 발생 시 일시적 성능 저하가 일어날 수 있다.
    4. 모니터링 시스템이 없을 시 즉각적인 대처가 어렵다.
  • 이 단점들을 한 번에 잡아주는 Cluster를 다음 포스트에 작성하면서 알아보자!

출처

https://coding-review.tistory.com/533

'DBMS' 카테고리의 다른 글

[Redis] Connection Mode-2  (0) 2024.12.08
[Redis] Spring batch vs. Scheduler  (0) 2024.10.17
[Redis] Transaction  (0) 2024.10.15