Files
document-haness/docs/virtualization/tech-log-studio/lab-environment-build/setup/setup-tear-down-the-lab-and-know-what-survives.md
T

18 KiB

id, kind, slug, title, topic, topicName, project, status, studio, pinnedVersions, source, sourceRevision, evidence
id kind slug title topic topicName project status studio pinnedVersions source sourceRevision evidence
87e0138d-dedc-4734-89b3-4ebe7c2df56d SETUP tear-down-the-lab-and-know-what-survives 실험대를 철거하고 무엇이 남는지 확인한다 lab-environment-build 실험대 환경 구성 virtualization 게시 전 https://hyeonworks.com/studio/documents/87e0138d-dedc-4734-89b3-4ebe7c2df56d/edit
name version
libvirt 12.7.0
name version
QEMU 11.1.1
name version
커널 7.2.2-arch1-1
name version
호스트 Arch Linux · i5-1135G7 · RAM 11,648MiB
name version
게스트 Debian 12 genericcloud
final/document.md#202-철거-실제-출력-전문
final/document.md#204-재구축할-때-무엇이-남아-있나
final/document.md#195-이-부의-출처와-범위
import-head 9465582b5d1630eb4ae7c4e078021486919bf6b6 · uncommitted-working-tree snapshot
../../../final/evidence/raw/lab-state-before-rebuild/58-authenticator-and-token.txt

실험대를 철거하고 무엇이 남는지 확인한다

게스트 세 대와 DHCP 예약을 지우고 무엇이 남았는지까지 확인하는 절차다. 세우는 가이드에는 이 부분이 없어서 2026-09-10 에 직접 돌리며 명령과 출력을 적었다. 되돌리는 명령은 이 절차에 없다.

관계

  • cloud-init 시드로 게스트 세 대를 만들고 SSH 가 키로 붙게 한다 여기서 지우는 것을 그 절차가 만들고, 다시 세울 때 돌아갈 곳도 거기다.
  • 실험대를 껐다 켜고 게스트 메모리를 다시 나눈다 워크로드를 정상으로 내리고 싶으면 그 편의 종료 순서를 먼저 돌고 온다. 여기서는 지우는 것이 목적이라 전원을 뽑는다.
  • 5120MB 를 줬는데 353MB 를 쓰고 있었다 — 선언한 양과 실제로 드는 양 회수된 디스크 3.1GB 를 읽는 방법이 그 기록과 같다. 선언한 크기와 실제로 차지한 크기는 다른 값이다.
  • 이 실험대의 certbot 은 HTTP-01 로 받고 있는가 DNS-01 로 받고 있는가 이 물음은 dns-cloudflare 관측으로 닫혔다. 여기서는 그 사실과 별개로 현재 자격증명이 재발급에 쓸 수 있는지를 철거 조건으로 본다.
  • 가이드대로 다시 쳐서 이 실험대가 같은 상태로 서는가 철거는 그 물음을 재기 위한 전제라, 지우고 다시 세워 봐야 답이 나온다.
  • 도구가 낸 출력은 대상의 상태가 아니다 — 그 명령이 무엇을 세는지부터 가른다 virbr0DOWN 으로 보이는 것을 고장으로 읽지 않는 근거가 그 기준이다.

본문

읽기 전에 — 어디서 치는가

전부 [lab host] 다. 게스트를 통째로 지우는 절차라 게스트 안에서 칠 명령이 하나도 없다.

조회와 삭제뿐이라 편집기를 여는 곳도 없고 CLI 를 그대로 쓴다.

되돌리기가 없다

virsh undefine --remove-all-storage 는 도메인 정의와 오버레이 디스크와 시드 ISO 를 한 번에 지운다. 지운 뒤에 되살리는 명령이 libvirt 에 없다. 실험대를 다시 쓰려면 base.qcow2 위에 오버레이를 새로 만들고 시드 ISO 를 다시 구워야 하고, 그 절차는 게스트 세 대를 만드는 편이 받는다.

