모든 것과 싸우기 - 데이터 중심 애플리케이션 설계 8장

Broccolism·2022년 3월 14일
3
post-thumbnail
post-custom-banner

데이터 중심 애플리케이션 설계, 마틴 클레프만 지음. OREILLY.

신뢰성 없는 구성 요소를 사용해 신뢰성 있는 시스템 구축하기

서론

대규모 컴퓨팅 시스템 구축 방법

  • 슈퍼컴퓨팅: 엄청나게 강력한 컴퓨터 1대로 모든걸 처리하기
  • 클라우드 컴퓨팅: 적당히 강력한 컴퓨터 N대로 모든걸 처리하기

이 둘 사이에 전통적인 기업의 데이터 센터가 있다.

여러가지 이유로 클라우드 컴퓨팅 환경이 각광받고 있다. 컴퓨터 여러대가 하나의 기능을 만들어야 한다. 그러기 위해 네트워크 통신을 하며 서로 정보를 주고받는다. 이번 장에서는 그 과정에서 생기는 문제와 원인을 알아본다. 워낙 문제가 많아서 해결책은 다음 장에 담았다.

분산 시스템 다루기가 어려운 원인

크게 2가지가 있다.

  • 비결정성: 사실 아무것도 보장되지 않는다. 네트워크 딜레이가 얼마나 날지, 기기 자체 결함이 언제 어떤 이유로 발생할지, 인적 오류가 언제 날지를 가늠할 수 없는 경우가 있다.
  • 부분 장애 가능성: 시스템 중 일부에만 결함이 생기고 나머지 부분은 잘 돌아가는 경우를 부분 장애가 발생했다고 한다. 부분장애가 일어났는지 인지하는 작업부터 시작해서 해결하는 그 과정이 결코 쉽지 않다. 어떤 때는 장애를 인지하는 것 자체가 어렵다.

모든 것이 잘못 될 수 있다

모든 것의 범위

책에서는 2가지를 집중적으로 다룬다. 네트워크와 시계다. 네트워크쪽에서는 우리가 흔히 겪는 지연에 대해 다룬다. 그 흔한 지연이 왜 흔한지도 알려준다. 시계쪽에서는 끊임없이 의심하는 자세를 보여준다. 지금 보고 있는 21시 07분 3초라는 시각이 사실은 그다지 정확하지 않을 수도 있다.

모든 것을 의심하는 이유

이렇게 모든걸 의심하는 이유는 뭘까? 기본적으로 이런 대규모 시스템의 구성 요소는 신뢰성을 보장하지 않기 때문이다. 그런 일이 계속해서 일어났고, 일어날 수 있는 가설이 등장하기 때문이다. 상어가 해저 케이블을 물어뜯어서 네트워크 통신이 끊긴 일화도 있었다고 한다.

하지만 놀랍게도 '신뢰성 없는 구성 요소를 사용해 신뢰성 있는 시스템 구축하기' 도전과제는 우리가 늘 해오던 일이다. 대표적인 예로 IP가 있다. IP는 신뢰성이 없다. 패킷은 누락, 지연, 중복될 수 있고 순서가 바뀔 수도 있다. 이를 보완하기 위해 TCP에서 손실된 패킷을 재전송하고 중복을 제거하며, 패킷을 순서에 맞게 재조립한다. 신뢰성있는 시스템이 만들어졌다.

모든 것이 잘못될 수 있다는 생각을 기반으로, 여러 케이스를 고려해야한다. 그렇게 점점 시스템의 신뢰성을 만들어가는 것이다.

신뢰성 없는 네트워크

패킷 지연은 우주의 법칙일까?

정말로 네트워크 지연은 필연적인 걸까? 그건 아니다. 마치 '질량을 가진 두 물체 사이에는 인력이 작용한다'는 만유인력의 법칙처럼 '네트워크 통신에는 지연이 발생한다'는 법칙이 있는게 아니다.

하지만 우리의 선택으로 인해 생긴 결과인 것은 맞다. 대체 왜 이런 선택을 했을까?

