이 글은 ElasticCache redis 클러스터 scale up 이 후 springboot에서 발생했던 오류를 소개합니다. 그리고 오류 원인과 해결방법을 설명합니다.
1. 발생했던 오류
A팀은 ElasticCache redis 클러스터 성능을 높이기 위해 scale up을 결정했습니다. 그러나 ElasticCache를 scale up한 지 약 10분 뒤, Spring Boot pod에서 에러 로그가 발생했습니다. 에러 로그에는 pod가 ElasticCache 인스턴스에 접속하지 못한다는 내용이 포함되어 있었습니다.
Reconnecting. last destination was ....
2. 오류 해결
저희는 Reconnecting오류를 보고 ElasticCache 인스턴스 IP가 변경되어서 접속 못한 것을 추측했습니다. 그래서 pod를 재부팅하여 새로운 인스턴스 IP에 연결할 수 있도록 했습니다. 다행히 예상이 맞아 오류가 해결되었습니다.
3. 오류 원인
오류 원인은 springboot에서 커넥션 관리하는 라이브러리가 새로운 인스턴스 IP를 인식하지 못했습니다. 이 뜻을 이해하려면 redis 클러스터 토폴로지 개념을 알아야 합니다.
3.1 redis 클러스터 토폴리지
토폴리지라는 용어는 네트워크 구성을 의미합니다. 그러므로 redis 클러스터 토폴리지는 redis 클러스터 구성을 위한 네트워크 구성입니다.
Redis 클러스터는 모든 인스턴스 간에 통신을 수행하며, 이러한 구조를 메시(mesh) 토폴로지라고 합니다. 각 인스턴스는 클러스터 정보를 공유하기 위해 Gossip 프로토콜을 사용해 데이터를 주고받습니다.
redis 클러스터는 메시 토폴로지 덕분에 변경된 인스턴스 정보를 모든 인스턴스가 압니다. 예를 들어 인스턴스 IP가 변경되면 클러스터 인스턴스(노드)정보가 업데이트 됩니다.
3.2 애플리케이션에서도 토폴로지 동기화가 필요
스프링부트처럼 애플리케이션은 redis 메시 토폴로지에 속하지 않습니다. 그러므로 애플리케이션이 스스로 토폴로지 정보를 주기적으로 가져와서 redis 클러스터 정보를 업데이트 해야 합니다.
애플리케이션이 redis 클러스터 토폴로지를 업데이트 하지 않으면, 처음 실행되었을 때 가져온 토폴로지 정보를 가지고 있습니다. 따라서 애플리케이션은 변경된 redis 클러스터 토폴로지에 연결을 하지 않아 에러가 발생합니다.
4. lettuce라이브러리에서 redis 클러스터 토폴로지 동기화
애플리케이션이 redis 클러스터 토폴로지를 동기화 설정을 하려면 사용하는 redis 연결설정 라이브러리 옵션을 살펴봐야 합니다.
springboot에서는 jedis, lettuce 등의 redis 연결 설정 라이브러리를 사용합니다. 이 글에서는 lettuce라이브러리 설정을 다룹니다.
lettuce에서는 ClusterTopologyRefreshOptions 객체의 enablePeriodicRefresh옵션을 설정하면, lettuce클라이언트가 주기적으로 redis 클러스터 토폴로지 정보를 가져옵니다.
ClusterTopologyRefreshOptions topologyRefreshOptions = ClusterTopologyRefreshOptions.builder()
.dynamicRefreshSources(true)
.enablePeriodicRefresh(Duration.ofSeconds(30))
.build();
ClusterTopologyRefreshOptions설정은 lettuce client 객체에 적용해줘야 합니다. 전체 코드는 저의 github에 공개되어 있습니다.
@Bean
public RedisConnectionFactory redisConnectionFactory() {
// reference: https://docs.aws.amazon.com/AmazonElastiCache/latest/dg/BestPractices.Clients-lettuce.html
// topology refresh option
ClusterTopologyRefreshOptions topologyRefreshOptions = ClusterTopologyRefreshOptions.builder()
.dynamicRefreshSources(true)
.enablePeriodicRefresh(Duration.ofSeconds(30))
.enableAllAdaptiveRefreshTriggers()
.adaptiveRefreshTriggersTimeout(Duration.ofSeconds(30))
.build();
// Redis Cluster 설정
RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration();
redisClusterConfiguration.clusterNode(host, port);
ClientOptions clientOptions = ClusterClientOptions.builder()
.topologyRefreshOptions(topologyRefreshOptions)
.build();
// Lettuce Client 설정
LettuceClientConfiguration clientConfiguration = LettuceClientConfiguration.builder()
.clientOptions(clientOptions)
.readFrom(ReadFrom.REPLICA_PREFERRED)
.build();
return new LettuceConnectionFactory(redisClusterConfiguration, clientConfiguration);
}
5. 오류 재현 실습
오류재현 실습은 저의 유튜브링크를 참고해주세요.
오류를 쉽게 재현하기 위해 springboot는 컨테이너 이미지로 생성했습니다.
- 실습 github: https://github.com/choisungwook/portfolio/tree/master/backend/springboot_lettuce_connection
- 컨테이너 이미지 목록
- redis 클러스터 토폴로지를 동기화 안하는 springboot: choisunguk/lettuce-connection:v1
- 디버그 로그가 있는 springboot: choisunguk/lettuce-connection:v2
- redis 클러스터 토폴로지를 동기화하는 springboot: choisunguk/lettuce-connection:v3.1
참고자료
- https://docs.google.com/presentation/d/1FtEFBCubpcqMJ6C7YV55KAxjhZ5znYn6A3f1c341Lcg/edit#slide=id.g25a881cd3a5_0_166
https://jojoldu.tistory.com/418
https://github.com/redis/lettuce/wiki/Connection-Pooling
https://learn.microsoft.com/ko-kr/azure/azure-cache-for-redis/cache-best-practices-connection
https://velog.io/@komment/Spring-Boot-Redis-Cluster-with-lettuce-redisson
https://iizz.tistory.com/200
https://blog.leocat.kr/notes/2022/04/15/lettuce-config-for-redis-cluster-topology-refresh
이하공백
'전공영역 공부 기록' 카테고리의 다른 글
pod 안전성 설정 - Readiness gate (0) | 2024.12.24 |
---|---|
EKS Auto Mode는 무엇이고 왜 출시되었을까? (2) | 2024.12.15 |
쿠버네티스 버전을 업그레이드 할 때 어떤 전략을 선택해야할까? (in-place, 복제) (0) | 2024.10.20 |
hashicorp vault secret엔진을 v2로 업그레이드하는 방법 (0) | 2024.10.13 |
Hashicorp vault 소개 (0) | 2024.10.11 |