지우기 전에 알아 둘 것은 이 절차가 건드리지 않는 쪽이다. base.qcow2~/workspace/cloud/kc-lab-{1,2}.yaml~/.ssh/configkc-lab-* 항목은 그 자체로 살아 있어서 재구축 때 다시 쓴다. 무엇이 어느 쪽인지는 아래 표가 아홉 행으로 가른다.

지우기 전에 먼저 본다

무엇을 확인하는가 — 지금 무엇이 있는지. 철거 뒤와 견줄 값을 여기서 받아 둔다.

virsh list --all
virsh vol-list default
virsh net-dumpxml default | grep -A5 dhcp
df -h /

어디를 봐야 하는가 — 네 값이다. 도메인이 몇 대인가, 볼륨이 몇 개인가, <host> 예약이 몇 줄인가, 루트 파일시스템이 얼마나 찼는가. 2026-09-10 의 이 호스트에서는 도메인 3 대에 볼륨 7 개, 예약 3 줄, df -h / 가 11G 였다.

이 결과가 의미하는 것 — 이 네 값이 철거의 성공 판정 기준이 된다. 여기를 건너뛰면 지운 뒤에 「원래 몇 개였지」를 되짚을 방법이 없다.

2026-09-17 에 다시 세운 실험대에서 같은 네 줄을 쳤다(observed). 도메인 3 대에 볼륨 7 개, 예약 3 줄까지는 2026-09-10 과 같고 디스크만 11G 가 아니라 18G 다. 그사이에 k3s 와 컨테이너 이미지가 쌓였다. 그래서 이 네 값은 「이 호스트의 정답」이 아니라 지금 이 실행에서 받아 두는 대조군이다 — 위에 실은 숫자와 다르다고 잘못된 것이 아니다.

도메인   kc-lab-1 · kc-lab-edge · kc-lab-2  (셋 다 running)
볼륨     base.qcow2 + kc-lab-{1,2,edge}.qcow2 + seed-kc-lab-{1,2,edge}.iso  = 7
예약     52:54:00:aa:bb:{10,11,12}  = 3 줄
디스크   /dev/nvme0n1p3  226G  18G  197G  9% /

실행 절차

1. 게스트 세 대를 지운다

목적 — 도메인 정의와 그 게스트가 쓰던 디스크를 함께 없앤다.

세 게스트의 전원을 뽑고 정의와 디스크를 지운 뒤, 무엇이 없어졌는지 바로 센다.

for v in kc-lab-edge kc-lab-2 kc-lab-1; do
  virsh destroy "$v"
  virsh undefine "$v" --remove-all-storage
done
virsh list --all
virsh vol-list default

예상 결과 — 게스트 한 대마다 네 줄이 나온다. 아래는 kc-lab-edge 한 대분이다(observed).

Domain 'kc-lab-edge' destroyed
Domain 'kc-lab-edge' has been undefined
Volume 'vda'(/var/lib/libvirt/images/kc-lab-edge.qcow2) removed.
Volume 'vdb'(/var/lib/libvirt/images/seed-kc-lab-edge.iso) removed.

왜 필요한가Volume 줄이 두 개 나오는지가 끝났다는 판정이다. vda 가 오버레이 디스크이고 vdb 가 시드 ISO 다. --remove-all-storage 를 빠뜨리면 도메인만 사라지고 디스크 파일은 지워지지 않아, 같은 이름으로 다음 virt-install 을 돌릴 때 「이미 있다」로 실패한다. 그리고 virsh destroy 는 종료 신호를 보내지 않고 전원을 뽑는다. 워크로드를 정상으로 내리고 싶으면 종료 순서를 다루는 편을 먼저 돌고 온다.

문제가 생기면 — ② 의 virsh vol-list default 에 게스트 디스크나 시드 ISO 가 보이면 --remove-all-storage 가 빠진 것이다. 남은 볼륨은 virsh vol-delete --pool default seed-kc-lab-1.iso 처럼 이름을 하나씩 대서 지운다.

2. DHCP 예약 세 줄을 지운다

