etcd가 중요한 이유
Kubernetes 클러스터의 모든 상태는 etcd에 저장됩니다. Deployment, Service, ConfigMap, Secret, RBAC 정책 — 전부 etcd 안에 있습니다. etcd가 손상되면 클러스터 전체가 복구 불능이 됩니다. 워크로드 백업 도구인 Velero는 Kubernetes 리소스 수준의 백업이지만, etcd 백업은 클러스터 자체의 완전한 스냅샷을 제공합니다.
etcd 아키텍처 이해
etcd는 Raft 합의 알고리즘 기반의 분산 키-값 저장소입니다:
# etcd 클러스터 상태 확인
ETCDCTL_API=3 etcdctl
--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
endpoint status --write-out=table
# 출력 예시
+------------------------+------------------+---------+---------+
| ENDPOINT | ID | VERSION | DB SIZE |
+------------------------+------------------+---------+---------+
| https://10.0.0.1:2379 | 8e9e05c52164694d | 3.5.12 | 45 MB |
| https://10.0.0.2:2379 | 91bc3c398fb3c146 | 3.5.12 | 45 MB |
| https://10.0.0.3:2379 | fd422379fda50e48 | 3.5.12 | 45 MB |
+------------------------+------------------+---------+---------+
| 항목 | 설명 |
|---|---|
| 멤버 수 | 홀수 권장 (3, 5, 7). 과반수 생존 시 정상 동작 |
| DB 크기 | 기본 2GB 제한. 큰 클러스터는 8GB까지 확장 |
| WAL | Write-Ahead Log. 트랜잭션 일관성 보장 |
| 스냅샷 | WAL 압축 후 저장되는 시점 백업 |
etcd 스냅샷 백업
etcd 백업의 핵심은 etcdctl snapshot save입니다:
#!/bin/bash
# etcd-backup.sh — 프로덕션 백업 스크립트
ETCD_ENDPOINTS="https://127.0.0.1:2379"
ETCD_CACERT="/etc/kubernetes/pki/etcd/ca.crt"
ETCD_CERT="/etc/kubernetes/pki/etcd/server.crt"
ETCD_KEY="/etc/kubernetes/pki/etcd/server.key"
BACKUP_DIR="/var/backups/etcd"
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
BACKUP_FILE="${BACKUP_DIR}/etcd-snapshot-${TIMESTAMP}.db"
RETENTION_DAYS=30
# 백업 디렉토리 생성
mkdir -p "${BACKUP_DIR}"
# 스냅샷 생성
ETCDCTL_API=3 etcdctl
--endpoints="${ETCD_ENDPOINTS}"
--cacert="${ETCD_CACERT}"
--cert="${ETCD_CERT}"
--key="${ETCD_KEY}"
snapshot save "${BACKUP_FILE}"
# 스냅샷 무결성 검증
ETCDCTL_API=3 etcdctl snapshot status "${BACKUP_FILE}"
--write-out=table
# 백업 파일 체크섬 저장
sha256sum "${BACKUP_FILE}" > "${BACKUP_FILE}.sha256"
# 오래된 백업 정리
find "${BACKUP_DIR}" -name "etcd-snapshot-*.db"
-mtime +${RETENTION_DAYS} -delete
find "${BACKUP_DIR}" -name "*.sha256"
-mtime +${RETENTION_DAYS} -delete
echo "Backup completed: ${BACKUP_FILE}"
echo "Size: $(du -h ${BACKUP_FILE} | cut -f1)"
CronJob으로 자동 백업
Kubernetes CronJob으로 정기 백업을 자동화합니다:
apiVersion: batch/v1
kind: CronJob
metadata:
name: etcd-backup
namespace: kube-system
spec:
schedule: "0 */6 * * *" # 6시간마다
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 3
jobTemplate:
spec:
template:
spec:
hostNetwork: true # etcd 엔드포인트 접근
nodeSelector:
node-role.kubernetes.io/control-plane: ""
tolerations:
- key: node-role.kubernetes.io/control-plane
effect: NoSchedule
containers:
- name: etcd-backup
image: registry.k8s.io/etcd:3.5.12-0
command:
- /bin/sh
- -c
- |
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
etcdctl snapshot save /backup/etcd-${TIMESTAMP}.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
etcdctl snapshot status /backup/etcd-${TIMESTAMP}.db
# 30일 이전 백업 삭제
find /backup -name "etcd-*.db" -mtime +30 -delete
volumeMounts:
- name: etcd-certs
mountPath: /etc/kubernetes/pki/etcd
readOnly: true
- name: backup-volume
mountPath: /backup
volumes:
- name: etcd-certs
hostPath:
path: /etc/kubernetes/pki/etcd
- name: backup-volume
persistentVolumeClaim:
claimName: etcd-backup-pvc
restartPolicy: OnFailure
외부 스토리지로 전송
로컬 디스크 백업만으로는 노드 장애 시 복구할 수 없습니다. S3 또는 원격 스토리지로 전송해야 합니다:
#!/bin/bash
# etcd-backup-s3.sh
# ... 스냅샷 생성 (위와 동일) ...
# S3 업로드
aws s3 cp "${BACKUP_FILE}"
"s3://my-etcd-backups/cluster-prod/${TIMESTAMP}/"
--storage-class STANDARD_IA
# 암호화된 백업 (GPG)
gpg --symmetric --cipher-algo AES256
--passphrase-file /etc/etcd-backup/passphrase
--output "${BACKUP_FILE}.gpg"
"${BACKUP_FILE}"
aws s3 cp "${BACKUP_FILE}.gpg"
"s3://my-etcd-backups/cluster-prod/encrypted/${TIMESTAMP}/"
# 로컬 파일 정리
rm -f "${BACKUP_FILE}" "${BACKUP_FILE}.gpg"
etcd 복구 절차
복구는 클러스터 전체를 중단하고 진행해야 합니다. 단계별 절차:
1. 단일 노드 복구 (가장 일반적)
# Step 1: 모든 컨트롤 플레인 노드에서 etcd와 API Server 중지
# (Static Pod이므로 매니페스트 이동)
sudo mv /etc/kubernetes/manifests/etcd.yaml /tmp/
sudo mv /etc/kubernetes/manifests/kube-apiserver.yaml /tmp/
# Step 2: 기존 etcd 데이터 백업 후 제거
sudo mv /var/lib/etcd /var/lib/etcd.backup.$(date +%s)
# Step 3: 스냅샷에서 복구
ETCDCTL_API=3 etcdctl snapshot restore /backup/etcd-20260319.db
--name=master-1
--initial-cluster=master-1=https://10.0.0.1:2380
--initial-cluster-token=etcd-cluster-1
--initial-advertise-peer-urls=https://10.0.0.1:2380
--data-dir=/var/lib/etcd
# Step 4: 권한 수정
sudo chown -R root:root /var/lib/etcd
# Step 5: Static Pod 매니페스트 복원
sudo mv /tmp/etcd.yaml /etc/kubernetes/manifests/
sudo mv /tmp/kube-apiserver.yaml /etc/kubernetes/manifests/
# Step 6: 클러스터 상태 확인 (1-2분 대기)
kubectl get nodes
kubectl get pods --all-namespaces
2. 멀티 노드 클러스터 복구
# 모든 노드에서 동시에 실행 (각 노드별 설정 다름)
# Node 1
ETCDCTL_API=3 etcdctl snapshot restore /backup/etcd-20260319.db
--name=master-1
--initial-cluster="master-1=https://10.0.0.1:2380,master-2=https://10.0.0.2:2380,master-3=https://10.0.0.3:2380"
--initial-cluster-token=etcd-cluster-restored
--initial-advertise-peer-urls=https://10.0.0.1:2380
--data-dir=/var/lib/etcd
# Node 2
ETCDCTL_API=3 etcdctl snapshot restore /backup/etcd-20260319.db
--name=master-2
--initial-cluster="master-1=https://10.0.0.1:2380,master-2=https://10.0.0.2:2380,master-3=https://10.0.0.3:2380"
--initial-cluster-token=etcd-cluster-restored
--initial-advertise-peer-urls=https://10.0.0.2:2380
--data-dir=/var/lib/etcd
# Node 3 (동일 패턴, name과 peer URL만 변경)
복구 후 검증 체크리스트
#!/bin/bash
# verify-restore.sh
echo "=== etcd 클러스터 상태 ==="
ETCDCTL_API=3 etcdctl
--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
endpoint health --write-out=table
echo "=== Kubernetes 노드 상태 ==="
kubectl get nodes -o wide
echo "=== 시스템 Pod 상태 ==="
kubectl get pods -n kube-system
echo "=== 모든 네임스페이스 Pod 상태 ==="
kubectl get pods --all-namespaces | grep -v Running | grep -v Completed
echo "=== Deployment 상태 ==="
kubectl get deployments --all-namespaces
echo "=== Secret/ConfigMap 개수 ==="
echo "Secrets: $(kubectl get secrets --all-namespaces --no-headers | wc -l)"
echo "ConfigMaps: $(kubectl get cm --all-namespaces --no-headers | wc -l)"
etcd 상태 모니터링
백업도 중요하지만, etcd 이상 징후를 조기에 감지하는 것이 더 중요합니다:
# DB 크기 확인 (2GB 제한 주의)
ETCDCTL_API=3 etcdctl endpoint status --write-out=json |
python3 -c "import json,sys; d=json.load(sys.stdin);
print(f'DB Size: {d[0]["Status"]["dbSize"]/1024/1024:.1f} MB')"
# 키 수 확인
ETCDCTL_API=3 etcdctl get / --prefix --keys-only | wc -l
# 컴팩션 (불필요한 히스토리 정리)
REVISION=$(ETCDCTL_API=3 etcdctl endpoint status
--write-out=json | python3 -c
"import json,sys; print(json.load(sys.stdin)[0]['Status']['header']['revision'])")
ETCDCTL_API=3 etcdctl compact $REVISION
ETCDCTL_API=3 etcdctl defrag
# Prometheus 메트릭 확인
# etcd_server_has_leader = 1 (리더 존재 여부)
# etcd_disk_wal_fsync_duration_seconds (디스크 성능)
# etcd_mvcc_db_total_size_in_bytes (DB 크기)
K8s Prometheus 모니터링에서 etcd 메트릭 수집 설정을 자세히 다루고 있습니다.
운영 베스트 프랙티스
- 백업 주기: 최소 6시간마다, 중요 클러스터는 1시간마다
- 백업 검증: 월 1회 이상 실제 복구 테스트 수행
- 3-2-1 규칙: 3개 백업, 2종류 미디어, 1개는 외부 저장
- 암호화: etcd에는 Secret이 포함되므로 백업 파일 반드시 암호화
- DB 크기 감시: 2GB 임계값 80%에 알림 설정
- 컴팩션·디프래그: 주기적으로 실행하여 DB 비대화 방지
- SSD 필수: etcd는 디스크 지연에 민감, HDD는 절대 금지
마치며
etcd 백업은 Kubernetes 운영의 마지막 보루입니다. etcdctl snapshot save로 스냅샷을 생성하고, CronJob으로 자동화하며, 외부 스토리지로 전송하세요. 복구 절차는 반드시 사전에 테스트하고 문서화해야 합니다. 정기적인 컴팩션과 DB 크기 모니터링으로 etcd 건강 상태를 유지하면, 최악의 상황에서도 클러스터를 복원할 수 있습니다.