Programming-[Infra]/Cloud-AWS

Elasticache 연결 문제 - <unresolved>:6379, Redis, Springboot, Lettuce

컴퓨터 탐험가 찰리 2024. 2. 15. 09:04
728x90
반응형

 

문제상황

 

Elasticache - Redis를 사용하기 위해 Redis cluster를 생성하고 스프링부트 - LettuceConnectionFactory를 통해 연결할려는데, 아래와 비슷한 에러가 발생했다.

 

Caused by: io.lettuce.core.RedisConnectionException: Unable to connect to 
{레디스 엔드포인트 주소}.cache.amazonaws.com/<unresolved>:6379

at io.lettuce.core.RedisConnectionException.create(RedisConnectionException.java:78)
at io.lettuce.core.RedisConnectionException.create(RedisConnectionException.java:56)
at io.lettuce.core.AbstractRedisClient.getConnection(AbstractRedisClient.java:350)
at io.lettuce.core.RedisClient.connect(RedisClient.java:216)

at...

 

 

보통 호스트 주소에 unresolved라고 뜨면 DNS 서버의 문제인 경우가 많은데, 아래 시도 방법에서 다루겠지만 DNS 서버 문제가 아니였다.

 

 

해결 방법

ref) AWS 공식문서

https://repost.aws/ko/knowledge-center/elasticache-redis-cluster-fix-connection

https://docs.aws.amazon.com/ko_kr/AmazonElastiCache/latest/red-ug/BestPractices.Clients.html

 

 

공식문서에 문제 원인에 대해서 잘 기술되어있다. 다만 나의 경우 공식문서에서 힌트만 얻었고, 진짜 해결책은 실험적으로 얻을 수 있었다...

  • 레디스 클러스터를 생성할 때 '전송 중 암호화 (TLS)'라는 항목을 굳이 ON하지 않았었는데, 이 항목을 켜니 해결되었다.

LettuceConnection을 까보니 굳이 TLS 방식을 처리하는 것도 아닌 것 같았는데, 위 링크 중 BestPractices의 Lettuce 관련 모범사례를 살펴보니 마치 전송 중 암호화 옵션은 기본적으로 켜는 것 같아서 켜봤더니 해결되었다..

 

 

해결 과정 기록

 

인프라, 연결성 검토

VPC, 서브넷(IPv4 CIDR 주소), Security Group(SG) 등을 점검

 

ECS로 생성한 EC2와 Elasticache의 클러스터가 동일한 VPC내에 존재하고 서로 통신할 수 있는 서브넷 구조인지 확인해야한다. 콘솔에서 VPC에 들어가서 VPC 및 서브넷 구성을 살펴보고 EC2 및 Elasticache의 네트워크에서 해당 VPC 및 서브넷이 설정되어있는지 확인한다.

 

SG의 경우 Elasticache의 인바운드가 EC2가 포함된 SG로부터의 6379 포트를 허용하는지 체크하면 된다. 혹시 EC2의 아웃바운드가 모든 포트 및 주소에 대해(보안상 직접 지정할 필요가 없는 경우) 잘 설정되어있는지도 살펴봐도 좋다.

 

 

DNS 서버 점검

 

DNS가 정상적으로 설정되어 출력되는지 확인한다. nslookup이라는 툴을 통해서 할 수 있다. amazon linux 계열이라 설치는 sudo yum install bind-utils 명령어를 통해 할 수 있었다.

 

nslookup을 날려보면 아래처럼 Non-authoritative answer: 방식으로 해당하는 ip가 뜬다.

 

만약 DNS 설정이 제대로 설정되어있지 않다면, 위 AWS 공식 문서에서처럼 아래 같은 에러가 뜬다.

Could not connect to Redis at nonexistent.1234id.clustercfg.euw1.cache.amazonaws.com:6379: Name or service not known

 

 

 

문서에 나와있는대로 VPC의 DNS 설정 활성화 옵션들을 체크해보고 해결해야한다. 라우팅 테이블과 ACL도 확인한다.

 

 

 

 

