2 minute read

오류를 감지하는 가장 확실한 방법은 타임아웃이다. 그렇다면 얼마나 타임아웃을 길게 설정해야 할까?

타임아웃을 길게 설정할까? 짧게 설정할까?

타임아웃을 길게 설정하면, 노드가 죽었음을 판단하기까지 사용자는 긴 시간을 기다리거나 에러 메시지를 볼 수 있다.

타임아웃을 짧게 설정하면, 빠르게 문제를 파악할 수 있지만, 네트워크의 부하 스파이크가 발생하는 것 처럼 일시적인 지연이라면 오판할 수 있다. 이 뿐만이 아니라, 타임아웃을 짧게 설정하면 문제가 더 심각해진다. 한 노드가 실제로 살아 있지만, 잠시동안 메일을 보내는 도중에 응답을 할 수 없는 상황이라고 하자. 이때 다른 노드가 그 작업을 대신 받아서 처리한다면, 결국 두 번 처리가 되는 것이다.

패킷의 지연시간을 최대로 보장하는 네트워크 시스템을 가정해보자.

  • 모든 패킷은 시간 d 내에 전달되며 시간 d 보다 더 걸리지 않는다.
  • 실패하지 않은 노드는 항상 일정 시간 r 이내에 요청을 처리할 수 있다.
  • 따라서, 모든 요청이 성공한다면, 2d + r 시간 내에 응답을 받는다.

하지만, 위와 같은 타임아웃 계산의 문제점은 무엇일까? 실제 사례에서는 이러한 시스템이 없다. 대신, 비동기 네트워크로 구성되어 무제한 지연을 야기한다. 다시 말해, 패킷을 빠르게 전달하는 것이 우선이며 패킷이 도착하는 것에는 상한선을 두지 않는다. 또한, 대부분의 서버 구현은 최대 시간 내에 요청을 처리할 수 있다고 보장할 수 없다. 타임아웃을 섣불리 낮게 설정해버리면 패킷이 왕복하는 시간이 일시적으로 급증하여도 시스템의 균형이 깨질 수 있다.

네트워크 혼잡과 대기열

차를 패킷에 비유하고 네트워크를 도로라고 비유하자. 차가 도로를 왕복하는 시간은 도로 혼잡도에 따라 다르다. 네트워크 상의 패킷이 지연되는 다양한 이유는 대기열(queueing) 때문이다.

  • 여러 노드가 동일한 목적지에 패킷을 동시다발적으로 보내면,네트워크 스위치는 큐에 채워 넣는다. 만약 스위치의 큐가 가득차면 패킷이 손실(drop)된다.
  • 목적지까지 패킷이 도달했지만, 모든 CPU 코어가 작업중일 때, 운영체제는 들어오는 요청 패킷을 큐에 넣는다.
  • 가상화 환경에서 구동중인 하나의 운영체제는 다른 가상 머신이 CPU 코어를 사용 중일때 수십 밀리초 동안 멈춘다. 이 시간 동안 VM은 네트워크로부터 요청을 처리할 수 없다. 따라서, 가상 머신 모니터가 요청 패킷을 대기열에 추가한다.
  • TCP는 흐름 제어(혼잡 회피 또는 역압)를 수행할 수 있다. 이는 노드가 과부하를 피하기 위해 보내는 속도를 제한할 수 있다는 의미다. 다시 말해, 데이터가 네트워크에 진입하기도 전에 큐에 들어가게 된다.

이러한 모든 요인들이 네트워크 지연에 의해 발생한다. 시스템이 최대한의 능력치에 도달할 때 대기열 지연은 더욱 큰 폭으로 늘어난다. 이로 인해 발생하는 문제점은 시스템이 큐를 비워버리게 된다는 것이다.

퍼블릭 클라우드는 내가 네트워크 지연 문제를 제어할 수 없다

AWS와 같은 퍼블릭 클라우드는 여러 고객들이 자원들을 공유하는 방식이다. 예를 들어, 네트워크 링크와 네트워크 스위치가 대표적이다. 만약 가까운 이웃이 많은 자원을 사용 중이라면 네트워크 지연을 겪을 수 있다.

그래서 우리는 타임아웃을 어떻게 결정해야할까? 정답은 통계적으로 결정해야 한다는 것이다. 가장 먼저, 충분한 기간에 걸쳐 네트워크 왕복 시간의 분포를 측정하는 것이다. 이후 애플리케이션의 특징을 고려하여 실패 감지 지연과 조기 타임아웃의 위험성 사이의 트레이드-오프를 정해야 한다.

더 좋은 방법은 타임아웃을 상수로 설정하는 것이 아니라, 시스템이 지속적으로 응답시간을 체크하여 타임아웃을 자동으로 조정하는 것이다. 이러한 방법은 Phi Accrual failure detector라는 알고리즘이 대표적이며, Akka와 Cassandra가 이러한 방식을 차용하고 있다.