본문 바로가기
J-H-T/Redis

[REDIS-7단계] Redis Cluster → Redis Cluster 동기화!

by METAVERSE STORY 2026. 8. 17.
반응형

 

 

이제 7단계 — Redis Cluster → Redis Cluster 동기화로 넘어가겠습니다.
이번 단계부터가 현재 실제 환경과 가장 직접적으로 연결되는 핵심 구간입니다.

기준 환경은 계속 동일하게 잡겠습니다.

온프레미스 K8s

Source Redis Cluster
3 Master + 3 Replica
        │
        │ RedisShake
        ▼
Target Redis Cluster
3 Master + 3 Replica

Redis Cluster는 데이터를 하나의 Master가 모두 들고 있는 구조가 아니라,
총 16,384개의 Hash Slot을 여러 Master가 나눠 담당합니다.
Redis 공식 문서도 Key를 CRC16(key) mod 16384 방식으로 Slot에 매핑한다고 설명합니다. Redis

 

🟥 7단계 — Redis Cluster → Redis Cluster 동기화

7.1 먼저 전체 구조

예를 들어 Source가:

Source Redis Cluster

Shard 1
Master 1
Slot 0 ~ 5460
   │
Replica 1

Shard 2
Master 2
Slot 5461 ~ 10922
   │
Replica 2

Shard 3
Master 3
Slot 10923 ~ 16383
   │
Replica 3

Target도:

Target Redis Cluster

Shard 1
Master 1
   │
Replica 1

Shard 2
Master 2
   │
Replica 2

Shard 3
Master 3
   │
Replica 3

라고 하겠습니다.

전체 Migration은:

             SOURCE

       M1      M2      M3
       │       │       │
       │       │       │
       └───┬───┴───┬───┘
           │
           ▼
     ┌──────────────┐
     │  RedisShake  │
     │     Pod      │
     └──────┬───────┘
            │
            ▼
       M1      M2      M3

             TARGET

입니다.


 

7.2 Standalone과 가장 큰 차이

Standalone에서는:

Source 1대
   │
   ▼
RedisShake
   │
   ▼
Target 1대

였습니다.

하지만 Cluster는:

Source

M1
M2
M3

모두 데이터가 다릅니다.

예:

M1
user:100
session:200

M2
product:100
cart:300

M3
order:500
payment:100

따라서 RedisShake가:

M1만 읽음

하면 전체 데이터의 일부만 Migration됩니다.


 

7.3 RedisShake는 Cluster를 어떻게 읽을까?

Source 설정을:

[sync_reader]
cluster = true
address = "source-redis:6379"

로 합니다.

cluster=true가 핵심입니다.

RedisShake는 초기 Cluster Node에 접속한 뒤 Cluster topology를 파악하고,
Source의 Shard 수만큼 Standalone Reader를 만들어 병렬로 읽습니다.
공식 아키텍처 문서도 Cluster Reader가 Source shard 수에 맞춰 Reader를 생성하고 각 shard에서 병렬로 데이터를 읽는다고 설명합니다. Tair Open Source

쉽게 보면:

                RedisShake

        ┌──────────┼──────────┐
        │          │          │
     Reader1    Reader2    Reader3
        │          │          │
        ▼          ▼          ▼

       M1         M2         M3

입니다.


7.4 Reader별로 Full + Incremental을 수행

각 Source Master마다:

M1
 │
 ├─ RDB Full
 └─ Incremental Stream
       │
       ▼
    Reader1
M2
 │
 ├─ RDB Full
 └─ Incremental Stream
       │
       ▼
    Reader2
M3
 │
 ├─ RDB Full
 └─ Incremental Stream
       │
       ▼
    Reader3

가 됩니다.

RedisShake의 sync_reader는 Replica처럼 Master에 연결해 Full RDB + Incremental AOF stream을 받아 처리합니다. Tair Open Source


 

7.5 중요한 점: Replica 데이터는 별도로 또 Migration하지 않는다

Source가:

M1 → R1
M2 → R2
M3 → R3

라고 하겠습니다.

