-
[데이터 중심 애플리케이션] 분산 시스템의 골칫거리Software Development/Database 2021. 11. 20. 23:43
분산 시스템을 다루는 것은 한 컴퓨터에서 실행되는 소프트웨어를 작성하는 일과는 근본적으로 다르다. 핵심적인 차이는 뭔가 잘못될 수 있는 방법이 많다는 것이다.
결함과 부분 장애
단일 머신의 프로그램은 돌아가거나 안 돌아가거나 둘 중 하나다. 분산 시스템에서는 예측할 수 없는 방식으로 고장난다. 이를 부분 장애라 한다.
클라우드 컴퓨팅과 슈퍼컴퓨팅
고성능 컴퓨팅: 일기예보, 분자 동력학과 같은 계산 비용이 매우 높은 과학 계산 작업. 단일 노드 컴퓨터에 가깝다. 공유 메모리와 원격 직접 메모리 접근(RDMA)을 사용해 통신.
클라우트 컴퓨팅: 온라인, 상용장비, 낮은 비용으로 동일한 성능을 제공하나 실패율이 높음. 고장난 상태를 가정.
신뢰성 없는 네트워크
분산 시스템은 비공유 시스템, 즉 네트워크로 연결된 다수의 장비다. 다른 장비의 메모리나 디스크에 접근할 수 없다. 네트워크가 장비들 사이의 유일한 통신 수단.
인터넷과 데이터센터 내부 네트워크 대부분은 비동기 패킷 네트워크다. 네트워크는 메세지가 언제 도착할지 혹은 도착하기는 할 것인지 보장하지 않는다.
- 요청이 손실됐을 수 있다.
- 요청이 큐에서 대기하다 나중에 전송될 수 있다.
- 원격 노드에 장애가 생길 수 있다.
- 원격 노드가 일시적으로 응답하기를 멈췄지만 나중에는 다시 다시 응답하기 시작할 수 있다.
- 요청을 처리했지만 네트워크에서 손실됐을 수 있다.
- 응답이 지연 혹은 나중에 전송될 수 있다.
이런 문제를 다루는 흔한 방법은 타임 아웃
현실의 네트워크 결함
아직 신뢰성 있는 네트워크를 만드는 완전한 방법은 없다. 중간 규모의 데이터센터에 관한 한 연구에서 매달 12번의 네트워크 결함이 발생. 그중 절반은 단일 장비의 연결이 끊어지게 했고 나머지 절반은 전체 랙의 연결이 끊어지게 했다.
스위치 소프트웨어 업그레이드 중 생기는 문제는 네트워크 토폴로지 재구성을 유발할 수 있다. 패킷 1분 이상 지연 가능하다. 상어가 해저 케이블을 물어뜯을 수 있다.
네트워크 결함이 드물더라도 일어날 수 있다는 사실은 소프트웨어가 이를 처리할 수 있어야 함을 의미한다. 네트워크 상으로 통신할 때마다 실패할 가능성이 있다. 피할 방법은 없다.
네트워크 결함의 오류 처리가 정의되고 테스트되지 않는다면 나쁜 일이 제멋대로 생길 수 있다. 네트워크 결함을 견뎌내도록 꼭 처리할 필요는 없지만, 사용자에게 오류 메세지를 보여주는 것도 타당한 방법이다.
카오스 몽키 테스트는 고의로 네트워크 결함을 내고 시스템의 반응을 테스트한다.
결함 감지
- 로드밸런스는 죽은 노드로 요청을 그만 보내야 한다.
- 단일 리더 복제를 사용하는 분산 데이터베이스에서 리더에 장애가 나면 팔로워 중 하나가 리더로 승격돼야 한다.
타임아웃과 기약 없는 지연
패킷의 최대 지연 시간이 보장된 네트워크를 사용하는 가상의 시스템을 예로, 모든 패킷은 어떤 시간 d 내에 전송되거나 손실되지만 전송 시간이 결코 d보다 더 걸리지는 않는다. 장애가 나지 않는 노드는 항상 요청을 r 시간 내에 처리한다고 보장할 수 있다고 가정한다. 성공한 요청은 2d + r 시간내에 처리 가능하다. 이를 타임아웃으로 잡는 것이 합리적이다.
현실은 그렇지 않다. 기약 없는 지연이 있고 서버 구현은 최대 시간 내에 요청을 처리한다고 보장할 수 없다.
네트워크 혼잡과 큐 대기
컴퓨터 네트워크에서 패킷 지연의 변동성은 큐 대기 때문인 경우가 많다.
- 여러 다른 노드가 동시에 같은 목적지로 패킷을 보내려고 할 때 네트워크 스위치는 패킷을 큐에 넣고 한 번에 하나씩 목적지 네트워크 링크로 넘겨야 한다. 네트워크 링크가 붐비면 패킷은 슬롯을 얻을 수 있을 때까지 잠시 기다려야 할 수도 있다. 데이터가 많아서 스위키 큐를 꽉 채울 정도가 되면 패킷이 유실되어 재전송해야 한다.
- 모든 CPU 코어가 바쁘다면 네트워크에서 들어온 요청은 애플리케이션에서 처리할 준비가 될 때까지 운영체제가 큐에 넣어 둔다. 장비의 부하에 따라 큐에서 대기하는 시간은 제각각일 수 있다.
- 가상 환경에서 실행되는 운영체제는 수십밀리초 동안 멈출 때가 흔하다. 네트워크 지연의 변동성을 증가시킨다.
- TCP는 흐름 제어를 수행한다. 혼잡 회피, 배압이라고도 하는 흐름 제어는 노드가 네트워크 링크가 수신 노드에 과부하를 가하지 않도록 자신의 송신율을 제한하는 것이다.
맵리듀스 같은 일괄 처리 작업부하는 네트워크 링크를 포화시키기 쉽다.
실험적으로 타임아웃을 설정할 수 밖에 없다. 시스템이 지속적으로 응답 시간과 변동성을 측정하고 관찰된 응답 시간 분포에 따라 타임아웃을 자동으로 조절하게 하는 것이다.
동기 네트워크 대 비동기 네트워크
네트워크를 신뢰성있게 만들어서 소프트웨어에서는 걱정할 필요가 없게 할 수 없을까?
전화 네트워크는 회선이 만들어진다. 동기식이다. ISDN 네트워크는 초당 4000프레임의 고정된 비율로 실행된다. 통화를 하게 되면 각 프레임 내에 16비트의 공간을 할당한다. 양측은 250마이크로초마다 정확히 16비트의 오디오 데이터를보낼 수 있도록 보장된다. 데이터가 여러 라우터를 거치더라도 큐 대기 문제를 겪지 않는다. 네트워크의 다음 홉(hop)에 통화당 16비트의 공간이 이미 할당됐기 때문이다. 큐 대기가 없기 때문에 종단 지연 시간이 고정되어 있다.
그냥 네트워크 지연을 예측 가능하게 만들 수는 없을까?
전화 네트워크의 회선은 TCP 연결과 매우 다르다. TCP 연결은 고정이 아닌 가용한 네트워크 대역폭을 기회주의적으로 사용한다. 데이터센터 네트워크와 인터넷이 circuit-switch 네트워크라면 왕복 시간의 최대치를 보장할 수 있다. 이더넷과 IP는 큐 대기의 영향을 받는 packet-switch 프로토콜이고 따라서 네트워크에 기약 없는 지연이 있다. 이 프로토콜에는 회선의 개념이 없다.
왜 데이터센터 네트워크와 인터넷은 패킷을 교환을 사용할까? 순간적으로 몰리는 트래픽에 최적화됐기 때문이다. 순간적으로 몰리는 데이터 전송에 회선을 쓰면 네트워크 용량을 낭비하고 전송이 불필요하게 느려진다. TCP는 가용한 네트워크 용량에 맞춰 데이터 전송률을 동적으로 조절한다.
타임아웃에 올바른 값은 없다. 실험을 통해 결정해야 한다.
신뢰성 없는 시계
분산 시스템에서는 통신이 즉각적이지 않으므로 시간은 다루기 까다롭다. 장비간의 통신에서 시간이 걸린다. 이 사실은 여러 장비가 관련될 때 어떤 일이 발생한 순서를 알아내기 어렵게 만들기도 한다.
네트워크 개별 장비는 자신의 시계를 갖고 있다. 장비간 시간을 동기화 시키기 위해서는 네트워크 시간 프로토콜로 서버 그룹에서 보고한 시간에 따라 컴퓨터 시계를 조정할 수 있게 한다.
단조 시계 대 일 기준 시계
일 기준 시계
리눅스의 clock_gettime(CLOCK_REALTIME), 자바의 System.currentTimeMills()는 에포크 이래로 흐른 초 수를 반환한다. 일 기준 시계는 NTP로 동기화된다.
단조 시계
타임아웃이나 서비스 응답 시간 같은 지속 시간을 재는 데 적합하다. clock_gettime(CLOCK_MONOTONIC), System.namoTime()이 있다.
시계 동기화와 정확도
- 드리프트 현상: 장비의 온도에 따라 변한다.
- 컴퓨터 시계나 NTP와 너무 많이 차이나면 동기화를 거부되거나 강제 리셋이 된다.
- 방화벽으로 막히면 잘못된 설정이 얼마 동안 알려지지 않을 수도 있다.
- NTP 동기화는 네트워크 지연만큼만 좋을 수 있다.
- 윤초가 발생하면 1분이 59초 혹은 61초가 되어 설계된 시스템에서는 시간에 관한 가정이 엉망이 된다.
- 가상 장비는 시간을 엄수하기 어렵다. CPU 코어가 VM끼리 공유할 때 수십밀리초 지연이 생길 수 있다. 애플리케이션 관점에서 시계가 이상할 수 있다.
동기화된 시계에 의존하기
소프트웨어는 네트워크에 가끔 결함이 생길 수 있다는 가정하에 설계돼야 하며 소프트웨어는 이런 결함을 우아하게 처리해야 한다.
동기화된 시계가 필요한 소프트웨어를 사용한다면 필수적으로 모든 장비 사이의 시계 차이를 모니터링해야 한다. 다른 노드와 시계가 너무 차이나는 노드는 죽은 것으로 선언되고 클러스터에서 제거돼야 한다. 이런 모니터링을 하면 너무 큰 피해를 입기 전에 고장 난 시계를 알아채도록 보장할 수 있다.
지식, 진실, 그리고 거짓말
분산 시스템에는 공유 메모리가 없고 지연 변동이 큰 신뢰할 수 없는 네트워크를 통해 메세지를 보낼 수 있을 뿐이며 부분 장애, 신뢰성 없는 시계, 프로세스 중단에 시달릴 수 있다.
분산 시스템의 지식과 진실에 관해 알아본다.
진신을 다수결로 결정된다
리더와 잠금
어떤 노드가 이전에 리더였더라도 시간이 흐른 사이에 다른 노드들이 그 노드가 죽었다고 선언하면 그 노드는 강등되고 다른 리더가 이미 선출됐을지도 모른다.
노드의 과반수가 어떤 노드가 죽었다고 선언했음에도 그 노드가 선택된 자인 것처럼 계속 행동한다면 신중하게 설계되지 않은 시스템에서는 문제를 유발할 수 있다. 이런 노드는 자신이 정학 능력 내에서 다른 노드들에게 메세지를 보낼 수 있고 다른 노드들이 그 노드를 믿는다면 시스템은 전체적으로 뭔가 잘못된 일을 할 수도 있다.
펜싱 토큰
자신이 선택된 자라고 잘못 믿고 있는 노드가 나머지 시스템을 방해할 수 없도록 보장해야 한다. 이 목적을 달성하는 단순한 기법으로 펜싱이 있다.
잠금 서버가 잠금이나 임차권을 승인할 때마다 펜싱 토큰도 반환한다고 가정한다. 펜싱 토큰을 잠긍이 승인될 때마다 증가하는 숫자다. 그러면 클라이언트가 쓰기 요청을 저장소 서비스로 보낼 때마다 자신의 현재 펜싱 토큰을 포함하도록 요구할 수 있다.
잠금 서비스로 주키퍼를 사용하면 트랜잭션 ID zxid나 노드 버전 cversion을 펜싱 토큰으로 사용할 수 있다. 이들은 단조 증가가 보장되므로 필요한 속성을 지닌다.
서비스의 클라이언트들이 항상 잘 동작할 것이라고 가정하는 것은 현명하지 못하다. 클라이언트는 서비스를 실행하는 사람들의 우선 사항과 매우 다른 우선 사항을 가진 사람들이 실행하는 경우가 흔하기 때문이다. 따라서 스스로를 뜻하지 않게 폭력적인 클라이언트로부터 보호하려는 서비스는 서버 측에서 토큰을 확인하는 게 좋다.
비잔틴 결함
펜싱 토큰은 부주의에 의한 오류에 빠진 노드를 감지하고 차단할 수 있다. 그러나 노드가 고의로 시스템의 보장을 무너뜨리려 한다면 가짜 펜싱 토큰을 포함한 메세지를 보내기만 하면 된다.
이 책에서는 노드들이 신뢰성은 없지만 정직하다고 가정한다. 노드들은 느리거나 결코 응답하지 않을 수 있고 그들의 상태는 뒤처질 수도 있지만 노드가 응답한다면 그 노드는 "진실"을 말한다고 가정한다. 노드는 자신이 아는 한, 프로토콜의 규칙에 따라 동작한다.
분산 시스템의 문제는 노드가 "거짓말"을 할지도 모른다는 위험이 있다면 훨씬 더 어려워진다. 예를 들어 어떤 노드가 실제로는 받지 않은 특정 메세지를 받았다고 주장할 수도 있다. 이런 동작을 비잔틴 결함이라고 하며 이렇게 신뢰할 수 없는 환경에서 합의에 도달하는 문제를 비잔틴 장군 문제라고 한다.
- 항공우주 산업 환경에서 컴퓨터의 메모리나 CPU 레지스터에 저장된 데이트는 방사선에 오염돼서 그 컴퓨터가 다른 노드에게 전혀 예측할 수 없는 방식으로 반응할 수 있다.
- 여러 조직이 참여하는 시스템에서 어떤 참여자들은 다른 사람을 속이거나 사취하려고 할지도 모른다. 이런 환경에서는 노드가 다른 노드의 메세지를 그냥 믿는 것은 안전하지 않다. 메세지가 악의를 가지고 보내졌을 수 있기 때문이다.
웹 애플리케이션은 최종 사용자가 제어하는 웹 브라우저 같은 클라이언트의 행동이 임의적이고 악의적이라고 예상해야 한다. 이는 입력 확인, 살균, 출력 이스케이스핑이 매우 중요한 이유다. 예를 들어 SQL 주입 공격과 크로스사이트 스크립팅을 막아야 한다.
'Software Development > Database' 카테고리의 다른 글
[데이터 중심 애플리케이션] 일괄 처리 (0) 2021.12.26 [데이터 중심 애플리케이션 설계] 일관성과 합의 (0) 2021.12.05 MongoDB 읽기 성능에 관하여. (0) 2021.11.08 [데이터 중심 애플리케이션] 트랜잭션 (0) 2021.11.07 [데이터 중심 애플리케이션] 파티셔닝(분산 데이터) (0) 2021.10.31