네트워크 회선을 구축하는 방식에는 2가지가 있다. 서킷 스위칭 네트워크, 패킷 스위칭 네트워크. 서킷 스위칭 네트워크의 예는 전화선이 있다. 한 회선을 두 사람이 독점한다. 매번 고정된 회선을 사용하기 때문에 지연이 어느정도일지도 예상할 수 있다. 하지만 이 고정적이라는 특징이 서킷 스위칭의 단점이 되기도 한다. 사용자 수를 유동적으로 조절할 수 없기 때문이다.

패킷 스위칭 네트워크는 순간적으로 몰리는 트래픽에 최적화되어 있다. 늘 같은 양의 자원을 사용하는게 아니라 필요한 순간에 필요한 양의 자원을 사용할 수 있다. 반면, 패킷 지연이 발생한다는 단점도 있다.

비공유 분산 시스템 환경에서는 주로 비동기 패킷 네트워크를 사용한다. 분산 시스템이라서 네트워크 지연이 일어나는게 아니라, 패킷 스위칭 방식을 썼기 때문에 지연이 일어난다. 비용 절감과 극도로 높은 신뢰성 사이에서 비용 절감을 택한 것이다.

네트워크 결함 감지

패킷 지연이 일어날 수 있음을 인정했다. 그 다음에 할 일은 이 지연을 잘 핸들링하는 것이다. 죽은 노드가 생겼으면 이 노드로는 더이상 요청을 보내지 말아야하고, 타임아웃이 발생했는지 등을 감지할 수 있어야 한다.

타임아웃은 얼마나 길어야 할까? 유감스럽게도 간단한 답은 없다.

...아쉽게도 아직까지 완벽한 답을 찾지는 못한 것 같다.

이외에도 책에서는 네트워크 혼잡과 큐 대기, 예측 가능한 지연과 같은 네트워크 결함과 관련된 다양한 주제를 다룬다.

신뢰성 없는 시계

네트워크 시간 프로토콜

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

이를 보완하기 위한 메커니즘이 몇개 있다. 가장 널리 쓰이는건 네트워크 시간 프로토콜(Network Time Protocol, NTP)이다.

시계의 종류

2가지가 있다. 일 기준 시계(time-of-day clock)은 우리가 흔히 알고 있는 그 시계다. 특정 달력에 따라 현재 날짜와 시간을 반환한다. 예를 들어 자바의 System.currentTimeMillis()는 에포크(epoch) 이래로 몇초가 흘렀는지를 리턴한다.

단조 시계(monotonic clock)은 타임아웃이나 서비스 응답 시간 같은 시간을 재는데 사용한다. 시각을 반환하지 않는다. 정확히는, 단조 시계에서 반환된 절대적인 시각은 의미가 없다. 두 구간 사이의 시간을 재기 위해 만들어진 시계이기 때문이다. 분산 시스템에서 경과 시간을 재기 위해 단조 시계를 쓴다.

시계에서 생길 수 있는 문제

여러 노드에서 같은 시점에 각자 다른 시각을 보고 있을 수 있다. 이렇게 되면 '시간 순으로 정렬'을 더이상 믿을 수 없게 된다. 책에서는 이런 문제를 다루기 위한 시계 동기화에 대한 내용도 소개한다.

분산 시스템의 실체

정족수 quorum

다수결의 법칙이 여기서도 통한다.

진실은 다수결로 결정된다.
분산 시스템은 한 노드에만 의존할 수 없다.

노드에 언제든지 장애가 날 수 있기 때문이다. 심지어는 노드 자기 자신이 살아있다고 잘못 판단하는 경우도 생긴다.

여러가지 분산 알고리즘은 노드들 간 투표에 의존한다. 이를 정족수라고 한다. 노드 하나에 100% 의존하지 않고, 여러 노드에 각각 (100/N)%씩 의존하는 전략을 택한 것이다.

리더와 잠금

