Kubernetes의 뇌, etcd가 클러스터 상태를 지키는 방법
클러스터가 깨졌을 때 가장 먼저 의심받는 녀석
Kubernetes 장애 대응을 몇 번 해보면 묘한 패턴이 보입니다. 뭔가 이상할 때마다 "etcd부터 확인해봐"라는 말이 나온다는 거죠. 처음엔 그냥 관성적인 멘트인 줄 알았습니다. 그런데 몇 번 더 겪고 나니 그럴 만한 이유가 있더라고요.
Kubernetes에서 etcd는 말 그대로 모든 것의 중심입니다. API 서버도, 스케줄러도, 컨트롤러 매니저도 아닌 etcd가 가장 중요한 구성 요소라는 게 처음엔 좀 의외였어요. 보통 "제일 중요한 게 뭐냐"고 물으면 API 서버를 떠올리니까요. 그런데 따져보면 당연한 얘기입니다. 나머지 컴포넌트들은 etcd가 없으면 애초에 할 일이 없거든요. 상태를 읽을 데가 없고, 쓸 데도 없습니다.
그래서 이 글은 etcd를 한 번 제대로 뜯어보는 글입니다. 동작 원리부터 운영하면서 뼈저리게 배운 것들까지.

etcd가 정확히 뭐하는 녀석인가
etcd는 분산 키-값 저장소입니다. 여러 서버에 걸쳐 동작하면서도 모든 노드가 동일한 상태를 유지하는, 꽤 똑똑한 사전이라고 보면 됩니다.
이름부터 힌트를 줍니다. 리눅스의 /etc 디렉터리는 시스템 설정 파일이 모여 있는 곳이죠. etcd는 distributed /etc의 줄임말입니다. 한 대가 아니라 클러스터 전체의 설정 저장소라는 뜻이에요. 이름 하나에 설계 의도가 다 들어 있는 셈입니다.
Kubernetes는 처음부터 etcd를 백엔드 스토어로 삼았습니다. Pod 하나를 만들 때마다, 서비스를 등록할 때마다, Secret을 생성할 때마다 그 모든 정보가 etcd에 기록됩니다.
실제로 etcd 안을 들여다보면 이런 구조로 되어 있습니다:
/registry/deployments/default/my-app
/registry/pods/kube-system/coredns-abc123
/registry/secrets/default/my-secret
/registry/services/specs/default/my-service
kubectl get pod my-pod -o yaml을 실행할 때 화면에 찍히는 그 YAML이 결국 여기서 나오는 겁니다. kubectl은 API 서버에 묻고, API 서버는 etcd에서 꺼내오는 거죠.
Raft 알고리즘으로 동기화를 지키는 법
프로덕션에서는 etcd를 단일 인스턴스로 돌릴 수 없습니다. 그 노드 하나가 죽으면 클러스터 전체 상태가 통째로 날아가니까요. 그래서 보통 3~5개 노드로 클러스터를 구성하는데, 여기서 Raft 합의 알고리즘이 등장합니다.
Raft는 규칙이 아주 엄격한 민주주의에 가깝습니다:
- 리더 한 명만 존재: 모든 쓰기 요청은 리더를 거쳐야 합니다
- 팔로워는 따라만 함: 리더가 시키는 대로 하고, 리더에 문제가 생기면 새 리더 선출을 준비
- 과반수 동의가 필수: 데이터 쓰기는 과반수가 동의해야 완료
쓰기 과정을 풀어보면 이렇습니다:
- 쓰기 요청이 리더에게 도착 ("새 배포 생성해줘")
- 리더가 자기 로그에 추가하고 모든 팔로워에게 전송
- 팔로워들이 로그에 기록하고 "OK" 응답
- 과반수가 응답하면 리더가 커밋 처리
- 리더가 팔로워들에게 "커밋했다"고 알림
- 클라이언트에게 "성공" 응답
여기서 한 가지 짚어둘 게 있습니다. 쓰기 한 번이 끝나려면 결국 네트워크 왕복과 과반수 동기화를 거쳐야 한다는 점이에요. etcd가 빠른 디스크와 안정적인 네트워크에 민감한 이유가 바로 이 구조에서 나옵니다.
홀수 개의 노드를 쓰는 이유도 여기에 있습니다. 3노드에서는 2개, 5노드에서는 3개가 동의해야 하죠. 4노드 클러스터는 3노드와 똑같이 1개까지만 장애를 견디면서 비용과 복잡성만 늘립니다. 멤버 하나 더 붙였는데 가용성은 그대로니, 안 하느니만 못한 구성인 셈이죠.
리더가 죽으면 어떻게 될까요? 팔로워들은 리더의 하트비트를 기다리다가, 일정 시간(보통 150~300ms) 동안 신호가 없으면 "리더가 죽었나 보다" 판단하고 선거를 시작합니다. 가장 먼저 과반수 표를 얻는 노드가 새 리더가 되고요. 이 타임아웃 값이 왜 중요한지는 뒤에서 디스크 얘기를 할 때 다시 나옵니다.
API 서버만이 etcd와 대화한다
Kubernetes 아키텍처에서 놓치면 안 되는 포인트 하나. 오직 API 서버만 etcd와 통신합니다. 스케줄러도, 컨트롤러 매니저도, kubelet도 etcd에 직접 접근하지 않아요.
API 서버가 일종의 게이트키퍼 역할을 하는 겁니다. 인증, 인가(RBAC), 검증을 모두 처리한 다음에야 etcd에 쓰거나 읽습니다.
이 구조의 장점은 분명합니다. etcd는 "누가 요청하는지", "권한이 있는지"를 신경 쓸 필요가 없습니다. 보안 판단은 전부 API 서버가 떠안고, etcd는 저장에만 집중하면 되니까요. 책임을 한 군데로 모은 깔끔한 설계입니다. 반대로 말하면 API 서버를 우회해 etcd를 직접 만지는 순간, 그 모든 안전장치를 건너뛰게 된다는 뜻이기도 하고요. 이게 직접 접근을 막아둔 진짜 이유입니다.
API 서버는 gRPC로 2379 포트를 통해 etcd와 통신하고, etcd 노드끼리는 2380 포트로 대화합니다. 모든 연결은 mutual TLS로 보호되어 있어서 인증서가 잔뜩 필요해요. 인증서 만료로 클러스터가 멈추는 사고가 종종 나는 것도 이 지점입니다.
Watch API도 빼놓을 수 없습니다. Kubernetes의 컨트롤 루프는 "원하는 상태 vs 실제 상태"를 끊임없이 맞춰나가는데, 매번 폴링하지 않고도 변경사항을 즉시 알 수 있는 건 이 기능 덕분입니다. 폴링으로 했다면 etcd가 받아내는 부하가 전혀 다른 차원이 됐을 거예요.
새 배포를 생성하면 API 서버가 etcd에 저장하고, etcd는 즉시 컨트롤러 매니저에게 watch 이벤트로 알려줍니다. 그러면 deployment 컨트롤러가 깨어나서 ReplicaSet과 Pod를 만들기 시작하죠. 이벤트 기반 파이프라인이 etcd를 기점으로 흐르는 구조입니다.
etcd에 들어있는 것, 없는 것
여기는 명확히 구분해둘 필요가 있습니다.
etcd에 저장되는 것들:
- 모든 Kubernetes API 객체 (Pod, Deployment, Service, ConfigMap, Secret 등)
- 클러스터 설정과 메타데이터
- 컴포넌트들의 리더 선출 잠금
etcd에 저장되지 않는 것들:
- 컨테이너 로그 (노드 로컬 스토리지에)
- 메트릭 (모니터링 스택으로)
- 컨테이너 이미지
- 애플리케이션 데이터
etcd는 범용 데이터베이스가 아닙니다. 고일관성이 필요한 소량의 설정 데이터를 위해 설계된 거예요. 이걸 일반 DB처럼 쓰려다 사고 나는 경우를 가끔 봅니다. 권장 용량도 8GB 이하로 유지하라고 되어 있고, 이 선을 넘기기 시작하면 성능이 눈에 띄게 흔들립니다.
용량이 계속 늘어나면 defragmentation을 해줘야 합니다:
etcdctl defrag
업데이트와 삭제가 반복되면서 생긴 파편화된 공간을 정리하는 과정이에요. 데이터베이스 청소기라고 보면 됩니다.

