티스토리 뷰

728x90

기본적으로 Aurora를 사용한다면 마스터 노드는 쓰기(update) 등을 담당하고, 슬레이브 노드는 읽기를 담당한다.

 

Aurora RDS postgres를 사용하던 중 마스터 노드가 failover 되었으면 슬레이브 노드 중 장애 조치 우선순위가 높은 노드가 마스터 노드가 된다.

 

문제는 failover 되었을 경우 기존에 WAS와 맺어놓은 컨넥션들은 수동으로 이를 탐지하지 못하고 이전의 마스터 노드와 TCP 연결을 맺어놓은 컨넥션들은 이전의 마스터 노드로 요청이 간다.

 

그렇다면 무슨 문제가 발생할까? 

읽기 노드에서는 UPDATE를 처리할 수 없다는 에러를 맞이하게 된다. 따라서 Failover에 대해 어떻게 대응할 것인지에 대한 전략이 필요하다.

 

AWS 공식문서에는 다음과 같은 방법을 권유하고 있다. 

https://docs.aws.amazon.com/ko_kr/AmazonRDS/latest/AuroraUserGuide/AuroraPostgreSQL.BestPractices.FastFailover.html

 

Amazon Aurora PostgreSQL를 사용한 빠른 장애 조치 - Amazon Aurora

Amazon Aurora PostgreSQL를 사용한 빠른 장애 조치 다음에서는 장애 조치가 가능한 한 빨리 발생하도록 하는 방법을 배울 수 있습니다. 장애 조치 후 신속하게 복구하기 위해 Aurora PostgreSQL DB 클러스터

docs.aws.amazon.com

// Sets internal TTL to match the Aurora RO Endpoint TTL
java.security.Security.setProperty("networkaddress.cache.ttl" , "1");
// If the lookup fails, default to something like small to retry
java.security.Security.setProperty("networkaddress.cache.negative.ttl" , "3");

위 옵션은 JVM DNS 캐시 시간을 짧게 가져가는 전략이다.JVM에 DNS에 대한 캐시를 짧게 가져간다는 것은 Aurora에서 제공해주는 두개의 엔드포인트( 클러스터 엔드포인트, 읽기 전용 엔드포인트)가 바라보는 실제 DB node에 대한 IP의 캐시를 짧게 가져간다는 것인데 이는 이미 적용이 되어있는 상황임에도 불구하고 장애를 맞이하게 되었다. 

 

이유를 분석하기 위해 수동으로 Failover 시켜주었고 정상 동작까지 시간을 보았더니 30분이라는 시간이 걸렸다.

conn lifetime 30분

 

컨넥션풀에 살아있는 컨넥션의 수명시간이 남아있다면 이미 TCP 연결을 맺어놓은 컨넥션은 기존의 DB node에 계속 요청이 정상적으로 흘렀던 것이고 이를 DB node에서는 처리할 수 없어 오류를 뱉은 것이다. 

 

HikariCP를 사용하고 있던 저자는 기존의 컨넥션의 lifetime으로 설정되어 있던 30분을 1분으로 수정하고 POC를 진행했다.

spring:
  datasource:
    hikari:
      pool-name: 
      auto-commit: false
      driver-class-name: org.postgresql.Driver
      jdbc-url: jdbc:postgresql://
      minimum-idle: 30
      maximum-pool-size: 30
      connection-timeout: 30000 # 30초
      max-lifetime: 60000     # 1분

conn lifetime1분

 

약 1분이라는 시간이 지나고(컨넥션을 사용하고 있으면 시간이 늘어날 수도 있음)  WAS 쪽에서 컨넥션을 다시 맺기 때문에 정상적으로 처리가 되었다. pool에서 컨넥션을 1분마다 다시 만드는 것은 cost 의 문제가 되는데 이게 옳은 방법일까라는 생각이 들었다.

https://techblog.woowahan.com/7242/

위의 우아한 기술 블로그를 참고해보니 Aurora Failover 시의 유연한 작동을 위하여 50초로 설정한 것을 확인할 수 있었다.

 

필자는 AWS 공식 문서에서 제공하는 다른 방법을 시도해보기로 했다.

첫번째로 타겟 서버를 지정해주는 방식을 활용해보았다.

마스터 엔드포인트에는 &targetServerType=primary, 슬레이브는 &targetServerType=secondary 파라미터를 추가해주었다..

* preSecondary를 준다면 리더를 1순위로, 받을 수 없는 리더가 없다면 라이터로 줄 수 있다.

 

이 후 다시 수동으로 Failover 시켜 POC를 진행해보았다.

conn has been closed
30s timeout

 

파라미터에 타겟 서버를 지정하니, 이전 처럼 미리 만들어놓은 컨넥션의 TCP 연결을 사용하지 않는다. pool에서 getConnection을 할 때 targetServerType을 검증하는데 이에 실패한다. 30초가 지나도 해당하는 컨넥션이 생기지 않아 타임아웃이 발생하고 컨넥션이 closed 된 이후 재연결을 시도한다. 내부적으로 targetServerType을 체크하는 로직이 추가되어 이전 처럼 기존 연결된 DB node에 요청이 가기전에 handshake 하는 단계가 추가된 것 같았다.

targetServer

pool에서 컨넥션을 가져오는 timeout 설정 시간인 30s 이후 정상 요청을 확인할 수 있었다. 

 

두번째로 연결문자열 방식을 사용하였다. 

jdbc-url에 두 개의 엔드포인트 ( 클러스터, 리더)를 명시하고 targetServer를 지정하면 이 두 엔드포인트가 바라보는 node의 IP를 확인하고 이 중 targetServerType을 확인하여 connection을 생성하는 구조이다. 

jdbc:postgresql://myauroracluster.cluster-c9bfei4hjlrd.us-east-1-beta.rds.amazonaws.com:5432,
myauroracluster.cluster-ro-c9bfei4hjlrd.us-east-1-beta.rds.amazonaws.com:5432
/postgres?user=<primaryuser>&password=<primarypw>&loginTimeout=2
&connectTimeout=2&cancelSignalTimeout=2&socketTimeout=60
&tcpKeepAlive=true&targetServerType=primary

이 방법이 failover 시 가장 빠르게 장애 복구하는 것을 확인하였다.

연결 문자열 설정

처음 예상에는 jdbc-url에 두개의 엔드포인트를 연결하여도 이전에 첫번째 방법처럼 만들어놓은 pool 안에 컨넥션들은 이미 맺어놓은 tcp에 의해  timeout을 받을거라 생각했는데 컨넥션을 만들고 이에 해당하는 DB node의 IP를 사용하기에, timeout을 받기전에 새 연결을 시도하게 되어 가장 유연하게 활용할 수 있었다. JVM DNS cache와 같이 가져가야하는 옵션인 것 같다. 

 

마스터노드가 failover 되기전에 상황을 인지하고 대응해야하지만 피치 못할 사정이 생겨 Failover시 이를 사용하는 connection 설정 쪽에서 WAS가 됐든 추가 작업이 있어야 다운타임 시간을 최소화 할 수 있다. 다양한 방법마다 Trade off가 발생하기에 상황에 맞게 가장 좋은 최선책을 고르는 것이 중요하지 않을까 싶다.

'DevOps > 기타' 카테고리의 다른 글

도메인 강제로 바꾸기 (127.0.0.1)  (0) 2022.09.15
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
글 보관함