Replica는 Master의 같은 데이터를 가지고 있습니다.

따라서 기본적으로:

M1 데이터
+
R1 데이터

둘 다 Target으로 복사

하는 게 아닙니다.

보통 Source Cluster의 Primary/Master shard 기준 데이터를 읽습니다.

M1 ─┐
M2 ─┼─→ RedisShake
M3 ─┘

Replica는 Source HA 용도입니다.


 

7.6 Target Cluster에서는 어떻게 저장될까?

RedisShake가 Source에서:

SET user:100 kim

이라는 데이터를 읽었습니다.

Target은 Cluster입니다.

Target이 Key를 다시 계산합니다.

user:100
   │
   ▼
Hash Slot 계산
   │
   ▼
예: Slot 2500
   │
   ▼
Target Master 1

다른 Key:

product:500
   │
   ▼
Slot 8000
   │
   ▼
Target Master 2

입니다.

즉 RedisShake가 단순히:

Source M1 → Target M1
Source M2 → Target M2
Source M3 → Target M3

로 물리 복사하는 구조는 아닙니다.


 

7.7 이 부분이 정말 중요하다 ⭐

RedisShake는 Source의 Redis 파일을 Target 동일 Master에 복사하는 도구가 아닙니다.

정확하게는:

Source M1
Source M2
Source M3
     │
     ▼
 Redis Commands
     │
     ▼
 Target Cluster
     │
     ▼
Target Slot 기준으로 재배치

입니다.

그래서 Source와 Target의 Master 수가 같을 필요도 없습니다.

예:

Source
3 Master

        ↓

RedisShake

        ↓

Target
6 Master

도 원리상 가능합니다.

Target의 Slot 배치에 따라 Key가 분산됩니다.


 

7.8 Source 3 Master → Target 6 Master 예

Source:

M1
Slot 0~5460

M2
5461~10922

M3
10923~16383

Target은 6 Master:

TM1
0~2730

TM2
2731~5460

TM3
5461~8191

TM4
8192~10922

TM5
10923~13652

TM6
13653~16383

일 수 있습니다.

그러면 Source M1의 데이터도 Target에서는:

Source M1
   │
   ├─ 일부 → TM1
   └─ 일부 → TM2

처럼 나뉠 수 있습니다.


 

7.9 Target 설정

Target도 Cluster이므로:

[redis_writer]
cluster = true
address = "target-redis:6379"

입니다.

공식 RedisShake Writer 문서도 Target이 Cluster이면 cluster=true를 설정하고, address에는 Cluster 노드 중 하나를 초기 주소로 지정할 수 있다고 설명합니다. Tair Open Source

전체 최소 설정:

[sync_reader]
cluster = true
address = "source-redis:6379"
sync_rdb = true
sync_aof = true

[redis_writer]
cluster = true
address = "target-redis:6379"

입니다.


 

7.10 K8s에서는 Initial Service만 연결되면 끝인가?

아닙니다.

이게 Cluster Migration의 가장 중요한 네트워크 포인트입니다.

예:

RedisShake
   │
   ▼
source-redis Service
   │
   ▼
Source M1

여기까지 성공했다고 합시다.

RedisShake가 Cluster 정보를 확인해서:

M1 = 10.244.1.10:6379
M2 = 10.244.2.20:6379
M3 = 10.244.3.30:6379

를 얻었습니다.

그 다음 RedisShake가 실제로:

10.244.1.10:6379
10.244.2.20:6379
10.244.3.30:6379

에 연결할 수 있어야 합니다.

따라서:

Initial Service 접근 성공 ≠ Cluster Migration 준비 완료

입니다.


 

7.11 Source 쪽 통신 요구사항

RedisShake 기준:

RedisShake
   │
   ├─ Source 초기 Endpoint :6379
   ├─ Source Master 1 :6379
   ├─ Source Master 2 :6379
   └─ Source Master 3 :6379

모두 가능해야 합니다.

Target도:

RedisShake
   │
   ├─ Target 초기 Endpoint :6379
   ├─ Target Master 1 :6379
   ├─ Target Master 2 :6379
   └─ Target Master 3 :6379

가 되어야 합니다.


 

7.12 같은 K8s Cluster라면

예:

Namespace: redis-source
Namespace: migration
Namespace: redis-target

이라면 RedisShake Pod가:

source-redis.redis-source.svc.cluster.local

와:

target-redis.redis-target.svc.cluster.local

에 접근합니다.

Pod별 DNS를 쓰는 구조라면:

redis-0.redis-headless.redis-source.svc.cluster.local
redis-1.redis-headless.redis-source.svc.cluster.local
redis-2.redis-headless.redis-source.svc.cluster.local

등에도 접근할 수 있어야 합니다.


 

7.13 서로 다른 K8s Cluster라면 난이도가 크게 올라간다

예:

운영센터 K8s
Source Redis Cluster

        │
        │ IDC Network
        ▼

DR센터 K8s
RedisShake + Target

이라고 하겠습니다.

Source Cluster가 RedisShake에게:

10.244.1.10
10.244.2.20
10.244.3.30

같은 Source K8s 내부 Pod IP를 알려줬는데 DR센터에서 해당 IP를 라우팅할 수 없다면:

Initial Connection   ✅

Cluster topology     ✅

각 Master Connection ❌

이 됩니다.

그래서 Cross-K8s Cluster Migration에서는 Redis Cluster가 광고하는 주소가 RedisShake에서 Routable한지가 핵심입니다.


7.14 Target Cluster 상태부터 확인해야 한다

Target이 정상 Redis Cluster인지 먼저 확인합니다.

예:

redis-cli -h target-redis -p 6379 CLUSTER INFO

중요한 항목:

cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384

입니다.

Redis 공식 문서에 따르면 cluster_state=ok는 Cluster가 정상적으로 요청을 받을 수 있음을 의미하며, 정상적인 Slot 할당에서는 cluster_slots_assigned가 16384여야 합니다. Redis


 

7.15 Source도 똑같이 확인

redis-cli -h source-redis -p 6379 CLUSTER INFO

최소:

cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384

를 봅니다.

Migration을 시작하기 전에 Source 자체 Cluster가 깨져 있다면 RedisShake 문제가 아니라 Source 문제부터 해결해야 합니다.


 

7.16 CLUSTER NODES 확인

redis-cli -h source-redis -p 6379 CLUSTER NODES

여기서:

Master
Replica
Node 주소
Slot
FAIL 여부

를 확인합니다.

예:

node-A ... master ... 0-5460
node-B ... master ... 5461-10922
node-C ... master ... 10923-16383
node-D ... slave node-A
node-E ... slave node-B
node-F ... slave node-C

이런 구조입니다.


 

7.17 왜 Node 주소를 꼭 봐야 하나?

RedisShake가 이 topology 정보를 기반으로 각 Source Shard에 접근하기 때문입니다.

예를 들어:

CLUSTER NODES

M1 192.168.10.10:6379
M2 192.168.10.11:6379
M3 192.168.10.12:6379

라면 RedisShake에서:

nc -zv 192.168.10.10 6379
nc -zv 192.168.10.11 6379
nc -zv 192.168.10.12 6379

같은 방식으로 실제 연결 가능 여부를 검증하는 것이 좋습니다.


 

7.18 이제 Full Sync

RedisShake를 실행하면 Source Shard별로:

Source M1 ─ RDB ─┐
Source M2 ─ RDB ─┼──→ RedisShake
Source M3 ─ RDB ─┘

Full 데이터를 받습니다.

RedisShake는 이를 임시 disk에 저장하고 RDB를 Redis command로 파싱해서 Target에 보냅니다. Tair Open Source


 

7.19 Full 중 Source Write는 계속 발생

Application이 계속 Source를 사용합니다.

Application
     │
     ▼
Source Cluster

SET
HSET
DEL
INCR
...

Full RDB 처리 중 발생한 변경은 Incremental stream으로 이어집니다.

