본문 바로가기
0️⃣Algorithm&자료구조&codingTest/Book&Study

[데이터 중심 애플리케이션] - 8장 분산시스템의 골칫거리

by jyu_seo_ 2026. 1. 23.

Part 2. 분산 데이터

 

지난 몇개 장에서 되풀이 됐던 주제는 시스템이 잘못된것을 어떻게 처리하느냐였다.(복제 서버 장애 복구, 복제지연, 트랜잭션의 동시성 제어 등) 실제 환경의 시스템에서 나타날 수 있는 다양한 에지 케이스를 잘 이해할수록 이것들을 잘 처리할 수 있다.

 

이렇게 결함에 대한 이야기를 많이 했지만, 아직도 너무나 낙관적이다. 현실은 훨씬 더 비관적이다. 이제부터 잘못될 가능성이 있다는 것들은 모두 잘못된다고 가정한다.

 

분산 시스템은 단일컴퓨터에서 실행하는 소프트웨어를 작성하는 일과는 근본적으로 다르다. 이번장에서는 네트워크 관련문제와 시계/타이밍 문제를 조사하고, 이것들을 어느정도로 회피하여, 분산시스템을 유지할 수 있도록 보장을 제공하는 알고리즘을 살펴본다.

결국 엔지니어로서의 우리의 임무는 모든게 잘못되더라도 제 역할을 해내는 시스템을 구축하는 것이다.

 

결함과 부분 장애

단일 컴퓨터에서 프로그램은 상당히 예측가능한 방식으로 동작한다(동작하거나, 동작하지 않거나). 그 이유는

하드웨어가 올바르게 동작하면 같은 연산은 항상 같은 연산을 내며, 만약 하드웨어 문제가 발생한다면 잘못된 결과를 반환하지 않고 완전히 동작하지 않을 것이기 때문이다. 이는 컴퓨터를 설계할 때 의도적으로 선택한 것이다. 컴퓨터는 구현 기반이 되는 불분명한 물리적 현실을 감추고, 수학적 완벽함을 갖고 동작하는 이상화된 시스템 모델을 보여준다.

 

하지만 네트워크로 연결된 여러 컴퓨터에서 실행되는 소프트웨어에서는 상황이 근본적으로 다르다. 분산 시스템에서 이상화된 시스템모델은 동작하지 않으며, 물리적 세계의 지저분한 현실을 마주하게 된다.(전원 장애, 스위치 장애, HVAC 시스템 장애 등)

 

분산 시스템에서는 시스템의 어떤 부분은 잘 동작하지만 다른 부분은 예측할 수 없는 방식으로 고장날 수 있다. 이를 partial failure(부분 장애)라고 한다. 부분 장애는 비결정적이기 때문에 어떨 때는 동작하지만 어떨 때는 실패한다. 심지어 뭔가 성공했는지도 알지 못할 수도 있는데, 이는 메세지가 네트워크를 거쳐 전송되는 시간도 비결정적이기 때문이다.

 

비결정성과 부분 장애 가능성이 분산 시스템을 다루기 어렵게 한다.

 

클라우드 컴퓨팅과 슈퍼컴퓨팅

대규모 컴퓨팅 시스템 구축 방법에 관한 몇 가지 철학이 있다.

  • 대규모 컴퓨팅의 한쪽 끝에는 high-perfomance computing, HPC(고성능 컴퓨팅) 분야가 있다. 수천개의 CPU를 가진 슈퍼컴퓨터는 보통 일기예보나 분자 동력학처럼 계산 비용이 매우 높은 과학 계산 작업에 쓰인다.
  • 다른 극단에는 클라우드 컴퓨팅이 있다. 명확히 정의되지는 않지만, 멀티 테넌트 데이터센터, IP 네트워크(Ethernet으로 연결된)로 연결된 commodity(상용)컴퓨터, elastic/on-demand(신축적/주문식) 자원 할당, metered billing(계량 결제)와 흔히 관련돼 있다.
  • 전통적인 기업형 데이터센터는 이 두 극단의 중간 지점에 있다.

이런 철학에 따라 결합 처리 방법도 매우 다르다.

 

