캐시 모듈(Redis) 활용 가이드
레디스 클러스터란
Redis cluster는 데이터를 자동으로 여러 개의 Redis 노드에 나누어 저장할 수 있는 방법을 제공한다. 또한 일부 노드가 죽거나 통신이 되지 않을 때에도 작업을 계속할 수 있는 가용성을 제공한다
장점
샤딩(Sharding)
노드 간에 데이터 세트를 자동으로 분할할 수 있는 기능가용성
노드의 하위 집합에서 장애가 발생하거나 클러스터의 나머지 부분과 통신할 수 없을 때 작업을 계속할 수 있는 기능이다. 즉, 실제적인 측면에서 일부 노드가 실패하거나 통신할 수 없을 때도 정상적으로 작동한다. 그러나 더 큰 고장이 발생할 경우(예: 대부분의 마스터를 사용할 수 없는 경우) 클러스터 작동이 중지된다.
클러스터 구성도
3개 서버의 가상환경을 구축하고 아래와 같이 구조로 클러스터를 구성하겠습니다.
![42424-images/image-20231129160506.png 42424-images/image-20231129160506.png](images/image-20231129160506.png)
서버 1
에 문제가 생겨서 더 이상 통신이 되지 않는다면 자동으로 slave 노드 중 하나가 master가 되어 작업을 계속 할 수 있다.
특징
자동 분할: 클러스터에서는 데이터가 자동으로 분할되어 각 마스터 노드에 저장됩니다. 이는 키의 해시 값을 통해 결정됩니다.
데이터 접근: 클라이언트는 어느 노드에 접속하든 클러스터 전체의 데이터에 접근할 수 있습니다. 요청된 키가 해당 노드에 없는 경우, 클라이언트는 해당 키를 가진 노드로 리디렉션됩니다.
복제 및 고가용성: 각 마스터 노드는 하나 이상의 슬레이브 노드를 가질 수 있으며, 이들은 마스터 노드의 데이터를 복제합니다. 이를 통해 데이터의 안정성과 고가용성이 보장됩니다.
동기화: 마스터 노드와 슬레이브 노드 간에는 지속적인 데이터 동기화가 일어납니다. 이 과정을 통해 데이터의 일관성과 무결성이 보장됩니다.
자동 장애 전환: Redis 클러스터에서는 자동 장애 전환(Failover)이 가능합니다. 마스터 노드에 장애가 발생하면, 슬레이브 노드 중 하나가 자동으로 마스터 노드의 역할을 맡게 됩니다.
Docker 환경에서 Redis 클러스터 구축하기
docker-compose.yml 설정
version: '3' services: redis-master1: image: redis:6.0.9-alpine command: redis-server --port 3000 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes ports: - 3000:3000 - 13000:13000 volumes: - redis-master1-data:/data networks: redis-net: ipv4_address: 172.27.0.10 redis-master2: image: redis:6.0.9-alpine command: redis-server --port 3001 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes ports: - 3001:3001 - 13001:13001 volumes: - redis-master2-data:/data networks: redis-net: ipv4_address: 172.27.0.3 redis-master3: image: redis:6.0.9-alpine command: redis-server --port 3002 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes ports: - 3002:3002 - 13002:13002 volumes: - redis-master3-data:/data networks: redis-net: ipv4_address: 172.27.0.4 redis-slave1: image: redis:6.0.9-alpine command: redis-server --port 3003 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes ports: - 3003:3003 - 13003:13003 volumes: - redis-slave1-data:/data networks: redis-net: ipv4_address: 172.27.0.5 redis-slave2: image: redis:6.0.9-alpine command: redis-server --port 3004 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes ports: - 3004:3004 - 13004:13004 volumes: - redis-slave2-data:/data networks: redis-net: ipv4_address: 172.27.0.6 redis-slave3: image: redis:6.0.9-alpine command: redis-server --port 3005 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes ports: - 3005:3005 - 13005:13005 volumes: - redis-slave3-data:/data networks: redis-net: ipv4_address: 172.27.0.7 redis-cluster-init: image: redis:6.0.9-alpine command: > sh -c "sleep 10 && echo yes | redis-cli --cluster create 10.131.4.85:3000 10.131.4.85:3001 10.131.4.85:3002 10.131.4.85:3003 10.131.4.85:3004 10.131.4.85:3005 --cluster-replicas 1 --cluster-yes" depends_on: - redis-master1 - redis-master2 - redis-master3 - redis-slave1 - redis-slave2 - redis-slave3 networks: redis-net: driver: bridge ipam: config: - subnet: 172.27.0.0/16 volumes: redis-master1-data: redis-master2-data: redis-master3-data: redis-slave1-data: redis-slave2-data: redis-slave3-data:Redis 마스터 노드:
redis-master1
,redis-master2
,redis-master3
이라는 세 개의 마스터 노드가 있다. 각각 다른 포트(3000, 3001, 3002)와 볼륨을 사용한다.Redis 슬레이브 노드:
redis-slave1
,redis-slave2
,redis-slave3
이라는 세 개의 슬레이브 노드가 있으며, 이들도 각각 다른 포트(3003, 3004, 3005)와 볼륨을 사용한다.네트워킹: 모든 노드는
redis-net
이라는 동일한 네트워크에 연결되어 있으며, 각 노드에는 고정 IP 주소가 할당되어 있다.클러스터 초기화:
redis-cluster-init
서비스는 클러스터를 초기화하는 데 사용한다. 이 서비스는 모든 마스터와 슬레이브 노드가 시작된 후에 실행되며,redis-cli --cluster create
명령을 사용하여 클러스터를 구성한다.볼륨: 각 Redis 노드는 데이터를 저장하기 위해 독립적인 볼륨을 사용한다. 이를 통해 컨테이너가 재시작되어도 데이터가 유지된다.
브리지 네트워크: 사용자 정의 브리지 네트워크 (
redis-net
)가 설정되어 있으며, 이를 통해 컨테이너 간의 격리와 네트워킹이 관리된다.Redis 클러스터를 효율적으로 관리하고 운영할 수 있게 해주며, Docker Compose를 사용함으로써 여러 컨테이너를 쉽게 설정하고 관리할 수 있다.
docker-compose 명령어 입력
# 실행 docker-compose up # 백그라운드에서 실행 docker-compose up -d # 서비스 중지 docker-compose stop # 서비스 다운 docker-compose down # 실행중인 서비스 확인하기 docker-compose ps # 서비스 로그 확인하기 docker-compose logs실행 확인
3개의 master와 3개의 slave 가 실행 된 걸 확인 할 수 있다.
캐모마일 프로젝트 적용
dependency 등록(pom.xml)
<dependency> <groupId>net.lotte.chamomile.module</groupId> <artifactId>chamomile-cache-redis</artifactId> </dependency>yaml 파일 설정
chmm: cache: instance: redis #none/redis/hazelcast ## 아래 클러스터는 redis cluster 적용시에만 필요 mode: cluster redis-nodes: - 10.131.4.85:3002 - 10.131.4.85:3001 - 10.131.4.85:3000 redis-max-redirects: 6 host: 10.131.4.85클러스터 관련 설정은 클러스터 설정 진행시에만 작성한다.
mode : cluster -> 레디스 클러스터 진행시 적용 한다.
redis-nodes : 클러스터를 구성하는 Redis 노드 들의 주소와 포트 목록이다.
redis-max-redirects : 클라이언트가 올바른 노드에 도달할 때까지 리디렉션 되는 최대 횟수이다
host : Redis 클러스터에 연결하는 데 사용되는 public IP 주소 이다.
클러스터 관련 빈 커스터마이징 chamomile-cache-redis에서 클러스터로 redis connection factory빈을 생성하는 코드를 다음과 같이 선언하여 커스터마이징을 진행하면 된다.
ChamomileCacheRedisCustomConfiguration.java
public class ChamomileCacheRedisCustomConfiguration { @Bean @ConditionalOnProperty(prefix = "chmm.cache", name = "mode", havingValue = "cluster") public RedisConnectionFactory redisConnectionFactory() { // Redis 클러스터 구성을 설정합니다. RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration(chamomileCacheProperties.getRedisNodes()); redisClusterConfiguration.setMaxRedirects(chamomileCacheProperties.getRedisMaxRedirects()); // 클러스터의 노드의 상태 변경을 감지하고 처리하기 위한 리프레시 옵션을 설정합니다. // 이 옵션은 클러스터의 노드의 상태 변경되었을 때 자동으로 갱신되도록 합니다. ClusterTopologyRefreshOptions clusterTopologyRefreshOptions = ClusterTopologyRefreshOptions.builder() .enableAllAdaptiveRefreshTriggers() .enablePeriodicRefresh(Duration.ofHours(1L)) .build(); // 클라이언트 옵션을 설정합니다. 위에서 정의한 토폴로지 리프레시 옵션을 포함합니다. ClientOptions clientOptions = ClusterClientOptions.builder() .topologyRefreshOptions(clusterTopologyRefreshOptions) .build(); // 클러스터 컨테이너에 내부 DNS 설정을 무시하고, // 사용자 정의 매핑을 제공합니다. MappingSocketAddressResolver resolver = MappingSocketAddressResolver.create(DnsResolvers.UNRESOLVED, hostAndPort -> { return HostAndPort.of(chamomileCacheProperties.getHost(), hostAndPort.getPort()); }); // Lettuce 클라이언트에 사용될 리소스를 설정합니다. // 여기에는 위에서 정의한 주소 해석 로직(resolver)가 포함됩니다. ClientResources clientResources = ClientResources.builder() .socketAddressResolver(resolver) .build(); // Lettuce 클라이언트 구성을 설정합니다. // 이 구성에는 명령 타임아웃, 클라이언트 옵션, 클라이언트 리소스 및 읽기 선호도가 포함됩니다. LettuceClientConfiguration clientConfiguration = LettuceClientConfiguration.builder() .commandTimeout(Duration.of(10, ChronoUnit.SECONDS)) .clientOptions(clientOptions) .clientResources(clientResources) .readFrom(ReadFrom.REPLICA_PREFERRED) .build(); return new LettuceConnectionFactory(redisClusterConfiguration, clientConfiguration); } }