시스템이 오직 하나의 뭔가를 만들고 유지해야 할 때가 있다. 예를 들어, 어느 시점이든 DB에 write 작업을 할 수 있는 노드는 1개뿐이어야 한다. 이럴 때 보통 잠금(locking)을 통해 정확히 그 1개만 존재함을 보장하는 방식을 사용한다.

책에서는 이런 잠금 또한 분산 시스템에서는 완전히 신뢰할 수 없다고 주장하며 예시를 보여준다.

비잔틴 문제

  • 비잔틴 결함
  • 비잔틴 장군 문제
  • 비잔틴 내결함성

'비잔틴'이 들어가는 여러가지 용어가 있다. 실제로 비잔티움 제국은 아무것도 잘못한게 없다. 하지만 이런 용어가 생겼다.

노드에 결함이 발생해서 오류가 나는게 아니라, 노드가 작정하고 다른 노드와 인간을 속일 때 비잔틴 결함이 생겼다고 한다. 이렇게 되면 노드는 서로를 신뢰할 수 없게 된다. 이런 환경에서 합의에 도달하는 문제를 비잔틴 장군 문제라고 하며, 그럼에도 불구하고 시스템이 잘 돌아가도록 만들어졌다면 비잔틴 내결함성을 지닌 시스템이라고 한다.

세상에, 이제 믿을게 하나도 없다.

해결책: 알고리즘

알고리즘은 general 해야 한다.

구글의 OAuth 2.0을 생각해보자. 특정 언어나 프레임워크, OS 등 환경에 관계없이 어디서든 적용할 수 있다. 알고리즘은 이렇게 general해야 한다. 환경에 구애받지 않고 어디서든 쓸 수 있어야 한다.

시스템 모델

2가지 측면으로 나눠서 생각할 수 있다.

  • 시간에 대한 모델: 동기식, 반동기식, 비동기식
  • 노드 결함에 대한 모델
    • 죽으면 멈추는(crash-stop) 결함: 노드에 장애가 나면 영원히 사용할 수 없고 돌아오지 않는다.
    • 죽으면 복구하는(crash-recovery) 결함: 노드에 장애가 나면 일시적으로 사용할 수 없게되다가 복구한다.
    • 비잔틴(임의적인) 결함: 비잔틴 결함을 허용한다. 노드는 다른 노드를 속이는 것을 포함해 말 그대로 모든 것을 할 수 있다.

현실 시스템을 모델링 할 때는 crash-recovery + 반동기식 모델을 사용한다.

숲과 나무를 동시에 보자

분산 시스템 문제를 해결할 때 무조건 큰 그림만 보는 것도 좋지 않다. 어떤 문제를 단일 컴퓨터에서 해결할 수 있다면 그 방법을 쓰는 것이 맞고, 반대의 경우라면 전체적인 구조를 고려하는게 맞을 것이다. 단일 컴퓨터와 분산 시스템 환경 이 둘을 모두 고려하면 훨씬 효율적인 해결법을 찾을 수 있다.


다음 장에서는...

이번 장 요약을 쓰는 내내 좀 괴로웠다. 해결책은 나오지 않고 계속해서 문제만 소개했기 때문이다. 정말 다행히도 다음 장에서는 해결책을 다룬다고 한다.
8장 제목은 '분산 시스템의 골칫거리'였고 9장 제목은 '일관성과 합의'다. 다음 장에서 과연 모든 문제를 풀 수 있을지 궁금해진다.

profile
설계를 좋아합니다. 코드도 적고 그림도 그리고 글도 씁니다. 넓고 얕은 경험을 쌓고 있습니다.
post-custom-banner

2개의 댓글

comment-user-thumbnail
2022년 3월 15일

오늘 8장 초입까지 읽었는데 문제를 나열하는 파트였군요
용어들은 익숙치 않지만 개념 자체는 어디서 한 번쯤 들어본 적 있는 느낌이있네요!
완독까지 화이팅입니다!! ㅎㅎ

1개의 답글