고성능 컴퓨팅의 경우 작업을 체크포인트로 지속성 있는 저장소에 저장한다. 만약 노드. 하나에 장애가 난다면 전체 클러스터를 멈추고, 작업 부하를. 중단하는 것이다. 복구 후에는 마지막 체크포인트부터 계산을 재시작 한다.

 

이 책에서는 인터넷 서비스를 구현하는 시스템을 집중적으로 다루며, 이는 보통 슈퍼컴퓨터와 매우 다르다.

 

지속성

  • 날씨 시뮬레이션 같은 오프라인(일괄 처리) 작업은 멈췄다 재시작해도 충격이 덜하다.
  • 반면 여러 인터넷 관련 애플리케이션은 언제라도 사용자에게 지연 시간이 낮은 서비스를 제공해야 한다는 점에서 온라인이다. 수리를 위해 클러스터를 중단시키는 것처럼 서비스를 이용할 수 없게 하는 것은 허용되지 않는다.

하드웨어

  • 슈퍼컴퓨터는 전형적으로 특화된 하드웨어를 사용해 구축한다. 각 노드는 매우 신뢰성이 높으며 노드 사이에는 공유 메모리와 remote direct memory access, RDMA(원격 직접 메로리 접근)을 사용해 통신한다.
  • 반면 클라우드 서비스의 노드는 상용 장비를 사용해 구축한다. 규모의 경제 덕에 낮은 비용으로 동일한 성능을 제공하지만 실패율도 높다.

네트워크

  • 거대한 데이터센터의 네트워크는 흔히 IP와 이더넷을 기반으로 하며 높은 bisection bandwidth(양단 대역폭)을 제공하기 위해 Clos topoloy(클로스 토폴로지)로 연결돼 있다.
  • 반면 슈퍼컴퓨터는 통신 패턴이 정해진 HPC 작업부하에서 높은 성능을 보여주는 다차원 mesh(메시)나 torus(토러스) 같은 특화된 네트워크 토폴로지를 자주 사용한다.

고장

  • 시스템이 커질수록 구성 용소 중 하나가 고장날 가능성도 높아진다. 시간이 지나면서 고장난 것은 수리되고 새로운 것이 고장나지만 수천 개의 노드가 있는 시스템에서는 항상 뭔가 고장난 상태라고 가정하는 게 합리적이다. 오류 처리 전략에 그냥 포기하는 것을 포함한다면 대형 시스템은 유용한 일을 하기보다 결함으로부터 복구하는 데 많은 시간을 쓰게 될 수도 있다.

운영/유지보수

  • 시스템이 장애가 난 노드를 감내할 수 있고 전체적으로는 계속 동작할 수 있다면 이는 운영과 유지보수에 매우 유용한 특성이 된다. 이를테면 끊김 없이 사용자에게 서비스를 계속 제공하면서 한 번에 노드 하나씩 재시작하는 순회식 업그레이드를 할 수 있다. 클라우드 환경에서 가상 장비 하나의 성능이 좋지 않으면 그냥 그것을 죽이고 새 가상 장비를 요청할 수 있다(새로 할당된 것은 더 빠르기를 바라면서).

배포

  • 지리적으로 분산된 배포(지연 시간을 줄이기 위해 사용자와 지리적으로 가까운 곳에 데이터를 보관)를 할 때 통신은 대부분 인터넷을 거치기 쉬운데, 이는 로컬 네트워크에 비해 느리고 신뢰성도 떨어진다.
  • 반대로 슈퍼컴퓨터는 일반적으로 모든 노드가 가까운 곳에 함께 있다고 가정한다.

분산 시스템이 동작하게 만들려면 부분 장애 가능성을 받아들이고 소프트웨어에 내결함성 메커니즘을 넣어야 한다. 바꿔 말하면 신뢰성 없는 구성요소를 사용해 신뢰성 있는 시스템을 구축해야 한다.

 

몇 개의 노드로만 구성된 작은 시스템이라도 부분 장애를 고려하는 것은 매우 중요하다. 구성 요소들이 대부분 올바르게 동작하겠지만,조만간 어떤부분에 결함이 생길것이고, 소프트웨어가. 그 결함을 처리해야 한다. 결함 처리는 소프트웨어 설계의 일부여야 한다. 분산시스템에서 생길수 있는 결함을광범위하게 고려하고 테스트하는 것이 중요하다.

