-
ETCD 이해하기SW개발/Kubernetes 2025. 8. 10. 02:26
Kubernetes(K8s)를 처음 접하면 수많은 컴포넌트와 용어에 압도되지만, 그 중심에는 ETCD라는 아주 중요한 저장소가 있으며, Kubernetes는 모든 클러스터 상태와 설정 정보를 ETCD에 저장합니다.
- 어떤 Pod이 어디서 동작 중인지
- Service의 엔드포인트 정보
- ConfigMap과 Secret
- 노드 상태, 네임스페이스, 이벤트 로그 등
이 모든 것이 ETCD에 기록됩니다.
즉, ETCD가 없으면 클러스터의 상태를 잃어버리게 되고, Kubernetes는 ‘내가 뭘 하고 있었는지’ 전혀 기억하지 못하게 됩니다.
1. ETCD란 무엇인가?
결국, ETCD는 분산 키-값 저장소(Distributed Key-Value Store)입니다.
- Raft 합의 알고리즘을 사용하여 여러 노드에 걸쳐 데이터 일관성 유지
- 빠른 읽기/쓰기, 고가용성(HA), 내결함성(Fault Tolerance) 제공
ETCD는 단순한 데이터베이스가 아니라, 분산 시스템의 상태를 안전하게 보관하는 합의 기반 저장소입니다.
2. 왜 Kubernetes는 ETCD를 쓸까?
Kubernetes는 컨트롤 플레인에서 상태 저장소로 ETCD를 사용합니다.
- kube-apiserver가 모든 요청을 처리하며, 상태 변경 사항을 ETCD에 기록
- ETCD에서 데이터를 읽어 클러스터 상태를 복원하거나, 변경 사항을 watch
- 강한 일관성이 필요한 Kubernetes 특성상, eventually consistent 스토어보다 합의 기반 저장소가 적합
장점
- 일관성 (Consistency) – 모든 ETCD 노드가 동일한 상태 유지
- 내결함성 (Fault Tolerance) – 일부 ETCD 노드 장애 시에도 데이터 보존
- 고가용성 (High Availability) – 리더 장애 시 자동 선출로 서비스 지속
3. ETCD의 핵심 개념
3.1 RSM (Replicated State Machine)
Etcd는 Replicated state machine(이하 RSM)입니다. 분산 컴퓨팅 환경에서 서버가 몇 개 다운되어도 잘 동작하는 시스템을 만들고자 할 때 선택하는 방법의 하나로, State Machine Replication이 있습니다. 이는 똑같은 데이터를 여러 서버에 계속하여 복제하는 것이며, 이 방법으로 사용하는 머신을 RSM이라고 합니다.
RSM은 위 그림과 같이 command가 들어있는 log 단위로 데이터를 처리합니다. 데이터의 write를 log append라 부르며, 머신은 받은 log를 순서대로 처리하는 특징을 갖습니다. 하지만, 똑같은 데이터를 여러 서버에 복제해놨다고 해서 모든 게 해결되지는 않습니다. 오히려 더 어려운 문제가 생기기도 합니다. Robust 한 RSM을 만들기 위해서는 데이터 복제 과정에 발생할 수 있는 여러 가지 문제를 해결하기 위해 컨센서스(consensus)를 확보하는 것이 핵심입니다. Consensus를 확보한다는 것은 RSM이 아래 4가지 속성을 만족한다는 것과 의미가 같으며, etcd는 이를 위해 Raft 알고리즘을 사용하였습니다.
3.2 Quorum (과반수 합의)
Quorum은 분산 시스템에서 합의를 이끌어내기 위해 필요한 최소한의 노드 수를 의미합니다.
즉, 전체 노드 중 과반수 이상의 응답을 확보해야 “이 변경 사항이 안전하다”고 판단하고 커밋을 진행합니다.공식 정의
노드 개수가 n개일 때,
- 예:
- 3노드 클러스터 → Quorum = 2
- 5노드 클러스터 → Quorum = 3
- 7노드 클러스터 → Quorum = 4
왜 필요한가?
- 데이터 일관성(Consistency) 보장
일부 노드가 장애나 네트워크 분리(Partition)로 인해 다른 데이터를 갖고 있어도, 과반수의 합의를 기준으로 데이터를 결정하기 때문에 잘못된 데이터가 커밋되는 것을 방지합니다. - Split-Brain 방지
네트워크 장애로 클러스터가 둘로 쪼개졌을 때, Quorum이 없는 쪽은 쓰기를 중단하므로 서로 다른 리더가 동시에 존재하는 상황을 막습니다.
3.3 Raft 알고리즘
Raft는 분산 시스템에서 여러 노드가 동일한 상태를 유지하도록 보장하는 합의(Consensus) 알고리즘입니다.
데이터를 복제(Replication)하는 과정에서 발생할 수 있는 순서 불일치나 데이터 충돌을 방지하고, 장애 상황에서도 안전하게 복구할 수 있도록 설계되었습니다. ETCD는 바로 이 Raft를 사용해 모든 노드의 상태를 동기화합니다.Raft의 세 가지 핵심 역할
Raft 클러스터의 노드는 세 가지 상태 중 하나로 동작합니다.
- Leader
- 클라이언트 요청을 수신
- 로그(Log)를 기록하고 Followers에 복제
- 주기적으로 Heartbeat 전송
- Follower
- Leader의 로그 복제 요청 처리
- Leader Heartbeat 수신
- Candidate
- Leader가 없을 때 선거 시작
- 투표를 요청하여 Leader가 되려 시도
Raft의 세 가지 주요 절차
(1) Leader 선출 (Leader Election)
etcd 클러스터를 3대의 서버로 처음 구성하면, 모든 서버는 Follower 상태로 시작하며, 각 서버의 term 값은 0입니다. 초기에는 Leader가 없기 때문에 어떤 서버도 Heartbeat를 보내지 않습니다.
Election Timeout 발생
Leader가 없는 상태에서 서버 중 한 대가 Election Timeout을 맞이합니다.
Timeout이 발생한 서버는:- 상태를 Candidate로 변경
- term 값을 1 증가
- 클러스터 내 다른 서버에게 RequestVote RPC 요청 전송
투표 과정
다른 서버(Follower)는 RequestVote를 받으면:
- 자신의 term과 로그 상태를 Candidate와 비교
- Candidate보다 term이 높거나 로그가 최신이면 거절
- 그렇지 않다면 투표(OK 응답) 진행
초기 상태에서는 거절 조건이 없으므로, 모든 서버가 투표를 승인합니다.
Quorum 확보와 Leader 당선
Candidate는 자기 자신을 포함하여 받은 투표 수가 **Quorum(과반수)**에 도달하면 Leader가 됩니다.
3대 클러스터 → Quorum = 2 → 2표 이상 확보 시 당선
Heartbeat 전송
Leader가 된 서버는 주기적으로 **AppendEntries RPC(Heartbeat)**를 다른 서버에 전송합니다.
Heartbeat에는:- Leader의 term
- Leader의 로그 인덱스 정보가 포함됩니다.
Follower의 term 업데이트
Follower는 AppendEntries RPC를 받으면:
- 받은 term이 자신의 term보다 크면 term 값을 갱신
- Leader의 로그 정보를 기준으로 로그를 동기화
클러스터 운영 시작
Leader 선출이 완료되면 클러스터는 외부에서 들어오는 쓰기(Write)와 읽기(Read) 요청을 처리할 준비가 완료됩니다.
(2) 로그 복제 (Log Replication)
Leader가 선출된 이후, etcd(Raft)는 모든 쓰기 요청을 Leader를 통해 처리하고, 그 변경을 Follower에 동일한 순서로 복제합니다. 핵심은 “같은 명령을 같은 순서로 실행”하게 만드는 것입니다.
기본 흐름 (쓰기 요청 처리)
- 클라이언트 → Leader
클라이언트의 쓰기 요청이 Leader에 도착합니다. - Leader 로그 추가
Leader는 요청을 새로운 로그 엔트리(entry) 로 추가합니다. (인덱스·term 부여) - AppendEntries RPC 전송
Leader는 각 Follower에게 AppendEntries RPC를 보냅니다.
이 RPC에는 다음이 포함됩니다.- prevLogIndex, prevLogTerm (연속성 확인용 앵커)
- 새 엔트리들(entries)
- leaderCommit (Leader가 알고 있는 커밋 위치)
- Follower의 검사 및 기록
Follower는 prevLogIndex/prevLogTerm가 자신의 로그와 연속되는지 확인합니다.- 연속이면: 새 엔트리를 추가하고 성공(OK) 응답
- 불연속이면: 거절하고 자신의 충돌 정보를 바탕으로 재동기화 필요
- 과반수(Quorum) 확인 → 커밋
Leader는 과반수 Follower로부터 OK를 받으면 해당 인덱스를 커밋(commit) 으로 표시합니다.
(Raft 규칙: 현재 term의 엔트리가 과반수에 저장되면 커밋 가능) - 적용 & 클라이언트 응답
커밋된 엔트리를 Leader가 상태 머신(State Machine)에 적용(apply) 하고, 성공을 클라이언트에 응답합니다.
이후 Heartbeat/AppendEntries에 포함된 leaderCommit 값을 통해 Follower들도 같은 인덱스까지 적용합니다.
(3) 리더 다운(Leader down)
리더가 사라지면 Follower들은 Election Timeout 동안 Heartbeat(AppendEntries) 를 받지 못하게 되고, 타임아웃이 만료된 노드가 선거를 시작합니다.
새 리더 선출 흐름
- Leader → Candidate 전환 & RequestVote 전송
- Member A 는 term을 1 증가시키고 Candidate 가 되어 클러스터에 RequestVote RPC 를 보냅니다.
- 이때 자신의 lastLogTerm/lastLogIndex(로그 최신성)와 term 정보를 함께 전송합니다.
- 투표 판단 규칙(로그 최신성)
- 투표하는 Follower는 후보의 로그가 “자신만큼(또는 더) 최신”인지를 보고 투표 여부를 결정합니다.
- Raft의 최신성 비교는 (a) lastLogTerm가 큰 쪽이 최신, (b) 같다면 lastLogIndex가 큰 쪽이 최신입니다.
- Member B에서 Election Timeout 발생 → 선거 재시도
- 이어서 Member B가 타임아웃에 도달하면 Candidate가 되어 RequestVote 를 보냅니다.
- Member B는 Member A와 같은 term이지만 더 최신 로그를 보유하므로, Member A는 OK(GrantVote) 를 회신합니다.
- Quorum 충족 시 Member B가 새로운 Leader 로 당선됩니다.
- Follower 복귀 처리
- Member B가 보낸 첫 Heartbeat(AppendEntries) 를 받은 Member A는 자신의 상태를 Follower 로 유지/전환하고,
필요 시 term 갱신 + 로그 동기화를 수행합니다.
- Member B가 보낸 첫 Heartbeat(AppendEntries) 를 받은 Member A는 자신의 상태를 Follower 로 유지/전환하고,
핵심: 더 최신 로그를 가진 노드만 리더가 되도록 하여 이미 커밋된 항목이 절대 후퇴하지 않게 보장합니다.
가용성(Availability)
- 새 리더(Member A)가 선출되면, Quorum 수의 노드가 살아 있는 한 etcd는 정상적으로 쓰기/읽기를 처리합니다.
- 이전 리더가 복구되더라도, lastIndex/term가 뒤쳐진 상태이므로 복귀 즉시 Follower로 스스로 강등되고 term를 상향 갱신한 뒤 리더의 로그에 맞춰 재동기화합니다.
불필요한 선거 폭주 방지(분산 환경의 현실 대응)
리더 다운 직후, 네트워크 지연·패킷 손실 등으로 선거가 연속해서 실패하면 짧은 시간이더라도 가용성이 떨어질 수 있습니다. 이를 완화하기 위해 Raft/etcd는 다음을 사용합니다.
- Randomized Election Timeout
- 각 노드의 타임아웃을 무작위 범위로 설정해 동시 선거(투표 분할) 가능성을 줄입니다.
- Pre-Vote (etcd 구현됨)
- 실제 term 증가 전에 “내가 리더가 될 수 있을지”를 미리 확인하는 사전 투표 단계입니다.
- 네트워크가 부분 분리된 노드가 불필요하게 term을 올려 안정적인 클러스터를 흔드는 일을 방지합니다.
4. Member의 추가와 삭제
시나리오
- 현재 3노드 etcd 클러스터, lastIndex = 10101.
- etcd는 로그만 누적하지 않고 주기적으로 스냅샷(snapshot) 을 생성해 파일시스템에 저장합니다.
- --snapshot-count 기본값은 100,000으로, 이 개수만큼 로그가 적용되면 스냅샷을 찍습니다.
- 여기에 4번째 서버를 추가하려는 요청이 들어옵니다.
구성 변경은 “그대로” 로그로 흘린다
etcd는 멤버 추가/삭제 같은 런타임 구성 변경(reconfiguration) 도 별도의 경로로 처리하지 않고, 평소와 같은 로그 복제(AppendEntries) 메커니즘으로 전달합니다.
- 리더는 사용자 요청을 받아 새 구성 Cnew(전체 멤버 목록을 포함)를 로그 엔트리로 추가하고 Followers에 전파합니다.
- 차이점: 구성 엔트리는 커밋을 기다리지 않고 쓰이는 순간부터 유효합니다.
여러 구성 엔트리가 존재하면 가장 최신 엔트리가 효력을 가집니다. - Cnew에 의해 클러스터 크기가 4가 되면, 이후 리더가 어떤 엔트리를 커밋하는 데 필요한 응답 수(Quorum)는 3(4/2+1) 으로 계산됩니다.
신규 노드 빠른 캐치업: 스냅샷 전송
리더는 추가 요청을 OK 한 뒤, 신규 멤버가 빠르게 따라잡도록 스냅샷을 전송합니다.
- 보낼 스냅샷은 “리더가 디스크에 보유한 최신 스냅샷”에 “현재 리더의 미반영 로그”를 합쳐 만든 새 스냅샷입니다.
- 신규 노드는 스냅샷으로 DB를 복원해 AppendEntries 를 문제없이 수용할 수 있는 상태가 됩니다(로그 연속성 확보).
위험: 캐치업 중 추가 멤버 요청 → 커밋 지연/블로킹
신규 4번째 노드가 아직 완전히 캐치업하지 못한 상태에서 또 다른 멤버 추가 요청이 들어오면, 리더는 두 번째 구성 Cnew2 를 로그에 기록합니다.
- Quorum은 여전히 3이지만, 예를 들어 index 10103 까지 복제된 Follower 수가 충분치 않다면 Cnew2 커밋이 지연됩니다.
- 이 상태에서 index 10104 에 기록되어야 할 새 write 가 들어오면 블로킹 되고, 클라이언트 타임아웃으로 가용성 저하가 발생할 수 있습니다.
해법: Learner로 먼저 넣고, 다 따라잡으면 승격
Raft/etcd는 이런 문제를 줄이기 위해 Learner 상태(제4의 역할)를 제공합니다. (etcd v3.4+ 지원)
- Learner 는 클러스터 멤버이지만 Quorum 계산에서 제외됩니다.
→ 캐치업 중인 노드 때문에 커밋 기준(Quorum)이 변화하지 않으므로 기존 가용성 보호. - 리더는 Learner에게 스냅샷/로그를 전송해 완전 캐치업 시킵니다.
- 충분히 따라잡은 뒤 member promote(promote API) 로 정식 투표 노드(voter) 로 승격합니다.
- 캐치업이 불충분하면 승격 거절(안전 요구 조건 미충족) — 그림의 canPromote(idx) == false 케이스.
한 번에 하나: 구성 변경 Restriction
구성 엔트리는 기록 즉시 효력을 가지므로, 복제가 충분하지 않은 구성 엔트리가 여러 개 쌓이면 위험합니다.
Raft의 원칙은 런타임 구성 변경을 “한 번에 하나씩” 처리하는 것.
etcd는 기본 restriction 으로, 리더 로그에 미커밋 구성 엔트리가 남아 있으면 새 구성 엔트리를 거부합니다.운영 가이드 (권장 절차)
- member add --learner 로 신규 노드를 Learner 로 추가
- 스냅샷/로그로 캐치업 완료 여부 모니터링(지표: raft.apply.index, db.size, 네트워크 지연 등)
- member promote 로 승격(거절 시 재시도 전 충분한 동기화 보장)
- 구성 엔트리 커밋 확인 후에 다음 멤버 추가 진행
- 가능하면 홀수 노드 유지(3→5→7…)로 장애 허용성 최적화
Member 삭제
etcd에서는 멤버 삭제도 멤버 추가 때와 동일하게, 일반 로그 복제(AppendEntries) 경로로 처리합니다.
삭제 대상이 Leader인 경우에는 예외 규칙이 적용됩니다.1) Leader 삭제의 특수 처리(안전 커밋 → Step down)
- 클라이언트가 “Leader 삭제”를 요청하면, Leader는 새 구성(Cnew) 엔트리를 로그에 기록해 전파합니다.
- 이때 커밋에 필요한 복제 수를 셀 때 Leader 자신은 제외합니다.
- 예: 4노드(Quorum=3)에서 Leader 삭제라면, 나머지 3대에 Cnew가 복제돼야 커밋 가능.
- 커밋이 끝나면 Leader는 클라이언트에 OK 를 응답하고 Step down(리더 하차) 합니다.
- Step down 이후에는 Heartbeat를 보내지 않습니다.
- 남은 서버 중 한 노드가 Election Timeout으로 선거를 시작해 새 Leader를 선출합니다.
- 앞 단계에서 Leader가 자기 자신을 제외한 Quorum 복제를 보장했기 때문에, 새 Leader 선출은 성공합니다.
2) Step down 이전에 들어온 쓰기 요청은?
- 구성 엔트리(config log) 는 “로그에 쓰이는 순간부터” 효력이 있지만,
Leader 삭제 시에는 커밋되기 전까지 기존 Leader가 계속 리더 역할을 수행합니다. - 따라서 아직 Cnew가 커밋되지 않은 상태에서 write가 들어오면, Leader는 자기 자신을 제외한 Quorum 에 대해 로그 복제를 진행합니다.
- Cnew가 커밋되어 Step down 하더라도, 나머지 서버가 새 Leader를 선출하고 안전성(Safety)을 유지하도록 하기 위함입니다.
3) 멤버 삭제의 Restriction(제약)
여러 번의 삭제 요청으로 가용성이 떨어지지 않도록 etcd는 삭제 시 제약을 둡니다.
여기서 started는 현재 정상 동작 중인 서버 수를 의미합니다.- 예시 상황: 5노드 클러스터, 현재 started=4, quorum=3
- 멤버 1대 삭제 → 클러스터 크기 4, quorum=3 유지, started가 3으로 감소
- 연속으로 한 대 더 삭제 → 클러스터 크기 3, quorum=2로 하향
- started가 2가 되더라도 quorum(2) 보다 작지 않으므로 요청 수락(엔트리 기록 가능)
- 또 한 대 삭제 시도 → 예상 started=1, quorum=2
- started < quorum 이므로 리더는 요청을 거절
- 요점: 삭제로 인해 started < quorum 이 되는 상황을 etcd가 차단합니다.
(안전한 선거/커밋을 위해 최소 과반 동작 노드가 항상 남아야 함)
운영 팁
- Leader 삭제는 가능하면 Maintenance 윈도우에서 진행하고,
Cnew 커밋 → Step down → 새 리더 확인 순서를 모니터링하세요. - 여러 멤버를 잇달아 지울 때는 매 삭제 후 클러스터 크기와 quorum/started 재계산을 습관화하세요.
- 가급적 홀수 노드(3→5→7) 를 유지하면 장애 허용성이 좋아집니다
마무리
이번 글에서는 ETCD의 핵심 개념인
- RSM(Replicated State Machine) – 모든 노드가 동일한 상태를 유지하게 하는 구조
- Quorum – 합의와 안전성을 보장하는 과반수 원칙
- Raft 알고리즘 – Leader 선출과 로그 복제를 통해 일관성을 유지하는 합의 방식
그리고 클러스터 멤버 추가/삭제 에 대해서 살펴봤습니다.
'SW개발 > Kubernetes' 카테고리의 다른 글
[k8s] watch/list 그리고 Thunder herd (2) 2025.07.20 [번역] Kubernetes의 resource memory limit 이해하기 (0) 2021.04.26