LabHub
배우기 러닝패스 코스

쿠버네티스 운영 실무 · etcd 스냅샷이 답이 아닐 때 · 실습

어제 지운 네임스페이스만 되살려 달라는 요청

LabHub 에서 이어서 보기

목표

종류가 섞인 네임스페이스 한 벌을 오브젝트 단위로 내보내고, 그 클러스터에서만 뜻이 있는 필드를 스크립트로 걷어 낸 뒤 다른 네임스페이스로 되살린다. 낡은 소유자 참조가 복원본을 조용히 지우는 것을 실제로 보고, 복원 순서를 틀렸을 때의 오류를 확인하고, 복원 검증을 기계가 하게 만든다.

왜 중요한가

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 파일 하나를 읽어 정리한 결과를 표준출력에만 찍습니다. 지워야 하는 것은 metadatauid·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-shopNotFound. 지우기 전에 /root/ops-backup/raw/ 의 네 파일이 모두 있는지 먼저 확인하세요.
5. 네임스페이스 ops-shop-restored 를 만들고 /root/ops-backup/clean/ 의 네 파일을 그 네임스페이스로 복원하세요(정리한 매니페스트에는 네임스페이스가 없으므로 명령에서 지정합니다). Deployment 파드 2개가 Running 이 될 때까지 기다린 뒤 /root/ops-backup/restored.tsv 에 네 줄로 저장하세요 — Deploymentcatalog, Servicecatalog, ConfigMapcatalog-config, Widgetw1 이고 종류 이름 오름차순입니다.
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-victimgone, orphan-fixedalive.
7. /root/ops-backup/gadget.yaml 에 커스텀 리소스 Gadget g1 을 쓰세요 — apiVersion: ops.example.com/v1, 네임스페이스 ops-shop-restored, spec.colorblue 입니다. 아직 그 종류의 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 에 저장하세요.

참고

단계 8개

  1. 되살릴 한 벌을 만든다
  2. 있는 그대로 내보낸다
  3. 그 클러스터에서만 뜻이 있는 것을 지운다
  4. 사고를 만든다
  5. 다른 네임스페이스로 되살린다
  6. 낡은 소유자를 남기면 복원한 것이 사라진다
  7. 순서를 틀리면 복원이 실패한다
  8. 복원이 됐는지 사람이 눈으로 세지 않게 한다