Source M1 ─ Full + Incremental ─┐
Source M2 ─ Full + Incremental ─┼→ RedisShake
Source M3 ─ Full + Incremental ─┘

그래서 서비스 중단 없이 Target을 따라잡게 만드는 것이 sync_reader의 핵심입니다. Tair Open Source


 

7.20 Incremental 단계

Full이 완료돼도 RedisShake는 계속 실행됩니다.

Source

SET A
DEL B
HSET C
INCR D

      │
      ▼

RedisShake

      │
      ▼

Target

Target Cluster는 각 Key의 Slot에 맞게 담당 Master로 명령을 처리합니다.


 

7.21 Cluster에서는 Lag도 Shard별로 확인

예:

M1 Lag
거의 0

M2 Lag
거의 0

M3 Lag
5GB

이라면:

Migration 완료 ❌

입니다.

M3가 아직 Source를 못 따라잡았기 때문입니다.

RedisShake Cluster Reader는 각 Source shard를 별도 Reader로 병렬 처리하기 때문에 특정 shard만 병목이 될 수 있습니다. Tair Open Source


 

7.22 왜 한 Shard만 느릴 수 있나?

예:

M1 Write
20MB/s

M2 Write
30MB/s

M3 Write
300MB/s

이라면 M3가 Hot Shard입니다.

또는:

M3
Large Key 다수

M3 Node CPU 95%

M3 Network 병목

일 수도 있습니다.

그래서:

전체 Redis Dataset = 3TB

만 보는 것보다:

M1 0.5TB
M2 0.7TB
M3 1.8TB

처럼 Shard skew를 확인해야 합니다.


 

7.23 Target의 Slot 배치가 Source와 같아야 하나?

반드시 같지는 않습니다.

Source:

3 Master

Target:

6 Master

도 가능합니다.

중요한 것은 Target Cluster 자체가:

16384 Slots
정상 배치

되어 있는 것입니다.

Redis Writer는 Target이 Cluster일 때 Cluster mode를 사용하여 명령을 적절한 노드에 전달합니다. Tair Open Source


 

7.24 다만 Multi-Key Command는 주의 ⚠️

Redis Cluster에서는 한 명령이 여러 Key를 사용한다면 보통 해당 Key들이 같은 Hash Slot에 있어야 합니다.

예를 들어:

MSET key1 value1 key2 value2

에서 key1, key2가 서로 다른 Slot이면 문제가 될 수 있습니다.

RedisShake Writer 공식 문서도 Target이 Cluster인 경우 Source에서 전달되는 multi-key 명령의 Key가 동일 Slot 조건을 만족해야 한다고 주의합니다. Tair Open Source

이 때문에 Redis Cluster 애플리케이션에서는 Hash Tag:

{user:100}:profile
{user:100}:cart

같은 방식을 사용하기도 합니다.


 

7.25 Redis 버전도 확인

예:

Source
Redis 7.x

Target
Redis 6.x

처럼 Target이 더 낮은 버전이면 Source에서 사용하는 명령이나 데이터 형식을 Target이 지원하지 못할 수 있습니다.

RedisShake Writer 문서도 가능한 한:

Target Redis Version ≥ Source Redis Version

방향을 권장합니다. Tair Open Source

따라서 보통:

Redis 6 → Redis 7
✅

Redis 7 → Redis 6
⚠️

관점으로 봅니다.


 

7.26 Target은 가능하면 비워두고 시작

신규 Migration이면:

Target
Empty

상태가 관리하기 쉽습니다.

왜냐하면 기존 Target에 같은 Key가 있으면 Source에서 들어오는 값과 충돌할 수 있기 때문입니다.

예:

Source
user:100 = kim

Target
user:100 = park

Migration 이후 어떤 값이 남아야 하는지 명확한 정책이 필요합니다.

그래서 기본 Migration에서는 신규/비어 있는 Target이 가장 단순합니다.


 

7.27 실제 shake.toml 예

현재 환경을 단순화하면:

[sync_reader]
cluster = true
address = "source-redis.redis-source.svc.cluster.local:6379"
username = ""
password = "SOURCE_PASSWORD"
tls = false
sync_rdb = true
sync_aof = true