백업은 선택이 아니다
etcd 백업은 협상 불가능한 필수사항입니다. etcd가 클러스터의 뇌라면, 백업 없이 운영하는 건 백업 없이 단일 디스크에 프로덕션 DB를 올려두는 것과 다르지 않아요.
etcd는 Kubernetes 제어 평면에서 유일한 상태 저장 컴포넌트입니다. etcd를 백업해두면 전체 클러스터 상태를 복원할 수 있지만, 백업 없이 etcd를 잃으면 그 안의 모든 리소스가 한꺼번에 사라집니다. Pod, Deployment, Secret까지 전부요.
스냅샷 생성은 간단하고 무중단으로 가능합니다:
ETCDCTL_API=3 etcdctl snapshot save /opt/backup/etcd-$(date +%Y%m%d).db \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key
복원할 때는 최근에 도입된 etcdutl을 쓰는 게 좋습니다:
# etcd 3.5 이상에서 권장
etcdutl --data-dir /var/lib/etcd-restored snapshot restore /opt/backup/etcd-20260407.db
그리고 한 가지 더. 백업은 떠두는 것보다 복원이 되는지 확인하는 게 진짜 백업입니다. 스냅샷은 매일 잘 쌓이는데 정작 복원 절차를 한 번도 돌려본 적이 없는 클러스터, 생각보다 흔합니다. 복원 리허설을 해본 적 없는 백업은 백업이 아니라 그냥 파일이에요.
프로덕션 운영 시 알아둘 것들
실제 프로덕션에서 etcd를 굴려보면서 얻은 교훈들을 정리하면:
1. 디스크 I/O가 전부다 etcd는 CPU나 메모리보다 디스크 지연에 훨씬 민감합니다. WAL(Write Ahead Log)을 써서 커밋마다 fsync를 해야 하거든요. 공유 스토리지나 느린 디스크를 물리면 fsync가 밀리고, 그게 곧 선거 타임아웃과 클러스터 불안정으로 번집니다. 앞에서 리더 선출 타임아웃이 중요하다고 했던 게 여기서 연결됩니다. 디스크가 느리면 멀쩡한 리더도 죽은 걸로 오해받아요. SSD는 선택이 아니라 필수입니다.
2. 핵심 시스템에는 5노드 3노드는 1개 장애까지, 5노드는 동시에 2개 장애까지 견딥니다. 절대 멈추면 안 되는 프로덕션 클러스터라면 5개 멤버로 가세요. 다만 노드를 늘리면 그만큼 합의에 참여할 멤버가 많아져 쓰기 지연도 조금 늘어납니다. 무조건 많이가 답은 아니라는 거죠.
3. 정기적인 defrag 필요
etcdctl defrag를 정기적으로 돌려야 하는데, 리더를 defrag하면 잠시 리더십 이전이 발생합니다. 그래서 순서가 중요해요. 팔로워부터 먼저, 리더는 마지막에.
4. 모니터링 지표들
etcd_server_leader_changes_seen_total: 빈번한 리더 교체는 불안정 신호etcd_disk_wal_fsync_duration_seconds: p99가 10ms를 넘으면 디스크가 고생 중이라는 뜻etcd_mvcc_db_total_size_in_bytes: 데이터베이스 크기 모니터링
이 세 지표는 etcd 대시보드에 항상 띄워두는 편입니다. 장애가 터지고 나서 보는 것과, 평소에 추세를 보는 건 완전히 다른 얘기라서요.

결국 모든 길은 etcd로
etcd는 매일 직접 손대는 컴포넌트는 아닙니다. 평소엔 존재감이 거의 없죠. 그런데 모든 걸 조용히 떠받치고 있는 기둥이 또 etcd입니다. kubectl 명령 하나하나가 결국 etcd를 거쳐가고, 모든 컨트롤 루프가 etcd에 의존하고, 모든 리더 선출이 etcd를 통해 이뤄집니다.
Kubernetes에는 "문제는 늘 DNS"라는 농담이 있죠. etcd는 "문제는 늘 etcd"가 될 수 있는 자리라 더 신경이 쓰입니다. 다른 점이 있다면, DNS는 끊겨도 보통 일부 기능만 멈추지만 etcd가 흔들리면 클러스터 전체가 흔들린다는 거예요. 영향 범위가 다릅니다.
그래서 나는 새 클러스터를 받으면 etcd의 디스크부터 확인하는 버릇이 생겼습니다. 화려하지 않은 컴포넌트지만, 제대로 이해하고 운영하면 가장 든든한 동반자가 되어주거든요. 표면을 보면 API 서버가 주인공 같지만, 구조를 보면 진짜 중심은 늘 etcd입니다.