Redis 의 클러스터 구성

상용환경에서 Redis 를 운영하게 된다면 당연히 standalone mode 로 운영할 수 없습니다.

Redis 는 Cluster 또는 Sentinel 이란 mode 를 제공하고 있으며 이는 고가용성을 보장합니다.

아래 포스팅은 Redis Cluster mode 를 설정하고 실행하는 내용을 남겨놓았습니다.

참고로 구글링하면 많은 분들이 Redis Cluster 와 Redis Sentinel 의 비교 글은 매우 많으니 꼭 비교 후 선택하시는 것을 추천합니다.



Redis Cluster 를 선택한 이유

단일 Redis Server 를 운영햐는 방식인 standalone mode 는 그 설정 및 운영 방법은 매우 단순하나 장애에 취약합니다. 이를 극복하기 위해선 Redis Server 를 노드 단위로 구성하고 관리할 수 있는 Redis Clsuter mode 를 검토하게 되었습니다.

Redis Clsuter mode 의 주요 특징은 아래와 같습니다.

  1. 각 노드는 특정 데이트 슬롯을 관리하므로 데이터는 여러 Redis 노드에 분산 저장한다.

  2. 자동 Failover 기능을 통해 일부 노드에 장애가 발생해도 클러스터는 지속적으로 동작할 수 있다. 단 장애가 발생한 노드의 복구는 Slave 노드가 구성되어 있을 때만 가능하다.

  3. Redis 노드를 Master와 Slave로 구성하여 Master 노드의 데이터를 실시간으로 복제(Replication)함으로써 Master 노드에 장애가 발생했을 때 Slave 노드를 통해 데이터를 복구할 수 있다. (정확히는 데이터 복구라기 보단 Slave 노드가 가 Master 노드로 승격되어 대체된다.)

처음엔 Redis 서버 구성에 많은 시간을 투자할 수 없는 상황이다보니 단순히 Master-Slave 구조만 도입하여 최소한의 Failover 만 고려하고 싶었습니다. 하지만 좀더 안정적인 Redis 서버 운영, 차후 확장성 및 Redlock 이란 분산 락 기능을 사용하기 위해 Redis Cluster mode 를 Master-Slave 구조와 함께 도입하게 되었습니다.

(Redlock 관련 내용은 차후 별도 포스팅으로 작성하도록 하겠습니다.)



Redis Cluster 구성 계획

Redis Cluster 를 구성하기 위해선 우선 Master 노드와 Slaver 노드 운영할 수 있는 Redis Server 여러대를 확보하여야 합니다. 이때 반드시 고려하여야할 사항은 Master 노드의 수량은 Slave 노드의 수량보다 반드시 크거나 같아야 한다는 점 입니다.

예를 들어 가용한 서버가 10대가 있다면 5대를 Master 노드로 두고 5대를 Slave 노드로 둔다면 1:1 구조의 Replication 이 가능해집니다. 또다른 예로 가용한 서버 10대 중 3대를 Master 노드로 두고 7대를 Slave 노드로 둔다면 이또한 1대의 1:1 또는 1:2 구조의 Replication 이 가능해집니다.

하지만 Master 노드가 Slave 노드보다 더 많다면 Replication 이 이루지지 않는 Master 노드가 발생할 수 있다는 점에 유의하여야 합니다.

저의 경우 총 6대의 가용한 서버에 1개씩 Redis 노드를 구성할 수 있는 상황이였습니다. 저는 6대의 Redis 노드를 1:1 구조로 Replication 이 가능하도록 3대는 Master 노드, 3대는 Slaver 노드로 구성해보겠습니다.

(보통 3대 이상의 홀수 Master 노드 구성을 권장합니다.)



Note: Redis 클러스터 구성 시 권장 Master 노드 수량은 홀수 입니다. Redis 클러스터는 분산 환경에서의 데이터 일관성과 장애 조치를 보장하기 위해 다수결(Quorum) 원칙을 사용하기 때문입니다. 클러스터는 장애 조치를 결정할 때 전체 노드 중 과반수의 동의를 필요로 합니다. 예를 들어 5대의 Master 노드가 있을 경우 클러스터는 3대 이상의 Master 노드의 동의를 필요로 합니다. 이는 클러스터가 장애 발생 시 더 신뢰할 수 있는 결정을 내리는 데 도움을 줍니다.




