PHPRedis - unable to send command at the specified node오류 해결

aaron.park·2023년 11월 28일
0

사내삽질기

목록 보기
1/1
post-thumbnail
post-custom-banner

개요

bitnami/redis-cluster-helm로 구성된 k8s기반 Redis Cluster와, PHP 클라이언트인 PHPRedis 연결 사용 중인데, 간혹 Redis Cluster를 재배포하면 Unable to send command at the specified node 오류와 함께 command가 실행이 되지 않는 이슈가 있어서 공유하려고 한다.

결론부터 말하자면, slot cache가 원인이었다.

Slot Cache란?

Slot Cache는 Redis Cluster에서 사용되는 메커니즘으로, 각 노드가 어떤 슬롯을 담당하고 있는지에 대한 정보를 캐시에 저장하는 것을 의미한다. 이를 통해 클러스터의 상태를 파악하고 명령을 올바른 노드로 전달할 수 있다.

PHPRedis 클라이언트에서도 Slot Cache가 구현되어 있는데, 아래와 같이 php.ini에서 설정을 할 수 있다.

[redis]
redis.cluster.cache_slots = 1

그런데 내부적으로 ip 정보를 현재 노드 정보를 기반으로 캐싱하기에, 노드 정보가 dns 기반으로 설정되었다면, 바뀐 IP에 대해서 대응하지 못할 것이다.

예를 들어, 다음을 살펴보자.

$redisCluster = new RedisCluster(null, [
    '1.2.3.4:6379',
    '5.6.7.8:6379',
    '9.10.11.12:6379',
...
]);

보통은 위와 같이 설정될텐데, 나의 경우는 k8s에 연결되어 있기 때문에 아래와 같이 설정된 상태이다.

$redisCluster = new RedisCluster(null, [
    'cluster-rc-0-pod.cluster-service-headless.namespace:6379',
    'cluster-rc-1-pod.cluster-service-headless.namespace:6379',
    'cluster-rc-2-pod.cluster-service-headless.namespace:6379',
...
]);

문제점

그럼 뭐가 문제냐?

예를 들어, 현재 위와 같은 pod명에는 실제로 아래와 같이 ip가 할당되어 있다고 가정하자.

  • cluster-rc-0-pod.cluster-service-headless.namespace:6379: 10.0.1.10
  • cluster-rc-1-pod.cluster-service-headless.namespace:6379: 10.0.2.20
  • cluster-rc-2-pod.cluster-service-headless.namespace:6379: 10.0.3.30

그런데 만약 redis cluster pod 재배포가 일어난다면, 아래와 같이 pod IP가 변경되었다고 가정하자.

  • cluster-rc-0-pod.cluster-service-headless.namespace:6379: 10.0.1.40
  • cluster-rc-1-pod.cluster-service-headless.namespace:6379: 10.0.2.50
  • cluster-rc-2-pod.cluster-service-headless.namespace:6379: 10.0.3.60

이 경우 slot cache가 활성화 되어있을 때, 현재 위 코드 상으로 변경점은 없으므로, 기존에 캐싱된 아이피 (10.0.1.10 ~ 10.0.3.30)를 가져와 사용을 하게 된다. 즉, 업데이트된 IP를 사용하지 않는다.

따라서, 잘못된 IP를 내부적으로 가지고 있으므로, Unable to send command at the specified node 에러가 나게 된다.

해결책

다음과 같이 gethostbyname 함수를 사용하여 DNS 쿼리를 수행하여 해결하였다.

(아래 코드는 이해를 돕기 위함으로 실제로는 적절히 변경해야 한다)

$redisCluster = new RedisCluster(null, [
    gethostbyname('cluster-rc-0-pod.cluster-service-headless.namespace') . ':6379',
    gethostbyname('cluster-rc-1-pod.cluster-service-headless.namespace') . ':6379',
    gethostbyname('cluster-rc-2-pod.cluster-service-headless.namespace') . ':6379',
    ...
]);

위와 같이 gethostbyname 함수를 사용하면, DNS 레코드를 직접 쿼리하여 IP 주소를 받아올 수 있으며, 이렇게 수정된 코드를 사용하면 Redis Cluster POD의 IP가 변경되었을 때에도 올바른 IP 주소를 사용하여 연결이 가능하다.

profile
애런 퐉의 블로그
post-custom-banner

0개의 댓글