포스트

데이터 중심 애플리케이션 설계08


분산 시스템의 골칫거리

분산 시스템에서는 언제나 문제가 일어날 수 있다.

분산 시스템을 다루는 것은 한 컴퓨터에서 실행되는 소프트웨어를 작성하는 일과는 근본적으로 다르다. 서버 개발자가 분산 환경에서 마주하게 되는 가장 큰 도전은 부분 실패의 비결정적 특성을 다루는 것이다. 분산 시스템에서 시스템이 커질수록 구성 요소 중 하나가 고장날 가능성도 높다. 수천 개의 노드가 있는 시스템은 항상 뭔가 고장난 상태라고 가정하는게 합리적이다(실제로 argoCd 에 빨간불이 없는걸 본 적이 없다..)

결함 및 부분적 실패 (Faults and Partial Failures)

단일 컴퓨터 vs 분산 시스템

단일 컴퓨터 환경의 특징:

  • 완전히 작동하거나 완전히 고장 나는 이분법적 상태
  • 대부분의 버그는 잘못 작성된 소프트웨어의 결과
  • 예측 가능한 방식의 장애

분산 시스템 환경의 특징:

  • 부분적 실패(partial failure) 발생: 일부 구성 요소는 정상 작동하지만 다른 부분은 예측할 수 없는 방식으로 고장
  • 부분장애는 비결정적이라서 다루기 어렵다
  • 요청 성공 여부를 확신할 수 없는 상황 발생

슈퍼컴퓨터 vs 클라우드 컴퓨팅 철학

슈퍼컴퓨터: 부분적 실패를 전체 실패로 확대하는 경향 (작업을 체크포인트에서 재시작) 클라우드 컴퓨팅: 서비스 연속성을 위해 부분적 실패를 허용하도록 설계

신뢰성 있는 시스템 구축

신뢰성 없는 구성 요소를 사용해 신뢰성 있는 시스템을 구축해야 한다는 도전 과제는 이미 많은 곳에서 해결되고 있다. 대표적인 예가 바로 TCP over IP 이다:

  • IP는 신뢰성이 없다 (패킷 누락, 지연, 중복, 순서 변경 가능)
  • TCP는 손실된 패킷 재전송, 중복 제거, 순서에 맞는 재조립을 통해 신뢰성 확보
  • 따라서, TCP 위의 레벨에서는 패킷 문제를 신경쓰지 않아도 된다

신뢰할 수 없는 네트워크 (Unreliable Networks)

비공유 아키텍처와 비동기 패킷 네트워크

분산 시스템은 공유할 것이 없는(shared-nothing) 아키텍처를 기반으로 하며, 네트워크로 연결된 다수의 장비가 비동기 패킷 네트워크를 통해 통신한다.

네트워크 오류의 8가지 유형

네트워크 요청이 실패할 수 있는 방식들:

  1. 요청 손실 (네트워크 케이블 문제 등)
  2. 요청이 큐에서 대기하다가 나중에 전송
  3. 원격 노드 실패
  4. 원격 노드 일시 정지 후 응답
  5. 응답 손실
  6. 응답이 큐에서 대기하다가 나중에 전송

타임아웃: 분산 시스템의 유일한 오류 감지 방법

타임아웃만이 결함을 감지하는 확실한 수단이지만, 적절한 타임아웃 길이 설정은 매우 어려운 문제다:

타임아웃이 긴 경우:

  • 노드 실패 감지가 느려짐
  • 복구 시간 증가

타임아웃이 짧은 경우:

  • 일시적 지연을 실패로 오판할 위험
  • 불필요한 부하 전가 가능

적응형 타임아웃 메커니즘

관찰된 응답 시간 분포에 따라 타임아웃을 자동으로 조정할 수 있다.

네트워크 혼잡과 큐 대기

패킷 지연의 주요 원인들:

  • 네트워크 스위치에서의 큐 대기
  • 수신 측 운영체제에서의 큐 대기
  • 가상화 환경에서의 큐 대기
  • TCP 플로우 컨트롤(backpressure)

신뢰할 수 없는 시계 (Unreliable Clocks)

벽시계 vs 단조 시계

벽시계 (Time-of-day Clock):

  • 특정 시점을 측정
  • NTP 조정으로 인해 뒤로 점프하거나 앞으로 건너뛸 수 있음(시간이 거꾸로 가기도..)

단조 시계 (Monotonic Clock):

  • 항상 앞으로만 진행
  • 기간 측정에 적합
  • 분산 시스템에서 경과 시간을 재기 위해 단조 시계를 쓴다
  • 절대값은 의미가 없으며 다른 컴퓨터와 비교 불가(서버마다 서버시간이 다른 이유 ㅠ)