Redis Cluster 구성 노드의 실행

Redis Cluster 노드 구성 계획을 완료하였다면 다음으로 redis.conf 파일 커스텀 통한 세부 설정 과정 및 Redis Cluster 구성 노드들의 사전 실행이 필요합니다. 우선 redis.conf 파일을 준비해보겠습니다.

redis.conf 에서 클러스터를 생성하기 위해 제가 커스텀한 설정은 daemonize, port, cluster-enabled, cluster-node-timeout, cluster-replica-no-failover, maxmemory, dir 입니다. 저는 7가지 설정을 아래와 같이 path/to/redis.conf 파일 상에서 편집하여 모든 Redis 노드가 동일한 설정으로 부여하였습니다. (참고로 정상적인 Replication 을 위해선 maxmemory 옵션은 꼭 동일하여야 합니다.)

(위 설정 외에도 redis.conf 파일의 다양한 옵션이 존재하니 꼭 학습해보셨으면 합니다. 위 설정관련 상세 설명은 이전 포스팅을 참고 부탁드립니다.)

# By default Redis does not run as a daemon. Use 'yes' if you need it.
# Note that Redis will write a pid file in /var/run/redis.pid when daemonized.
daemonize yes

(...생략...)

# 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 6379

(...생략...)

# 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

(...생략...)

# 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 multiple of the node timeout.
#
cluster-node-timeout 5000

(...생략...)

# This option, when set to yes, prevents replicas from trying to failover its
# master during master failures. However the replica can still perform a
# manual failover, if forced to do so.
#
# This is useful in different scenarios, especially in the case of multiple
# data center operations, where we want one side to never be promoted if not
# in the case of a total DC failure.
#
cluster-replica-no-failover no

(...생략...)

# Set a memory usage limit to the specified amount of bytes.
# When the memory limit is reached Redis will try to remove keys
# according to the eviction policy selected (see maxmemory-policy).
#
# If Redis can't remove keys according to the policy, or if the policy is
# set to 'noeviction', Redis will start to reply with errors to commands
# that would use more memory, like SET, LPUSH, and so on, and will continue
# to reply to read-only commands like GET.
#
# This option is usually useful when using Redis as an LRU or LFU cache, or to
# set a hard memory limit for an instance (using the 'noeviction' policy).
#
# WARNING: If you have replicas attached to an instance with maxmemory on,
# the size of the output buffers needed to feed the replicas are subtracted
# from the used memory count, so that network problems / resyncs will
# not trigger a loop where keys are evicted, and in turn the output
# buffer of replicas is full with DELs of keys evicted triggering the deletion
# of more keys, and so forth until the database is completely emptied.
#
# In short... if you have replicas attached it is suggested that you set a lower
# limit for maxmemory so that there is some free RAM on the system for replica
# output buffers (but this is not needed if the policy is 'noeviction').
#
maxmemory 512mb

(...생략...)

# The working directory.
#
# The DB will be written inside this directory, with the filename specified
# above using the 'dbfilename' configuration directive.
#
# The Append Only File will also be created inside this directory.
#
# Note that you must specify a directory here, not a file name.
dir /path/to/


redis.conf 파일 커스텀이 완료되었다면 이제 클러스터 구성에 사용할 모든 Redis 노드들을 실행합니다.


$ /path/to/redis-server /path/to/redis.conf
( redis 실행 진행중... )


실행이 완료되었다면 redis-cliINFO 명령어를 활용해서 Redis 서버의 상태와 설정도 확인해보겠습니다. redis-server 는 Redis 서버와 실행을 위한 명령어라면 redis-cli 는 이미 실행중인 Redis 서버와 상호작용하는 클라이언트 프로그램입니다.


$ /path/to/redis-cli -p 6379 info
# Server
redis_version:5.0.9
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:44b36008b0c25568
redis_mode:cluster
(...생략...)