[redis_writer]
cluster = true
address = "target-redis.redis-target.svc.cluster.local:6379"
username = ""
password = "TARGET_PASSWORD"
tls = false

핵심은:

sync_reader.cluster = true
redis_writer.cluster = true

입니다.

Source와 Target 모두 Redis Cluster이기 때문입니다. Tair Open Source


 

7.28 실행

K8s 기준:

kubectl apply -f redis-shake-configmap.yaml
kubectl apply -f redis-shake-secret.yaml
kubectl apply -f redis-shake-pod.yaml

Pod:

kubectl get pod -n redis-migration

RedisShake 로그:

kubectl logs \
  -n redis-migration \
  redis-shake \
  -f

에서:

Source 연결
Cluster topology
PSync
RDB
Writer
Incremental
Error

을 확인합니다.


 

7.29 Migration 전 점검 순서

실무에서는 RedisShake부터 실행하지 않습니다.

먼저:

① Source Cluster 정상?
② Target Cluster 정상?
③ Source 16384 Slot 정상?
④ Target 16384 Slot 정상?
⑤ Master/Replica 상태 정상?
⑥ RedisShake → Source 각 Master 접근?
⑦ RedisShake → Target 각 Master 접근?
⑧ Disk/PVC 충분?
⑨ Source/Target Redis 버전 확인
⑩ ACL/Password 확인

을 확인합니다.


 

7.30 명령어로 보면

Source Cluster 상태

redis-cli \
  -h source-redis \
  -p 6379 \
  CLUSTER INFO

Source Node 정보

redis-cli \
  -h source-redis \
  -p 6379 \
  CLUSTER NODES

Target

redis-cli \
  -h target-redis \
  -p 6379 \
  CLUSTER INFO
redis-cli \
  -h target-redis \
  -p 6379 \
  CLUSTER NODES

정상 Cluster라면 전체 16,384 Slot이 할당되어 있어야 합니다. Redis


 

7.31 데이터 개수는 어떻게 비교?

Cluster에서는 특정 Master에서 그냥:

DBSIZE

한 번만 실행하면 해당 Node 데이터 개수만 볼 수 있으므로 전체 Cluster 비교로 오해하면 안 됩니다.

예:

M1 DBSIZE = 3M
M2 DBSIZE = 4M
M3 DBSIZE = 5M

이면 전체 개념적으로:

약 12M Keys

입니다.

따라서 Source/Target 비교도 Master별 합산해서 보는 것이 좋습니다.


 

7.32 그렇다고 Key Count만 같으면 끝인가?

아닙니다.

예:

Source
10,000,000 Keys

Target
10,000,000 Keys

이어도:

Value 다름
TTL 다름
Data Type 다름

가능합니다.

따라서 검증은 단계적으로:

① Key Count
② Sample Key
③ Value
④ Type
⑤ TTL
⑥ 필요 시 전체 정합성 검사

로 갑니다.


 

7.33 Cluster 상태 변화도 주의해야 한다

Migration 도중 Source에서:

M1 장애
   ↓
R1 승격

이 발생할 수 있습니다.

또는:

Slot Resharding
Node 추가/삭제

같은 topology 변화가 발생할 수 있습니다.

RedisShake 공식 Migration Mode 문서는 장기간 동기화나 topology 변경을 계속 추적하는 용도로 RedisShake를 보는 것은 주의해야 한다고 설명합니다. Tair Open Source

따라서 Migration Window 동안 가능하면:

Cluster Resharding
Scale-Out/In
계획 Failover

같은 변경 작업은 피하는 것이 좋습니다.


 

7.34 Migration 중 Source Application은 계속 Source를 본다

Application
     │
     ▼
Source Cluster
     │
     ▼
RedisShake
     │
     ▼
Target Cluster

이 상태입니다.

Target에는 Application Write를 넣지 않습니다.

즉 RedisShake Migration은 기본적으로:

Source → Target

단방향

입니다.


 

