ETCD 테스트 기록
테스트 환경
3 Node Stacked ETCD 구성에서 멤버 삭제/추가, Leader 재선출 시나리오를 테스트했다.
사전 준비 — 환경 변수 설정
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# etcd pod 이름 확인
kubectl get pod -n kube-system | grep etcd
# 전체 클러스터 상태 조회
kubectl exec -it etcd-porthos-vnl-pub-kr1-dop-rl-cp-1 -n kube-system -- \
etcdctl \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
--endpoints=https://192.168.1.91:2379,https://192.168.1.56:2379,https://192.168.1.16:2379 \
endpoint status --write-out=table
# 개별 노드 조회 (cp-1)
kubectl exec -it etcd-porthos-vnl-pub-kr1-dop-rl-cp-1 -n kube-system -- \
etcdctl \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
--endpoints=https://127.0.0.1:2379 \
endpoint status --write-out=table
초기 상태
| 항목 | 값 |
|---|---|
| Leader | cp-1 |
| DB Size | 230 MB |
| RAFT TERM | 41 (Leader 선출이 41회 발생) |
시나리오 1 — 멤버 삭제
cp-3의 member ID c302c00356f1848a를 클러스터에서 제거했다.
Quorum 유지 확인
2/3 노드가 정상이므로 Quorum은 유지됐다. 단, kubectl 응답 latency가 증가했다.
발생한 현상과 로그 분석
1. etcd 내부 — ConfChange가 Raft로 합의되는 순간
member remove 실행 직후 etcd 로그에 다음이 찍혔다.
1
2
3
4
5
6
7
8
{"msg":"applied a configuration change through raft",
"local-member-id":"2a40d8ba966d12fe",
"raft-conf-change":"ConfChangeRemoveNode",
"raft-conf-change-node-id":"a30526f5dacd19af"}
{"msg":"removed remote peer",
"local-member-id":"2a40d8ba966d12fe",
"removed-remote-peer-id":"a30526f5dacd19af"}
member remove는 단순히 목록에서 지우는 게 아니라 Raft 합의 과정을 거쳐 클러스터 전체에 반영된다. ConfChangeRemoveNode가 log entry로 기록되고, 쿼럼이 달성되면 commit된다. 이후 cp-3과의 TCP 연결을 즉시 끊는다.
2. cp-3이 계속 재연결을 시도하는 현상
commit 이후에도 cp-3은 자신이 제거됐다는 걸 모르고 계속 연결을 시도했다. Leader는 이를 거부했다.
1
2
3
4
{"msg":"rejected stream from remote peer because it was removed",
"local-member-id":"2a40d8ba966d12fe",
"remote-peer-id-stream-handler":"2a40d8ba966d12fe",
"remote-peer-id-from":"a30526f5dacd19af"}
이 로그가 수십 초간 반복됐다. cp-3 입장에서는 자신이 클러스터에서 제거됐다는 신호를 직접 받지 못하기 때문이다.
3. kube-apiserver — Lease 갱신 타임아웃
ConfChange가 처리되는 순간 kube-apiserver의 etcd write 요청들이 동시에 타임아웃됐다.
1
2
3
4
5
6
7
8
E0328 00:03:55 timeout.go:140 method="PUT"
path="/apis/coordination.k8s.io/v1/namespaces/kube-system/leases/kube-scheduler"
E0328 00:03:55 timeout.go:140 method="PUT"
path="/apis/coordination.k8s.io/v1/namespaces/kube-system/leases/kube-controller-manager"
E0328 00:03:55 status.go:71 err="etcdserver: request timed out,
possibly due to previous leader failure"
kube-scheduler와 kube-controller-manager의 Lease 갱신이 동시에 실패했다. etcd가 ConfChange를 처리하는 동안 write 요청을 잠깐 받지 못했기 때문이다. 이게 kubectl hang의 실제 원인이다.
4. cp-1 재시작 — WAL flush 후 재초기화
멤버 구성 변경이 WAL에 flush되면서 cp-1이 재시작됐다. 이는 정상 동작이다. 재시작 후에도 멤버 구성이 유지되는 이유가 바로 WAL에 기록됐기 때문이다.
시나리오 2 — 멤버 추가
클러스터에 멤버 등록
1
2
3
4
5
6
7
8
kubectl exec -it etcd-porthos-vnl-pub-kr1-dop-rl-cp-1 -n kube-system -- \
etcdctl \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
--endpoints=https://192.168.1.91:2379 \
member add porthos-vnl-pub-kr1-dop-rl-cp-3 \
--peer-urls=https://192.168.1.16:2380
출력 결과:
1
2
3
4
ETCD_NAME="porthos-vnl-pub-kr1-dop-rl-cp-3"
ETCD_INITIAL_CLUSTER="porthos-vnl-pub-kr1-dop-rl-cp-1=https://192.168.1.91:2380,porthos-vnl-pub-kr1-dop-rl-cp-2=https://192.168.1.56:2380,porthos-vnl-pub-kr1-dop-rl-cp-3=https://192.168.1.16:2380"
ETCD_INITIAL_ADVERTISE_PEER_URLS="https://192.168.1.16:2380"
ETCD_INITIAL_CLUSTER_STATE="existing"
member add 직후 cp-3은 unstarted 상태다. 멤버 등록만 됐을 뿐 etcd 프로세스가 아직 없는 상태로, 이 시점부터 쿼럼 기준이 3으로 증가한다.
cp-3 데이터 초기화 후 재기동
기존 데이터를 삭제하지 않으면 디스크에 남은 이전 member ID와 클러스터에 등록된 새 ID가 충돌해 CrashLoopBackOff가 발생한다.
1
2
3
4
5
{
"msg": "rejected Raft message to mismatch member",
"local-member-id": "c302c00356f1848a",
"mismatch-member-id": "f383bce7688605f2"
}
데이터 디렉터리를 초기화한다.
1
sudo rm -rf /var/lib/etcd && sudo mkdir -p /var/lib/etcd && sudo chmod 700 /var/lib/etcd
삭제 후 kubelet이 etcd를 재기동하면 Leader가 즉시 snapshot 전송을 시작한다.
대형 snapshot 전송 확인 (Issue #13913)
1
2
kubectl logs etcd-porthos-vnl-pub-kr1-dop-rl-cp-1 -n kube-system | \
grep -E "sending snapshot|sent database snapshot"
1
2
{"msg":"sending database snapshot","snapshot-index":15995741,"remote-peer-id":"a30526f5dacd19af","size":"230 MB"}
{"msg":"sent database snapshot","snapshot-index":15995741,"remote-peer-id":"a30526f5dacd19af","size":"230 MB"}
230 MB snapshot이 약 2초 만에 전송됐다. I/O 경합이 있는 환경에서는 이 구간이 수십 초로 늘어나 heartbeat 누락 -> 리더 재선출로 이어진다고 한다. (#13913)
시나리오 3 — Leader 강제 종료 및 재선출
Leader 삭제
1
kubectl delete pod etcd-porthos-vnl-pub-kr1-dop-rl-cp-1 -n kube-system --grace-period=0 --force
결과 — 1초 내 복구, RAFT TERM 증가
Leader(cp-1)가 종료되자 나머지 Follower들이 election timeout을 감지하고 새 Leader를 선출했다. RAFT TERM이 42로 증가한 것이 그 증거다.
cp-1은 Static Pod이므로 kubelet이 즉시 재기동시켰고, 새 Leader로부터 heartbeat를 수신하자 자동으로 Follower로 전환됐다.
로그 확인
1
2
kubectl logs etcd-porthos-vnl-pub-kr1-dop-rl-cp-2 -n kube-system | \
grep -E "became leader|elected leader" | tail -5
1
2
{"level":"info","ts":"2026-03-27T23:58:05.750027Z","logger":"raft","caller":"etcdserver/zap_raft.go:77","msg":"b9933c48f2424f14 became leader at term 42"}
{"level":"info","ts":"2026-03-27T23:58:05.750042Z","logger":"raft","caller":"etcdserver/zap_raft.go:77","msg":"raft.node: b9933c48f2424f14 elected leader b9933c48f2424f14 at term 42"}
주의 사항
Leader 장애 직전, 쿼럼 달성 전에 전송된 Write는 유실될 수 있다.






