쿠버네티스 운영 실무 · etcd 스냅샷이 답이 아닐 때 · 실습
어제 지운 네임스페이스만 되살려 달라는 요청
목표
종류가 섞인 네임스페이스 한 벌을 오브젝트 단위로 내보내고, 그 클러스터에서만 뜻이 있는 필드를 스크립트로 걷어 낸 뒤 다른 네임스페이스로 되살린다. 낡은 소유자 참조가 복원본을 조용히 지우는 것을 실제로 보고, 복원 순서를 틀렸을 때의 오류를 확인하고, 복원 검증을 기계가 하게 만든다.
왜 중요한가
etcd 스냅샷은 클러스터 전체를 한 시점으로 되돌리는 도구다. 그래서 어제 지워진 네임스페이스 하나만 되살려 달라는 요청에는 쓸 수 없고, 다른 클러스터로 옮길 수도 없다. 이럴 때 필요한 것이 오브젝트 수준 백업인데, 여기에는 스냅샷에 없는 어려움이 있다. 내보낸 오브젝트를 그대로 되돌려 넣으면 안 된다. uid·resourceVersion 은 그 클러스터에서만 뜻이 있고, status 는 컨트롤러가 다시 쓸 값이며, ownerReferences 의 uid 는 복원한 클러스터에 존재하지 않는다 — 이 마지막 것이 특히 고약하다. 가비지 컬렉터가 주인 없는 오브젝트로 보고 오류도 이벤트도 없이 지워 버린다.
단계
1. /root/ops-backup/crd.yaml 에 CustomResourceDefinition widgets.ops.example.com 을 쓰세요 — 그룹 ops.example.com, 종류 Widget(복수형 widgets), 네임스페이스 범위, 버전 v1(served·storage 모두 참)이고 스키마에 정수 spec.size 가 있습니다. 그다음 /root/ops-backup/scene.yaml 에 네임스페이스 ops-shop 의 오브젝트 네 개를 한 파일에 쓰세요 — Deployment catalog(레플리카 2, 라벨 app: catalog, 이미지 nginx:1.27.3), 같은 이름의 ClusterIP Service catalog(셀렉터 app: catalog, 포트 80), ConfigMap catalog-config(currency: KRW, page-size: "20"), Widget w1(spec.size 3). 네임스페이스를 만들고 전부 적용하세요.
2. 네 오브젝트를 각각 /root/ops-backup/raw/ 아래에 가공하지 않고 그대로 내보내세요 — deploy.yaml(Deployment catalog), svc.yaml(Service catalog), cm.yaml(ConfigMap catalog-config), widget.yaml(Widget w1). kubectl get <종류> <이름> -o yaml 의 출력을 손대지 말고 그대로 저장합니다.
3. /root/ops-backup/clean.sh 를 만드세요 — 인자로 받은 YAML 파일 하나를 읽어 정리한 결과를 표준출력에만 찍습니다. 지워야 하는 것은 metadata 의 uid·resourceVersion·creationTimestamp·generation·managedFields·ownerReferences·namespace, 최상위 status, 그리고 Service 의 spec.clusterIP·spec.clusterIPs·spec.ports[].nodePort 입니다. 이 스크립트로 raw/ 의 네 파일을 정리해 같은 이름으로 /root/ops-backup/clean/ 에 저장하세요.
4. 네임스페이스 ops-shop 를 통째로 지우고, 지워진 것을 확인한 뒤 /root/ops-backup/gone.txt 에 한 줄로 기록하세요 — ops-shop 탭 NotFound. 지우기 전에 /root/ops-backup/raw/ 의 네 파일이 모두 있는지 먼저 확인하세요.
5. 네임스페이스 ops-shop-restored 를 만들고 /root/ops-backup/clean/ 의 네 파일을 그 네임스페이스로 복원하세요(정리한 매니페스트에는 네임스페이스가 없으므로 명령에서 지정합니다). Deployment 파드 2개가 Running 이 될 때까지 기다린 뒤 /root/ops-backup/restored.tsv 에 네 줄로 저장하세요 — Deployment 탭 catalog, Service 탭 catalog, ConfigMap 탭 catalog-config, Widget 탭 w1 이고 종류 이름 오름차순입니다.
6. /root/ops-backup/victim.yaml 에 ConfigMap orphan-victim 을 쓰세요 — 네임스페이스 ops-shop-restored, 데이터 note: restored-with-stale-owner, 그리고 metadata.ownerReferences 에 이 클러스터에 없는 소유자를 적습니다(apiVersion: v1, kind: ConfigMap, name: catalog-config-old, uid 는 아무 UUID 나). 적용한 뒤 그 오브젝트가 사라질 때까지 조건 반복문으로 기다리세요. 그다음 /root/ops-backup/fixed.yaml 에 같은 데이터를 가진 ConfigMap orphan-fixed 를 ownerReferences 없이 써서 적용하고, /root/ops-backup/owner.tsv 에 두 줄로 결과를 남기세요 — orphan-victim 탭 gone, orphan-fixed 탭 alive.
7. /root/ops-backup/gadget.yaml 에 커스텀 리소스 Gadget g1 을 쓰세요 — apiVersion: ops.example.com/v1, 네임스페이스 ops-shop-restored, spec.color 는 blue 입니다. 아직 그 종류의 CRD 가 없는 상태에서 먼저 적용해 보고 오류를 /root/ops-backup/order-error.txt 에 저장하세요(표준오류 포함). 그다음 /root/ops-backup/gadget-crd.yaml 에 CRD gadgets.ops.example.com 을 쓰고(그룹 ops.example.com, 종류 Gadget, 복수형 gadgets, 네임스페이스 범위, 버전 v1, 문자열 spec.color) 적용한 뒤 커스텀 리소스를 다시 적용하세요. 마지막으로 /root/ops-backup/restore-order.txt 에 복원 순서를 네 줄로 적으세요 — namespace, crd, custom-resource, workload 를 올바른 순서로 한 줄씩.
8. /root/ops-backup/inventory.tsv 에 복원 목록을 네 줄로 적으세요 — <종류> 탭 <이름> 탭 <기대값> 이고, 기대값은 Deployment 는 spec.replicas, Service 는 spec.ports[0].port, ConfigMap 은 data 의 키 개수, Widget 은 spec.size 입니다. 그다음 /root/ops-backup/verify-restore.sh 를 만드세요 — 이 표를 읽어 ops-shop-restored 의 실제 오브젝트와 대조하고 맞으면 OK <종류>/<이름> <값>, 틀리면 MISMATCH <종류>/<이름> 기대=<값> 실제=<값> 을 표준출력에만 찍고 한 줄이라도 틀리면 0 이 아닌 코드로 끝나야 합니다. 그 출력을 /root/ops-backup/verify.txt 에 저장하세요.
참고
kubectl get <종류> <이름> -o yaml은 서버가 채운 필드까지 전부 내보냅니다.yq eval 'del(...)'는 없는 경로를 지우라고 해도 오류를 내지 않습니다.metadata.namespace를 지워 두면kubectl apply -n <대상>으로 복원할 곳을 정할 수 있습니다.- CRD 는 클러스터 범위라 네임스페이스를 지워도 남지만 그 커스텀 리소스는 함께 사라집니다.
- 흔한 실수: ownerReferences 를 그대로 담아 두어 복원한 오브젝트가 조용히 사라진다.
- 흔한 실수: 복원 순서를 적어 두지 않아 복구 당일에 종류를 모른다는 오류를 만난다.
- 참고: https://kubernetes.io/docs/concepts/architecture/garbage-collection/
단계 8개
- 되살릴 한 벌을 만든다
- 있는 그대로 내보낸다
- 그 클러스터에서만 뜻이 있는 것을 지운다
- 사고를 만든다
- 다른 네임스페이스로 되살린다
- 낡은 소유자를 남기면 복원한 것이 사라진다
- 순서를 틀리면 복원이 실패한다
- 복원이 됐는지 사람이 눈으로 세지 않게 한다