LabHub
배우기 러닝패스 코스

리더가 둘이었다 · 진짜 구현에서 다시 본다 · 이론

과반을 잃으면 느려지는 것이 아니라 멈춘다

LabHub 에서 이어서 보기

한 줄 요약

etcd 는 Raft 를 그대로 구현한 저장소이고, 앞에서 손으로 만든 임기와 정족수가 같은 이름으로
드러난다. 가장 자주 오해되는 대목은 읽기다 — 과반을 잃으면 쓰기뿐 아니라 기본 읽기도 멈춘다.

왜 이게 필요했나

습작은 배우기에는 좋지만 믿을 근거가 되지는 않는다. 내가 만든 규칙이 장난감의 규칙인지
진짜 규칙인지는, 같은 숫자와 같은 말이 실제 시스템에서 나오는 것을 보아야 확인된다.
etcd 는 그 확인에 가장 알맞은 대상이다. 쿠버네티스의 모든 오브젝트가 여기에 들어 있고,
문서가 스스로를 "메타데이터를 일관되고 내결함성 있게 저장" 하는
"대규모 분산 시스템을 위한 범용 기반" 이라고 소개한다
([etcd — 왜 etcd 인가](https://etcd.io/docs/v3.5/learning/why/)).

어떻게 동작하나

세 멤버를 같은 --initial-cluster 문자열로 띄우면 그들이 서로를 한 클러스터로 인식한다.
문서의 정적 부트스트랩 예시는 이름, 피어 주소, 클라이언트 주소, 그리고 클러스터 토큰을
함께 준다. 토큰을 두는 이유는 문서가 밝히듯 "각 클러스터에 고유한 토큰을 주어" 서로 다른
클러스터가 섞이지 않게 하기 위해서다
([etcd 클러스터 구성](https://etcd.io/docs/v3.5/op-guide/clustering/)).

뜬 뒤에는 앞의 모듈에서 만든 값이 그대로 보인다.

ENDPOINT          ID                IS LEADER   RAFT TERM   RAFT INDEX127.0.0.1:22379   99f0eb44090117f8  false       2           14127.0.0.1:22479   e64076ee26a8ab0d  false       2           14127.0.0.1:22579   8e05ae0f7f2c67be  true        2           14

RAFT TERM 이 임기이고 RAFT INDEX 가 로그의 자리 번호다. etcdctl move-leader
리더를 옮기면 임기가 하나 오른다 — 앞에서 새 리더가 임기를 올리고 뽑히던 것과 같은 일이다.

정족수의 표는 문서에 그대로 있다. 1대는 과반 1에 견딜 수 있는 고장 0대, 3대는 2와 1대,
5대는 3과 2대, 7대는 4와 3대다. 문서는 클러스터를 일곱 대 이하로 두라고 권하고,
"홀수 크기의 클러스터는 짝수 크기와 같은 수의 고장을 견디면서 노드는 더 적다" 고 적는다
([etcd FAQ](https://etcd.io/docs/v3.5/faq/)).

가장 중요한 대목은 읽기다. etcd 의 기본 읽기는 선형화 읽기(linearizable read) 이고,
문서는 선형화를 "동시에 실행되는 프로세스들이 적용한 각 연산이 호출과 응답 사이의 어느
한 시점에 즉시 일어난 것처럼 보이는 것" 으로 정의한다. 그리고 이어서 "선형화에는
비용이 따른다 — 선형화된 요청은 Raft 합의 과정을 거쳐야 하기 때문" 이라고 적는다
([etcd API 보장](https://etcd.io/docs/v3.5/learning/api_guarantees/)). 그래서 과반을
잃은 클러스터는 느려지는 것이 아니라 읽기까지 멈춘다. 같은 문서는 요청의 일관성 모드를
serializable 로 두면 "정족수 기준으로 낡은 데이터에 접근할 수 있는 대신 선형화된
접근이 살아 있는 합의에 의존하면서 생기는 성능 부담을 없앤다" 고 설명한다 — 낡은 값을
받아들일 수 있다면 그 길이 있다는 뜻이다.

여기까지 오면 앞의 세 실습에서 쓴 규칙과 이 문서의 문장이 같은 말을 하고 있다는 것이
보인다. 우리가 has_majority 에 적은 '절반 초과' 가 문서의 (n/2)+1 이고,
commit_index 가 세던 것이 여기서는 RAFT INDEX 이며, 임기를 올리며 뽑히던 새 리더가
move-leader 뒤의 RAFT TERM 이다. 이름만 다르다.

현장에서 만나는 모습

쿠버네티스 클러스터가 "아무것도 안 되는" 상태의 상당수가 이것이다. 컨트롤 플레인 노드
세 대 중 두 대가 동시에 내려가면 kube-apiserver 는 살아 있어도 조회조차 되지 않는다.
etcd 가 과반을 잃어 읽기를 확정할 수 없기 때문이다. 이때 남은 한 대를 아무리 들여다봐도
원인이 그 기계 안에 없다.

etcd 문서는 일시적인 정족수 상실은 네트워크가 돌아오면 자동으로 안전하게 재개되지만
영구적인 정족수 상실은 치명적이라고 구분한다. 그 구분이 운영에서 뜻하는 바는
하나다 — 정족수를 잃은 상태에서 조급하게 멤버를 지우거나 새로 넣지 말고, 먼저 돌아올
수 있는 멤버가 있는지부터 확인하는 것이다.

한 가지 더. 이 실습이 쓰는 포트가 표준인 2379 가 아닌 이유는, 같은 파드 안에서 다른
실습용 컨트롤 플레인이 이미 그 포트를 쓰고 있기 때문이다. 포트를 옮기는 것만으로
클러스터가 하나 더 생긴다는 사실 자체가, etcd 가 클러스터를 '주소와 토큰의 묶음' 으로만
구분한다는 뜻이기도 하다.

다음 실습에서 할 것

같은 파드 안에서 etcd 세 멤버를 정적 부트스트랩으로 띄운다. 리더를 옮겨 임기가 오르는
것을 보고, 하나를 멈춘 채 써 보고, 둘을 멈춘 채 쓰기와 읽기를 모두 시도한다. 마지막에는
앞의 세 실습에서 손으로 만든 규칙과 여기서 본 숫자를 맞춰 본다.