신뢰성 없는 네트워크

이 책에서 주로 다루는 분산 시스템은 비공유 시스템, 즉 네트워크로 연결된 다수의 장비다. 각 장비는 자신만의 메모리와 디스크를 갖고 있으며, 다른 장비에 접근하기 위해서는 이 장비들이 통신하는 유일한 수단인 네트워크를 사용해야 한다.

 

비공유가 시스템을 구축하는 유일한 방법은 아니지만, 몇 가지 이유로 인터넷 서비스를 구축하는 주된 방법이 됐다.

  • 특별한 하드웨어가 필요하지 않아서 상대적으로 저렴함.
  • 상품화된 클라우드 서비스를 활용할 수 있음.
  • 지리적으로 분산된 여러 데이터센터에 중복 배치함으로써 높은 신뢰성을 확보할 수 있음.

인터넷과 데이터센터 내부 네트워크 대부분(흔히 이더넷)은 asynchronous packet network(비동기 패킷 네트워크)다. 이런 종류의 네트워크에서 노드는 다른 노드로 메시지(패킷)를 보낼 수 있지만 네트워크는 메시지가 언제 도착할지 혹은 메시지가 도착하기는 할 것인지 보장하지 않는다. 따라서 요청을 보내고 응답을 기다릴 때 여러 가지가 잘못될 수 있다.

요청을 보낸 후 응답을 받지 못했다면(a) 요청이 손실됐는지, (b) 원격 노드가 다운됐는지, (c)응답이 손실됐는지 구별할수 없다.

 

  • 요청이 손실됐을 수 있다(누군가 네트워크 케이블을 뽑았을지도 모른다)
  • 요청이 큐에서 대기하다 나중에 전송될 수 있다(네트워크나 수신자에 과부하가 결렸을 수 있다.)
  • 원격 노드에 장애가 생겼을 수 있다.(죽었거나 전원이 나갔을 수 있다)
  • 원격 노드가 일시적으로 응답하기를 멈췄지만(가비지 컬렉션 휴지가 길어졌을수 있다). 나중에는 다시 응답하기 시작할수 있다.
  • 원격 노드가 요청을 처리했지만 응답이 네트워크에서 손실됐을 수 있다(네트워크 스위치의 설정이 잘못됐을 수 있다.)
  • 원격 노드가 요청을 처리했지만 응답이 지연되다가 나중에 전송될 수 있다(네트워크나 요청을 보낸. 장비에 과부하가 걸렸을수 있다.)

결국 전송 측은 패킷이 전송됐는지 아닌지조차 구별할 수 없으며, 그 이유도 알 수 없다. 알 수 있는 유일한 정보는 응답을 아직 받지 못했다는 사실 뿐이다.

 

이런 문제를 다루는 흔한 방법은 타임아웃이다. 타임아웃을 통해 얼마간의 시간이 지나면 응답 대기를 멈추고 응답이 도착하지 않는다고 가정한다. 그러나 타임아웃이 발생해도,  원격 노드가 응답을 받았을 수도 있고, 요청이 타임아웃 이후에 도착할 수도 있다.

 

현실의 네트워크 결함

수십년 동안 컴퓨터 네트워크를 구축했지만, 아직도 신뢰성 있는 네트워크를 구축하는 방법을 찾지는 못했다.

데이터센터처럼 제어된 환경에서도, 네트워크 문제는 다양한 이유로 발생한다. 또한 이런 문제는 네트워크 장비를 중복 추가하는 것으로는 기대만큼 결함을 줄여주지 못한다. 네트워크 중단의 주요 원인인 인적 오류로부터 보호해주지 못하기 때문이다.

EC2 같은 공개 클라우드 서비스 또한 일시적인 네트워크 결함이 자주 발생하며, 네트워크 문제는 소프트웨어 업데이트 떄문에, 상어가 해저 케이블을 물었기 떄문에, 네트워크가 한 방향으로만 흐르는 오류가 발생했기 때문에 발생한다.

