분단
네트워크 장애로 노드들이 두 무리 이상으로 갈려 서로 메시지를 주고받지 못하게 된 상태입니다. 갈린 쪽에서는 상대가 죽은 것인지 잠깐 안 보이는 것인지 가려낼 수 없습니다. 끊긴 링크가 돌아오면 분단은 끝납니다.
상세
분단은 노드 사이의 통신이 평소처럼 되지 않는 상태입니다. RabbitMQ 공식 문서는 분단을 두 노드가 평소 하던 방식으로 서로 통신하지 못하게 되는 일이라고 적습니다. etcd 공식 운영 가이드는 같은 일을 클러스터가 두 쪽으로 갈리는 것으로 적습니다. 한쪽은 멤버 과반을 쥐고 다른 한쪽은 소수만 쥡니다. 과반을 쥔 쪽이 쓸 수 있는 클러스터가 되고, 소수 쪽은 쓸 수 없게 됩니다.
flowchart TD
C["노드 세 대가 서로 보인다"] -->|"링크가 끊긴다"| P["두 무리로 갈린다"]
P --> M["다수파 · 쓸 수 있다"]
P --> N["소수파 · 쓸 수 없다"]
M -->|"분단이 걷힌다"| H["소수파가 다수파의 리더를 알아보고 상태를 되찾는다"]
N -->|"분단이 걷힌다"| H
갈린 쪽에서 보면 상대가 죽은 것인지 연결만 끊긴 것인지 알 방법이 없습니다. Elasticsearch 공식 운영 가이드는 원격 구역의 장애와 구역 사이의 단순한 연결 상실을 구별하는 것이 불가능하다고 적습니다. etcd 문서도 분단을 소수 팔로워 장애나 리더 장애와 비슷한 것으로 적습니다. 안 보이는 노드를 다루는 문제와 같은 자리에 놓인다는 뜻입니다.
리더가 어느 쪽에 남느냐로 뒷일이 갈립니다. etcd 문서는 리더가 다수파에 있으면 다수파 입장에서 이 장애가 소수 팔로워 장애로 보인다고 적습니다. 리더가 소수파에 있으면 그것은 리더 장애입니다. 소수파에 남은 리더는 스스로 물러나고 다수파가 새 리더를 뽑습니다.
양쪽이 각자 리더를 뽑아 버리면 스플릿 브레인입니다. etcd 문서는 etcd 에 스플릿 브레인이 없다고 적습니다. 멤버를 넣고 빼는 일이 명시적이고, 그런 변경마다 현재 멤버 과반의 승인을 받기 때문입니다.
분단은 고쳐집니다. etcd 문서는 분단이 걷히면 소수파가 다수파 쪽 리더를 자동으로 알아보고 자기 상태를 되찾는다고 적습니다. Elasticsearch 문서는 자동으로 최대한 빨리 복구한다고 적으면서 대가도 같이 적습니다. 분단 동안 클러스터의 일부를 쓸 수 없을 수 있고, 분단이 치유되고 나면 빠진 데이터를 다시 맞추고 스스로 균형을 되잡는 데 시간과 자원을 써야 합니다.
발생 조건
두 가지가 함께 서야 합니다. 노드 사이의 패킷이 실제로 오가지 못해야 합니다. 그 끊긴 시간이 판정 타임아웃을 넘겨야 합니다. 하나를 빼면 분단으로 판정되지 않습니다.
노드 수는 이 둘에 들어가지 않습니다. 노드 수가 정하는 것은 분단이 나느냐가 아니라 갈린 뒤에 한쪽이 과반을 쥐느냐입니다.
노드 구성
분단은 노드가 두 대만 있어도 납니다. 정의부터 두 노드가 평소 하던 방식으로 서로 통신하지 못하게 되는 일입니다. 노드 수가 가르는 것은 그 다음입니다. 갈린 한쪽이 과반을 쥐면 그쪽이 계속 돕니다. 어느 쪽도 과반을 못 쥐면 둘 다 멈춥니다. RabbitMQ 공식 문서는 클러스터 노드 수마다 견디는 노드 장애 수와 분단 내성을 이렇게 적습니다.
| 클러스터 노드 수 | 견디는 노드 장애 수 | 분단을 견디나 |
|---|---|---|
| 1 | 0 | 해당 없음 |
| 2 | 0 | 아니오 |
| 3 | 1 | 예 |
| 4 | 1 | 한쪽에 과반이 있으면 예 |
| 5 | 2 | 예 |
두 대짜리 클러스터는 분단을 견디지 못합니다. 네 대짜리는 한쪽에 과반이 있을 때만 견딥니다. 두 대 칸이 견디지 못한다고 적힌 것은 분단이 안 난다는 뜻이 아닙니다. 나기는 나는데 어느 쪽도 과반이 아니라는 뜻입니다. 분단만 재현할 생각이면 두 대를 세워 링크를 끊는 것이 가장 작은 구성입니다. 갈린 한쪽이 과반을 쥐고 계속 도는 데까지 보려면 세 대를 세우고 2 대 1 로 가릅니다.
끊는 방식
노드 사이의 패킷이 실제로 오가지 못해야 합니다. Jepsen 은 분단을 인위로 만드는 도구를 공개하면서
끊는 방식마다 이름을 달아 두었습니다. complete-grudge 는 노드 무리를 받아 어떤 노드도 자기 무리
밖의 노드와 말이 통하지 않게 만듭니다. partition-random-node 는 노드 하나만 나머지 네트워크에서
떼어 냅니다. bridge 는 네트워크를 반으로 자르되 가운데 노드 하나가 양쪽과 끊김 없이 양방향으로
통하도록 남깁니다. partition-halves 는 :start 를 받으면 네트워크를 두 쪽으로 자르고 :stop 을
받으면 도로 잇습니다.
판정 방아쇠
끊긴 시간이 판정 타임아웃을 넘겨야 합니다. 클러스터는 상대가 몇 초 안 보인다고 바로 빼지 않습니다. Elasticsearch 공식 설정 레퍼런스의 기본값은 이렇습니다.
cluster.fault_detection.follower_check.interval 1s
cluster.fault_detection.follower_check.timeout 10s
cluster.fault_detection.follower_check.retry_count 3
cluster.fault_detection.leader_check.interval 1s
cluster.fault_detection.leader_check.timeout 10s
cluster.fault_detection.leader_check.retry_count 3
cluster.election.max_timeout 10s
뽑힌 마스터는 1초마다 클러스터의 다른 각 노드에 팔로워 체크를 보내고, 한 번의 응답을 10초까지
기다립니다. 각 노드마다 연속 3회 실패해야 그 노드를 고장 난 것으로 보고 클러스터에서 뺍니다. 반대
방향도 같습니다. 각 노드는 1초마다 뽑힌 마스터를 확인하고 10초까지 기다리며, 연속 3회 실패하면
마스터가 고장 났다고 보고 새 마스터를 찾거나 뽑으려 듭니다. cluster.election.max_timeout 은 노드가
첫 선거를 시도하기 전에 기다리는 시간의 상한입니다. 오래가는 분단이 선거를 지나치게 띄엄띄엄 만들지
않게 하려는 값이고 기본값은 10초입니다.
flowchart TD
A["일부 노드 사이 패킷이 끊긴다"] --> B{"끊긴 시간이 타임아웃과 재시도 횟수를 넘겼나"}
B -- "아니오" --> C["아무 일도 없다"]
B -- "예" --> D["분단으로 판정된다"]
D --> E{"한쪽에 과반이 있나"}
E -- "아니오" --> F["어느 쪽도 진행하지 못한다"]
E -- "예" --> G["다수파만 쓸 수 있다 · 소수파는 멈춘다"]
안 터지는 선
끊긴 시간이 판정 타임아웃보다 짧으면 아무 일도 일어나지 않습니다. 몇 초 끊긴 것으로는 노드가 클러스터
에서 빠지지 않습니다. Redis 공식 클러스터 명세도 같은 선을 값으로 적습니다. 마스터가 페일오버되려면
마스터 과반에게 최소 node_timeout 동안 닿지 않아야 하고, 분단이 그 시간 전에 고쳐지면 쓰기는 하나도
없어지지 않습니다.
조건을 다 맞췄는데도 원인이 분단이 아닐 수 있습니다. Elasticsearch 공식 트러블슈팅 가이드는 지연이나 팔로워 체크 재시도 횟수 초과로 노드가 빠지는 일이 심한 네트워크 지연 때문일 수도 있고, 처리가 밀렸거나 과부하가 걸린 노드 때문일 수도 있다고 적습니다. CPU(Central Processing Unit, 중앙처리장치) · 힙 · 디스크가 그런 자리입니다. 노드가 빠졌다는 사유만으로는 둘 중 무엇인지 알 수 없다고 못 박습니다. 가려내려면 다른 흔적을 봅니다. 같은 문서는 가비지 컬렉션 정지가 Elasticsearch 가 기본으로 남기는 가비지 컬렉션 로그에 기록된다고 적습니다. 이 로그로 그 노드가 힙을 많이 쓰며 긴 가비지 컬렉션 정지를 겪고 있는지 아닌지 확인합니다. 가상 머신 정지는 대개 시스템 시계에 불연속을 남기고, Elasticsearch 가 그것을 로그에 적습니다.
예시
Redis Cluster
Redis 공식 클러스터 명세는 분단이 났을 때 없어지는 쓰기의 범위를 node_timeout 하나로 적습니다.
마스터가 페일오버되려면 마스터 과반에게 최소 node_timeout 동안 닿지 않아야 합니다. 분단이 그 전에
고쳐지면 쓰기는 하나도 없어지지 않습니다. 분단이 node_timeout 보다 오래가면 그때까지 소수파 쪽에서
한 쓰기가 전부 없어질 수 있습니다.
다만 소수파 쪽은 다수파와 접촉 없이 node_timeout 이 지나자마자 쓰기를 거부하기 시작합니다. 그래서
소수파가 더는 쓸 수 없게 되기까지의 창에는 최대치가 있습니다. Redis Cluster 는 분단의 소수파 쪽에서
쓸 수 없습니다. 다수파 쪽은 node_timeout 에 몇 초를 더한 뒤 다시 쓸 수 있게 됩니다. 복제본 하나가
뽑혀 자기 마스터를 페일오버하는 데 그 몇 초가 듭니다.
같은 문서는 스플릿 브레인 상태의 노드 상태 합의 규칙도 적습니다. 그런 장치들 때문에 클러스터가 스플릿 브레인 조건에 놓이면 보통 모든 노드가 거의 같은 시점에 쓰기 받기를 멈춥니다.
MongoDB 레플리카셋
MongoDB 공식 매뉴얼은 프라이머리가 소수파에 갇혔을 때의 동작을 적습니다. 분단이 프라이머리를 투표권 있는 노드 소수와 함께 떼어 놓을 수 있습니다. 프라이머리가 자신이 레플리카셋에서 투표권 있는 노드의 소수만 볼 수 있다는 것을 알아채면, 스스로 물러나 세컨더리가 됩니다. 그와 별개로, 자기 자신을 포함해 투표권 있는 노드의 과반과 통신할 수 있는 멤버가 새 프라이머리가 되기 위한 선거를 엽니다. 과반 표를 처음 받은 멤버가 새 프라이머리가 됩니다.
Elasticsearch 두 구역 배치
Elasticsearch 공식 운영 가이드는 노드를 두 구역에 대칭으로 놓았을 때를 적습니다. 두 구역이 각자 독립적으로 선거를 열 수 있다면 연결 상실이 스플릿 브레인 문제로 이어지고 그래서 데이터가 없어집니다. Elasticsearch 는 어느 구역의 노드도 마스터로 뽑지 않는 것으로 이를 피하고 데이터를 지킵니다. 그 노드가 최신 클러스터 상태를 갖고 있고 클러스터에 다른 마스터가 없다는 것을 확신할 수 있을 때까지입니다. 연결이 회복될 때까지 마스터가 아예 없다는 뜻일 수 있습니다.
경계
데이터를 여러 조각으로 나눠 두는 테이블 파티셔닝도 분단인가. 아닙니다.
같은 낱말을 씁니다. 영어로는 둘 다 partition 입니다. 그런데 가리키는 것이 다릅니다. PostgreSQL 공식 문서는 파티셔닝을 논리적으로 하나인 큰 테이블을 더 작은 물리 조각으로 쪼개는 일이라고 적습니다. 사람이 미리 정해 두는 설계 결정입니다.
분단 쪽은 메시지가 없어지는 장애입니다. 브루어 추측을 다룬 Gilbert 와 Lynch 의 논문은 CAP(Consistent, Available, Partition-Tolerant, 일관성 · 가용성 · 분단 내성)의 분단 내성을 모형으로 세우면서, 네트워크가 한 노드에서 다른 노드로 보낸 메시지를 임의로 얼마든지 잃을 수 있게 둔다고 적습니다. 네트워크가 분단되면 갈린 한쪽 노드가 다른 쪽 노드로 보낸 메시지는 전부 없어집니다.
가르는 선은 하나입니다. 파티셔닝은 데이터를 어디에 둘지 사람이 고른 것이고, 분단은 보낸 메시지가 도착하지 않는 것입니다. 테이블을 파티션으로 갈라 두어도 노드 사이에 메시지가 오가는 한 분단은 아닙니다.
관련 항목
갈림을 판정하는 장치
정족수 · 정족수 상실 · 리더 선출 · 합의 · Raft · 티브레이커 · 보팅 온리 노드
갈린 뒤에 벌어지는 사건
스플릿 브레인 · 페일오버 · 데이터 유실 · 재동기화
분단을 전제로 세운 설계
분단 내성 · CAP · 브루어 추측(Brewer's Conjecture) · 복제 · 선형화 가능성
분단과 헷갈리는 원인
가비지 컬렉션 정지 · 가상 머신 정지 · 과부하
분단을 알아채고 만드는 도구
팔로워 체크 · 리더 체크 · 클러스터 장애 감지 · Jepsen
분단을 각자 다르게 처리하는 제품
etcd · MongoDB · Elasticsearch · Redis Cluster · RabbitMQ
같은 낱말을 쓰는 이웃
다른 이름: 네트워크 분단 · network partition · netsplit