개발일기장

Part2. 08장 분산 시스템의 골칫거리 본문

책 정리/데이터 중심 애플리케이션 설계

Part2. 08장 분산 시스템의 골칫거리

게슬 2021. 10. 6. 12:05
728x90

분산 시스템을 다루는 것은 한 컴퓨터에서 실행되는 SW를 작성하는 일과는 근본적으로 다르다. 핵심적인 차이는 뭔가 잘못될 수 있고 새롭고 흥미진진한 방법이 많다는 점. 엔지니어는 모든 게 잘못되더라도 제 역할을 해내는 시스템을 구축하는 것이다.

 

결함과 부분 장애

대부분 잘못 작성한 소프트웨어의 결과일 뿐이다. 하드웨어가 올바르게 동작하면 같은 연산은 항상 같은 결과를 낸다.(결정적)

네트워크로 연결된 여러 컴퓨터에서 실행되는 소프트웨어를 작성할 때는 근본적으로 상황이 다르다. 분산 시스템에서는 더 이상 이상화된 시스템 모델에서 동작하지 않는다. 분산 시스템에서는 시스템의 어떤 부분은 잘 동작하지만 다른 부분은 예측할 수 없는 방식으로 고장나는 것도 무리가 아니다. 이를 부분 장애(partial failure)라고 한다. 비결정적이라서 어렵다. 심지어 뭔가 성공했는지 아닌지 알지 못할 수도 있다.

 

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

슈퍼컴퓨터에서 실행되는 작업은 보통 가끔씩 계산 상태를 지속성 있는 저장소에 체크포인트로 저장한다. 장애가 발생하면 마지막 체크포인트로부터 계산을 재시작 한다. 단일 노드 컴퓨터에 가깝다.

분산 시스템이 동작하게 만들려면 부분 장애 가능성을 받아들이고 소프트웨어에 내결함성 메커니즘을 넣어야 한다. 신뢰성 없는 구성 요소를 사용해 신뢰성 있는 시스템을 구축해야 한다.(완벽한 신뢰성은 없다.) 결함 처리는 소프트웨어 설계의 일부여야 하며 결함이 발생하면 소프트웨어가 어떻게 동작할지 알아야 한다.

 

신뢰성 없는 네트워크

비공유로 시스템을 구축하는 것은 특별한 하드웨어가 필요하지 않아서 상대적으로 저렴하고, 상품화된 클라우드 서비스를 활용할 수 있으며, 분산된 여러 데이터센터에 중복 배치함으로써 높은 신뢰성을 확보할 수 있다. 인터넷과 데이터센터 내부 네트워크 대부분은 비동기 패킷 네트워크다. 노드는 다른 노드로 메시지를 보낼 수 있지만 네트워크는 메시지가 도착할 것이라고 보장하지는 않는다.

전송 측은 패킷이 전송됐는지 아닌지 조차 구별할 수 없다.  수신 측에서 응답 메시지를 보내는 것이지만 이것도 손실되거나 지연될 수 있다. 이런 문제를 다루는 흔한 방법은 타임아웃이다.

현실의 네트워크 결함

네트워크 결함이 드물더라도 결함이 일어날 수 있다는 사실은 소프트웨어가 이를 처리할 수 있어야 한다는 뜻이다. 오류 처리가 정의되고 테스트되지 않는다면 나쁜 일이 제멋대로 생길 수 있다. 반드시 네트워크 결함을 견뎌내도록(tolerating) 처리할 필요는 없다. 네트워크가 믿을 만하다면 문제가 있을 때 그냥 사용자에게 오류 메시지를 보여주는 것도 타당한 방법이다. 그러나 SW가 네트워크 문제에 어떻게 반응하는지 알고 시스템이 그로부터 복구할 수 있도록 보장해야 한다.

 

결함 감지

1. 로드 밸런서는 죽은 노드로 요청을 그만 보내야 한다.

2. 단일 리더 복제를 사용하는 분산 데이터베이스에 리더가 장애가 나면 팔로워 중 하나가 리더로 승격돼야 한다.