INFO 명령어 결과값이 정상적으로 노출되었고 원하는 설정이 잘 반영되었는지 확인합니다. 이제 Redis Cluster 를 구성할 총 6대의 노드가 준비되었습니다.



Redis Cluster 생성

다음으로 준비된 6대의 Redis 노드를 활용하여 6대의 노드를 Master 노드와 Slave 노드로 묶어 Redis Cluster 를 생성해보겠습니다. Cluster 의 생성은 redis-cli 통해 이루어 집니다.


$ /path/to/redis-cli --cluster create 10.0.1.1:6379 10.0.1.2:6379 10.0.1.3:6379 10.0.1.4:6379 10.0.1.5:6379 10.0.1.6:6379 --cluster-replicas 1


위 명령어에 대해 좀더 상세히 설명해보겠습니다.

-- cluster create 명령어는 redis-cli 통해 새로운 클러스터를 생성할 수 있는 명령어로 클러스터를 구성할 Redis 노드의 IP 주소와 포트번호를 함께 입력해줍니다.

--cluster-replicas 옵션은 각 Master 노드가 가질 Slave 노드의 수를 지정합니다. 이 옵션 값은 반드시 전체 노드수의 절반보다 크거나 같아야 정상적인 Replication 이 가능합니다.

위 명령어의 실행이 완료되었다면 클러스터가 성공적으로 생성되었는지 확인해봅니다.


$ /path/to/redis-cli -c -p 7000 cluster nodes
3b830fa49ce18dca453ba256de2d2069d11376fe 10.0.1.1:6379@16379 myself,master - 0 1723941736000 1 connected 0-5460
975fd47bb59d3c88595d44bad2449aca49289bda 10.0.1.2:6379@16379 slave 3ff4dadd2114b58fccc3c8ade5ee4226ec3ef71f 0 1723941737205 10 connected
3ff4dadd2114b58fccc3c8ade5ee4226ec3ef71f 10.0.1.3:6379@16379 master - 0 1723941736000 10 connected 10923-16383
b7d46351d9ab76fd60824527bee47155e574c061 10.0.1.4:6379@16379 slave 497b95907b73cc3c9eb6cd41442f1b25725a1a95 0 1723941736603 8 connected
497b95907b73cc3c9eb6cd41442f1b25725a1a95 10.0.1.5:6379@16379 master - 0 1723941736000 8 connected 5461-10922
11a6e4d7af788676592fdfcef1ae8a18c3d44645 10.0.1.6:6379@16379 slave 3b830fa49ce18dca453ba256de2d2069d11376fe 0 1723941736204 5 connected


위와같이 명렁어의 결과값이 나왔습니다. 노출된 명령어 결과값이 잘 이해되진 않지만 자세히 보면 총 6대의 서버가 master, slave 로 지정된게 보이실 겁니다. 그리고 nodes.conf 란 파일이 dir 옵션 경로에도 생기셨을 겁니다. 위 명령어와 유사한 내용을 담고 있는 이 파일에 대해 분석해보겠습니다.


node.conf 파일의 분석

node.conf 파일은 클러스터의 중요 정보를 저장하여 Redis 서버가 재시작할 때 클러스터 구성을 복원하는데 사용됩니다. 만약 어떠한 장애 발생으로 Redis 서버가 종료되었다면 재기동 시 node.conf 파일을 참고하여 자동으로 클러스터 기존 구성을 확인하고 클러스터에 재참여 하게 됩니다.

아래와 같이 node.conf 파일의 결과값을 좀더 디테일하게 분석해보겠습니다.


3b830fa49ce18dca453ba256de2d2069d11376fe 10.0.1.1:6379@16379 myself,master - 0 1723941736000 1 connected 0-5460


3b830fa49ce18dca453ba256de2d2069d11376fe 노드 ID: 각 노드에 할당된 고유한 식별자(ID)입니다. 이 ID는 클러스터 내에서 노드를 식별하는 데 사용됩니다.

10.0.1.1:6379@16379 노드 간 연결 정보: 노드의 IP 주소(10.0.1.1)와 Redis 서버 포트(6379), 그리고 클러스터 간 통신을 위한 포트(@16379)입니다. 이 정보는 노드 간 연결을 설정하는 데 사용됩니다.

