Files
document-haness/docs/virtualization/tech-log-studio/lab-environment-build/setup/setup-tear-down-the-lab-and-know-what-survives.md
T
DongHyeonkaandClaude Opus 5 bbd87dc9ab fix(setup): Studio 편집기가 다시 쓰는 공백을 저장소에서 미리 맞춘다
Studio 의 블록 편집기는 본문을 화면에 풀 때 공백을 정규화한다. 이어진 빈 줄
둘을 하나로 줄이고, 닫는 코드펜스 뒤에 빈 줄이 없으면 하나를 넣는다. 저장된
값은 저장소와 바이트가 같지만, 사람이 편집 화면을 열고 저장을 누르는 순간
그 공백이 저장소와 갈린다 — 내용은 그대로인데 SHA 만 달라져서 어느 쪽이
정본인지 알 수 없게 된다.

setup 35편을 훑어 9편 11곳을 찾아 저장소 쪽을 편집기와 같은 모양으로 맞췄다.
빈 줄만 움직였고 내용 줄은 하나도 바꾸지 않았다(추가 2 · 삭제 9).

- 이어진 빈 줄 둘 → 하나: d4 · d4a(3곳) · a7 · b7a · c2 · b0 · tear-down
- 닫는 펜스 뒤 빈 줄 추가: c1 · create-three-guests

코드블록 안의 빈 줄과 frontmatter 는 건드리지 않았다.

관문: check_body PASS · check_prose error 0 · check_evidence 두 프로젝트 문제 없음 ·
verify-tech-log-tree error 0 · verify-project-layout error 0

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 20:03:53 +09:00

17 KiB

kind, slug, title, topic, topicName, project, status, pinnedVersions, source, sourceRevision
kind slug title topic topicName project status pinnedVersions source sourceRevision
SETUP tear-down-the-lab-and-know-what-survives 실험대를 철거하고 무엇이 남는지 확인한다 lab-environment-build 실험대 환경 구성 virtualization 게시 전
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-이-부의-출처와-범위
9465582b5d1630eb4ae7c4e078021486919bf6b6

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

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

관계

  • cloud-init 시드로 게스트 세 대를 만들고 SSH 가 키로 붙게 한다 여기서 지우는 것을 그 절차가 만들고, 다시 세울 때 돌아갈 곳도 거기다.
  • 실험대를 껐다 켜고 게스트 메모리를 다시 나눈다 워크로드를 정상으로 내리고 싶으면 그 편의 종료 순서를 먼저 돌고 온다. 여기서는 지우는 것이 목적이라 전원을 뽑는다.
  • 5120MB 를 줬는데 353MB 를 쓰고 있었다 — 선언한 양과 실제로 드는 양 회수된 디스크 3.1GB 를 읽는 방법이 그 기록과 같다. 선언한 크기와 실제로 차지한 크기는 다른 값이다.
  • 이 실험대의 certbot 은 HTTP-01 로 받고 있는가 DNS-01 로 받고 있는가 인증서를 지우지 않고 남기는 까닭이 이 물음이 안 닫혔기 때문이다. 답이 나오면 정책이 바뀐다.
  • 가이드대로 다시 쳐서 이 실험대가 같은 상태로 서는가 철거는 그 물음을 재기 위한 전제라, 지우고 다시 세워 봐야 답이 나온다.
  • 도구가 낸 출력은 대상의 상태가 아니다 — 그 명령이 무엇을 세는지부터 가른다 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="" 이 된다. 세 줄을 값 그대로 쓰는 편이 안전하다.

인증서는 건드리지 않는다

이 절의 전제가 03·04 뒤로 깨져 있다(2026-09-17, observed). 아래 본문은 인증서가 랩 호스트의 /etc/letsencrypt/ 에 있다고 보고 그 디렉터리를 정책으로 남긴다. 그런데 03 이 nginx 를 kc-lab-edge 로 옮겼고 04 가 certbot 을 거기 깔았다. 지금 서빙하는 인증서는 그 게스트 안에 산다. 그리고 이 절차의 1번은 게스트를 --remove-all-storage 로 지운다 — 인증서는 그 안에서 같이 사라진다.

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

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 한다

그래서 아래 백업 명령은 틀린 기계를 묶는다. 묶어야 할 것은 게스트 쪽이다.

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

「철거해도 남는 것」 표의 패키지 줄도 같은 이유로 틀렸다. 그 줄은 nginxcertbot 을 남는 쪽에 적어 두었는데, 둘 다 게스트 안에 있으므로 게스트와 함께 사라진다. 랩 호스트에 남는 것은 libvirt · qemu · kubectl 과, 쓰이지 않는 호스트 쪽 certbot 이다.

/etc/letsencrypt/ 는 정책으로 남긴다. 한도 때문이 아니다 — Let's Encrypt 의 「같은 이름 조합에 주당 중복 5장」 제한은 가끔 재구축하는 정도로는 근처에도 못 간다.

남기는 까닭은 지금 재발급이 되는지를 모르기 때문이다. 이 실험대의 이름 셋은 tailnet 주소를 가리키고, 100.64.0.0/10 은 CGNAT(Carrier-Grade NAT, 통신사 공용 주소 변환)용 예약 대역이라 공개 인터넷에서 라우팅되지 않는다. HTTP-01 검증은 Let's Encrypt 가 우리 서버로 들어오는 방식이므로 그 주소로는 검증이 성립하지 않는다. 지금 설정이 DNS-01 이면 지우고 다시 받으면 끝이고, HTTP-01 이면 검증 방식부터 손봐야 한다. 어느 쪽인지는 certbot 설정을 읽는 열린 물음이 한 줄로 닫는다.

§204 는 HTTP-01 인 경우를 「못 한다」가 아니라 「일이 하나 생긴다」로 적었다. 최악이라도 A 레코드를 공인 IP 로 잠깐 돌려 받거나 DNS-01 로 전환하면 되고, 다만 재구축을 시작하자마자 그 일부터 하게 된다. 그래서 이 정책은 「어느 쪽인지 모르는 채로는 지우지 않는다」가 전부다.

그래도 지워야 한다면 먼저 백업한다.

sudo tar czf ~/letsencrypt-backup-$(date +%Y%m%d-%H%M%S).tgz -C /etc letsencrypt

복원은 반대로 한 줄이다. {{STAMP}} 는 위 명령이 만든 파일 이름에 찍힌 시각을 그대로 옮겨 넣는다.

sudo tar xzf ~/letsencrypt-backup-{{STAMP}}.tgz -C /etc

구성 값

지우는 대상은 게스트 셋과 그 셋의 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) 남는다 다음 오버레이의 바닥
패키지 (libvirt · qemu · nginx · certbot · kubectl) 남는다 재설치가 무의미
~/workspace/cloud/kc-lab-{1,2}.yaml 남는다 키와 비밀번호가 들어 있다
~/.ssh/configkc-lab-* 항목 남는다 재구축해도 IP 가 같다
libvirt default 네트워크 정의 남는다 예약만 지웠다
/etc/letsencrypt/ 남긴다 (정책) 재발급이 되는지를 아직 모른다
게스트 디스크 · 시드 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 에 이 호스트에서 실제로 돌려 받은 출력이고, 그 뒤에 다시 돌려 검증하지는 않았다. 다시 돌리면 실험대가 없어지기 때문이다.