결함은 일어날 수 있기 때문에 소프트웨어는 네트워크 결함을 처리할 수 있어야 한다. 그렇지 않으면 클러스터가 교착 상태에 빠지거나, 데이터가 날라가거나, 소프트웨어가 예측 못한 일을 할 수도 있다.

반드시 네트워크 결함을 tolerating(견뎌내도록) 처리할 필요는 없다. 평소에 네트워크가 믿을 만 하다면 간혹 발생하는 오류를 사용자에게 고지하면 된다. 하지만 소프트웨어가 네트워크 문제에 어떻게 반응하는지 알고 시스템이 그로부터 복구할 수 있도록 보장해야 한다. 따라서 Chaos Monkey(카오스 몽키)처럼 시스템을 고의로 고장내고, 이 상황에서의 시스템의 반응을 테스트하는 것이 일리가 있다.

결함 방지

많은 시스템은 결함 있는 노드를 자동으로 감지할 수 있어야 한다. 예를들어

  • 로드 밸런서는 죽은 노드를 그만 보내야 한다(즉, 죽은 노드는 순번에서 빠진 것으로 간주해야 한다).
  • 단일 리더 복제를 사용하는 분산 데이터베이스에서 리더에 장애나 나면 팔로워 중 하나가 리더로 승격돼야 한다

하지만 불행하게도. 네트워크에 관한 불확실성 때문에 노드가 동작 중인지 아닌지 구별하기 어렵다. 이런 상황에서 뭔가 동작하지 않는다고 명시적으로 알려주는 피드백을 받을 수도 있다.

  • 노드가 실행 중인 장비에 연결할 수 있지만 목적지 포트에서 수신 대기하는 프로세스가 없다면(예를 들어 프로세스가 죽었다면) 운영체제가 친절하게 RST나 FIN 패킷을 응답으로 보내서 TCP 연결을 닫거나 거부한다. 그러나 노드가 요청을 처리하다 죽었으면 원격 노드에서 데이터가 실제로 얼마나 처리됐는지 알 방법이 없다.
  • 노드 프로세스가 죽었지만(또는 관리자가 죽였지만) 노드의 운영체제는 아직 실행 중이라면 스크립트로 다른 노드에게 프로세스가 죽었다고 알려서 다른 노드가 타임아웃이 만료되기를 기다릴 필요 없이 빠르게 역할을 넘겨받을 수 있게 할 수 있다. 예를 들어 HBase가 이렇게 한다.
  • 데이터센터 내 네트워크 스위치의 관리 인터페이스에 접근할 수 있으면 질의를 보내 하드웨어 수준의 링크 장애(예를 들어 원격 장비의 전원이 내려갔는지)를 감지할 수 있다. 인터넷을 통해 연결하거나, 스위치 자체에 대한 접근을 할 수 없는 공용 데이터센터를 사용하거나, 네트워크 문제 때문에 관리 인터페이스에 연결할 수 없다면 이 선택지를 배제된다.
  • 접속하려면 IP 주소에 도달할 수 없다고 라우터가 확신하면 ICMP Destination Unreachable 패킷으로 응답할 수도 있다. 그러나 라우터가 마법 같은 장애 감지 능력이 없다면 네트워크의 다른 참여자들과 동일한 제한이 적용된다.

원격 노드가 다운되고 있다는 빠른 피드백은 유용하지만 여기에 의존할 수는 없다. TCP가 패킷이 전달됐다는 확인 응답(ack)을 했더라도, 애플리케이션 그것을 처리하기 전에 죽을 수도 있다. 요청이 성공했음을 확신하고 싶다면 애플리케이션 자체로부터 긍정 응답을 받아야 한다.

역으로 뭔가 잘못되면 스택의 어떤 수준에서 오류 응답을 받을지도 모르지만 일반적으로 아무 응답도 받지 못할 것이라고 가정해야 한다. 따라서 몇 번 재시도를 해 보고(TCP는 사용자 모르게 재시도를 하지만 애플리케이션 수준에서 재시도할 수도 있다) 타임아웃이 만료되기를 기다렸다가 타임아웃 내에 응답을 받지 못하면 마침에 노드가 죽었다고 선언할 수 있다.

 

