High Availability : 기본적인 장애 감지 알고리즘의 속성과 종류
이 글은 특정 구현에 종속되는 내용을 제외한 이론 위주의 정리 글입니다.
장애 감지 알고리즘이 필요한 이유?
우리가 개발하는 서비스가 단일 시스템이 아닌 분산 시스템 구조를 지닌다면 특정 서비스에 장애가 발생하더라도 모든 서비스 노드에게 전파되지 않기 때문에, 사용자 요청으로 인해 식별되기 전까지 모를 수 있습니다.
장애가 발생한 서비스에 따라서 시스템에 잘못된 값을 설정하거나 중복이 발생할 수 있고 (Index Sharding Service) 사용자가 보는 캐싱된 데이터가 소실되는 경우도 발생할 수 있습니다. (Feed Cache Service 등)
- 물론 모든 장애가 서비스 종료, 충돌과 같은 완전 중단 상태를 의미하는 것은 아니며, 상황에 따라 부하로 인한 응답 지연, 네트워크 지연 이슈 혹은 데드락으로 인한 것일 수 있습니다.(이런 상태는 시간이 지나면 해결되는 문제이기 때문입니다.) 그렇기에 좀 더 정확한 감지 방식이 필요합니다.
이러한 장애 상황을 빠르게 대처하기 위해 필요한 것이 장애 감지 알고리즘 혹은 감지 방식 입니다. 이러한 방식은 실제 구현하는 장애 감지 서비스뿐만 아니라, Redis, Zookeeper, Kafka, Cassandra, Akka 등의 Opensource 등에서도 사용되고 있습니다.
장애 감지 알고리즘의 속성?
장애 감지 알고리즘의 속성으로는 크게 완전성, 효율성, 정확성이 있으며 이중 효율성과 정확성은 사용하는 방법에 따라서 조정이 가능한 속성입니다.
특정 자료에서는 완성도와 정확도 만을 이야기하기도 합니다.
완전성
특정 서비스에 장애가 발생하면 모든 서비스는 해당 서비스에 장애가 발생한 사실을 인지하고 이와 관련된 처리를 수행하여 결과를 반환하여야 한다는 속성입니다.
효율성
얼마나 빨리 장애를 감지하는지 평가하는 속성이자 척도입니다. 이는 장애 상태에 빠진 서비스를 얼마나 빨리 감지하느냐 에 따라서 효율성을 결정하게 됩니다.
정확성
얼마나 정확하게 장애를 감지했는지 평가하는 속성이자 척도입니다. 이 속성은 위에서 잠깐 언급했던 것과 같이 정상적인 서비스이지만 다량의 요청을 처리하며 발생하는 부하, 네트워크 지연 이슈 혹은 데드락으로 인해 일정 시간 동안 응답하지 못하는 상태의 서비스를 장애로 판단하느냐 에 따라서 정확성을 결정하게 됩니다.
효율성과 정확성의 트레이드 오프
정확성을 설명하면서 장애 감지 시스템의 결과가 거짓일 수 있다는 점을 알려드렸습니다. 즉 정상 프로세스에 장애가 발생하였다고 잘못 판단하는 경우가 있는데 이러한 경우는 효율성이 높은 알고리즘에서 주로 발생하게 됩니다.
효율성을 중시하는 알고리즘일수록 빠르게 결과를 도출하기 위해 검증하는 시간과 로직을 최소한으로 하기 때문입니다. 그렇다면 반대로 정확성을 중시하는 알고리즘은 장애를 감지하고 판단하는데 시간이 오래 걸리지만 좀 더 적은 리소스로 서비스의 상태를 관리할 수 있습니다.
- 여기서 말하는 리소스는 이는 잘못된 결과로 인해 서비스를 죽이고 다시 배포하는 등의 작업 리소스를 이야기합니다.
결국 효율성과 정확성을 완벽하게 충족하는 장애 감지 서비스는 없으며, 상황에 따라서 올바른 감지 시스템을 구성하고 사용하는 것이 중요합니다.
Ping과 Heartbeat
Ping과 Heartbeat는 유사한 장애 감지 방식입니다. 장애를 감지하는 서비스는 주기적으로 다른 서비스들에게 메시지를 보내고(broadcasting) 결과를 받아 정상적인 상태임을 판단하게 됩니다.
이 2가지 방식은 Timeout을 사용하느냐(Ping), 하지 않느냐(Heartbeat)로 구분 지을 수 있습니다.
여기에서 설명하는 Heartbeat는 All to All 방식의 Heartbeat입니다.
Ping은 메시지를 보내는 주기와 Timeout 설정을 기준으로 감지 정확도가 좌우됩니다. 그렇기에 서비스 A에게 보낸 1번째 메시지의 응답이 2번째 메시지 전송 이후에 도착한다면, 장애 감지 시스템은 이 서비스를 장애가 발생한 서비스로 판단할 수 있습니다.
- 즉 특정 서비스의 상태를 파악할 수 있는 방법이 아니며, 요청에 대한 결과라는 단순한 지표를 기준으로 장애를 탐지하게 됩니다.
- 이는 완전성을 지키고 효율성 또한 극대화되었으나, 정확도가 낮은 장애 알고리즘임을 의미합니다.
Heartbeat는 Timeout을 사용하지 않는 대신에 각각의 서비스가 특정 서비스에 몇 번의 요청을 전송하였는지 기록하여 설정된 임계 값에 도달하였을 때 장애로 판단하는 방식입니다. 그렇기에 정확도를 높이기 위해서는 적절한 임계 값 설정이 필요합니다.
- 초기에 전송되는 메시지에는 발신자의 정보와 동일 메시지의 중복 전송을 방지하기 위해 고유한 식별자를 기록하게 됩니다.
Heartbeat outsourcing
Heartbeat outsourcing은 기본적으로 Heartbeat의 동작 방식을 따르나 각각의 서비스가 모든 서비스에게 메시지 요청을 보내는 것(broadcasting) 이 아니라 인접한 서비스에만 메시지를 날려 장애를 감지하고 판단하는 방식입니다.
그렇기에 각 서비스는 모든 서비스 존재를 알고 있지 않도록 범위를 나누어 메시지 요청으로 인한 장애 감지의 신뢰성과 떨어진 효율성을 어느 정도 보완하게 됩니다.
- 각 서비스는 주변에 위치하는 서비스들에게만 요청을 전파하는 방식으로 동작합니다.
- Heartbeat를 사용하는 서비스의 수가 수백 또는 수천 개라고 가정하였을 때, 패킷이 지나는 네트워크의 크기가 커지고 자연스레 네트워크 이슈가 발생할 여지도 증가하여 장애로 잘못 판단하는 경우가 늘어날 수 있기 때문입니다.
φι-accrual detection
이 방식은 서비스의 상태를 프로세스 충돌 확률을 연속적으로 계산하고(Time series) 이를 통해 장애를 결정하는 방식입니다.
- cassandra, akka, Hazelcast 등의 opensource가 해당 방식을 사용 및 지원하고 있습니다.
우선 특정 배열이나 리스트 형태의 자료구조 또는 TDB에 메시지 응답 도착 시간을 저장하고 Sliding Window 알고리즘을 활용하여 일정 범위의 값을 읽어와 다음 메시지의 응답 시간 또는 응답 확률을 예측한 뒤 실제 메시지를 요청하여 연산된 결과와 비교합니다.
그리고 비교한 결과를 통해 Suspicion level을 계산하여 이를 기준으로 노드의 중단, 부하 또는 정상 상태를 판단하게 됩니다.
- Suspicion level 임계 값을 동적으로 조정할 수 있으며, 다양한 임계 값을 지정하여 여러 가지 행위를 수행할 수도 있습니다.
φι-accrual detection을 사용하기 위해서는 지속적인 메시지에 대한 표본을 구하고 이를 저장할 자료구조를 가지고 있어야 하며, 표본에 대한 평균과 분산 값을 계산하여 분포 도를 추정한 뒤 도착할 확률과 프로세스 활성 상태의 정확도를 나타내는 φι를 계산해야 합니다.
- 또한 특정 임계 값을 지정하여 상태를 결정하는 방법과 상태에 따른 Callback을 구성하여야 합니다.