분산 프로토콜 : SWIM

     

     

    1. 클러스터란 무엇인가?

    사용자 관점에서 클러스터는 '단일 머신'처럼 보이도록 만들어주고, 동적으로 변화하는 다른 서버 네트워크와 통신할 때 발생하는 모든 복잡성으로부터 안전하게 보호하는 역할을 한다. 클러스터에 많은 서버가 있더라도 사용자 입장에서는 하나의 서버로 접속하는 것처럼 보일 수 있고, API를 사용할 때도 하나의 서버와 통신하는 것처럼 동작하게 해준다. 

    그런데 클러스터가 이 기능을 구현하기 위해서 해결해야하는 프로토콜과 책임이 존재한다. 

     


    2. 클러스터 0계층

    아래는 클러스터가 가져야 할 가장 기본적인 부분이다. 이것은 클러스터 0계층이라고 부를 수 있다.

     

    1.1 어떻게 클러스터에 조인할까?

    클러스터에 조인하려는 새로운 서버 노드가 있다면, 이미 클러스터에 조인한 다른 노드와 통신하는 방법을 알아야 한다. 어디서 그런 정보를 찾을 수 있을까?

    • 서버 노드가 클러스터에 조인할 때, 컨택 포인트 리스트를 응답으로 전송
    • etcd, Zookeeper 같은 3rd party service를 사용. 이 서비스는 Node Registry 역할을 함. 
    • 호스트 환경에서는 k8s DNS, mDNS 서비스등을 이용할 수 있음. 

     


    1.2 클러스터에 속한 다른 노드를 어떻게 알 수 있을까?

    이 문제를 다루는 주요한 주제는 멤버쉽 프로토콜이다. 동적 클러스터에서는 Active discovered 노드를 추적하고, 노드가 가입/탈퇴하면 이를 업데이트하고 가십하는 방식으로 수행된다. 여기서 많은 결정은 배포 시나리오에 따라 달라진다. 예를 들어 동일 IDC 내에서 클러스터를 구성하는 서비스는 모바일 디바이스 메시와는 다른 특성을 가져야만 한다. 

    • SWIM : 동일 IDC 센터에서 호스팅되는 백엔드 서비스인 경우, 각 노드는 클러스터의 상태에 대한 전체 정보를 보관함. 
    • 다른 멤버쉽 프로토콜 (HyParView) : 클러스터의 일부만 볼 수 있게 해줌. 클러스터 규모가 큰 경우에 적합.

     


    1.3 한 노드에서 다른 노드로 메세지를 보내려면? 

    이것 역시 멤버쉽 프로토콜과 관련있다. 

    • 대부분의 IDC 지향 시스템은 시스템의 모든 노드가 다른 모든 노드에 연결되어 완전히 연결된 메시를 형성할 수 있따는 보수적인 가정을 가짐.
    • 다른 시나리오에서는 네트워크 특성으로 일부 노드가 다른 노드에 연결되지 못할 수도 있음. 

    일반적으로는 클라이언트 - 서버 아키텍쳐를 이야기하지만 클러스터 프로토콜에 대해서 이야기할 때는 Advanced한 시나리오를 적용해야한다. 왜냐하면 '서버' 자체가 '단일 엔티티'가 아니기 떄문이다. 

     


    1.4 죽은 노드를 어떻게 감지할 수 있을까?

    이것은 Failure Detection(실패 감지)로 알려져 있다. 가장 오래된 방법은 timeout 이내에 ping ↔ ack를 주고 받거나, 모든 Connection이 주어진 시간 간격 이내에 Heartbeat 메세지를 보내는 것이다. 

    • Heartbeat은 주로 전송 계층(TCP)에 직접 구현되기도 하고, 때로는 TCP 위에 실패 감지기를 올릴 수도 있다. 문제점은 TCP는 OS에 관리하기 때문에 응답성이 뛰어나지만, Application Layer는 그렇지 않을 수 있다. Application 계층에서는 Deadlock이 발생한다거나 할 수 있기 때문이다. 
    • 멤버쉽 프로토콜은 자체 Heartbeat 알고리즘을 사용하기도 한다. Heartbeat이 누락된 것이 노드가 죽은 것을 반드시 의미하지는 않는다. 예를 들어 Heartbeat을 받는 서버쪽에서 다른 요청을 처리하느라 과부하에 걸렸을 수도 있기 때문임.

    최근에 만들어진 프로토콜(예를 들면 SWIM, LifeGuard)은 위에서 이야기한 Heartbeat을 받는 서버쪽에서 다른 요청을 처리하느라 과부하에 걸렸을 수도 있음의 경우를 고려해서 죽는 노드를 감지해낼 수 있다.

     

     

    3.1 주기적인 네트워크 파티셔닝을 어떻게 감지하고 대응할 수 있을까?

    흔히 split-brain 시나리오로 알려진 문제다. 이것은 다음 관찰에서 발생한다.

    응답하지 않는 노드죽은 노드를 구분할 수 없다.

    이 경우 클러스터를 두 개로 분할하여 각 노드만 살아있다고 믿고 데이터 불일치, 데이터 손상을 유발할 수 있다. 예를 들어 여러 노드로 구성된 클러스터가 있을 때, 일부 노드가 네트워크 파티셔닝으로 다른 노드와 통신할 수 없게 되면, 각 노드는 '다른 노드는 죽은 노드'라고 판단하고 '나만 살아있는 노드'라고 판단할 수 있다. 즉, 여러 클러스터가 생기는 문제가 발생하고 각 클러스터는 독립적으로 작동한다. 그리고 각 노드가 가지고 있는 데이터나 상태는 서로 다를 수 있기 때문에 데이터 불일치 문제가 발생한다. 

     

    3.2 서로 다른 노드가 클러스터의 상태를 어떻게 추론하고 결정할 수 있을까?

    이 경우는 주로 분산 Database와 같이 데이터 관리를 담당하는 시스템에서 발생한다. 예를 들면 etcd가 있다. 각 노드는 들어오는 요청을 처리해야하는데, 네트워크 파티셔닝등으로 클러스터가 분리되었을 때 각 노드가 내리는 결정이 '서로 상충되는 결정'이 될 수 있다. 이 문제에 대한 일반적인 접근 방식을 살펴보자. 

    • Conflict 방지 : 일반적으로 노드가 결정을 내리기 전에 시스템 상태에 대한 합의를 도출해야한다는 전제를 이용해 충돌을 방지함. 이를 위해 노드들 사이에 리더를 설정하고 유지해야하며, Node Quorum을 이용해 동기화 해야한다. (Raft, ZAB, Paxos) 
    • Conflict 발생을 인정하고, Conflict 해결 : 긴 latency나 주기적으로 연결할 수 없는 서버가 많은 환경에서 고려하는 절충안이다. 모든 노드의 합의 없이도 각 노드가 독립적으로 최종 상태를 결정할 수 있게 해준다. 그리고 각 노드가 결정한 결정 사항이 최종적으로는 동일한 결론이 될 수 있도록 충분한 메타 데이터를 보강해준다.  (CRDTs)

    두번째 방식은 긴 latency가 있는 경우, 모든 Node의 Consensus를 이루는데 네트워크 문제로 실패할 가능성이 매우 크기 때문에 위와 같은 방식이 고려되기도 한다.

     

     

     

     

     

     

     

     

    https://www.bartoszsypytkowski.com/make-your-cluster-swim/

    댓글

    Designed by JB FACTORY