11번가 Core플랫폼개발팀이 Eureka 서버를 Multi-Zone 으로 구성하면서, IDC 와 EKS 의 서비스 디스커버리를 통합하던 Kubernetes Operator 인 eurekube-operator 에 Zone Failover 를 구현한 기록이다. Spring Cloud LoadBalancer 의 zone-preference 만으로는 Zone 전환이 안 돼, 실패한 인스턴스를 캐시에 담고 주기적으로 만료시키는 계층을 직접 넣었다.
핵심 포인트- 배경은 2016년 모놀리식을 MSA 로 전환해 만든 Spring Cloud 기반 Vine 플랫폼이다. 현재 약 720개 인스턴스와 70여 개 애플리케이션이 그 위에서 돈다.
- eurekube-operator 는 IDC 의 Client-side Service Discovery 와 EKS 의 Server-side Service Discovery 를 통합해 마이크로서비스 간 호출의 장소 투명성을 유지한다.
- Zone Failover 가 없으면 한 Zone 의 Eureka Server 가 죽었을 때 같은 Zone 인스턴스 정보가 동기화되지 않아, 다른 Zone 의 클라이언트가 대상 정보를 조회하지 못한다.
- 우선 참조 대상을 IDC 에서 EKS 로 바꾸면 Network Hop 이 줄어 비용도 저렴해진다. IDC 와 EKS 간 Replication 이 있어 EKS 에 등록해도 IDC 로 복제된다.
- Eureka Client 의존성이 있다면 availability-zones 설정과 재시도 관련 property 로 해결되지만, 이 Operator 는 WebClient 를 직접 등록해 쓰기 때문에 직접 구현해야 했다.
상세 정리- Operator 는 fabric8 의 Java 기반 K8s Client 로 대상 서비스 클라이언트의 Endpoint 정보를 가져와 Eureka Server 에 register, sendHeartbeat, unregister 를 수행한다.
- 요청에는 Spring WebClient 빈을 쓰고, Spring Cloud LoadBalancer 의 ReactorLoadBalancerExchangeFilterFunction 으로 Eureka 인스턴스를 임의 선택하는 로드밸런싱을 붙여뒀다.
- Spring Cloud LoadBalancer 는 Spring Cloud Commons 의 일부로 아키텍처가 Netflix Ribbon 과 같지만, Ribbon 이 블로킹 RestTemplate 만 지원하는 반면 논블로킹 WebClient 도 지원한다.
- 기본 필터 함수에는 재시도가 없어 선택한 인스턴스로의 요청이 실패하면 곧바로 다른 인스턴스를 고른다. retry 옵션을 켜면 재시도 기능이 있는 구현체 빈이 활성화된다.
- 로드밸런싱 정책은 RoundRobin 과 Random 두 가지이고 기본값은 RoundRobin 이다. ServiceInstanceListSupplier 가 인스턴스 목록을 공급한다.
- ServiceInstanceListSupplier 에는 로드밸런싱 전후에 실행할 메서드가 있어, 인스턴스를 반환하기 전과 WebClient 가 요청을 보내기 전 시점에 개입할 수 있다.
- zone-preference 설정을 쓰면 선호 Zone 의 인스턴스를 우선 고른다. 다만 선택만 할 뿐, 해당 Zone 의 모든 인스턴스가 실패해도 다른 Zone 으로 넘어가지 않는다.
- 그래서 두 가지 요구를 직접 만족시켜야 했다. 한 Zone 의 모든 인스턴스가 실패하면 다른 Zone 으로 전환할 것, 그리고 일정 시간이 지나면 원래 선호 Zone 으로 되돌아갈 것이다.
- 이 두 요구는 Eureka Client 쪽의 RetryableEurekaHttpClient 와 SessionedEurekaHttpClient 가 하던 일에 각각 대응한다.
- Eureka Client 의 세션은 기본 20분이며 20분에서 절반 범위로 흔들리다 새로 초기화돼, Primary Zone 이 복구되면 이상적 상태로 돌아간다.
- 구현은 Cache Layer 를 추가하는 방식이다. 요청이 실패한 인스턴스 정보를 캐시에 담아 로드밸런서가 인스턴스를 고를 때 참고하게 하고, 특정 주기로 캐시를 만료시켜 초기 상태로 되돌린다.
- 캐시 만료는 특정 인스턴스에 계속 붙어 있는 sticking 을 막는 역할도 한다. 높은 네트워크 지연 상황에서는 빠르게 실패하도록 설정을 둔다.
- 이 글은 Eureka 서버를 지역 분산시킨 1부와 Chaos Test 로 문제를 찾은 2부에 이은 3부이며, 앞선 글을 먼저 읽기를 권한다.
왜 읽나Eureka 를 여러 Zone 으로 늘렸는데 Failover 가 자동으로 따라오지 않아 막힌 팀에게, Spring Cloud LoadBalancer 의 확장 지점과 캐시 기반 Zone 전환 구현을 짚어주는 글.