7.35 Cutover 직전

Full Sync가 끝났습니다.

Full ✅

Incremental도 따라갑니다.

M1 Lag ↓
M2 Lag ↓
M3 Lag ↓

충분히 따라잡았다면:

① Application Write 제어

② 마지막 Incremental 반영

③ Source/Target 정합성 확인

④ Application Endpoint 변경

⑤ Target 서비스 시작

순서로 갑니다.


 

7.36 Cutover 그림

전환 전

Application
    │
    ▼
SOURCE

    │
 RedisShake
    ▼

TARGET

최종 동기화

Application Write
     STOP

       ↓

SOURCE = TARGET

전환 후

Application
    │
    ▼
TARGET

입니다.


 

7.37 Rollback도 생각해야 한다

예를 들어 Target 전환 후 문제가 발생했습니다.

Application
     │
     ▼
Target
      ❌

이때 단순히 Source로 돌아가면 될 것 같지만 문제는:

Cutover 이후 Target에서 발생한 Write

입니다.

RedisShake는 자동 양방향 Sync가 아니므로:

Target 변경분
→ Source 자동 복귀

❌

입니다.

따라서 Cutover 계획에는 반드시:

Rollback 가능 시간
Write Freeze
Target Write 발생 후 Rollback 방식

을 사전에 정의해야 합니다.

이건 나중 Cutover 단계에서 자세히 다루겠습니다.


 

7.38 Cluster → Cluster 전체 흐름

지금까지 배운 걸 하나로 합치면:

                Application
                    │
                    ▼

             SOURCE CLUSTER

       Shard1      Shard2      Shard3

         M1          M2          M3
         │           │           │
         R1          R2          R3

         │           │           │
         │ Full + Incremental    │
         ▼           ▼           ▼

          ┌──────────────────┐
          │   RedisShake     │
          │                  │
          │ Cluster Reader   │
          │                  │
          │ Reader1 ← M1     │
          │ Reader2 ← M2     │
          │ Reader3 ← M3     │
          │                  │
          │       ↓          │
          │ Cluster Writer   │
          └────────┬─────────┘
                   │
                   ▼

             TARGET CLUSTER

       Shard1      Shard2      Shard3

         M1          M2          M3
         │           │           │
         R1          R2          R3

RedisShake 내부에서는 Source shard 수에 맞춰 Reader들이 병렬로 동작하고, 처리된 데이터를 Cluster Writer가 Target으로 전달합니다. Tair Open Source


 

🎯 7단계 핵심 8가지

  1. Source Cluster의 데이터는 여러 Master에 분산되어 있습니다.
  2. RedisShake에서 Source는 **cluster=true**입니다.
  3. RedisShake는 Source Shard별 Reader를 병렬 구성합니다. Tair Open Source
  4. 각 Reader는 Full RDB + Incremental Stream을 처리합니다. Tair Open Source
  5. Target도 Cluster면 **redis_writer.cluster=true**입니다. Tair Open Source
  6. Source Master와 Target Master 수가 반드시 같을 필요는 없습니다.
  7. RedisShake Pod가 Source/Target 각 Cluster Node 주소에 접근 가능해야 합니다.
  8. Cutover 판단은 전체가 아니라 Shard별 Lag + 데이터 정합성까지 봐야 합니다.

한 문장으로 압축하면:

Redis Cluster → Redis Cluster Migration은 Source의 각 Master Shard에서 데이터를 병렬로 읽고, RedisShake가 이를 Redis 명령으로 처리한 뒤 Target Cluster의 Hash Slot 구조에 맞춰 다시 분산 저장하는 과정입니다.

그리고 환경에서는 다음 7.1단계가 특히 중요합니다.
운영 K8s Redis Cluster → DR/신규 K8s Redis Cluster에서 Headless Service, Pod IP, cluster-announce-ip/hostname, L4/VIP, 방화벽을 어떻게 설계해야 RedisShake가 각 Master에 실제로 접근할 수 있는지를 네트워크 그림과 함께 보는 단계입니다.

 

 

반응형

댓글