네트워크에 관한 불확실성 때문에 노드가 동작 중인지 아닌지 구별하기 어렵다.

프로세스만 문제가 생기고 노드는 정상적이면 운영체제가 연결을 닫거나 거부한다.

접속하려는 IP 주소에 도달할 수 없다고 라우터가 확신하면 ICMP 패킷으로 응답할 수도 있다.

원격 노드가 다운되고 있다는 빠른 피드백은 유용하지만 여기에 의존할 수는 없다. TCP 패킷이 전달됐다는 확인 응답을 했더라도 애플리케이션이 그것을 처리하기 전에 죽을 수도 있다. (ㅅㅂㅋㅋ존나 억지임)

 

타임아웃과 기약 없는 지연

성급하게 노드가 죽었다고 선언하면 문제가 된다. 노드가 죽었다고 선언되면 그 노드의 책무는 다른 노드로 전달돼야 해서 다른 노드와 네트워크에 추가적인 부하를 준다. 특히 노드가 실제로는 죽지 않았고 과부하 때문에 응답이 느릴 뿐일 수도 있다. 그 부하를 다른 노드로 전달하면 연쇄 장애를 유발할 수 있다.

우리가 사용하는 시스템은 대부분 어떤 것도 보장하지 않는다. 비동기 네트워크는 기약 없는 지연이 있고, 서버 구현은 대부분 어떤 최대 시간 내에 요청을 처리한다고 보장할 수 없다. 타임아웃이 낮으면 왕복 시간이 순간적으로 급증하기만 해도 시스템의 균형을 깨뜨린다.

 

네트워크 혼잡과 큐 대기

지연 시간에 민감한 애플리케이션은 TCP 대신 UDP를 사용한다. 신뢰성과 지연 변동성 사이에 트레이드 오프 관계가 있다. UDP는 흐름 제어를 하지 않고 손실된 패킷을 재전송하지 않으므로 네트워크 지연이 크게 변하게 하는 원인 중 일부를 제거한다. UDP는 지연된 데이터의 가치가 없는 상황에서 선택하면 좋다.

고정된 타임아웃을 설정하는 대신 시스템이 지속적으로 응답 시간과 그들의 변동성(jitter)을 측정하고 관찰된 응답 시간 분포에 따라 타임아웃을 자동으로 조절하게 하는 것이다.

 

동기 네트워크 VS 비동기 네트워크

전화 네트워크는 극단적인 신뢰성을 지닌다. 종단 지연시간이 낮아야 하며 목소리의 음성 샘플을 전송할 대역폭이 충분해야 한다. 통화를 할 때는 회선(circuit)이 만들어진다. 두 명 사이에 있는 전체 경로를 따라서 그 통화에 대해 고정되고 보장된 양의 대역폭이 할당된다. ISDN 네트워크는 초당 4000프레임의 고정된 비율로 실행된다. 이런 종류의 네트워크는 동기식이다. 데이터가 여러 라우터를 거치더라도 큐 대기 문제를 겪지 않는다. 다음 홉에 이미 공간이 할당됐기 때문이다. 큐 대기가 없으므로 네트워크 종단 지연 시간의 최대치가 고정돼 있다. 제한 있는 지연이라고 한다.

 

예측 가능한 네트워크 지연?

TCP 연결의 패킷은 가용한 네트워크 대역폭을 기회주의적으로 사용한다. 가변 크기의 데이터블록을 보내면 가능하면 짧은 시간 안에 전송하려고 할 것이다. TCP 연결이 유휴 상태에 있는 동안은 어떤 대역폭도 사용하지 않는다. 인터넷은 순간적으로 몰리는 트래픽(bursty traffic)에 최적화됐기 때문이다. 웹 페이지 요청, 이메일 전송, 파일 전송은 특별한 대역폭 요구사항이 없다. 가능하면 빨리 완료되기를 바랄 뿐이다. 그러므로 순간적으로 몰리는 데이터 전송에 회선(circuit)을 사용하면 네트워크 용량을 낭비하고 전송이 불필요하게 느려진다. TCP는 가용한 네트워크 용량에 맞춰 데이터 전송률을 동적으로 조절한다. 서비스 품질 (QoS)나 진입 제어를 잘 쓰면 패킷 네트워크에서 회선 교환을 흉내 내거나 제한 있는 지연을 제공하는 것도 가능하다.