목적 — libvirt default 네트워크에서 게스트 주소 예약을 빼고 동적 대역만 남긴다.

세 예약을 값 그대로 지운 뒤 네트워크 정의에 무엇이 남았는지 본다. 삭제할 때도 macnameip 세 속성을 다 준다.

virsh net-update default delete ip-dhcp-host \
  "<host mac='52:54:00:aa:bb:10' name='kc-lab-edge' ip='192.168.122.10'/>" \
  --live --config
virsh net-update default delete ip-dhcp-host \
  "<host mac='52:54:00:aa:bb:11' name='kc-lab-1' ip='192.168.122.11'/>" \
  --live --config
virsh net-update default delete ip-dhcp-host \
  "<host mac='52:54:00:aa:bb:12' name='kc-lab-2' ip='192.168.122.12'/>" \
  --live --config
virsh net-dumpxml default | grep -E "host mac|range start"

예상 결과 — ① 은 삭제마다 Updated network default persistent config and live state 를 낸다. ② 에는 <range> 한 줄만 남는다.

    <dhcp>
      <range start='192.168.122.2' end='192.168.122.254'/>
    </dhcp>

왜 필요한가 — 예약을 남겨 두면 다음 구축에서 예약이 두 벌이 되거나, 넣으려 할 때 이미 있다고 거부당한다. --live--config 를 둘 다 주는 까닭도 넣을 때와 같다. --live 만 주면 재부팅에 예약이 되살아나고 --config 만 주면 지금 돌고 있는 dnsmasq 가 아직 예약을 들고 있다.

문제가 생기면 — 속성이 하나라도 비면 이렇게 거부된다.

error: Failed to update network default
error: XML error: Cannot use host name '' in network 'default'

zsh 에서 루프로 돌리면 이 오류를 만난다. zsh 는 따옴표 없는 변수를 단어 분리하지 않아서, bash 에서 되던 set -- $entry$1 에 문자열 전체를 넣고 $2$3 을 비워서 name="" 이 된다. 세 줄을 값 그대로 쓰는 편이 안전하다.

인증서는 edge guest에서 백업한다

2026-09-17 현재 서빙하는 인증서는 kc-lab-edge 안의 /etc/letsencrypt/ 에 있다(observed). 03에서 nginx를 edge guest로 옮겼고 04에서 certbot도 그 guest에 설치했다. 이 절차의 1번은 guest를 --remove-all-storage로 지우므로, 인증서도 백업하지 않으면 guest 디스크와 함께 사라진다.

두 기계를 나란히 열어 보면 이렇다.

ls -la /etc/letsencrypt/
ssh kc-lab-edge 'sudo ls -la /etc/letsencrypt/'
기계 /etc/letsencrypt/ certbot 타이머 서빙
랩 호스트 있다 (읽으려면 sudo) /usr/bin/certbot 없다 안 한다 — 80·443 을 안 듣는다
kc-lab-edge 있다 — cli.ini · renewal-hooks /usr/bin/certbot certbot.timer 한다

따라서 철거 전에 백업할 대상은 edge guest의 /etc/letsencrypt/ 하나다. 파일 이름은 host에서 한 번 만든 시각값으로 고정해, 원격 tar와 scp가 같은 파일을 가리키게 한다.

STAMP=$(date +%Y%m%d-%H%M%S)
ssh kc-lab-edge "sudo tar czf /tmp/letsencrypt-backup-$STAMP.tgz -C /etc letsencrypt"
scp "kc-lab-edge:/tmp/letsencrypt-backup-$STAMP.tgz" "$HOME/"

예상 결과 — host의 $HOME/letsencrypt-backup-$STAMP.tgz가 생긴다. 이 파일은 edge guest를 --remove-all-storage로 지우기 전에 host 쪽으로 빠져나온 복구본이다.

패키지도 host와 guest를 나눠 본다. kc-lab-edge 안의 nginxcertbot은 guest와 함께 사라지고, host에는 libvirt·qemu·kubectl과 현재 서빙에는 쓰이지 않는 host certbot이 남는다.