myself,master 노드의 역할: 해당 노드의 역할을 나타냅니다. master는 이 노드가 마스터 노드임을 나타내고 myself는 이 출력이 현재 redis-cli 명령을 실행한 노드에 해당한다는 의미입니다.

- Master 노드의 노드 ID가 나옵니다. 여기서는 -로 표시되어 이 노드가 Master 노드임을 나타냅니다.

0 마지막 핑 응답 시각: 마지막으로 클러스터의 다른 노드로부터 PING 메시지를 받은 시각의 타임스탬프입니다. 이 값이 0으로 표시된 것은 다른 노드로부터 PING 메시지를 아직 받지 않았거나, 처음 클러스터를 구성한 직후일 수 있습니다.

1723941736000 이 노드의 상태가 마지막으로 업데이트된 시각의 타임스탬프입니다. 타임스탬프는 밀리초 단위의 Unix 시간입니다.

1 노드의 내부 순서 번호: 노드의 내부적인 클러스터에서의 순서 번호(ID)입니다.

connected 노드의 상태: 이 노드의 현재 상태입니다. connected는 노드가 현재 클러스터에 정상적으로 연결되어 있음을 의미합니다.

0-5460 슬롯 할당: 이 노드가 담당하는 슬롯 범위를 나타냅니다. 여기서는 0부터 5460번까지의 해시 슬롯을 이 노드가 관리하고 있습니다.

위 설정을 참고하여 클러스터에 참여한 Redis 노드의 상태와 누가 Master 노드이고 누가 Slave 노드이며 Slave 는 어떠한 Master 노드를 Replication 하였는지도 알 수 있습니다.

위 결과값을 기준으로 좀더 보기 쉽게 클러스터에 참여한 Redis 노드간 관계를 테이블로 정리해보겠습니다.


노드 ID 노드 연결 정보 노드의 역할 Replication 노드 ID 상태
…1376fe 10.0.1.1:6379@16379 master - Connected
…89bda 10.0.1.2:6379@16379 slave …ef71f Connected
…ef71f 10.0.1.3:6379@16379 master - Connected
…74c061 10.0.1.4:6379@16379 slaver …5a1a95 Connected
…5a1a95 10.0.1.5:6379@16379 master - Connected
…d44645 10.0.1.6:6379@16379 slaver …1376fe Connected



Redis Cluster 의 Failover 처리

마지막으로 Redis Cluster 의 Failover 처리 과정을 직접 redis-cliINFO 명령어를 통해 살펴보겠습니다.

이전 INFO 명령어 결과값을 기준으로 현재 Redis Cluster 간 node 의 관계를 확인해보았는데요, 여기서 저는 노드 ID 가 ...1376fe 인 Master 노드의 Redis Server 를 종료해보고 node 의 관계가 어떻게 바뀌는지 보겠습니다.

다음 명령어를 사용하여 노드ID가 ...1376fe Master 노드의 Redis Server 를 종료합니다.


$ /path/to/redis-cli -p 6379 shutdown


다음으로 종료되지않은 임의의 노드에서 redis-cliINFO 명령어를 수행해보면 아래와 같은 결과값이 노출됩니다.


3b830fa49ce18dca453ba256de2d2069d11376fe 10.0.20.181:6379@16379 master,fail - 1723945502704 1723945501099 1 disconnected
975fd47bb59d3c88595d44bad2449aca49289bda 10.0.20.163:6379@16379 slave 3ff4dadd2114b58fccc3c8ade5ee4226ec3ef71f 0 1723945541235 10 connected
3ff4dadd2114b58fccc3c8ade5ee4226ec3ef71f 10.0.20.162:6379@16379 master - 0 1723945542000 10 connected 10923-16383
b7d46351d9ab76fd60824527bee47155e574c061 10.0.20.182:6379@16379 slave 497b95907b73cc3c9eb6cd41442f1b25725a1a95 0 1723945542234 8 connected
497b95907b73cc3c9eb6cd41442f1b25725a1a95 10.0.20.165:6379@16379 myself,master - 0 1723945541000 8 connected 5461-10922
11a6e4d7af788676592fdfcef1ae8a18c3d44645 10.0.20.164:6379@16379 master - 0 1723945543240 11 connected 0-5460