그러나 현재 배포된 기술로는 네트워크 지연과 신뢰성에 대해 어떤 보장도 할 수 없다. 네트워크 혼잡, 큐 대기, 기약 없는 지연이 발생할 것이라고 가정해야 한다. 결과적으로 타임아웃에 올바른값은 없으며 실험을 통해 결정해야 한다.

 

신뢰성 없는 시계

지속시간을 측정하는 것과 특정 시점을 기술하는 것이 있다.

분산 시스템에서는 통신이 즉각적이지 않으므로 시간은 다루기 까다롭다. 메시지가 네트워크를 거쳐서 전달되는 데 시간이 걸린다. 게다가 네트워크에 있는 개별 장비는 자신의 시계를 갖고 있다. 이 장치는 완벽히 정확하지는 않다. 시간을 어느 정도 동기화하는 메커니즘은 네트워크 시간 프로토콜(NTP)로 서버 그룹에서 보고한 시간에 따라 컴퓨터 시계를 조정할 수 있게 한다.

 

단조 시계 대 일 기준 시계

컴퓨터는 두개의 시계를 가진다.

일 기준 시계

직관적으로 시계에 기대하는 일을 한다. 현재 날짜와 시간을 반환한다. 일 기준 시계는 보통 NTP로 동기화한다. 한 장비의 타임스탬프는 다른 장비의 타임스탬스와 동일한 의미를 지닌다.

단조 시계

타임아웃이나 서비스 응답 시간 같은 지속 시간(시간 구간)을 재는 데 적합하다. 항상 앞으로 흐른다는 사실이다.(일 기준 시계는 거꾸로 뛸 수도 있다.) 한 시점에서 어떤 일을 한 후 나중에 확인할 때 두 값의 사이의 차이로 시간이 얼마나 흘렀는지 알 수 있다. 분산 시스템에서 경과 시간을 재는 데 단조 시계를 쓰는 것은 일반적으로 괜찮다. 다른 시계 사이에 동기화가 돼야 한다는 가정도 없고, 측정이 민감하지 않기 때문이다.

 

시계 동기화와 정확도

일 기준 시계는 NTP 서버나 다른 외부 시간 출처에 맞춰 설정돼야 유용하다. 그러나 신뢰성이 있거나 정확하지 않다. 동기화된 시계가 필요한 소프트웨어를 사용한다면 필수적으로 모든 장비 사이의 시계 차이를 조심스럽게 모니터링해야 한다. 노무 차이나는 노드는 죽은 것으로 선언되고 클러스터에서 제거돼야 한다.

이벤트 순서화용 타임스탬프

최종 쓰기 승리(last write wins, LWW)라고 불리며 다중 리더 복제, 리더 없는 데이터베이스에서 널리 사용된다. 따라서 가장 최근값을 유지하고 다르 것들을 버림으로써 충돌을 해소하고 싶은 유혹이 들더라도 최근의 정의는 로컬 기준 시계에 의존하며 그 시계는 틀릴 수도 있다는 것을 아는 것이 중요하다.

올바른 순서화를 위해서는 시계 출처가 측정하려고 하는 대상보다 보다 훨씬 정확해야 한다. 이른바 논리적 시계는 진동하는 수정 대신 증가하는 카운터를 기반으로 하며 이벤트 순서화의 안전한 대안이다. 일 기준 시간이나 경과한 초 수를 측정하지 않고 이벤트의 상대적인 순서만 측정한다.

 

시계 읽기는 신뢰 구간이 있다.

시계 읽기를 어떤 시점으로 생각하는 것은 타당하지 않다. 어떤 신뢰 구간에 속하는 시간의 범위로 읽는 게 나을 것이다. 예를 들어 어떤 시스템은 현재 시간이 해당 분의 10.3초와 10.5초 사이에 있다고 95%ㅎ 확신할 수 있으나 그보다 더 정확히는 알지 못할 것이다.

 