정책으로 남기는 것은 guest 안의 디렉터리 자체가 아니라 host로 복사한 인증서 backup tar다. 한도 때문도 아니고 검증 방식이 미확정이어서도 아니다. 2026-09-17 renewal 설정에서 authenticator = dns-cloudflare 를 직접 읽어 DNS-01 등록은 확인했다.

backup tar를 남기는 까닭은 현재 자격증명으로 다시 받을 수 있는지가 확인되지 않았기 때문이다. 같은 후속 원문에서 /etc/letsencrypt/cloudflare.ini 의 토큰 길이는 0자로 측정됐고 Cloudflare 검증은 Invalid request headers 로 실패했다. 따라서 DNS-01이라는 사실만으로 “guest를 지워도 다시 받으면 된다”고 결론내리지 않는다.

유효한 Cloudflare 토큰을 다시 준비하고 certbot renew --dry-run 이 성공한 원문까지 남기면 그때 보존 정책을 완화할 수 있다. 그 전에는 guest를 지우기 전에 실제 서빙 중인 /etc/letsencrypt/ 를 host로 백업하고 그 tar를 보존한다.

복원도 새로 만든 kc-lab-edge 안으로 되돌린다. host의 /etc에 풀지 않는다. {{STAMP}}에는 백업 파일 이름의 시각값을 넣는다.

BACKUP="$HOME/letsencrypt-backup-{{STAMP}}.tgz"
scp "$BACKUP" kc-lab-edge:/tmp/letsencrypt-backup.tgz
ssh kc-lab-edge 'sudo tar xzf /tmp/letsencrypt-backup.tgz -C /etc'
ssh kc-lab-edge 'sudo certbot certificates && sudo nginx -t'

마지막 줄이 둘 다 성공해야 복원이 끝난 것이다. 인증서 파일이 돌아왔는지와 nginx가 그 경로를 실제로 읽을 수 있는지를 따로 확인한다.

구성 값

지우는 대상은 게스트 셋과 그 셋의 DHCP 예약이다.

게스트 예약의 MAC · IP 볼륨 둘
kc-lab-edge 52:54:00:aa:bb:10 · 192.168.122.10 kc-lab-edge.qcow2 · seed-kc-lab-edge.iso
kc-lab-1 52:54:00:aa:bb:11 · 192.168.122.11 kc-lab-1.qcow2 · seed-kc-lab-1.iso
kc-lab-2 52:54:00:aa:bb:12 · 192.168.122.12 kc-lab-2.qcow2 · seed-kc-lab-2.iso

지우는 순서는 kc-lab-edge · kc-lab-2 · kc-lab-1 이다. 1번의 for 목록이 그 순서로 적혀 있다.

확인 방법

무엇을 확인하는가 — 철거 전에 받아 둔 네 값이 전부 내려갔는지.

virsh list --all
virsh vol-list default
virsh net-dumpxml default | grep -A5 dhcp
df -h /
ip -br addr show virbr0

어디를 봐야 하는가 — 다섯 줄을 전후로 견준다(observed).

무엇을 보는가 철거 전 철거 후
virsh list --all 3 대 running (없음)
virsh vol-list default 7 개 base.qcow2 1 개
DHCP 예약 3 줄 0 줄
df -h / 11G 7.9G
virbr0 UP DOWN

이 결과가 의미하는 것 — 디스크에서 3.1GB 가 회수됐다. 잰 내역은 kc-lab-1 1.4GB 와 kc-lab-2 665MB 와 시드 ISO 세 개(각 370KB)이고, kc-lab-edge 의 디스크 크기는 재 두지 않았다. 합계에서 빼면 1GB 안팎인데 그것은 잰 값이 아니라 역산한 값이다.

그 역산은 아마 너무 컸다(2026-09-17, observed). 같은 호스트에 다시 세운 실험대의 볼륨 일곱을 virsh vol-info 로 재 보니 엣지가 가장 작다. 선언한 크기와 실제로 먹는 양이 크게 다르고, 오버레이라 쓴 만큼만 먹는다.