타임아웃과 기약 없는 지연

타임아웃만이 결함을 감지하는 확실한 수단이라면 타임아웃은 얼마나 길어야 할까?

  • 만약 타임아웃이 길다면 노드가 죽었다고 선언될 때까지 기다리는 시간이 길어지며, 사용자는 기다리거나 오류 메시지를 봐야한다.
  • 만약 타임아웃이 짧다면 결함을 빨리 발견하지만, 노드가 일시적으로 느려졌을 때도 죽었다고 잘못 선언될 수도 있다. 성급하게 노드가 죽었다고 선언하면 문제가 되는데, 해당 노드가 동작을 실행했고, 다른 노드가 또 동작을 실행하여 동작을 2번 실행하게 될 수도 있다. 또한 다른 노드에게 책무가 넘어가는 과정에서 노드와 네트워크에 오버헤드를 줄 수 있다. 만약 과부하 떄문에 노드의 응답이 느렸을 경우, 책무가 넘겨받은 노드가 또 과부화되어 연쇄 장애를 유발할 수 있다.

패킷의 최대 지연 시간이 보장된 네트워크를 가정한다면, 모든 패킷은 어떤 시간 d 내에 전송된다고 볼 수 있으며, 장애가 나지 않은 노드는 항상 요청을 r 시간 내에 처리한다고 볼 수 있다. 따라서 성공한 요청은 모두 2d + r 시간 내에 응답을 받는다고 보장할 수 있다. 이 시간을 초과하면 네트워크나 노드가 동작하지 않는다는 의미하고, 따라서 2d + r을 타임아웃으로 사용할 수 있다.

유감스럽게도 우리가 사용하는 시스템은 대부분 이 중 어떤 것도 보장하지 않는다. 비동기 네트워크는 unbounded delay(기약 없는 지연), 즉 패킷이 도착하는 데 걸리는 시간의 상한치가 없으며, 서버의 처리 속도를 보장할 수도 없다.

TCP VS UDP

  • TCP는 패킷이 손실되면 자동으로 재전송을 시도한다.
    • 애플리케이션에서는 이를 모르지만 그 결과로 생긴 지연으로 판단할 수 있다.
  • UDP는 흐름 제어를 하지 않고 손실된 패킷을 재전송하지 않는다. 네트워크 지연이 크게 변하게 하는 원인중 일부를 제거한다.
    • 그러므로, UDP는 지연된 데이터의 가치가 없는 상황에 선택하면 좋다.
    • 화상 회의나 인터넷 전화는 지연된 데이터의 가치가 없기 때문에 UDP를 사용한다.

동기 네트워크 vs 비동기 네트워크

  • 동기식 네트워크의 대표적인 예시는 전화 네트워크다. 전화네트워크는 극단적인 신뢰성을 가진다.
    • 전화 네트워크에서 통화할 때는 회선(circuit)이 만들어지며 통화가 끝날 때까지 유지된다.
    • 동기식 네트워크는 이미 특정 공간만큼의 회선이 할당되어 있기 때문에 데이터가 여러 라우터를 거치더라도 큐 대기 문제를 겪지 않는다.
  • 동기식 네트워크와 같이 회선을 할당하는 방식은 통화와 같은 초당 전송하는 비트 수가 고정되어 있는 경우 회선이 적절하지만 웹페이지 요청과 같이 순간적으로 몰릴수 있는 데이터 전송에 효율적이지 못하다.
  • 비동기 네트워크의 대표적인 예시는 인터넷이다. 인터넷은 대역폭을 동적으로 공유한다.
    • 전송 측은 가능하면 빨리 패킷을 보내기 위해 서로 밀치며 네트워크 스위치가 빈번하게 어떤 패킷을 보낼지(대역폭을 할당할지)결정한다. 이 방법은 큐 대기가 생길수 있지만 선로를 효율적으로 이용할 수 있다.
    • 이 방식은 자원을 최대한 효율적으로 사용한다. 하지만 지연이라는 큰 변동이 생기게 된다.
    • 통화와 같이 회선을 점유하는 방식은 해당 회선이 점유한 대역폭만큼을 계속 보유하기 때문에 실제 대역폭 만큼 데이터를 전송하지 않더라도 대역폭은 계속 할당된다. 대신 지연의 변동은 적다.

