๐ค ๊ฐ์
[Redis] Redis์ ๋ฐฑ์ ๊ณผ HA๋ฅผ ์ํ ์ ๋ต (RDB, AOF, Replication, Sentinel, Sharding, Cluster)
๐0. ์๋ก ํ ํ๋ก์ ํธ์์ Redis๋ฅผ ์ฌ์ฉํ๋ค๊ฐ, ์ ๊ธฐํ ํ์์ ๋ฐ๊ฒฌํ๋ค. ์ฐ๋ฆฌ ํ๋ก์ ํธ์์๋ ๋์ปค๋ฅผ ํตํด ๋ ๋์ค ์๋ฒ๋ฅผ ๋๋ฆฌ๊ณ ์์๊ณ , JWT Refresh Token์ ๋ ๋์ค ์๋ฒ์ ์ ์ฅํ๊ณ ์์๋๋ฐ ๋
hoons-dev.tistory.com
์ด์ ๊ธ์ ๋ณด๋ฉด, ๋ ๋์ค์ ๋ฐฑ์ ๊ณผ ๊ณ ๊ฐ์ฉ์ฑ์ ๋ํ ์์ธํ ์ด์ผ๊ธฐ๊ฐ ๋ด๊ฒจ์์ต๋๋ค.
Redis์ ๊ณ ๊ฐ์ฉ์ฑ์ ์ํด์ Cluster๋ฅผ ๊ตฌ์ฑํ ์ ์์ต๋๋ค. ๊ทธ ์ค์์๋ Docker Desktop for Mac์ ์ฌ์ฉํ๋ฉด์ Redis-Cluster๋ฅผ ๊ตฌ์ถํ ์ ์๋๋ก Docker-Compose ํ์ผ ๊ตฌ์ฑ๊ณผ SpringBoot Application ์ค์ ์ ๋ํด ์์๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
ํด๋ฌ์คํฐ์ ๋ํ ์์ธํ ๋ด์ฉ์ด ๊ถ๊ธํ๋ค๋ฉด, ์์ ์ด์ ๊ธ์ ์ฐธ์กฐํด์ฃผ์ธ์.
๐ Docker Compose ์ธํ
Docker์ ์ด๋ค ์ด๋ฏธ์ง๋ฅผ ์ฌ์ฉํ๋๋๊ฐ ๊ต์ฅํ ์ค์ํฉ๋๋ค.
์ผ๋ฐ์ ์ธ Redis ์ด๋ฏธ์ง๋ฅผ ์ฌ์ฉํ ์๋ ์๊ณ , bitnami์ Redis-Cluster ์ด๋ฏธ์ง๋ฅผ ์ฌ์ฉํ์ ๋ ์ข์ต๋๋ค.
ํ์ง๋ง ์ฌ๊ธฐ์์๋ Docker Desktop for Mac์ ์ฌ์ฉํ๋ฉฐ(M1 MacOS) Docker ํ๊ฒฝ์ ๊ตฌ์ฑํ๋ ค ํ๊ธฐ ๋๋ฌธ์ arm64v8/redis ์ด๋ฏธ์ง๋ฅผ ์ฌ์ฉํ์๋ ๊ฒ์ด ์ข์ต๋๋ค.
Linux ์๋ฒ ๋ฑ์์ Cluster๋ฅผ ๊ตฌ์ถ์ ์๋ํ๋ค๋ฉด, bitnami/redis-cluster๋ฅผ ๊ฒ์ํด ๊ตฌํํ๋ฉด ํธํ๊ฒ ๊ตฌ์ถํ ์ ์์๊ฒ๋๋ค.
Docker
hub.docker.com
โ 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
depends_on:
- redis-master-1
- redis-master-2
- redis-master-3
- redis-replica-1
- redis-replica-2
- redis-replica-3
๋ ๋์ค ํด๋ฌ์คํฐ์ ๊ตฌ์ฑ์ Master : Replica = 3 : 3์ ๋น์จ๋ก ๊ตฌ์ฑ๋์ด ์์ต๋๋ค.
์ด๋ฏธ์ง๋ ์ค๋ฆฌ์ฝ ๋งฅ ํ๋ซํผ์ ์ง์ํ๋ arm64v8/redis์ latest ๋ฒ์ ์ ์ฌ์ฉํ์ต๋๋ค.
network_mode๋ ๋ชจ๋ ๋ ธ๋๊ฐ redis-master-1์ผ๋ก ์ค์ ํจ์ผ๋ก์จ ๊ฐ์ ๋คํธ์ํฌ๋ฅผ ๊ณต์ ํ๋๋ก ๊ตฌ์ฑํ์ต๋๋ค.
๊ฐ ํด๋ฌ์คํฐ์ ๋ ธ๋๋ ๋ณผ๋ฅจ ๋ง์ดํธ๋ฅผ ํ๊ณ ์์ผ๋ฏ๋ก, docker-compose.yml๊ณผ ๊ฐ์ ๋๋ ํ ๋ฆฌ ์์น์
- redis-master-1.conf ~ redis-master-3.conf
- redis-replica-1.conf ~ redis-replica-3.conf
์ ์ค์ ํ์ผ์ด ์กด์ฌํด์ผ ํฉ๋๋ค. ํด๋น ์ค์ ํ์ผ ๋ด์์ ํฌํธ ๋ฒํธ์, ๋น๋ฐ๋ฒํธ, AOF ๋ฐฑ์ ๋ฐฉ์, enable cluster ์ค์ ์ ๋ง์น๊ณ ์ ์ฅํฉ๋๋ค.
defaultํ config ํ์ผ์ ์๋์ ๋งํฌ์์ Redis์ ๋ฒ์ ์ ๋ง๊ฒ ๋ค์ด๋ก๋ ๋ฐ์ ์ ์์ผ๋ฉฐ, ํด๋น ํ์ผ์์ ๊ฐ์ ์์ ํด ์ ์ฅํ๋ฉด ๋ฉ๋๋ค.
Redis์ ๋ฒ์ ์ด ๋ค๋ฅธ config ํ์ผ์ ๋ฐ์ ์ค์ ํ๋ฉด ์คํ ์ค ์ค๋ฅ๊ฐ ์๊ธธ ์ฌ์ง๊ฐ ์์ผ๋ฏ๋ก ๋ฐ๋์ ๋ง์ถฐ์ฃผ์๊ธฐ ๋ฐ๋๋๋ค.
Redis configuration
Overview of redis.conf, the Redis configuration file
redis.io
์๋๋ ํด๋ฌ์คํฐ ๋ ธ๋๋ก ๋ง๋ค๊ธฐ ์ํด config ํ์ผ์ ๋ํ์ ์ผ๋ก ์์ ํ ๋ถ๋ถ์ ๋๋ค.
# ํธ์คํธ ๋จธ์ ์์ ๋ ๋์ค๊ฐ ์ปค๋ฅ์
์ ๋ฐ์๋ค์ผ IP ์ค์
# bind 192.168.1.100 10.0.0.1 # listens on two specific IPv4 addresses
# bind 127.0.0.1 ::1 # listens on loopback IPv4 and IPv6
# bind * -::* # like the default, all available interfaces
# ์๋๋ ์์์
๋๋ค.
bind 127.0.0.1
# ๋ ๋์ค์ ํฌํธ ๋๋ฒ ์ค์
port 6379
# ๋ฐฑ์
๋ฐฉ์ AOF ๋ฐฉ์ ์ค์
appendonly yes
# 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
์์ ๊ฐ์ ์์ ์ ๊ฐ ๋ ธ๋ ๋ง๋ค ๋ ธ๋์ ๋ง๋ ๊ฐ์ผ๋ก (๋ง์คํฐ 3, ๋ ํ๋ฆฌ์นด 3 ๊ฐ ๋ ธ๋์ ๋ง๋ ํฌํธ ๋ฒํธ) conf ํ์ผ์ ๊ตฌ์ฑํ๋ฉด ๋ฉ๋๋ค.
์ต์ข ์ ์ผ๋ก redis-cluster-entry์์ ์๋์ ๊ฐ์ ๋ช ๋ น์ด๋ฅผ ํตํด ํด๋ฌ์คํฐ ๊ตฌ์ถ์ ์๋ฃํฉ๋๋ค.
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
๊ตฌ์ถ์ด ์๋ฃ๋ ํ, ์๋์ ๊ฐ์ด ๋์ปค ํ๋ก์ธ์ค๊ฐ ๊ตฌ๋๋๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค.
์๋๋, ๋ฐ์ดํฐ๊ฐ ์ ์ฝ์ ๋๋์ง ํ ์คํธ์ฉ์ผ๋ก, ๊ฐ๋จํ๊ฒ Medis GUI๋ฅผ ํตํด Connection์ ๋งบ์ต๋๋ค. ํ๋จ์ Cluster ๋ชจ๋๋ฅผ ์ค์ ํด์ MOVED ์๋ฌ๋ฅผ ํ์ฒ๋ฆฌ ํ ํ ๊ฒฐ๊ณผ ๊ฐ์ ๋ณด์ฌ์ฃผ๋๋ก ์ธํ ํฉ๋๋ค.
๊ฐ์ด ์ ๋์ค๋ ๊ฒ์ ํ์ธํ์ต๋๋ค.
๐ SpringBoot Application
์์ ๊ฐ์ด ๊ตฌ์ฑํ ๋ ๋์ค ํด๋ฌ์คํฐ์ ์คํ๋ง ๋ถํธ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฐ๊ฒฐํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
โ build.gradle
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
โ application.yml
spring:
data:
redis:
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
โ RedisConfig.java
@Configuration
public class NEORedisConfig {
private final String clusterNodes;
public NEORedisConfig(@Value("${spring.data.redis.cluster.nodes}") final String clusterNodes) {
this.clusterNodes = clusterNodes;
}
@Bean
public RedisTemplate<?, ?> redisTemplate() {
RedisTemplate<byte[], byte[]> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory());
return redisTemplate;
}
@Bean
public RedisConnectionFactory redisConnectionFactory() {
RedisClusterConfiguration clusterConfig = new RedisClusterConfiguration(Arrays.asList(clusterNodes.split(",")));
return new LettuceConnectionFactory(clusterConfig);
}
}
application.yml์์ ์ค์ ํ cluster์ ๋ ธ๋๋ค์ @Value ์ ๋ ธํ ์ด์ ์ ํตํด cluterNodes ํ๋์ ์ฝ์ ํฉ๋๋ค.
๊ทธ ํ, ๊ฐ ๋ ธ๋๋ค์ RedisClusterConfiguration ๊ฐ์ฒด๋ฅผ ์์ฑํด LettuceConnectionFactory์ ๋ฑ๋กํฉ๋๋ค.
์ด๊ฒ์ผ๋ก ๊ฐ๋จํ ์ฐ๊ฒฐ ์ค์ ์ ๋ง์น ์ ์์ต๋๋ค.