virsh vol-info --pool default kc-lab-edge.qcow2
base.qcow2             Capacity=3.00 GiB     Allocation=323.25 MiB
kc-lab-1.qcow2         Capacity=20.00 GiB    Allocation=5.40 GiB
kc-lab-2.qcow2         Capacity=20.00 GiB    Allocation=3.85 GiB
kc-lab-edge.qcow2      Capacity=10.00 GiB    Allocation=316.26 MiB
seed-kc-lab-{1,2,edge}.iso   Capacity=370.00 KiB   Allocation=372.00 KiB

이 값을 2026-09-10 의 역산 대신 그대로 끼워 넣지는 않는다 — 다른 날 다른 실험대이고 그 위에 올린 것도 다르다. 다만 엣지가 nginx 하나만 돌리는 게스트라 300MB 대에 머문다는 것은 두 실행에서 같을 가능성이 높고, 그렇다면 그때의 「1GB 안팎」은 과대 추정이다. 다시 철거할 때는 역산하지 말고 지우기 전에 이 명령으로 세 볼륨을 직접 잰다.

virbr0DOWN 인 것은 고장이 아니다. 브리지에 붙은 tap 인터페이스가 하나도 없어서 캐리어가 없는 것으로 표시될 뿐이고, 주소 192.168.122.1/24 는 그대로 있다. VM 을 다시 띄우면 그 VM 의 vnetN 인터페이스가 브리지에 붙으면서 UP 이 된다. virsh net-start 를 찾아 헤매지 않는다.

철거해도 남는 것과 사라지는 것

이걸 모르면 재구축이 「왜 이건 이미 있지」와 「왜 이건 없지」의 반복이 된다.

무엇 어떻게 되나
base.qcow2 (335MB) 남는다 다음 오버레이의 바닥
host 패키지 (libvirt · qemu · kubectl, 쓰이지 않는 host certbot) 남는다 guest 삭제와 무관한 host 설치 상태
edge guest의 nginx · certbot 사라진다 kc-lab-edge 디스크와 함께 삭제된다
~/workspace/cloud/kc-lab-{1,2}.yaml 남는다 키와 비밀번호가 들어 있다
~/.ssh/configkc-lab-* 항목 남는다 재구축해도 IP 가 같다
libvirt default 네트워크 정의 남는다 예약만 지웠다
edge guest의 /etc/letsencrypt/ 사라진다 guest 삭제 전에 host의 $HOME/letsencrypt-backup-<stamp>.tgz로 백업한다
host에 복사한 인증서 백업 tar 남긴다 (정책) DNS-01 등록은 확인됐지만 credential 유효성과 certbot renew --dry-run 성공은 아직 미확인
게스트 디스크 · 시드 ISO 사라진다 --remove-all-storage
DHCP 예약 사라진다 net-update delete
k3s · Keycloak · 모든 워크로드 사라진다 게스트와 함께

~/workspace/cloud/kc-lab-{1,2}.yaml 이 남는다는 것은 편한 일이면서 위험한 일이다. 그 파일에 SSH 공개키와 콘솔 로그인용 비밀번호가 들어 있으므로, 실험대를 접고 기계를 넘길 때는 이 절차만으로 끝났다고 보지 않는다.

이 절차가 감당하지 않는 것

호스트 계층 철거는 여기 없다. 원본이 deploy/lab/host/teardown-host.sh 를 가리키지만 그 스크립트가 반입되지 않아 내용을 모른다. nginx 설정과 DNAT 유닛과 certbot 훅을 어디까지 걷어내는지는 확인하지 못했다.

다시 세우는 쪽도 여기 없다. 게스트를 만드는 편부터 일곱 편이 그 순서를 받는다.

이 명령들은 2026-09-10 에 이 호스트에서 실제로 돌려 받은 출력이고, 그 뒤에 다시 돌려 검증하지는 않았다. 다시 돌리면 실험대가 없어지기 때문이다.