분산 시스템을 다루는 것,,
-> 소프트웨어 코딩과는 근본적으로 다름
엔지니어로서의 임무
-> 모든 게 잘못되더라도 제 역할을 해내는 시스템을 구축
분산 시스템에서 잘못될지도 모르는 것에 관한 지독하게 비관적이며 우울한 개요
1. 결함과부분 장애
하드웨어 이슈 발생
- 완전하게 동작하거나 vs 전체 장애 발생
-> 의도적인 컴퓨터 설계
BUT
네트워크 환경
부분장애(partial failure)발생가능
1)클라우드 컴퓨팅과 슈퍼컴퓨팅
a) 클라우드 컴퓨팅 특징
- 멀티 테넌트 데이터센터
- IP 네트워크(흔히 이더넷(Ethernet))로 연결된 상용(commodity) 컴퓨터
- 신축적(elastic)/주문식(on-demand) 자원 할당
- 계량 결제(metered billing)
- 클로스 토폴로지(Clos topology)
- 서비스 중단 없이 유지보수
- 지리적으로 분산된 배포면 로컬 네트워크에 비해 느리고 신뢰성도 떨어짐
b)슈퍼컴퓨팅/고성능 컴퓨팅컴퓨팅(high-performance computing, HPC)
- 수천 개의 CPU
- 특화된 하드웨어
- 공유 메모리
- 원격 직접 메모리 접근(remote direct memory access, RDMA)
- 다차원 메시(mesh)나 토러스(torus) 같은 특화된 네트워크 토폴로지
- 부분 장애를 전체 장애로 확대하는 구조 (단일 시스템처럼 작동)
* 신뢰성 없는 구성 요소로 신뢰성 있는 시스템 만들기
결함은 반드시 발생한다는 비관적 태도로 설계
- 오류 정정 코드(ECC): 일부 비트 오류 발생해도 데이터 복구 가능
- TCP/IP 모델: 신뢰성 없는 IP 위에 신뢰성 있는 TCP를 구현
2. 신뢰성 없는 네트워크
인터넷과 데이터센터 내부 네트워크
-> 대부분 비동기 패킷 네트워크(asynchronous packet network)
이러한 네트워크는 메시지도착 지연/여부를 보장하지 않음
->타임아웃 설정을 통해 다룸
=> 타임아웃 설정이 지나면 응답이 도착하지 않는다고 가정
1)현실의 네트워크 결함
수십년 동안 컴퓨터 네트워크를 구축해왔음
-> 완전히 신뢰할 수 있는 네트워크는 아직 없음
=> 중간 규모 데이터센터도 매달 평균 12번의 네트워크 결함 발생
EC2 같은 공개 클라우드 서비스
->일시적인 네트워크 결함이 자주 발생
네트워크 결함이 드물더라도 결함이 일어날 수 있다
=> 소프트웨어가 이를 처리할 수 있어야 한다
2)결함 감지
시스템은 결함을 자동으로 감지할 수 있어야 함
BUT
네트워크에 관한 불확실성때문에 감지가 어려움
동작하지 않음을 명시적으로 받는 방법
- TCP 연결 오류 (RST/FIN 패킷 등): 프로세스가 없을 때만 응답
- OS는 살아 있지만 프로세스만 죽은 경우: 스크립트로 다른 노드에 알릴 수 있음 (예: HBase)
- 네트워크 장비 상태 질의: 데이터센터 내부에서만 가능한 방법
- ICMP Destination Unreachable 패킷 응답: 라우터가 응답할 수 있지만, 제한적이고 불확실
3)타임아웃과기약없는 지연
타임아웃 시간 설정
a) 타임아웃이 길면
노드 죽음 선언이 길어짐
-> 사용자 입장에서는 지연 or 오류 메시지 발생
b) 타임아웃이 짧으면
일시적인 부하나 지연도 오작동으로 간주될 위험
노드가 살아 있어도 잘못된 실패 처리
-> 같은 작업이 중복 실행될 위험 (예: 이메일 두 번 전송)
네트워크 추가 부하 → 연쇄 장애 유발 가능
이상적인 네트워크
최대 지연 시간 d
노드 응답 시간 r → 2d + r
❗타임아웃의 합리적 기준
BUT
실제 시스템은 이를 보장하지않음
- 기약 없는 지연(unbounded delay)존재
- 노드 응답 시간 보장 불가
4)동기 네트워크 대 비동기 네트워크
a)동기 네트워크
제한 있는 지연 (bounded delay): 지연 최대치가 고정
회선 교환(circuit-switch)
지연과 패킷 유실 거의 없음
고정된 트래픽에 적합 (음성 통화 등)
b)비동기 네트워크
패킷 교환(packet-switch): 큐 대기, 혼잡 발생
회선 개념 없음
순간적으로 몰리는 트래픽(bursty traffic)에 최적화
->가용한 대역폭을 동적으로 사용
->지연 예측 불가
3.신뢰성없는시계
분산 시스템 통
-> 즉각적이지 않음
메시지가 네트워크를 거쳐서 다른 장비로 전달시 시간이 소요
❗각 장비는 자체 하드웨어 시계(수정 발진기 기반(quartz crystal oscillator))를 사용
-> 완벽히 정확하지 않아 서로 시계가 조금씩 다름 (빠르거나 느림)
❓시간을 어느정도라도 동기화 시키기
NTP (Network Time Protocol)
→ 서버 그룹에서 보고한 시간에 따라 컴퓨터 시계를 조정
=> 다시 GPS 수신자 같은 더욱 정확한 시간 출처로부터 시간을 얻음
1)시계동기화와정확도
정확한 시간을 알려주게 하는 방법은 정확하지 않은편
시계 동기화 이슈
a) 수정 시계의 드리프트
장비의 온도 등에 따라 시간이 빨라지거나 느려질 수 있음
예: 200ppm 드리프트 → 하루 최대 17초 오차
b) NTP의 한계
차이가 크면 동기화 거부 or 시계 리셋 발생
-> 시간이 거꾸로 흐르는 듯한 현상
방화벽 이슈 발생 시 오랜 시간 오류가 탐지되지 않을 수도 있음
네트워크 지연이 큰 경우 정확도에 한계
-> 클라이언트가 동기화를 포기할 수도 있
c) 윤초
1분이 59초 또는 61초가 되기도 함
→ 윤초를 고려하지 않으면 시스템 동작 오류
❗문지름(smearing) 방식 사용
: 윤초 조정을 하루에 걸쳐서 서서히 수행
d) 가상 머신 환경
VM 간 CPU 공유로 인해 시계가 멈추거나 앞으로 튀는 문제 발생
e)완전히 제어할 수 없는 장치
모바일, 임베디드 등 사용자 조작 가능한 장치에서는 하드웨어 시간 신뢰 불가
3)동기화된 시계에 의존하기
대부분의 시 간에 아주 잘 동작
-> 견고한 소프트웨어는 잘못된 시계에 대비할 필요가 있음
BUT
시계가 잘못된다는 것을 눈치채는것이 어려움
❗장비 사이의 시계 차이 모니터링 필요
a) 이벤트 순서화용 타임스탬프
❓두 클라이언트가 분산 DB에 쓰면 누가먼저 쓰게 되는가
❗ 최종 쓰기 승리 (last write wins, LWW)
시계가 빠른 쓰기가 항상 우선
이벤트 순서를 시계로 판단
-> 노드 간 시차로 인해 후속 이벤트가 더 이른 타임스탬프를 가질 수 있음
x=2가 x=1보다 나중에 쓰였지만, 시계 오차로 x=2가 무시됨
= 클라이언트 B의 연산이 손실 됨
❗ “최근”의 정의는 로컬 일 기준 시계에 의존
=> 그 시계는 틀릴 수도 있는 전재를 늘 가져야 함
❓ 엄격하게 동기화 된 NTP 시계를 쓴다면
네트워크 지연 및 시계 드리프트 발생
→ 패킷이 보내기 전에 도착한 것처럼 보일 수 있음
❓순서만이라도 제대로 보장하면
→ 시계 정확도가 네트워크 지연보다 훨씬 더 높아야 함
= 현실적으로 불가능에 가까운
❗논리적 시계(logical clock)
증가하는 카운터를 기반으로 하며 이벤트 순서화의 안전한 대안
이벤트의 상대적인 순서만 측정
-> 실제 시간은 무시하는 시계
b) 전역 스냅숏용 동기화된 시계
스냅숏 격리
작고 빠른 읽기 쓰기 트랜잭션과 크고 오래 실행되는 읽기 전용 트랜잭션 격리 수준
=> 일괄된 DB를 볼 수 있게 됨
단일노드 구현
- 단순한 카운터 사용
분산시스템 구현
- 전역 단조 증가와 인과성을 반영하는 ID 생성이 필요
-> 반영하지 못하면 병목이 되어버림
=> 상당히 구현이 까다로움
❓동기화된 일 기준의 시계를 ID로 사용하면?
❗스패너의 스냅숏 구현
각각 가장 이른 타임 스탬프와 가장 늦은 타임스탬프를 포함하는 두개의 신뢰구간 존재
A = [A earliset, A latest] / [42.001, 42.004]
B = [B earliset, B latest] / [42.006, 42.009]
두 시계의 신뢰 구간이 겹치지 않으면
(A earliest 〈 A latest 〈 B earliest 〈 B latest)
확실한 순서를 보장
-> 겹치면 확신할 수 없음
❗ 보장을 위해 커밋 전 의도적으로 신뢰 구간의 길이만큼 기다림
* 구글은 이를 위해 각 데이터센터에 GPS 수신기 또는 원자시계 설치
-> 약 7ms 이내로 동기화
4)프로세스 중단
파티션 마다 리더가 하나씩 있는 DB
- 리더만 쓰기가 허용
❓리더를 아는 법
❗ 임차권(Lease) 부여
: 일정 시간 동안 리더 자격을 부여하는 타임아웃이 있는 잠금
구현법
while (true) {
request = getIncomingRequest();
if (lease.expiryTimeMillis - System.currentTimeMillis() < 10000) {
lease = lease.renew();
}
if (lease.isValid()) {
process(request);
}
}
❌동기화된 시계에 의존
lease.expiryTimeMillis = 다른 노드에서 설정된 시간
System.currentTimeMillis() = 로컬 시스템 시계
시계 동기화가 깨지면
-> 코드 오작동 발생
❗로컬 단조 시계를 사용하도록 수정
❓lease.isValid() 다음 줄에서 process(request)되기 전 프로세스가 중단(Freeze)발생
❌ 중단된 사이에 리스는 만료
-> 다른 노드가 리더가 되어 동시 리더 상황 발생
❓중단이 왜 생기나
Stop-the-world GC중단, vm suspend/resume 등등 다양한 원인으로 발생
❓응답시간 보장은 어려운가
열심히 노력하면 중단의 원인을 제거할 수 있음
실시간 보장을 위해 필요한 조건
- 실시간 운영체제 (real-time operating system, RTOS): CPU 시간 스케줄링 보장
- 라이브러리: 최악의 실행 시간(Worst-case execution time) 명시
- GC 최소화 /제한
- 테스트, 측정, 검증을 통한 신뢰 확보
=>사용 가능한 언어/도구 매우 제한됨
* GC 최소화 /제한
GC 중단을 계획적 중단으로 간주
GC 중단 필요 경고 -> 해당 노드 요청 중단 -> 요청 없는 동안 GC 수행
=> 클라이언트로 부터 GC를 숨기고 응답시간 상위 백분위를 줄임
4.지식, 진실, 그리고 거짓말
❗분산 시스템의 진실
네트워크에 있는 노드는 어떤 것도 확실히 알지 못함
-> 오직 메시지를 주고받고 그 응답/무응답으로 추론할 뿐
-> 네트워크 문제와 노드 문제를 구별 할 수 없음
=> 시스템은 절대적이지 않음, 항상 불완전한 관측에 의존
1)진실은 다수결로 결정된다
즉 우리는 한 노드의 판단에 의존할 수 없음
-> 여러 노드로부터 최소 개수의 투표를 받아야함
❗정족수(quorum)
과반수 정족수는 충돌 없는 결정을 보장
개별 노드의 인식과 상관없이 다수결이 진실
ex) 노드 3개 중 2개 이상이 장애라 판단
-> 해당 노드는 장애로 간주
❗펜싱(fencing)
잠금이 부여될 때마다 증가하는 숫자 토큰을 발급
클라이언트가 자원에 접근 시
->토큰을 함께 보냄
A (33) -> 작업 중단
B (34) -> 정상 작업 시작 => 성공
A (33) -> 작업 재개 => 토근이 낮아 요청 거부
2)비잔틴 결함
앞선 유형: 노드들이 신뢰성은 없지만 정직하다고 가정
비잔틴 결함 (Byzantine fault)
노드들이 신뢰성은 없고 정직하지도 않음
-> 노드가 거짓말을 한다면?( 임의의 결함이 있거나 오염된 응답을 보냄)
비잔틴 장군 문제(Byzantine Generals Problem)
일부 노드가 거짓 정보를 보내도 다수의 노드가 올바른 합의에 도달
-> 시스템은 계속해서 올바르게 동작
=> 비잔틴 내결함성 (Byzantine Fault Tolerance)을 지님
현실은..
시스템이 비잔틴 내결함성을 지니도록 만드는 프로토콜은 매우 복잡
-> 내결함성을 지닌 임베디드 시스템은 하드웨어 수준의 지원에 의존함
=> 데이터 시스템에서 비잔틴 내결함성 솔루션을 배치 시 큰 비용 발생
=> 현실적으로 실용적이지 않아 고려하지 않고 설계 됨