프로세스 중단

분산 시스템의 노드는 어느 시점에 실행이 상당한 시간 동안 멈출 수 있다고 가정해야 한다. 심지어 함수 중간에서 멈출 수도 있다. 그동안 외부 세계는 계속 움직이며 멈춘 노드가 응답하지 않아서 죽었다고 선언할 수도 있다. 멈춘 노드는 스스로 잠들었다는 것을 알아채지 못한다.

응답 시간 보장.

물리적 물체를 제어하는 컴퓨터는 그들의 센서 입력에 빠르고 예측 가능하게 응답해야 한다. 데드라인이 명시된다. 엄격한 실시간 시스템(hard real-time)이라고 한다. 프로세스가 명시된 간격의 CPU 시간을 할당 받을 수 있게 보장되도록 스케줄링 해주는 실시간 운영체제가 필요하다.  임베디드 장치에서 가장 흔하게 사용된다.

지식, 진실, 거짓말

네트워크에 있는 노드는 어떤 것도 확실히 알지 못한다.

노드가 상황에 대한 자신의 판단을 반드시 믿을 수 있는 것이 아니라는 것이다. 분산 시스템은 한 노드에만 의존할 수는 없다. 노드에 언제든 장애가 나서 잠재적으로 시스템이 멈추고 복구할 수 없게 될 수도 있기 때문이다.

시스템 모델과 현실

알고리즘은 그들이 실행되는 HW, SW의 설정의 세부 사항에 너무 의존하지 않는 방식으로 작성해야 한다. 예상되는 결함의 종류를 어떻게든 정형화해야 한다. 시스템 모델(system model)을 정의해서 정형화하는데, 알고리즘이 가정하는 것은 기술한 추상화다.

타이밍 가정에 대해서는 세 가지 시스템 모델이 사용된다.

동기식 모델 : 어떤 고정된 상한치를 초과하지 않을 것임을 안다는 것뿐이다. 현실적인 모델이 아니다. 기약 없는 지연과 중단이 발생하기 때문이다.

부분 동기식 모델 : 대부분의 시간에는 동기식 시스템처럼 동작하지만 때때로 한계치를 초과한다는 뜻이다. 현실적인 모델이다.

비동기실 모델 : 알고리즘은 타이밍에 대한 어떤 가정도 할 수 없다. 시계가 없을 수도 있다.

 

정리

분산 시스템에서 나타날 수 있는 광범위한 문제

1. 네트워크로 패킷을 보내려고 할 때는 언제나 패킷이 손실되거나 임의대로 지연될 수 있다. 응답도 손실되거나 지연될 수 있으므로 응답을 받지 못하면 전달됐는지 아닌지 알 수 없다.

2. 노드의 시계는 다른 노드와 심하게 맞지 않을 수 있으므로 시계의 오차 구간을 측정할 좋은 수단이 없을 가능성이 크므로 시계에 의존하는 것은 위험하다.

3. 프로세스는 실행 도중 어느 시점에서 상당한 시간 동안 멈출 수 있고 다른 노드에 의해 죽었다고 선언될 수 있으며 되살아났을 때 멈췄다는 사실을 알지 못할 수도 있다.

 

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

결함을 견뎌내려면 그것을 감지하는 게 첫걸음이지만 그것조차 어렵다. 대부분의 분산 알고리즘은 타임아웃을 사용한다. 이것은 네트워크 장애와 노드 장애를 구별할 수 없고 노드가 죽은 것으로 잘못 의심받을 수 있다.

결함이 발견됐을 때 시스템이 이를 견딜 수 있게 만들기도 쉽지 않다.

분산 시스템을 원하는 이유에는 확장성만 있는 게 아니다. 내결함성과(데이터를 사용자와 지리적으로 가까운 곳에 둬서 달성하는) 짧은 지연 시간도 똑같이 중요한 목적이며 이들은 단일 노드로는 달성할 수 없다.

 

