--- kind: SETUP slug: tear-down-the-lab-and-know-what-survives title: 실험대를 철거하고 무엇이 남는지 확인한다 topic: lab-environment-build topicName: 실험대 환경 구성 project: virtualization status: 게시 전 pinnedVersions: - name: libvirt version: 12.7.0 - name: QEMU version: 11.1.1 - name: 커널 version: 7.2.2-arch1-1 - name: 호스트 version: Arch Linux · i5-1135G7 · RAM 11,648MiB - name: 게스트 version: Debian 12 genericcloud source: - final/document.md#202-철거-실제-출력-전문 - final/document.md#204-재구축할-때-무엇이-남아-있나 - final/document.md#195-이-부의-출처와-범위 sourceRevision: 9465582b5d1630eb4ae7c4e078021486919bf6b6 --- # 실험대를 철거하고 무엇이 남는지 확인한다 게스트 세 대와 DHCP 예약을 지우고 무엇이 남았는지까지 확인하는 절차다. 세우는 가이드에는 이 부분이 없어서 2026-09-10 에 직접 돌리며 명령과 출력을 적었다. 되돌리는 명령은 이 절차에 없다. ## 관계 - **cloud-init 시드로 게스트 세 대를 만들고 SSH 가 키로 붙게 한다** 여기서 지우는 것을 그 절차가 만들고, 다시 세울 때 돌아갈 곳도 거기다. - **실험대를 껐다 켜고 게스트 메모리를 다시 나눈다** 워크로드를 정상으로 내리고 싶으면 그 편의 종료 순서를 먼저 돌고 온다. 여기서는 지우는 것이 목적이라 전원을 뽑는다. - **5120MB 를 줬는데 353MB 를 쓰고 있었다 — 선언한 양과 실제로 드는 양** 회수된 디스크 3.1GB 를 읽는 방법이 그 기록과 같다. 선언한 크기와 실제로 차지한 크기는 다른 값이다. - **이 실험대의 certbot 은 HTTP-01 로 받고 있는가 DNS-01 로 받고 있는가** 인증서를 지우지 않고 남기는 까닭이 이 물음이 안 닫혔기 때문이다. 답이 나오면 정책이 바뀐다. - **가이드대로 다시 쳐서 이 실험대가 같은 상태로 서는가** 철거는 그 물음을 재기 위한 전제라, 지우고 다시 세워 봐야 답이 나온다. - **도구가 낸 출력은 대상의 상태가 아니다 — 그 명령이 무엇을 세는지부터 가른다** `virbr0` 가 `DOWN` 으로 보이는 것을 고장으로 읽지 않는 근거가 그 기준이다. ## 본문 ## 읽기 전에 — 어디서 치는가 전부 `[lab host]` 다. 게스트를 통째로 지우는 절차라 게스트 안에서 칠 명령이 하나도 없다. 조회와 삭제뿐이라 편집기를 여는 곳도 없고 CLI 를 그대로 쓴다. ## 되돌리기가 없다 `virsh undefine --remove-all-storage` 는 도메인 정의와 오버레이 디스크와 시드 ISO 를 한 번에 지운다. 지운 뒤에 되살리는 명령이 libvirt 에 없다. 실험대를 다시 쓰려면 `base.qcow2` 위에 오버레이를 새로 만들고 시드 ISO 를 다시 구워야 하고, 그 절차는 게스트 세 대를 만드는 편이 받는다. 지우기 전에 알아 둘 것은 이 절차가 건드리지 않는 쪽이다. `base.qcow2` 와 `~/workspace/cloud/kc-lab-{1,2}.yaml` 과 `~/.ssh/config` 의 `kc-lab-*` 항목은 그 자체로 살아 있어서 재구축 때 다시 쓴다. 무엇이 어느 쪽인지는 아래 표가 아홉 행으로 가른다. ## 지우기 전에 먼저 본다 **무엇을 확인하는가** — 지금 무엇이 있는지. 철거 뒤와 견줄 값을 여기서 받아 둔다. ```bash label="[lab host] 도메인 · 볼륨 · 예약 · 디스크 사용량을 한 번에 본다" virsh list --all virsh vol-list default virsh net-dumpxml default | grep -A5 dhcp df -h / ``` **어디를 봐야 하는가** — 네 값이다. 도메인이 몇 대인가, 볼륨이 몇 개인가, `` 예약이 몇 줄인가, 루트 파일시스템이 얼마나 찼는가. 2026-09-10 의 이 호스트에서는 도메인 3 대에 볼륨 7 개, 예약 3 줄, `df -h /` 가 11G 였다. **이 결과가 의미하는 것** — 이 네 값이 철거의 성공 판정 기준이 된다. 여기를 건너뛰면 지운 뒤에 「원래 몇 개였지」를 되짚을 방법이 없다. 2026-09-17 에 다시 세운 실험대에서 같은 네 줄을 쳤다(observed). 도메인 3 대에 볼륨 7 개, 예약 3 줄까지는 2026-09-10 과 같고 **디스크만 11G 가 아니라 18G 다.** 그사이에 k3s 와 컨테이너 이미지가 쌓였다. 그래서 이 네 값은 「이 호스트의 정답」이 아니라 **지금 이 실행에서 받아 두는 대조군**이다 — 위에 실은 숫자와 다르다고 잘못된 것이 아니다. ```text label="2026-09-17 의 네 값" 도메인 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. 게스트 세 대를 지운다 **목적** — 도메인 정의와 그 게스트가 쓰던 디스크를 함께 없앤다. 세 게스트의 전원을 뽑고 정의와 디스크를 지운 뒤, 무엇이 없어졌는지 바로 센다. ```bash label="[lab host] ① 게스트 셋을 전원부터 뽑고 정의와 디스크를 함께 지운다" for v in kc-lab-edge kc-lab-2 kc-lab-1; do virsh destroy "$v" virsh undefine "$v" --remove-all-storage done ``` ```bash label="[lab host] ② 남은 도메인과 볼륨을 센다" virsh list --all virsh vol-list default ``` **예상 결과** — 게스트 한 대마다 네 줄이 나온다. 아래는 `kc-lab-edge` 한 대분이다(observed). ```text label="[lab host] ① 의 출력 — 한 대분" 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` 네트워크에서 게스트 주소 예약을 빼고 동적 대역만 남긴다. 세 예약을 값 그대로 지운 뒤 네트워크 정의에 무엇이 남았는지 본다. 삭제할 때도 `mac` 과 `name` 과 `ip` 세 속성을 다 준다. ```bash label="[lab host] ① 예약 세 줄을 값 그대로 지운다" virsh net-update default delete ip-dhcp-host \ "" \ --live --config virsh net-update default delete ip-dhcp-host \ "" \ --live --config virsh net-update default delete ip-dhcp-host \ "" \ --live --config ``` ```bash label="[lab host] ② 예약이 빠지고 동적 대역만 남았는지 본다" virsh net-dumpxml default | grep -E "host mac|range start" ``` **예상 결과** — ① 은 삭제마다 `Updated network default persistent config and live state` 를 낸다. ② 에는 `` 한 줄만 남는다. ```text label="[lab host] ② 의 출력 — 동적 대역만 남았다" ``` **왜 필요한가** — 예약을 남겨 두면 다음 구축에서 예약이 두 벌이 되거나, 넣으려 할 때 이미 있다고 거부당한다. `--live` 와 `--config` 를 둘 다 주는 까닭도 넣을 때와 같다. `--live` 만 주면 재부팅에 예약이 되살아나고 `--config` 만 주면 지금 돌고 있는 dnsmasq 가 아직 예약을 들고 있다. **문제가 생기면** — 속성이 하나라도 비면 이렇게 거부된다. ```text label="[lab host] 이름이 빈 채로 삭제를 시도했을 때" 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` 로 지운다 — 인증서는 그 안에서 같이 사라진다. 두 기계를 나란히 열어 보면 이렇다. ```bash label="[lab host] 어느 쪽에 무엇이 있나" 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` | **한다** | 그래서 아래 백업 명령은 **틀린 기계를 묶는다.** 묶어야 할 것은 게스트 쪽이다. ```bash label="[lab host] 실제로 서빙하는 인증서를 묶는다" 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 ~/ ``` **「철거해도 남는 것」 표의 패키지 줄도 같은 이유로 틀렸다.** 그 줄은 `nginx` 와 `certbot` 을 남는 쪽에 적어 두었는데, 둘 다 게스트 안에 있으므로 게스트와 함께 사라진다. 랩 호스트에 남는 것은 `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 로 전환하면 되고, 다만 재구축을 시작하자마자 그 일부터 하게 된다. 그래서 이 정책은 「어느 쪽인지 모르는 채로는 지우지 않는다」가 전부다. 그래도 지워야 한다면 먼저 백업한다. ```bash label="[lab host] 인증서를 통째로 묶어 둔다" sudo tar czf ~/letsencrypt-backup-$(date +%Y%m%d-%H%M%S).tgz -C /etc letsencrypt ``` 복원은 반대로 한 줄이다. `{{STAMP}}` 는 위 명령이 만든 파일 이름에 찍힌 시각을 그대로 옮겨 넣는다. ```bash label="[lab host] 묶어 둔 인증서를 되돌린다" 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` 목록이 그 순서로 적혀 있다. ## 확인 방법 **무엇을 확인하는가** — 철거 전에 받아 둔 네 값이 전부 내려갔는지. ```bash label="[lab host] 철거 뒤 같은 네 값을 다시 본다" 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` 로 재 보니 엣지가 가장 작다. 선언한 크기와 실제로 먹는 양이 크게 다르고, 오버레이라 쓴 만큼만 먹는다. ```bash label="[lab host] 볼륨이 실제로 먹는 양을 본다" virsh vol-info --pool default kc-lab-edge.qcow2 ``` ```text 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 안팎」은 과대 추정이다. **다시 철거할 때는 역산하지 말고 지우기 전에 이 명령으로 세 볼륨을 직접 잰다.** `virbr0` 가 `DOWN` 인 것은 고장이 아니다. 브리지에 붙은 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/config` 의 `kc-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 에 이 호스트에서 실제로 돌려 받은 출력이고, 그 뒤에 다시 돌려 검증하지는 않았다. 다시 돌리면 실험대가 없어지기 때문이다.