ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • MacOS 로컬 환경에서 Docker Redis Cluster 설정 및 Springboot와 연동하기
    Redis 2024. 8. 9. 12:22

     

     

    Docker Compose

    Docker Desktop for Mac을 사용하며(M3 MacOS)

    Docker 환경을 구성하려 하기 때문에 arm64v8/redis 이미지를 사용하였습니다.

     

    docker-compose.yml

    version: '3'
    
    services:
      redis-master-1:
        container_name: redis-master-1
        image: arm64v8/redis:latest
        restart: always
        volumes:
          - ./redis-master-1.conf:/etc/redis-master-1.conf
        command:
          redis-server /etc/redis-master-1.conf
        ports:
          - "7001:7001"
          - "7002:7002"
          - "7003:7003"
          - "7004:7004"
          - "7005:7005"
          - "7006:7006"
    
      redis-master-2:
        container_name: redis-master-2
        image: arm64v8/redis:latest
        network_mode: "service:redis-master-1"
        restart: always
        volumes:
          - ./redis-master-2.conf:/etc/redis-master-2.conf
        command:
          redis-server /etc/redis-master-2.conf
    
      redis-master-3:
        container_name: redis-master-3
        image: arm64v8/redis:latest
        network_mode: "service:redis-master-1"
        restart: always
        volumes:
          - ./redis-master-3.conf:/etc/redis-master-3.conf
        command:
          redis-server /etc/redis-master-3.conf
    
      redis-replica-1:
        container_name: redis-replica-1
        image: arm64v8/redis:latest
        network_mode: "service:redis-master-1"
        restart: always
        volumes:
          - ./redis-replica-1.conf:/etc/redis-replica-1.conf
        command:
          redis-server /etc/redis-replica-1.conf
    
      redis-replica-2:
        container_name: redis-replica-2
        image: arm64v8/redis:latest
        network_mode: "service:redis-master-1"
        restart: always
        volumes:
          - ./redis-replica-2.conf:/etc/redis-replica-2.conf
        command:
          redis-server /etc/redis-replica-2.conf
    
      redis-replica-3:
        container_name: redis-replica-3
        image: arm64v8/redis:latest
        network_mode: "service:redis-master-1"
        restart: always
        volumes:
          - ./redis-replica-3.conf:/etc/redis-replica-3.conf
        command:
          redis-server /etc/redis-replica-3.conf
    
      redis_cluster_entry:
        image: arm64v8/redis:latest
        network_mode: "service:redis-master-1"
        container_name: redis_cluster_entry
        command: redis-cli --cluster create 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 127.0.0.1:7006 --cluster-yes -a {설정한 비밀번호}
        depends_on:
          - redis-master-1
          - redis-master-2
          - redis-master-3
          - redis-replica-1
          - redis-replica-2
          - redis-replica-3

    위 docker-compose.yml의 내용을 요약해보자면 아래와 같습니다.

    • Redis 클러스터는 Master와 Replica의 비율이 3:3으로 구성되어 있습니다.
    • 이미지는 실리콘 맥 플랫폼을 지원하는 arm64v8/redis의 최신 버전을 사용하고 있습니다.
    • 모든 노드가 같은 네트워크를 공유하도록 network_mode를 redis-master-1로 설정했습니다.

     

     

     

    Redis Cluster Mode를 위한 config 파일 작성

    아래 링크에서 Redis 버전에 맞춰 기본 양식의 config 파일을 다운로드할 수 있습니다.

    해당 파일에서 필요한 값들을 수정해서 저장하면 됩니다.

     

    Redis configuration

    Overview of redis.conf, the Redis configuration file

    redis.io

     

    아래는 클러스터 모드를 구축하기 위해 추가한 config 파일 내용입니다.

    port 부분은 master와 replica 각 노드에 맞는 port 번호로 conf 파일을 구성하면 됩니다.

    비밀번호를 설정하려면 requirepass {비밀번호}를 추가해줍니다.

    # IF YOU ARE SURE YOU WANT YOUR INSTANCE TO LISTEN TO ALL THE INTERFACES
    # COMMENT OUT THE FOLLOWING LINE.
    #
    # You will also need to set a password unless you explicitly disable protected
    # mode.
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    bind 0.0.0.0
    
    
    # Accept connections on the specified port, default is 6379 (IANA #815344).
    # If port 0 is specified Redis will not listen on a TCP socket.
    port 7001 
    
    
    # AOF and RDB persistence can be enabled at the same time without problems.
    # If the AOF is enabled on startup Redis will load the AOF, that is the file
    # with the better durability guarantees.
    # 
    # Note that changing this value in a config file of an existing database and
    # restarting the server can lead to data loss. A conversion needs to be done
    # by setting it via CONFIG command on a live server first.
    # 
    # Please check https://redis.io/topics/persistence for more information.
    appendonly yes
    
    
    ################################ REDIS CLUSTER  ###############################
    
    # Normal Redis instances can't be part of a Redis Cluster; only nodes that are
    # started as cluster nodes can. In order to start a Redis instance as a
    # cluster node enable the cluster support uncommenting the following:
    #
    cluster-enabled yes
    
    # Every cluster node has a cluster configuration file. This file is not
    # intended to be edited by hand. It is created and updated by Redis nodes.
    # Every Redis Cluster node requires a different cluster configuration file.
    # Make sure that instances running in the same system do not have
    # overlapping cluster configuration file names.
    #
    cluster-config-file nodes.conf
    
    # Cluster node timeout is the amount of milliseconds a node must be unreachable
    # for it to be considered in failure state.
    # Most other internal time limits are a multiple of the node timeout.
    #
    cluster-node-timeout 3000

     

     

    이제 앞서 작성한 docker-compose를 실행하게 되면

    redis-cluster-entry에서 docker-compose.yml에 포함 되어 있는 아래와 같은 명령어를 통해 클러스터 구축하게 됩니다.

    redis-cli --cluster create 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 127.0.0.1:7006 --cluster-yes

     

    혹시 앞의 redis-master-1.conf ~ redis-replica-3.conf 설정 단계에서

    requirepass {비밀번호}와 같이 별도의 비밀번호를 설정하지 않았다면

    위 docker-compose.yml에서 -a 옵션은 제거하면 됩니다.

     

     

    docker-compose up -d 명령어로 컨테이너를 실행하면 아래와 같이 잘 작동하는 것을 확인할 수 있습니다.

     

     

     

    테스트

    이제 실제로 데이터가 잘 삽입되는지 테스트를 해보도록 하겠습니다.

    보편적인 터미널로 직접 명령어를 작성해서 테스트 하는 방법과

    Medis를 통해서 좀 더 편하게 테스트 하는 방법이 있는데,

    레디스 클러스터 모드가 처음이라 터미널에서 직접 해보니깐 좀 흥미로운 점이 있었어서 둘다 기록하려합니다.

     

     

    터미널

    흥미로웠던 부분은 큰 네모 박스 부분이였습니다.

    "Redirected to slot [12539] located at 127.0.0.1:7005"가 눈에 띄는데,

     

    Redis 클러스터는 16,384개의 해시 슬롯으로 데이터를 분할합니다.

    키가 특정 슬롯에 매핑되며 이 슬롯이 위치한 노드에서 명령어가 실행됩니다.

    만약 클라이언트가 다른 노드에 연결되어 있다면(7001)

    클러스터는 자동으로 올바른 노드로 리다이렉트하여 작업을 처리하게 되고,

    해당 리다이렉션 과정이 "Redirected to slot [12539] located at 127.0.0.1:7005" 메시지로 나타나게 된 것입니다.

     

     

    Medis

    Medis GUI는 사용이 비교적 간단하고 직관적이라서 Redis를 관리하는 데 유용한 도구로,

    위와 같이 터미널에서 복잡한 과정을 보다 쉽게 테스트 할 수 있습니다.

    앱스토어에서 다운이 가능합니다.

    우선 위와 같이 host와 port를 설정해주시고, Mode는 Cluster로 설정하고 저장해줍니다.

    그리고 Connect를 클릭해줍니다.

     

    Command Query를 클릭해서 레디스 명령어를 입력하여 데이터가 잘 삽입되고 가져와지는지 테스트해줍니다.

    잘 가져오는 것을 확인할 수 있었습니다.

     

     

     


     

     

    Spring Boot와 연동하기

    이제 위에서 구성한 레디스 클러스터를 스프링부트와 연결해보도록 하겠습니다.

     

    build.gradle

    implementation 'org.springframework.boot:spring-boot-starter-data-redis'

     

    application.yml

    spring:
      data:
        redis:
          password: {설정한 비밀번호}
          cluster:
            nodes:
              127.0.0.1:7001,
              127.0.0.1:7002,
              127.0.0.1:7003,
              127.0.0.1:7004,
              127.0.0.1:7005,
              127.0.0.1:7006

     

    RedisClusterConfig.java

    @Configuration
    @EnableCaching
    public class RedisClusterConfig {
    
        @Value("${spring.data.redis.cluster.nodes}")
        private List<String> clusterNodes;
    
        @Value("${spring.data.redis.password}")
        private String password;
    
        @Bean
        public RedisConnectionFactory redisConnectionFactory() {
            List<RedisNode> redisNodes = clusterNodes.stream()
                    .map(clusterNode -> new RedisNode(clusterNode.split(":")[0], Integer.parseInt(clusterNode.split(":")[1])))
                    .toList();
            RedisClusterConfiguration clusterConfiguration = new RedisClusterConfiguration();
            clusterConfiguration.setClusterNodes(redisNodes);
            clusterConfiguration.setPassword(password);
    
            // Socket 옵션
            SocketOptions socketOptions = SocketOptions.builder()
                    .connectTimeout(Duration.ofMillis(100L))
                    .keepAlive(true)
                    .build();
    
            // Cluster topology refresh 옵션
            ClusterTopologyRefreshOptions clusterTopologyRefreshOptions = ClusterTopologyRefreshOptions.builder()
                    .dynamicRefreshSources(true)
                    .enableAllAdaptiveRefreshTriggers()
                    .enablePeriodicRefresh(Duration.ofMinutes(30L))
                    .build();
    
            // Cluster Client 옵션
            ClientOptions clientOptions = ClusterClientOptions.builder()
                    .topologyRefreshOptions(clusterTopologyRefreshOptions)
                    .socketOptions(socketOptions)
                    .build();
    
            // Lettuce Client 옵션
            LettuceClientConfiguration clientConfiguration = LettuceClientConfiguration.builder()
                    .clientOptions(clientOptions)
                    .commandTimeout(Duration.ofMillis(3000L))
                    .build();
    
            return new LettuceConnectionFactory(clusterConfiguration, clientConfiguration);
        }
    
        @Bean
        public RedisTemplate<?, ?> redisTemplate() {
            RedisTemplate<byte[], byte[]> redisTemplate = new RedisTemplate<>();
            redisTemplate.setKeySerializer(new StringRedisSerializer());
            redisTemplate.setValueSerializer(new StringRedisSerializer());
            redisTemplate.setHashKeySerializer(new StringRedisSerializer());
            redisTemplate.setHashValueSerializer(new StringRedisSerializer());
            redisTemplate.setConnectionFactory(redisConnectionFactory());
            return redisTemplate;
        }
    
    }

    위에 작성한 코드는 Spring Boot 애플리케이션이 Redis 클러스터와 통신하기 위해 필요한 설정을 정의한 코드입니다.

    클러스터 노드 정보, 소켓 및 클라이언트 옵션, 클러스터 토폴로지 갱신 방법 등을 포함하였습니다.

    또한, 애플리케이션이 Redis와 데이터를 주고받을 때 사용할 RedisTemplate 빈도 정의하였습니다.

     

     

    보다 자세한 내용은 아래 블로그를 참고해주세요!

     

    Spring Boot에 Redis Cluster 적용기 (w. lettuce, redisson)

    레디스 그림자 분신술

    velog.io

     

     

     

    다음에는 AWS Elasticache를 통해 클러스터 모드 적용을 했던 내용을 기록하고자합니다.

     

     

     

Designed by Tistory.