04. etcd 장애 실험 시나리오
etcd Raft 쿼럼 기본 원리
3-node etcd 클러스터:
- 쿼럼(과반수) = 2
- 1개 장애: 쿼럼 유지 (2/3 살아있음) → 클러스터 정상 운영
- 2개 장애: 쿼럼 붕괴 (1/3만 살아있음) → 클러스터 읽기 전용 / 쓰기 불가
- 3개 장애: 완전 다운
장애 허용 공식: floor((n-1)/2)
3노드: floor((3-1)/2) = 1개까지 허용
실험 전 준비
export KUBECONFIG=~/.kube/k8s-etcd-lab.conf
# 실험 전 etcd 상태 베이스라인 기록
kubectl get nodes
kubectl get pods -n kube-system
# etcd 헬스체크 (ctrl1에서)
ssh k8s-ctrl1 "sudo ETCDCTL_API=3 etcdctl \
--endpoints=https://203.0.113.11:2379,https://203.0.113.12:2379,https://203.0.113.13:2379 \
--cacert=/etc/ssl/etcd/ssl/ca.pem \
--cert=/etc/ssl/etcd/ssl/member-k8s-ctrl1.pem \
--key=/etc/ssl/etcd/ssl/member-k8s-ctrl1-key.pem \
endpoint health -w table"
시나리오 1: 단일 노드 장애 (쿼럼 유지)
목표: etcd 노드 1개 정지 후 클러스터 운영 영향 확인
장애 주입
# ctrl3의 etcd 서비스 정지
ssh k8s-ctrl3 "sudo systemctl stop etcd"
# 또는 virsh로 VM 자체를 강제 종료 (더 극단적)
virsh destroy k8s-ctrl3
관찰 포인트
# 1. etcd 클러스터 상태 - ctrl1에서 확인
ssh k8s-ctrl1 "sudo ETCDCTL_API=3 etcdctl \
--endpoints=https://203.0.113.11:2379,https://203.0.113.12:2379 \
--cacert=/etc/ssl/etcd/ssl/ca.pem \
--cert=/etc/ssl/etcd/ssl/member-k8s-ctrl1.pem \
--key=/etc/ssl/etcd/ssl/member-k8s-ctrl1-key.pem \
endpoint health"
# → ctrl3은 unreachable, ctrl1+ctrl2는 healthy
# 2. K8s 클러스터 - 여전히 정상 동작해야 함
kubectl get nodes
kubectl create deployment test-nginx --image=nginx
kubectl get pods
# 3. API Server 로그에서 etcd 연결 오류 확인
ssh k8s-ctrl1 "sudo journalctl -u kube-apiserver -n 50 | grep etcd"
복구
# etcd 서비스 재시작
ssh k8s-ctrl3 "sudo systemctl start etcd"
# 또는 VM 재시작
virsh start k8s-ctrl3
# 멤버 재합류 확인 (자동 재합류됨)
ssh k8s-ctrl1 "sudo ETCDCTL_API=3 etcdctl \
--endpoints=https://203.0.113.11:2379 \
--cacert=/etc/ssl/etcd/ssl/ca.pem \
--cert=/etc/ssl/etcd/ssl/member-k8s-ctrl1.pem \
--key=/etc/ssl/etcd/ssl/member-k8s-ctrl1-key.pem \
member list"
시나리오 2: 과반수 장애 (쿼럼 붕괴)
목표: etcd 노드 2개 정지 후 K8s 클러스터가 어떻게 반응하는지 관찰
주의: 이 상태에서는 kubectl 명령이 타임아웃됩니다.
장애 주입
# ctrl2, ctrl3 동시 정지
virsh destroy k8s-ctrl2
virsh destroy k8s-ctrl3
관찰 포인트
# 1. kubectl 응답 불가 확인
kubectl get nodes --request-timeout=10s
# → 타임아웃 또는 etcd connection refused
# 2. API Server 에러 로그
ssh k8s-ctrl1 "sudo journalctl -u kube-apiserver -f"
# → "etcdserver: request timed out"
# 3. etcd 자체 상태 (ctrl1에서)
ssh k8s-ctrl1 "sudo systemctl status etcd"
# → active이지만 리더 없음, no quorum
# 4. etcd 내부 로그로 Raft 메시지 확인
ssh k8s-ctrl1 "sudo journalctl -u etcd -n 100 | grep -E 'raft|leader|quorum'"
# 5. 기존에 실행 중인 파드는 유지됨 확인 (etcd 없어도 kubelet은 동작)
ssh k8s-ctrl1 "sudo crictl ps"
복구
virsh start k8s-ctrl2
virsh start k8s-ctrl3
# etcd 자동 Raft 재동기화 후 쿼럼 복구됨
# 쿼럼 복구 확인
ssh k8s-ctrl1 "sudo ETCDCTL_API=3 etcdctl \
--endpoints=https://203.0.113.11:2379 \
--cacert=/etc/ssl/etcd/ssl/ca.pem \
--cert=/etc/ssl/etcd/ssl/member-k8s-ctrl1.pem \
--key=/etc/ssl/etcd/ssl/member-k8s-ctrl1-key.pem \
endpoint health"
kubectl get nodes
시나리오 3: etcd 데이터 디렉토리 손상
목표: 특정 노드의 etcd 데이터가 손상됐을 때 멤버 제거 후 재추가
# ctrl3의 etcd 정지 후 데이터 삭제 (손상 시뮬레이션)
ssh k8s-ctrl3 "sudo systemctl stop etcd"
ssh k8s-ctrl3 "sudo rm -rf /var/lib/etcd/member"
# ctrl1에서 ctrl3 멤버 제거
ssh k8s-ctrl1 "sudo ETCDCTL_API=3 etcdctl \
--endpoints=https://203.0.113.11:2379 \
--cacert=/etc/ssl/etcd/ssl/ca.pem \
--cert=/etc/ssl/etcd/ssl/member-k8s-ctrl1.pem \
--key=/etc/ssl/etcd/ssl/member-k8s-ctrl1-key.pem \
member remove <ctrl3-member-id>"
# ctrl3을 새 멤버로 추가
ssh k8s-ctrl1 "sudo ETCDCTL_API=3 etcdctl \
--endpoints=https://203.0.113.11:2379 \
--cacert=/etc/ssl/etcd/ssl/ca.pem \
--cert=/etc/ssl/etcd/ssl/member-k8s-ctrl1.pem \
--key=/etc/ssl/etcd/ssl/member-k8s-ctrl1-key.pem \
member add k8s-ctrl3 --peer-urls=https://203.0.113.13:2380"
# ctrl3에서 etcd를 새 멤버로 재시작 (initial-cluster-state=existing)
# → experiments/04-restore-from-snapshot.sh 참조
관찰 도구 정리
| 도구 | 용도 |
|---|---|
etcdctl endpoint health |
각 멤버 헬스 |
etcdctl endpoint status |
리더·commit index 확인 |
etcdctl member list |
멤버 목록 |
journalctl -u etcd -f |
etcd Raft 로그 실시간 |
journalctl -u kube-apiserver |
API Server etcd 연결 상태 |
kubectl get events -A |
클러스터 레벨 이벤트 |
crictl ps |
kubelet 로컬 컨테이너 (etcd 없어도 동작) |
etcd 주요 메트릭 (Prometheus)
# etcd 메트릭 엔드포인트
curl -sk https://203.0.113.11:2381/metrics | grep -E "etcd_server_leader|etcd_server_proposals"
# 주요 메트릭
# etcd_server_leader_changes_seen_total → 리더 변경 횟수
# etcd_server_proposals_failed_total → 쿼럼 실패 횟수
# etcd_server_has_leader → 리더 존재 여부 (0/1)
# etcd_disk_wal_fsync_duration_seconds → 디스크 성능