etcd 상태와 앱 파일은 백업 대상도 복원 방법도 다릅니다. 앞에서 사용한 cp01·wk01·wk02에서 두 대상을 나눠 보관하고, 단일 컨트롤 플레인의 etcd 복원을 따라가 보겠습니다. 이어서 같은 Kubernetes 마이너 계열 안의 패치 변경 순서를 정리합니다.
목차
1. etcd와 앱 파일의 백업 범위
| 대상 | 보관 내용 | 그것만으로 복구되지 않는 것 |
|---|---|---|
| etcd 스냅샷 | Kubernetes API 객체와 클러스터 상태 | PV 안의 실제 파일·외부 DB |
| /etc/kubernetes | PKI·kubeconfig·static Pod 설정 | 워커의 앱 데이터 |
| Git·실습 매니페스트 | 앱과 정책의 원하는 선언 상태 | 실행 중 바뀐 데이터 |
| wk01의 local 데이터 | 7편의 history.log 등 | 클러스터 인증서·API 객체 |
cp01 한 대가 etcd와 API 서버를 함께 맡으므로 이 VM이 멈추면 새 배포와 스케줄링 같은 제어 작업도 중단됩니다. 워커의 일부 컨테이너가 계속 실행돼도 제어부가 정상이라는 뜻은 아닙니다. 가용성이 필요하다면 다중 컨트롤 플레인과 etcd 쿼럼, 로드밸런서, 장애 도메인을 설계해야 합니다. 한 Windows PC 위의 VM 세 대라는 한계도 남습니다.
2. cp01 etcd 스냅샷 생성
cp01의 labadmin 쉘에서 실행합니다. etcd 컨테이너에 포함된 etcdctl로 클라이언트 버전 차이를 줄입니다. 스냅샷을 호스트 마운트 경로에 잠시 만든 뒤 보호된 백업 디렉터리로 옮깁니다. 기존 데이터 파일을 복사한 것을 스냅샷으로 사용하지 않습니다.
BACKUP="/var/backups/k8s/$(date +%Y%m%d-%H%M%S)"
sudo install -d -m 0700 "$BACKUP"
ETCD_POD=etcd-cp01
ETCD_IMAGE=$(kubectl -n kube-system get pod "$ETCD_POD" \
-o jsonpath='{.spec.containers[0].image}')
kubectl -n kube-system exec "$ETCD_POD" -- etcdctl \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/healthcheck-client.crt \
--key=/etc/kubernetes/pki/etcd/healthcheck-client.key \
snapshot save /var/lib/etcd/lab-snapshot.db
sudo mv /var/lib/etcd/lab-snapshot.db "$BACKUP/etcd.db"
sudo chmod 0600 "$BACKUP/etcd.db"
printf '%s
' "$ETCD_IMAGE" | sudo tee "$BACKUP/etcd-image.txt" > /dev/null
sudo tar -C / -czf "$BACKUP/etc-kubernetes.tgz" etc/kubernetes
sudo chmod 0600 "$BACKUP/etc-kubernetes.tgz" "$BACKUP/etcd-image.txt"
etcd 스냅샷에는 Secret 등 민감한 상태가, PKI 백업에는 개인 키가 들어 있습니다. 암호화와 접근 제한을 적용하고 VM 밖의 별도 저장소에도 보관합니다. 보존 주기를 정하고 WordPress 업로드 폴더나 공개 Git에는 두지 않습니다. 저장 암호화를 사용한다면 암호화 키와 설정의 복구도 별도로 준비합니다.
3. 스냅샷 상태·해시 확인
sudo ctr -n k8s.io run --rm \
--mount "type=bind,src=$BACKUP,dst=/backup,options=rbind:ro" \
"$ETCD_IMAGE" lab-etcd-check \
/usr/local/bin/etcdutl snapshot status /backup/etcd.db --write-out=table
sudo sha256sum "$BACKUP/etcd.db" "$BACKUP/etc-kubernetes.tgz" \
| sudo tee "$BACKUP/SHA256SUMS" > /dev/null
리비전과 키 수, 크기 등이 나오는지 확인합니다. 해시는 전송 중 변경을 확인하는 용도이고 복원 시험을 대신하지는 않습니다. ctr -n k8s.io는 CRI의 containerd 네임스페이스를 사용합니다. 이미지 안의 etcdutl 경로나 포함 여부가 다르면 해당 etcd 버전의 공식 도구를 확인한 뒤 진행합니다.
4. 단일 etcd 복원 조건과 절차
아래 복원은 이 연재의 단일 etcd 멤버 cp01이며, 원래 IP·호스트명과 유효한
/etc/kubernetesPKI가 그대로 있는 경우에만 적용합니다. 전체 VM을 새로 만드는 재해 복구나 다중 멤버 etcd의 멤버 교체 절차와는 범위가 다릅니다. 원본 VM과 같은 이름·IP를 쓰는 복제 실습실을 동시에 같은 네트워크에 켜지 않습니다. VM 밖에 백업을 보관하고 실습용 되돌리기 지점을 만든 다음 진행합니다.
스냅샷 이후에 만든 객체를 복원 뒤 조회하면 어느 시점으로 돌아갔는지 확인하기 편해요. cp01에서 테스트 ConfigMap을 만들고 실제 백업 경로를 지정합니다. 보관한 etcd 매니페스트의 이름과 peer URL도 아래 값과 비교합니다.
kubectl -n lab create configmap after-snapshot --from-literal=created=after-backup
# 실제 생성된 디렉터리 이름으로 변경
RESTORE_BACKUP=/var/backups/k8s/20261018-120000
sudo test -s "$RESTORE_BACKUP/etcd.db"
sudo grep -E -- '--name=|--initial-cluster=|--initial-advertise-peer-urls=' \
/etc/kubernetes/manifests/etcd.yaml
스냅샷을 만든 뒤 ConfigMap을 만들었어야 합니다. 위 경로의 test가 실패하면 다음 명령을 진행하지 않습니다. 아래 작업 동안 Kubernetes API는 중단됩니다. 원래 데이터 디렉터리를 바로 지우지 않고 보관하며, static Pod 매니페스트는 감시 디렉터리 밖으로 이동합니다. 같은 디렉터리에 .bak 파일로 남기면 kubelet이 읽을 수 있습니다.
HOLD=/root/k8s-manifests-hold
sudo install -d -m 0700 "$HOLD"
sudo mv /etc/kubernetes/manifests/*.yaml "$HOLD/"
# kubelet이 static Pod를 종료할 때까지 반복 확인
sudo crictl ps
crictl ps에 etcd·kube-apiserver·kube-controller-manager·kube-scheduler가 모두 사라진 것을 확인한 뒤 다음으로 갑니다. kubelet부터 정지하면 기존 static Pod 컨테이너가 남을 수 있으므로 순서가 중요합니다. containerd 서비스는 복원 도구 실행에 필요하므로 계속 실행합니다.
sudo systemctl stop kubelet
ETCD_IMAGE=$(sudo cat "$RESTORE_BACKUP/etcd-image.txt")
OLD_ETCD="/var/lib/etcd.pre-restore-$(date +%Y%m%d-%H%M%S)"
sudo mv /var/lib/etcd "$OLD_ETCD"
RESTORE_PARENT=$(sudo mktemp -d /var/lib/etcd-restore.XXXXXX)
sudo ctr -n k8s.io run --rm \
--mount "type=bind,src=$RESTORE_BACKUP,dst=/backup,options=rbind:ro" \
--mount "type=bind,src=$RESTORE_PARENT,dst=/restore,options=rbind:rw" \
"$ETCD_IMAGE" lab-etcd-restore \
/usr/local/bin/etcdutl snapshot restore /backup/etcd.db \
--data-dir=/restore/data \
--name=cp01 \
--initial-cluster=cp01=https://192.168.80.10:2380 \
--initial-advertise-peer-urls=https://192.168.80.10:2380 \
--initial-cluster-token=lab-restore-20261018 \
--bump-revision=1000000000 --mark-compacted
복원 명령의 성공을 확인한 뒤에만 다음으로 진행합니다. 과거 리비전으로 돌아가면 Kubernetes 감시 캐시가 이전 정보를 유지할 수 있습니다. 리비전을 올리고 앞 구간을 compacted로 표시해 클라이언트가 상태를 다시 읽도록 합니다. 이 숫자는 복구 데이터량이나 보존 기간이 아닙니다.
sudo test -d "$RESTORE_PARENT/data/member"
sudo mv "$RESTORE_PARENT/data" /var/lib/etcd
sudo mv "$HOLD"/*.yaml /etc/kubernetes/manifests/
sudo systemctl start kubelet
# API 서버 기동 후 확인
kubectl get --raw='/readyz?verbose'
kubectl get nodes
kubectl -n lab get deployment,pods,svc,pvc
kubectl -n lab get configmap after-snapshot
마지막 ConfigMap 조회는 NotFound여야 합니다. 연결 거절이나 인증 실패를 “객체가 없어졌으니 성공”으로 보지 않습니다. API와 노드, 실제 웹 응답까지 정상이어야 복원 완료입니다. 실패 시에는 이전 데이터 디렉터리와 원본 매니페스트를 보존한 채 kubelet·etcd 로그를 확인합니다. 같은 복원 명령을 무작정 반복하지 않습니다.
5. local PV 파일 백업·복원
volume-check Pod를 삭제해 앱의 파일 쓰기가 없는 상태에서 wk01의 데이터를 백업합니다. 실제 DB는 엔진이 제공하는 일관된 백업이나 쓰기 중지 절차를 사용합니다. tar만 실행해 트랜잭션 일관성을 보장할 수는 없습니다.
# wk01에서 실행
sudo install -d -m 0700 /var/backups/k8s
sudo tar -C /var/local/k8s-lab -czf /var/backups/k8s/local-data.tgz data
sudo chmod 0600 /var/backups/k8s/local-data.tgz
# 복구 시험 전, 이 파일도 VM 밖으로 보관
# 기존 디렉터리를 보관하고 백업을 같은 위치로 복원
sudo mv /var/local/k8s-lab/data \
"/var/local/k8s-lab/data.before-restore-$(date +%Y%m%d-%H%M%S)"
sudo tar -C /var/local/k8s-lab -xzf /var/backups/k8s/local-data.tgz
sudo ls -ld /var/local/k8s-lab/data
# cp01에서 복구 파일 확인 후 정리
kubectl apply -f ~/k8s-lab/app/volume-check.yaml
kubectl -n lab wait --for=condition=Ready pod/volume-check --timeout=120s
kubectl -n lab logs volume-check
kubectl -n lab delete pod volume-check
이전 기록과 소유자 UID/GID를 확인합니다. etcd와 파일 백업의 시점도 맞춰 보세요. PVC만 복구되고 파일은 없거나, 파일이 다른 앱의 PVC에 연결되는 상황을 피해야 합니다.
6. 같은 마이너 계열의 패치 변경
아래는 1.36 계열 안에서 더 새 패치로 이동하는 절차입니다. 확인 시점의 저장소 최신은 1.36.5이므로 이미 그 버전이면 확인만 하고 실제 업그레이드는 새 패치가 제공될 때 수행합니다. 마이너 업그레이드는 한 계열씩 진행하고, 먼저 CNI·런타임 호환성 및 릴리스 변경 사항을 검토합니다. 버전 숫자만 바꿔 여러 마이너를 건너뛰지 않습니다.
# cp01에서 현재 버전과 저장소 확인
kubectl get nodes
sudo dnf --disableexcludes=kubernetes list --showduplicates kubeadm
sudo kubeadm upgrade plan
백업과 앱 상태를 확인한 뒤 TARGET을 실제로 제공되는 더 새 1.36 패치로 지정합니다. 아래의 1.36.5는 변수 형식 예시입니다. 이미 같은 버전이라면 이 변경 단계는 건너뜁니다. 새 쉘과 각 노드에서도 TARGET을 다시 지정합니다.
# cp01: 실제 대상 패치로 변경한 뒤에만 실행
TARGET=1.36.5
sudo dnf install -y "kubeadm-${TARGET}" --disableexcludes=kubernetes
sudo kubeadm upgrade apply "v${TARGET}"
kubectl drain cp01 --ignore-daemonsets --timeout=180s
sudo dnf install -y "kubelet-${TARGET}" "kubectl-${TARGET}" --disableexcludes=kubernetes
sudo systemctl daemon-reload
sudo systemctl restart kubelet
kubectl wait --for=condition=Ready node/cp01 --timeout=300s
kubectl uncordon cp01
그 다음 wk01, wk02를 한 대씩 진행합니다. 첫 워커가 Ready로 복귀하고 앱이 정상임을 확인하기 전에 다음 워커를 시작하지 않습니다. 아래 워커 노드의 명령은 대상 워커에서 실행하고, drain·uncordon은 cp01에서 실행합니다.
# cp01에서 먼저
kubectl drain wk01 --ignore-daemonsets --timeout=180s
# wk01에서: TARGET은 cp01과 같은 실제 대상 버전
TARGET=1.36.5
sudo dnf install -y "kubeadm-${TARGET}" --disableexcludes=kubernetes
sudo kubeadm upgrade node
sudo dnf install -y "kubelet-${TARGET}" "kubectl-${TARGET}" --disableexcludes=kubernetes
sudo systemctl daemon-reload
sudo systemctl restart kubelet
# cp01에서 복귀 확인. 이후 wk02에도 같은 순서를 적용
kubectl wait --for=condition=Ready node/wk01 --timeout=300s
kubectl uncordon wk01
kubectl get nodes -o wide
kubectl -n lab rollout status deployment/web --timeout=180s
containerd 교체는 Kubernetes 업그레이드와 나눠 진행합니다. versionlock을 의도적으로 풀고 호환 버전을 고른 뒤 노드별 drain, 검증, 재고정을 수행합니다. 한 번에 여러 계층을 바꾸지 않아야 실패 원인을 찾기 쉽습니다. 마이너 변경 때는 kubernetes.repo의 계열 URL도 공식 절차대로 바꿉니다.
7. 인증서·노드·백업 점검 기록
kubectl get nodes
kubectl get pods -A
kubectl -n lab get deployment,pdb,pvc
sudo kubeadm certs check-expiration
인증서 만료일과 디스크·inode, 재시작 증가, 백업 시각, 최근 복원 시험 날짜를 기록합니다. 인증서 갱신 뒤에는 static Pod 재시작과 사용자 kubeconfig 배포까지 공식 절차로 확인합니다. 정기 명령의 실행 여부뿐 아니라 실패를 확인할 사람과 시점도 정해 둡니다.
여기까지 따라왔다면 새 클러스터 구성과 요청 경로, 노드 점검, 상태·파일 복원을 다시 설명해 보세요. 막힌 부분을 다시 확인하는 데 앞의 실습을 사용할 수 있습니다. 이후에는 다중 컨트롤 플레인과 CSI, TLS 게이트웨이, 중앙 로그·메트릭 중 현재 필요한 것부터 확장하면 됩니다.
참고 자료
- etcd 3.6 스냅샷과 복구·리비전 처리
- etcd 백업·운영 안내
- kubeadm 업그레이드 순서
- Kubernetes 버전 차이 지원 정책
- kubeadm 인증서 관리
- 저장 데이터 암호화
공식 문서와 배포 파일 확인 기준: 2026년 10월 18일. 출력은 완료 여부를 판단하기 위한 예시이며 환경별로 달라질 수 있습니다.


![[쿠버네티스 학습 01] Windows 11·VMware와 Rocky Linux 실습 환경](https://blog.maruops.com/wp-content/uploads/2026/10/maruops-photo-107-768x502.jpg)
![[쿠버네티스 학습 02] Rocky Linux 고정 IP·시간·커널 설정](https://blog.maruops.com/wp-content/uploads/2026/10/maruops-photo-088-768x966.jpg)
답글 남기기