09장은 정리만

정리

인기 있는 일관성 모델인 선형성. 그 목적은 복제된 데이터가 오직 하나의 복사본만 있는 것처럼 보이게 하고 데이터에 대한 모든 연산을 원자적으로 만드는 것이다. 매력적이지만 느리다는 단점이 있다. 네트워크 지연이 큰 환경에서 특히 그렇다.

이벤트에 순서를 부과하는 인과성. 모든 연산을 하나의 전체 순서가 정해진 타임라인에 넣는 선형성과 달리 인과성은 더 약한 일관성 모델을 제공한다. 동시에 실행될 수도 있어서 버전 기록은 가지치기, 합치기가 있는 타임라인과 같다. 인과적 일관성은 선형성의 코니네이션 오버헤드가 없고 네트워크 문제에 덜 민감하다.

한 노드가 등록을 받아들이려면 다른 노드가 동시에 동일한 사용자명으로 등록하는 과정에 있지 않다는 것을 알아야 하는데 이 문제를 합의로 이끈다.

광범위한 문제가 실제로는 합의로 환원될 수 있고 서로 동일하다.

1. 선형성 compare-and-set 레지스터

2. 원자적 트랜잭션 커밋

3. 전체 순서 브로드캐스트

4. 잠금과 임차권

5. 멤버십/코디네이션 서비스

6. 유일성 제약 조건

노드가 하나만 있거나 결정하는 능력을 한 노드에만 준다고 하면 간단하다. 리더 데이터베이스에서 일어나는 일. 결정을 하는 모든 능력을 리더에게만 부여되는데, 단일 리더 데이터베이스가 선형성 연산과 유일성 제약 조건, 전체 순서가 정해진 복제 로그 등을 제공할 수 있는 이유다.

단일 리더에 장애가 나거나 네트워크가 끊겨서 리더에 접속할 수 없게 되면 아무 진행도 하지 못한다.

1. 리더가 복구될 때까지 기다리고 시스템이 그동안 차단되는 것을 받아들인다. 종료 속성을 만족하지 않기 때문에 합의를 완전히 해결하지는 않는다. 리더가 복구되지 않으면 시스템은 영원히 차단된다.

2.사람이 새 리더 노드를 선택하고 시스템이 그 노드를 사용하도록 재설정해서 수동으로 장애 복구를 한다. “불가항력에 의한 일종의 합의다. 장애 복구 속도는 사람이 행동하는 속도로 제한되며 일반적으로 컴퓨터 보다 느리다.

3. 자동으로 새 리더를 선택하는 알고리즘을 사용한다. 이 방법은 합의 알고리즘이 필요하고 불리한 네트워크 조건을 올바르게 처리하는 입증된 알고리즘을 사용하는 게 현명하다.

 

단일 리더 데이터베이스는 모든 쓰기마다 합의 알고리즘을 실행하지 않고 선형성을 제공할 수 있지만 리더십을 유지하고 변경을 위해서는 합의가 필요하다.

주키퍼 같은 도구는 애플리케이션이 사용할 수 있는 합의, 장애 감지, 멤버십 서비스를 위탁하는 데 중요한 역할을 수행한다.

리더 없는 복제 시스템과 다중 리더 복제 시스템은 보통 전역 합의를 사용하지 않는다. 이런 시스템에서 발생하는 충돌은 다른 리더에 걸친 합의가 없어서 생긴 결과지만 괜찮다. 그냥 선형성 없이 대처하고, 가지치기와 합치기의 버전 기록이 있는 데이터를 잘 처리하는 법만 있으면 괜찮다.

728x90

'책 정리 > 데이터 중심 애플리케이션 설계' 카테고리의 다른 글

Part3. 11장 스트림 처리  (0) 2021.10.12
Part3. 10장 일괄처리  (0) 2021.10.11
Part2. 07장 트랜잭션  (0) 2021.10.05
Part2. 06장 파티셔닝  (0) 2021.10.04
Part2. 05장 복제  (0) 2021.10.02
Comments