시계 동기화 문제

시계 정확도에 영향을 미치는 요인들:

  • 하드웨어 시계의 온도 민감성
  • NTP 서버와의 네트워크 지연
  • 가상화 환경에서의 시계 동기화 문제

마지막 쓰기 승리(LWW)의 위험성

카산드라는 충돌 해소 전략으로 최종 쓰기 승리(LWW)를 사용하는데 시계는 정확하지 않기 때문에 이로 인해 문제가 발생할 수도 있다. 벽시계 타임스탬프를 사용한 이벤트 순서 결정은 인과성 위반으로 이어져 데이터 손실을 초래할 수 있다.

Google Spanner의 TrueTime

정밀하게 동기화된 원자 시계를 사용하여 전역적으로 단조 증가하는 트랜잭션 ID를 제공하지만, 특수하고 고가의 하드웨어가 필요하다. 구글에서 API 를 제공하기는 함.

프로세스 일시정지 (Process Pauses)

일시정지의 원인

  • 가비지 컬렉션 (Stop-the-world GC): JVM에서 흔히 발생
  • 가상 머신 일시 정지: 하이퍼바이저 레벨에서의 일시 정지
  • 컨텍스트 스위칭: OS 레벨에서의 프로세스 전환
  • 페이징: 디스크로 스왑된 메모리 페이지 로딩
  • SIGSTOP 시그널: Unix 시스템에서의 프로세스 일시 정지

지식, 진실, 그리고 거짓 (Knowledge, Truth, and Lies)

분산 시스템에서의 진실

분산 시스템에서 노드는 자신의 시스템 상태에 대한 믿음이 항상 정확하다고 확신할 수 없다. 과반수(majority)의 노드들이 다른 노드를 실패했다고 선언하면, 해당 노드는 실패한 것으로 간주되어야 한다.

펜싱 토큰 (Fencing Token)

각 시스템 스템에서 락 서버가 락이나 리스를 부여할 때마다 펜싱 토큰(락이 부여될 때마다 증가하는 숫자)도 함께 생성한다.

펜싱 토큰의 작동 원리:

  1. 락 서버가 락을 부여할 때마다 단조 증가하는 토큰 생성
  2. 클라이언트가 저장소에 쓰기 요청 시 현재 펜싱 토큰 포함
  3. 저장소는 더 높은 번호의 펜싱 토큰을 받은 경우 낮은 번호 토큰의 요청 거부

이 메커니즘을 통해 잘못된 믿음으로 작동하는 노드가 시스템을 손상시키지 않도록 방지할 수 있다. 펜싱 토큰을 발급 / 검증하기 위한 zookeeper 와 같은 공통 모듈을 사용한다.

Split-Brain 시나리오와 해결책

Split-Brain 문제: 분산 시스템에서 2개 이상의 노드가 자신이 마스터 노드라고 생각하는 것. 데이터 불일치 문제를 야기할 수 있다.

해결 방법들:

  • 쿼럼 기반 시스템: 정족수 이상(쿼럼)의 노드들이 동의해야만 마스터로 선출이 가능하다.
  • 펜싱 메커니즘: 펜싱을 활용해서 1개만 선출되도록 한다.

비잔틴 결함

비잔틴 결함(Byzantine Faults):

  • 노드가 거짓말을 하거나 악의적으로 행동할 수 있는 경우, 문제를 훨씬 더 어렵게 만듦

비비잔틴 결함(Non-Byzantine Faults):

  • 대부분의 시스템이 가정하는 모델
  • 노드가 신뢰할 수 없지만 정직하다고 가정
  • 상대적으로 해결하기 쉬움(그리고, 항공 우주같은 분야가 아닌 이상 대부분의 실무에서는 비잔틴 결함까지 고려하지는 않음)

노드 실패 모델

크래시-정지 (Crash-stop): 노드가 실패하면 영원히 중지 크래시-복구 (Crash-recovery): 노드가 실패 후 복구될 수 있음 비잔틴 결함 (Byzantine Faults): 노드가 임의의 방식으로 행동 가능

안전성 및 활성 속성 (Safety and Liveness Properties)

안전성 (Safety)

  • “나쁜 일은 결코 일어나지 않는다”
  • 예시: 데이터 손상 없음, 고유성 위반 없음
  • 위반되면 되돌릴 수 없음
  • 유한한 시간 내에 위반 여부 판단 가능

활성 (Liveness)

  • “좋은 일은 결국 일어난다”
  • 예시: 요청이 결국 응답을 받음, 시스템이 결국 복구됨
  • 특정 시점에 충족되지 않을 수 있지만 미래에 만족될 가능성
  • 무한한 시간이 흘러야 위반 여부 판단 가능
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.