로컬 PC에서 Kubernetes에 배포한 레디스 클러스터에 클러스터 모드로 연결이 안 된다면?

 

로컬 PC에서 k8s 환경에 배포된 레디스 클러스터에 클러스터 모드로 연결하려 했는데, 연결이 되지 않았다.

이런 상황에서 어떤 해결책이 있는지, 내가 어떤 해결책을 택했는지를 이 글을 통해 정리해보고자 한다.

 

 

문제 상황: Standalone 모드로는 연결이 되는데, Cluster 모드로 연결이 안 돼요

클러스터 내 단일 노드에 포트 포워딩(kubectl port-forward) 후 연결하는 것은 잘 되었으나, 클러스터 모드로는 연결이 되지 않았다. 레디스 클러스터는 여러 노드가 데이터를 분산 관리하기 때문에, 나는 클러스터 모드로의 연결이 필요했다.

레디스 클러스터에 대해서 단순히 ‘샤딩을 한다’ 정도의 지식만 가지고 있던 나는 그냥 단순히 모든 노드에 포트포워딩을 한 뒤 모든 노드 IP:PORT를 명시하면 문제가 해결되지 않을까 했으나… 당연히 아니었다.

이제 아래에서는 왜 이런 문제가 발생하며, 이 문제를 어떻게 해결하는지를 살펴본다.

 

 

원인 파악: 레디스 클러스터에 연결할 때 일어나는 일

먼저, 어느 부분에서 막혔는지 파악하기 위해 정상 연결 과정을 살펴보자. 클라이언트와 레디스 클러스터 간 연결 과정은 아래와 같다.

즉, 연결 시점에 모든 아이피 목록을 제공할지라도 클라이언트는 그들 중 하나를 골라 연결을 시도한 다음, 나머지 노드의 아이피로는 연결 시점에 제공한 아이피가 아닌, 각 노드가 announce-ip로 제공하는 아이피를 사용하여 연결을 시도한다.

이제 감이 오겠지만, 기본적으로 Docker/Kubernetes 환경에서 레디스 클러스터 내 노드들은 announce-ip로 자신의 private IP를 제공하며, 그렇기 때문에 동일 네트워크로 연결되어 있지 않은 로컬에서는 연결에 실패한 것이다.

레디스 공식문서에서도, 도커 컨테이너같은 환경에서는 정상 동작하지 않을 수 있음을 명시하고 있다.

 

Scale with Redis Cluster

Horizontal scaling with Redis Cluster

redis.io

 

 

3가지의 해결 방법

1. 각 노드에 Public IP 부여 및 Announce 설정 변경

이 문제를 해결하는 가장 정석적인 방법이다. 각 노드가 Private IP를 응답하는 것이 문제이니, 각 노드에 외부에서 접근이 가능한 Public IP를 부여하고 이를 Announce하도록 설정을 변경하는 방법이다.

# redis-0
cluster-announce-ip 127.0.0.1
cluster-announce-port 6379
cluster-announce-bus-port 16379

# redis-1
cluster-announce-ip 127.0.0.1
cluster-announce-port 6380
cluster-announce-bus-port 16380

...

하지만 나는 이 방식을 사용하지 않았는데, 회사 내부 방화벽 규칙 상 NodePort와 같은 방식으로 각 노드를 퍼블릭하게 오픈해주어도 로컬에서 바로 연결이 가능한 상태는 아니었기 때문이다. (자세한 내용은 생략)

만약 이렇게 특수한 케이스가 아니어서 NodePort를 열거나 로드밸런서를 생성 및 연결해 각 노드에 Public IP를 부여함으로써 문제 해결이 가능한 케이스라면 나는 1번 방법을 권장한다.

2. 네트워크 브릿지 도구 사용

telepresence과 같이 내 로컬 PC를 마치 클러스터 내부의 구성원처럼 취급되도록 해주는 도구들도 존재한다.

이 도구들을 사용하면 이 도구들이 클러스터의 DNS 설정 등을 조작해 로컬 PC가 각 노드들과 동일한 가상 네트워크에 속하게 된 것처럼 동작해 Private IP를 announce해도 정상적으로 연결할 수 있게 된다.

하지만 나는 이 방식도 사용하지 않았는데, 단순히 내 문제를 해결하기 위한 도구라기에는 배보다 배꼽이 더 큰 해결책이라고 느끼기도 했고, 클러스터 접근/제어 권한을 넘긴다는 점에서 클러스터에 Side Effect가 생길 위험성이 있다고 판단했기 때문이다.

결국 ‘이게 정말 안전한 도구인가?’를 판단하려면 위 도구들을 내가 확실히 이해하고 사용해야 할 듯한데, 거기에 드는 비용이 커보여서 관뒀다. 더 가볍고 효율적인 해결책이 떠올랐기 때문이다.

3. 로컬 속이기