연결성 체크

curl

Elasticache는 VPC내에서만 통신이 가능하다. EC2에 접속하여 curl 명령어를 날려서 제대로 통신이 되는지 확인할 수 있다. 공식문서 예시대로 Connected... 라는 문구가 뜬다면 정상적으로 요청이 가는 것이다.

$ curl -v telnet://test.1234id.clustercfg.euw1.cache.amazonaws.com:6379
*  Trying 172.31.1.242:6379...
* Connected to test.1234id.clustercfg.euw1.cache.amazonaws.com (172.31.1.242) port 6379 (#0)

 

 

Reachability Analzyer

AWS에서 제공하는 Reachability Analyzer를 통해서도 연결을 점검할 수 있다. network manager에 들어가서 Reachability Analzyer로 소스 및 대상 주소를 설정하면 네트워크 상황을 도식으로 보여준다.

 

 

 

redis-cli로 확인

EC2 상에 redis-cli를 설치하고 연결성을 테스트해볼 수도 있다. 나의 경우 프리티어 단계를 사용해서 일단 Swap 메모리를 늘려주는 작업부터 필요했다.

EC2에 접속해서 아래 링크 내용대로 처리해주면 free 명령어를 쳤을 때 Swap 메모리가 존재함을 확인할 수 있다.

https://sundries-in-myidea.tistory.com/102

 

그리고나서 redis-cli를 설치하고 명령어를 통해 요청을 날려보면 된다. 상세한 내용은 AWS 공식 문서대로 하면 된다. EC2에 접속해서 redis-stable/src 디렉토리에서 실행해야한다.

https://docs.aws.amazon.com/ko_kr/AmazonElastiCache/latest/red-ug/nodes-connecting.html

 

 

코드 단에서의 문제

한 단계 더 내려와서 코드 단에서 문제가 없는지 검토했다.핵심만 기록한다. LettuceConnection을 잡는 쪽을 디버깅하다보니, verifyMode=FULL 옵션이 있어서 전송 중 암호화(TLS) 옵션을 켜봤었기도 하다.

 

아래는 테스트 기록

디버깅

로컬에서 디버깅해도 같은 에러가 나서, 코드 내의 DNS 서버가 문제가 있는 것 같다는 판단이 들었음

 

cacheInterceptor

  1. CacheResolver → singletonInstance → cahceManager → cacheWriter → connectionFactory → connectionProvider → delegate → client → redisURI에서 확인

<unresolved>는 안붙어있고 입력한 hostname, port대로 잘 입력됨. 

ssl = False인 것은 전송중 암호화를 하지 않아서 문제 없을듯

 

2. RedisCache.java → lookup method

  • redisURI 정상

3. LettuceConnectionFactory.java → delegate → client

  • redisURI 정상

4. StandaloneConnectionProvider.java

return connectionType.cast(readFrom.map(it -> this.masterReplicaConnection(redisURISupplier.get(), it))      .orElseGet(() -> client.connect(codec)));

 

  • client.redisURI 정상
  • redisURISupplier.redisURI 정상

host = "..."
socket = null
sentinelMasterId = null
port = 6379
database = 0
clientName = null
username = null
password = null
credentialsProvider = null
ssl = false
verifyMode = {SslVerifyMode@23456} "FULL"
startTls = false
timeout = {Duration@17930} "PT1M"
sentinels = {ArrayList@23457}  size = 0

→ verifyMode = FULL

 

verifyMode: SSL 연결 서버 인증서의 검증 방법을 지정합니다. 클라이언트가 FULL 설정되었다면 서버 인증서의 유효성을 완전히 확인하며, NONE으로 설정되었다면 인증서의 검증을 하지 않습니다.

 

 

추가 내용

Wireshark를 통해서 전송 중 암호화 옵션이 켜지지 않았을 때 네트워크단에서 송수신이 제대로 되는지 볼 수 있다. EC2에 wireshark을 설치하고 로그를 기록하면 되는데, 이 부분은 다음에 다른 글에 따로 기록해두어야겠다.

728x90
반응형