이를 다시 Redis 노드간 관계 테이블로 정리해보면 아래와 같습니다.


노드 ID 노드 연결 정보 노드의 역할 Replication 노드 ID 상태
…1376fe 10.0.1.1:6379@16379 Master, fail - Disconnected
…89bda 10.0.1.2:6379@16379 slave …ef71f Connected
…ef71f 10.0.1.3:6379@16379 master - Connected
…74c061 10.0.1.4:6379@16379 slaver …5a1a95 Connected
…5a1a95 10.0.1.5:6379@16379 master - Connected
…d44645 10.0.1.6:6379@16379 master - Connected


이를 통해 노드 ID가 ...1376fe 인 Master 노드는 상태가 Disconnected 로 변경되었고 이 노드를 대체하여 Slave 노드였던 노드 ID d44645 가 master 노드로 승격되었음을 확인할 수 있습니다. 만약 이 상태에서 Slave 노드가 없는노드 ID d44645 가 종료된다면 데이터가 영구히 유실될 수 있습니다.

이제 다시 노드 ID ...1376fe 를 재기동 해보겠습니다.


$ path/to/redis-server /path/to/redis.conf
( redis 실행 진행중... )


재기동 후 다시 redis-cliINFO 명령어를 수행해보면 아래와 같은 결과값이 노출됩니다.


3b830fa49ce18dca453ba256de2d2069d11376fe 10.0.20.181:6379@16379 myself,slave 11a6e4d7af788676592fdfcef1ae8a18c3d44645 0 1723945908000 1 connected
975fd47bb59d3c88595d44bad2449aca49289bda 10.0.20.163:6379@16379 slave 3ff4dadd2114b58fccc3c8ade5ee4226ec3ef71f 0 1723945910000 10 connected
3ff4dadd2114b58fccc3c8ade5ee4226ec3ef71f 10.0.20.162:6379@16379 master - 0 1723945910000 10 connected 10923-16383
b7d46351d9ab76fd60824527bee47155e574c061 10.0.20.182:6379@16379 slave 497b95907b73cc3c9eb6cd41442f1b25725a1a95 0 1723945910608 8 connected
497b95907b73cc3c9eb6cd41442f1b25725a1a95 10.0.20.165:6379@16379 master - 0 1723945911609 8 connected 5461-10922
11a6e4d7af788676592fdfcef1ae8a18c3d44645 10.0.20.164:6379@16379 master - 0 1723945911000 11 connected 0-5460


이를 다시 Redis 노드간 관계 테이블로 정리해보면 아래와 같습니다.


노드 ID 노드 연결 정보 노드의 역할 Replication 노드 ID 상태
…1376fe 10.0.1.1:6379@16379 slave …d44645 Connected
…89bda 10.0.1.2:6379@16379 slave …ef71f Connected
…ef71f 10.0.1.3:6379@16379 master - Connected
…74c061 10.0.1.4:6379@16379 slaver …5a1a95 Connected
…5a1a95 10.0.1.5:6379@16379 master - Connected
…d44645 10.0.1.6:6379@16379 master - Connected


최초 Master 노드였던 노드 ID 1376fe 는 재기동 과정을 통해 Salve 노드가 되었고 Slave 노드였던 ...d44645 는 Master 노드를 대체하여 Slave 노드에서 Master 노드로 변경되었음을 확인할 수 있습니다.

이를 통해 Redis Cluster 의 Failover 가 매우 정상적으로 잘 이루어지고 있음을 확인할 수 있습니다.



맺음말

지금까지 Redis 클러스터 구성 및 Failover 실습까지 진행해보았습니다. 생각보다도 매우 손쉽게 클러스터 구성도 가능하고 Failover 개념도 매우 이해하기 쉬워 충분히 누구나 이해하실 수 있으리라고 생각됩니다.

이후 포스팅에서는 실제 Java 소스에서 Redis 클러스터 구성에 접근하여 캐싱 데이터 저장 및 Redlock 기능을 활용한 분산락을 적용하는 방법까지 진행해보겠습니다.