이 방법은 정석적인 방법이라고 보기에는 어려우나, 단순히 로컬에서 레디스 클러스터 접속하기라는 작은 문제의 해결만이 필요했던 나에게는 안성맞춤인 방법이었다.

이 방법을 수행하기 위해서는 로컬 PC에서 2가지 설정만 변경해주면 된다.

  1. 루프백 인터페이스에 각 Private IP를 등록
  2. 각 가상 IP의 6379 포트로 들어오는 요청을 실제 클러스터 노드 Pod의 6379 포트로 포트 포워딩

따라서, 아래와 같이 설정하면 레디스 클라이언트가 전달받은 announce-ip는 실제 각 레디스 클러스터 노드에게 전달된다.

# 루프백 alias 등록 (Mac)
sudo ifconfig lo0 alias 10.0.0.1
sudo ifconfig lo0 alias 10.0.0.2
...

# 루프백 alias 등록 (Linux)
sudo ip addr add 10.0.0.1/32 dev lo
sudo ip addr add 10.0.0.2/32 dev lo
...

# 각 Pod에 port-forward (bind address 지정)
kubectl port-forward --address 10.0.0.1 pod/redis-0 6379:6379
kubectl port-forward --address 10.0.0.2 pod/redis-1 6379:6379
...

단순해 보이지만 위 과정을 수행하려면 여러 절차가 필요하니, 재사용 가능한 쉘 스크립트로 작성해두는 편이 좋다. Private IP가 변경될 수 있으니 IP를 하드코딩 하기보다는 kubectl을 통해 해당하는 파드들을 찾아 연결하도록 작성하는 편이 좋고, 클러스터 연결을 종료할 때에는 로컬의 변경 사항(루프백 alias 및 포트 포워딩 정보)을 롤백할 수 있도록 구성하면 더 좋다.

아래는 예시 스크립트이다.

#!/bin/bash

# Redis Cluster가 있는 네임스페이스와 레이블 설정
NAMESPACE="redis-system"
LABEL="app=redis-cluster"

# 1. Pod 정보 가져오기 (이름과 IP)
PODS=$(kubectl get pods -n $NAMESPACE -l $LABEL -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.podIP}{"\n"}{end}')

echo "🚀 Redis Cluster 로컬 연결 설정을 시작합니다..."

# 종료 시 정리를 위한 함수
cleanup() {
    echo -e "\n🧹 설정을 원복하고 프로세스를 종료합니다..."
    while read -r POD_NAME POD_IP; do
        # 할당했던 Alias IP 제거 (OS에 따라 다름)
        if [[ "$OSTYPE" == "darwin"* ]]; then
            sudo ifconfig lo0 -alias $POD_IP 2>/dev/null
        else
            sudo ip addr del $POD_IP/32 dev lo 2>/dev/null
        fi
    done <<< "$PODS"
    pkill -P $$ # 백그라운드 포트포워딩 프로세스 종료
    exit
}

trap cleanup SIGINT

# 2. IP 등록 및 포트 포워딩 실행
while read -r POD_NAME POD_IP; do
    echo "📍 Processing $POD_NAME ($POD_IP)..."

    # 로컬 루프백에 Pod IP 추가
    if [[ "$OSTYPE" == "darwin"* ]]; then
        sudo ifconfig lo0 alias $POD_IP 2>/dev/null
    else
        sudo ip addr add $POD_IP/32 dev lo 2>/dev/null
    fi

    # 해당 IP로 포트 포워딩 (백그라운드 실행)
    kubectl port-forward -n $NAMESPACE pod/$POD_NAME 6379:6379 --address $POD_IP > /dev/null &
done <<< "$PODS"

echo "✅ 모든 노드에 대한 연결 준비가 완료되었습니다."
echo "이제 GUI 툴에서 Pod IP($POD_IP 등)로 직접 연결하세요."
echo "종료하려면 Ctrl+C를 누르세요."

# 스크립트 유지
wait

 

 

마치며: 중요한 것은 기반 지식

이 방법은 네트워크의 기본 원리인 루프백(Loopback)과 인터페이스 에일리어싱(Aliasing)을 활용한 것이다. 정석적인 방법을 고르라면 당연히 1번이 정석이겠지만, 세상에는 가지각색 환경을 가진 기업이 존재하기 마련이고, 나는 내가 현재 처한 상황에서 가장 효율적인 3번 안으로 문제를 해결했다.

이번 트러블슈팅으로 결국 자신이 해결해야 하는 문제를 정확히 인지하고 그에 맞는 해결책을 제시하기 위해서는 역시 ‘기반 지식’을 알고 있는 것이 정말 중요하다는 것을 다시 한번 느꼈다.

단순히 "로컬 연결은 어렵겠다"에서 끝내지 않고, 레디스 클러스터의 동작 방식과 네트워크에 대한 지식이 어느정도 있었기에 이러한 우회로를 찾을 수 있지 않았나 싶다.

앞으로도 기반 지식의 학습을 소홀히 하지 않는 태도를 유지해야겠다.