신뢰성 없는 시계

네트워크에 있는 개별 장비는 자신의 시계를 갖고 있다. 이 장치는 완벽히 정확하지 않아서 각 장비는 자신만의 시간 개념이 있으며 이는 다른 장비보다 약간 빠를 수도 느릴 수도 있다.

일 기준 시계 대 단조 시계

  • 현대 컴퓨터는 최소 두 가지 종류의 시계를 갖고 있다. 일 기준 시계(time-of-day clock)와 단조 시계(monotinic clock)다.

1. 일기준 시계

  • 일 기준 시계는 벽시계 시간이라고도 하며 현재 날짜와 시간을 반환한다.
    • Java System.currentTimeMillis()는 epoch이래로 흐른 밀리초를 반환한다.
  • 일 기준 시계는 보통 NTP로 동기화 한다.
    • NTP로 동기화를 하더라도 네트워크 지연이 있기 때문에 모든 분산 시스템에서 완벽히 동일한 일 기준 시계를 가지는건 불가능하다.

2. 단조 시계

  • 단조 시계는 항상 앞으로만 흐르는 시계로 컴퓨터 별로 고유한 값을 가진다.
    • Java의 systemnanoTime()가 대표적인 예다.
    • 컴퓨터 별로 고유하기 때문에 다른 컴퓨터의 단조 시계와 비교하는건 의미가 없다.
  • 단조 시계는 타임아웃이나 서비스 응답 시간 같은 지속 시간과 같이 두 시점 사이에 흐른 시간이 얼마인지 재는데 적합하다.
  • 로컬 시계가 NTP보다 빠르거나 느릴 때 단조시계가 진행하는 진도수를 조정할 순 있지만 단조 시계가 앞이나 뒤로 뛰게 할수는 없다.
  • 단조 시계의 해상도는 보통 상당히 좋기 때문에 분산 시스템에서 경과 시간을 재는데 단조시계를 쓰는것이 일반적으로 좋다.

시계 동기화의 정확도

  • 하드웨어 시계와 NTP의 시계는 정확하지 않다. 다양한 사례로 시계의 정확도가 어긋날 수 있다.
    • 장비의 온도에 따라 하드웨어 시계에 영향을 줄 수 있다.
    • NTP 서버와의 지연으로 인해 오차가 발생할 수 있다.
  • 카산드라는 충돌 해소 전략으로 최종 쓰기 승리(LWW)를 사용하는데 시계는 정확하지 않기 때문에 이로 인해 문제가 발생할 수 있다.
    • 가장 최근 값을 유지한다 하더라도 결국 최근의 정의는 로컬 일 기준 시계에 의존하기 때문에 완벽히 정확할 수 없다는 것을 아는게 중요하다.

신뢰 구간을 활용한 순서 보장

  • 분산 시스템에서 각 시스템별로 시간차를 보장할 수 있는 신뢰 구간이 있다면 이를 통해 순서를 보장할 수 있다.
    • 신뢰 구간이 5ms이고 A작업이 1ms에 시작됐고 B작업이 7ms에 시작됐다고 하면 A작업은 B작업보다 빠른 시점에 수행되었음을 확신할 수 있다.
    • 이런식으로 순서를 보장하기 위해선 신뢰 구간까지 기다려야 하기 때문에 신뢰 구간을 최대한 짧게 유지하는 것이 중요하다.

지식, 진실, 그리고 거짓말

진실은 다수결로 결정된다.

  • 분산 시스템은 한 노드에만 의존할수 없다. 노드는 언제든 장애가 나서 잠재적으로 시스템이 멈추고 복구할 수 없게 될수 있다. 각 노드는 자신의 판단을 믿을수 없다.
  • 여러 분산 알고리즘은 정족수를 활용한다.
    • 정족수는 노드가 죽었다고 선언하는 것에 관한 결정에서도 사용된다.
    • 정족수를 이룬 노드들이 다른 노드를 죽었다고 선언하면 그 노드는 여전히 살아있을지 몰라도 죽었다고 간주되어야 한다.

리더와 잠금

  • 리더와 잠금을 분산 시스템에서 구현하려면 주의해야 한다.
    • 어떤 노드가 이전에 리더였더라도 네트워크가 잠시 중단되었을 뿐 실제로 그 노드가 살아있음에도 불구하고 다른 노드가 그 노드를 죽엇다고 선언해 새로운 리더를 선출했을 수도 있다.

 

  • 위 예시는 잠금을 잘못 구현해서 생긴 데이터 오염 버그를 보여준다.
  • 클라이언트 1이 임차권을 획득하고 stop-the-world로 중단위 되었을 때 임차권이 만료되어 클라이언트 2가 쓰기를 수행했지만 클라이언트 1은 여전히 임차권을 보유했다고 잘못 판단하여 쓰기 충돌이 발생한다.

펜싱 토큰

  • 위와 같은 문제를 해결하기 위한 단순한 기법으로 펜싱(fencing)기법이 있다.

  • 잠금 서버가 잠금이나 임차권을 승인할때 마다 값이 하나씩 증가하는 펜싱 토큰도 반환한다.
  • 클라이언트가 쓰기 요청을 보낼 때 자신의 현재 펜싱 토큰을 포함하도록 하여 이 토큰 값을 비교하여 쓰기를 수행하도록 판별할 수 있다.

비잔틴 결함

  • 펜싱 토큰은 부주의에 의한 오류에 빠진 노드를 감지하고 차단할 수 있다. 그러나 노드가 고의로 시스템 보장을 무너뜨리려 한다면 가짜 펜싱 토큰을 보내기만 하면 된다.
  • 보통 노드들이 신뢰성은 없을 수 있지만 정직하다고 가정한다. 노드가 거짓말을 할지도 모른다는 위험이 있다면 훨씬 더 어려워진다.
  • 이러한 동작을 비잔틴 결함 이라고 하며 이렇게 신뢰할 수 없는 환경에서 합의에 도달하는 문제를 비잔틴 장군 문제 라고 한다.
    • 일부 노드가 오작동하고 악의적인 공격자가 네트워크를 방해하더라도 시스템이 계속 올바르게 동작한다면 비잔틴 내결함성을 지닌다고 한다.
    • 이런 관심사는 항공기와 같은 시스템에서 필요로 한다.
  • 대부분의 서버 측 데이터 시스템에서는 조직이 모든 노드를 제어하고 관리하기 때문에 비잔틴 내결함성 솔루션을 배치하는 것을 실용적이지 못하다.
  • 비잔틴 내결함성은 중앙 권한 없는 Peer-to-peer 네트워크에 더 적합하다.

정리

분산 시스템에서 나타날 수 있는 신뢰성 문제는 광범위 하다.

  • 네트워크로 패킷을 보내려고할 때 언제나 패킷이 손실되거나 지연될 수 있다. 응답도 손실되거나 지연될 수 있으므로 응답을 받지 못하면 메세지가 잘 전달됐는지 확신할 수 없다.
  • 노드의 시계는 다른 노드의 시계와 심하게 맞지 않을 수 있고 시간이 갑자기 앞뒤로 뛸 수도 있다.
  • 프로세스는 실행 도중 어느 시점에서든지 상당한 시간동안 멈출 수 있고 다른 노드들에 의해 죽었다고 선언될 수 있으며 잠시 멈춘 노드는 자신이 죽었다는걸 알지 못할 수도 있다.

부분 실패가 생길 수 있다는 사실은 분산 시스템의 뚜렷한 특성이다.

  • 분산 시스템에서 우리는 구성요소의 일부가 고장 나더라도 전체로서의 시스템은 계속 동작할 수 있도록 부분 실패에 대한 내성을 소프트웨어에 내장하려고 노력한다.
  • 정확한 메커니즘이 없어 원격 노드의 생존을 파악하기 위해 타임아웃을 사용하지만 노드의 일시적인 중단, 네트워크 장애로 인해 확신할 수 없다.