Files
document-haness/docs/virtualization/tech-log-studio/lab-environment-build/setup/setup-prepare-the-lab-host-for-virtualization.md
T
DongHyeonkaandClaude Opus 5 024362d096 fix(setup): 실험대를 새로 세워 setup 35편을 밟고 어긋난 명령과 결과를 고친다
기반 가이드 7단계로 실험대를 철거하고 다시 세운 뒤 virtualization setup 9편과
keycloak-session-store 26편을 순서대로 밟았다. 24편은 끝까지, 11편은 되는 데까지
밟았고 밟은 범위를 편마다 적었다.

명령이 못 도는 것을 고쳤다.

- kubectl 을 `kc-lab-1` 에서 치라고 적었는데 그 기계에 kubeconfig 가 없다.
  라벨 639개와 각 편의 「어디서 치는가」를 `[lab host]` 로 옮겼다
- `-o custom-columns=…[0]…` 이 zsh 에서 글로브로 읽혀 안 돈다. 28곳에 따옴표
- busybox `sed` 가 끝 개행을 안 붙여 A-3 의 측정이 언제나 0 이었다
- `--token-file ~/node-token` 뒤에 그 파일을 지우면 k3s agent 가 재부팅을
  못 견딘다. `/etc/rancher/node-token` 으로 옮기는 처방을 재서 넣었다
- 게스트에 없는 도구를 전제로 한 명령 넷 — `conntrack`·`dig`·`strings`·`nginx -v`
- `echo` 와 JWT 헤더가 `"이름" : [ 값 ]` 으로 찍는데 문서는 공백 없이 옮겨 적어
  그 실측으로 만든 grep·sed 가 한 줄도 못 잡는다
- B-0 이 `directAccessGrantsEnabled` 와 계정 완성을 빠뜨려 B-3 이 못 돈다
- D-4·D-4a 가 `test-server` 와 `certbot-renew.*` 를 가리키는데 실제로는
  `kc-lab-edge` 의 `certbot.service` 다
- `virsh setmaxmem --config` 를 `dominfo` 로 판정하면 틀린다. `--inactive` 로
- `LIBVIRT_DEFAULT_URI` 를 rc 에만 넣으면 `ssh host '명령'` 에서 안 먹는다

결과가 조건부인 것을 갈랐다.

- readiness 는 즉시 안 뒤집힌다. A-1·A-2 의 60초 창을 적었다
- 03 의 층 ②③ `301` 은 04 이후의 값이고 그 단계에서는 `404` 다
- A-0 의 로그 필터를 요청 직후에 치면 정반대 결론이 나온다
- A-5 의 한 방향 차단은 잠깐 `1` 이었다 `2` 로 돌아온다

증거는 두 프로젝트의 `evidence/raw/` 에 99벌을 README 와 함께 남겼다. 비밀은
길이만 적었고 화면에 찍힌 토큰은 가렸다.

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

417 lines
24 KiB
Markdown

---
id: 7c66a553-0008-4294-a27a-687bd1bda0c1
kind: SETUP
slug: prepare-the-lab-host-for-virtualization
title: lab host 에 가상화 패키지를 깔고 virsh 가 sudo 없이 돌게 만든다
topic: lab-environment-build
topicName: 실험대 환경 구성
project: virtualization
status: 게시 전
studio: "https://hyeonworks.com/studio/documents/7c66a553-0008-4294-a27a-687bd1bda0c1/edit"
pinnedVersions:
- name: libvirt
version: 12.7.0
- name: QEMU
version: 11.1.1
source:
- final/document.md#186-단계-00-lab-host-가상화-준비
- final/document.md#185-가이드-묶음이-스스로-정한-규약
- final/document.md#184-이-부의-출처와-범위
sourceRevision: 9465582b5d1630eb4ae7c4e078021486919bf6b6
---
# lab host 에 가상화 패키지를 깔고 virsh 가 sudo 없이 돌게 만든다
물리 기계 한 대를 게스트를 올릴 수 있는 상태로 바꾸는 절차다. 가상화 패키지 넷을 깔고, libvirt 소켓을 켜고, 사용자를 `libvirt` 그룹에 넣고, 연결 URI 를 시스템 단위로 고정한다. 끝나면 `virsh list --all``sudo` 없이 통과한다.
## 관계
- **cloud-init 시드로 게스트 세 대를 만들고 SSH 가 키로 붙게 한다**
다음 단계이고, 여기서 고정한 `qemu:///system``default` 네트워크 위에서 `virt-install` 이 돈다.
- **qcow2 파일 한 장이 담는 것 — 매핑표와 데이터 클러스터가 같은 파일 안에 있고, 파일 밖을 가리키는 것은 백킹 파일 경로 하나다**
여기서 깐 qemu 가 다루는 디스크 형식을 그 글이 설명한다. 다음 단계의 오버레이가 성립하는 근거도 거기 있다.
- **도구가 낸 출력은 대상의 상태가 아니다 — 그 명령이 무엇을 세는지부터 가른다**
`virsh list` 의 빈 표와 `net-list` 에서 `--all` 을 뺐을 때 안 보이는 네트워크를 어떻게 읽어야 하는지 그 기준이 정한다.
- **단계별 구축 문서는 작성 시점이 아니라 실행 순서로 검증한다**
이 절차의 만드는 명령이 왜 재실행으로 검증되지 않은 채 남았는지를 그 기준이 가른다.
- **가이드대로 다시 쳐서 이 실험대가 같은 상태로 서는가**
같은 명령을 다시 쳐서 이 호스트가 같은 상태가 되는지는 아직 재지 않았다.
## 본문
<!-- body:start -->
## 읽기 전에 — 어디서 치는가
명령은 전부 `[lab host]` 에서 친다. 게스트가 아직 없어서 다른 셸 표시가 나오지 않는다.
`sudo` 가 붙는 곳은 둘이다. 패키지를 깔 때와 systemd 유닛을 만질 때이고, `virsh` 는 3번이 끝나면 `sudo` 없이 돈다. 그 뒤로 `sudo virsh` 를 치면 root 환경으로 돌아 사용자 홈의 설정을 못 보므로, 붙이지 않는 쪽이 맞는 형태다.
| 표시 | 어느 기계 | 어떻게 들어가나 |
|---|---|---|
| `[lab host]` | `test-server`. `virsh` 가 도는 곳 | `ssh test-server` |
편집기를 여는 곳은 한 군데다. 4번에서 `~/.bashrc` 를 연다. 나머지는 전부 조회·설치·유닛 조작이라 운영자가 평소에 치는 CLI 를 그대로 쓴다.
## 이 단계가 세우는 것
가이드 00 의 「이 단계가 끝나면」은 한 줄이다.
> `virsh list` 가 sudo 없이 돌고, VM 을 만들 수 있는 상태가 된다.
그 한 줄이 요구하는 것을 풀면 여덟이다.
| 무엇 | 값 |
|---|---|
| 저장소 | `~/workspace/keycloak-pattern` — 뒤 단계의 `deploy/...` 상대경로가 이 디렉터리 기준이다 |
| 패키지 (Arch) | `qemu-full` · `libvirt` · `virt-install` · `dnsmasq` |
| 패키지 (Debian/Ubuntu) | `qemu-system-x86` · `libvirt-daemon-system` · `virtinst` · `cloud-image-utils` |
| 판 번호 | libvirt `12.7.0` · `QEMU emulator version 11.1.1` |
| 유닛 | `libvirtd.socket``.service` 가 아니다 |
| 그룹 | `donghyeon libvirt wheel` |
| 연결 URI | `LIBVIRT_DEFAULT_URI=qemu:///system` |
| 가상 네트워크 | `default` / `active` / autostart `yes``virbr0` · `192.168.122.0/24` |
네 패키지가 하는 일이 서로 다르다.
| 무엇 | 하는 일 |
|---|---|
| qemu | 실제로 가상 기계를 돌리는 것 |
| libvirt | qemu 를 관리하는 층 (`virsh` 가 여기 붙는다) |
| virt-install | VM 을 만드는 명령 |
| dnsmasq | 가상 네트워크의 DHCP·DNS |
## 전제와 되돌리기
전제는 가이드가 두 줄로 적었다 — 물리 기계 한 대가 있고, 배포판은 상관없다. 이 실험대는 Arch Linux 로 세웠고 배포판이 다르면 패키지 이름만 달라진다. 앞 단계가 없으므로 이 절차는 다른 무엇도 전제하지 않는다.
**되돌리는 절차는 원본 가이드 00 에 없다**(unknown). 가이드 7편 가운데 되돌리기를 적은 편은 단계 03 하나다. 이 단계가 호스트에 남기는 것은 다섯이다.
| 남는 것 | 어디에 |
|---|---|
| 패키지 넷 | 배포판 패키지 데이터베이스 |
| `libvirt` 보조 그룹 | 사용자 계정 |
| `libvirtd.socket` 활성화 | systemd |
| `export` 한 줄 | `~/.bashrc` |
| `default` 네트워크의 autostart | libvirt 설정 |
무엇을 어떤 순서로 걷어내는지는 가이드에 적혀 있지 않고 이 실험대도 걷어내 본 적이 없다. 여기에 되돌리는 명령을 적으려면 지어내야 하므로 적지 않는다.
## 세우기 전에 먼저 본다
두 확인은 아무것도 바꾸지 않는다. 기계를 켤 때 먼저 도는 펌웨어와 그 설정 화면을 BIOS(Basic Input/Output System)라고 부르는데, 거기서 가상화가 꺼져 있으면 뒤가 전부 헛일이므로 먼저 본다.
### 확인 ① CPU 가 하드웨어 가상화 확장을 내놓고 있는가
```bash label="[lab host] CPU 플래그에서 vmx 나 svm 을 찾는다"
grep -Eo 'vmx|svm' /proc/cpuinfo | head -1
```
**어디를 봐야 하는가** — 출력 한 줄이 전부다. `vmx`(Intel) 또는 `svm`(AMD) 중 하나가 찍히는가, 아니면 아무것도 안 찍히는가.
**이 결과가 의미하는 것** — 찍혔으면 이 호스트에서 KVM 을 쓸 수 있다. 빈 출력은 CPU 가 못 한다는 뜻이 아니라 대개 BIOS 에서 꺼져 있다는 뜻이다. 재부팅해 Intel VT-x 나 AMD-V 를 켜고 다시 잰다. 여기서 막히면 뒤의 어떤 단계도 의미가 없으므로 진행하지 않는다.
### 확인 ② 커널이 그 확장을 실제로 잡고 있는가
```bash label="[lab host] 올라온 KVM 모듈을 본다"
lsmod | grep kvm
```
출력은 이 실험대에서 캡처해 두지 않았다(unknown). 가이드도 줄 모양만 적었다.
```text
kvm_intel ...
kvm ...
```
**어디를 봐야 하는가** — 왼쪽 첫 열의 모듈 이름 두 개. 벤더 모듈(`kvm_intel` 또는 `kvm_amd`)과 공용 `kvm` 이 둘 다 있어야 한다. 셋째 열은 이 모듈을 쓰고 있는 쪽의 수라서, 아직 VM 이 없으면 0 으로 나온다.
**이 결과가 의미하는 것** — 두 줄이면 커널이 하드웨어 가상화를 쓸 준비를 마쳤고 `virt-install` 이 KVM 가속으로 뜬다. `kvm` 만 있고 벤더 모듈이 없으면 확인 ① 의 BIOS 설정이 커널까지 안 넘어왔다. 아무것도 없으면 확인 ① 로 돌아간다. 모듈을 직접 올려 보면 거부 사유가 그대로 나온다.
```bash label="[lab host] 벤더 모듈을 손으로 올려 거부 사유를 받는다"
sudo modprobe kvm_intel
```
**왜 이걸 먼저 보나** — KVM 없이도 QEMU 는 돌지만 소프트웨어 에뮬레이션이 되어 수십 배 느려진다. 게스트가 뜨긴 뜨는데 느리다면 대개 여기서 갈린다. 그 상태로 게스트 셋을 올리면 원인을 게스트 안에서 찾게 된다.
## 실행 절차
### 1. 저장소를 lab host 에 받는다
**목적** — 뒤 단계가 `deploy/` 아래 파일을 쓴다. 단계 05·06 의 `kubectl apply -f deploy/lab/k8s/...` 가 그것이고, 경로는 저장소 루트 기준이다.
① 이미 세웠다 철거한 적이 있으면 무엇이 남아 있는지 먼저 본다.
```bash label="[lab host] ① 작업 디렉터리에 무엇이 있는지 본다"
ls ~/workspace
```
② 받는다.
```bash label="[lab host] ② 저장소를 받고 그 안으로 들어간다"
git clone https://git.learn.hyeonworks.com/donghyeon.kang/keycloak-pattern.git ~/workspace/keycloak-pattern
cd ~/workspace/keycloak-pattern
```
**예상 결과** — 매니페스트 두 장이 보인다.
```bash label="[lab host] ③ 뒤 단계가 쓸 매니페스트를 확인한다"
ls deploy/lab/k8s/
```
`keycloak-cluster.yaml` 과 `observability.yaml` 이 보이는가만 본다.
**왜 필요한가** — 이후 단계에서 `deploy/...` 로 시작하는 상대경로가 나오면 전부 이 디렉터리 안에서 친다. lab host 에 저장소가 없으면 `cp: cannot stat` 이나 `error: the path ... does not exist` 로 막히고, 이 실험대가 실제로 그 형태로 겪었다. 한 번 세웠다 철거했으면 이 디렉터리가 없을 수 있다. 철거는 VM 과 디스크와 네트워크만 지우고 저장소는 각자 관리라, `~/workspace` 에 cloud-init 시드만 남아 있는 상태가 흔하다. ① 을 먼저 치는 까닭이 여기 있다.
**문제가 생기면** — `ls` 가 아무것도 못 찾으면 클론이 실패한 것이니 ② 를 다시 친다. 클론은 됐는데 `deploy/lab/k8s/` 가 없으면 다른 브랜치를 받았다.
### 2. 가상화 패키지를 깐다
**목적** — `virsh` 와 `virt-install` 을 PATH 에 올리고 가상 네트워크의 DHCP 를 준비한다. PATH 는 셸이 실행 파일을 찾아 다니는 디렉터리 목록을 말한다.
```bash label="[lab host] ① Arch 에서 네 패키지를 깐다"
sudo pacman -S --needed qemu-full libvirt virt-install dnsmasq
```
Debian 이나 Ubuntu 면 이름이 다르다. 이 실험대는 Arch 로 세웠고 아래 줄은 가이드가 참고로 적어 둔 것이다(external).
```bash label="[lab host] Debian/Ubuntu 라면 이름이 이렇게 바뀐다"
sudo apt install qemu-system-x86 libvirt-daemon-system virtinst cloud-image-utils
```
```bash label="[lab host] ② 두 실행 파일이 PATH 에 들어왔는지 본다"
virsh --version
qemu-system-x86_64 --version
```
**예상 결과** — 판 번호 두 줄. 이 실험대는 libvirt `12.7.0` 과 `QEMU emulator version 11.1.1` 이었다(observed).
**왜 필요한가** — 여기서 나오는 libvirt 판 번호가 뒤의 옵션 이름을 가른다. 훨씬 낮은 판이면 `virt-install --cloud-init` 같은 옵션의 동작이 달라질 수 있으니, 다음 단계에서 막힐 때 이 번호를 같이 본다.
**문제가 생기면** — `command not found` 면 패키지가 안 깔렸다. 번호가 나오면 깔렸다.
### 3. libvirt 를 띄우고 권한을 받는다
**목적** — 지금 이 셸이 `sudo` 없이 libvirt 에 붙게 한다.
```bash label="[lab host] ① 소켓 유닛을 켜고 부팅에도 켜지게 한다"
sudo systemctl enable --now libvirtd.socket
```
```bash label="[lab host] ② 지금 사용자를 libvirt 보조 그룹에 넣는다"
sudo usermod -aG libvirt "$USER"
```
③ 로그아웃했다 다시 들어온다. 보조 그룹은 로그인할 때 정해지므로 `usermod` 만으로는 지금 셸에 반영되지 않는다.
```bash label="[lab host] ④ 그룹과 권한을 함께 확인한다"
groups # libvirt 가 보여야 한다
virsh list --all # sudo 없이 돌아야 한다
```
**예상 결과** — `groups` 가 이렇게 나왔다(observed).
```text
donghyeon libvirt wheel
```
`virsh list --all` 은 머리글만 있는 빈 표를 내놓는다. 아직 VM 을 안 만들었으므로 표가 비어 있는 쪽이 정상이고, 봐야 할 것은 표의 내용이 아니라 명령이 오류 없이 통과했는가다.
**왜 필요한가** — `libvirtd.service` 가 아니라 `.socket` 을 켠다. 소켓 활성화라 데몬이 미리 떠 있지 않아도 `virsh` 가 접속하는 순간 systemd 가 띄우고, 자원을 아끼며, 데몬을 재시작해도 클라이언트가 끊기지 않는다.
**문제가 생기면** — `groups` 에 `libvirt` 가 없으면 `usermod` 는 됐지만 지금 로그인 세션이 옛 그룹 목록을 들고 있다. `usermod -aG` 가 고치는 것은 `/etc/group` 파일이다. 프로세스의 그룹 목록은 로그인할 때 한 번 읽혀 고정되므로 이미 떠 있는 셸에는 소급 적용되지 않는다. 두 곳을 나란히 보면 그 상태가 그대로 드러난다.
```bash label="[lab host] 셸이 들고 있는 목록과 파일의 내용을 나란히 본다"
id # 현재 셸이 들고 있는 그룹 (여기 libvirt 가 보여야 함)
getent group libvirt # /etc/group 의 실제 내용 (이쪽엔 바로 반영됨)
```
두 결과가 다르면 재로그인이 필요하다는 뜻이다. 급하면 `newgrp libvirt` 로 그 셸만 갱신한다.
`groups` 에는 있는데 `virsh` 가 `Permission denied` 를 내면 그룹이 아니라 소켓 문제이므로 유닛 상태를 본다.
```bash label="[lab host] 그룹이 맞는데 거절당할 때 유닛부터 본다"
systemctl status libvirtd.socket
```
### 4. 연결 URI 를 시스템 단위로 고정한다
**목적** — `virsh` 가 VM 을 만들 곳과 같은 하이퍼바이저를 보게 한다. `virsh` 는 기본으로 사용자 단위인 `qemu:///session` 에 붙는데 VM 은 시스템 단위인 `qemu:///system` 에 만들어야 한다. 이걸 안 맞추면 만든 VM 이 목록에 안 보인다.
① 설정 파일을 연다.
```bash label="[lab host] ① 로그인 셸 설정을 편집기로 연다"
nano ~/.bashrc
```
② 파일 끝에 이 줄을 더한다. 저장은 `Ctrl+O` 다음 `Enter`, 나가기는 `Ctrl+X`.
```text
export LIBVIRT_DEFAULT_URI=qemu:///system
```
③ 저장한 설정을 현재 셸에 반영하고 확인한다.
```bash label="[lab host] ③ 지금 셸에 반영하고 어느 하이퍼바이저를 보는지 본다"
source ~/.bashrc
virsh uri
```
**예상 결과**(observed)
```text
qemu:///system
```
끝의 한 낱말만 본다. `system` 인가 `session` 인가.
**왜 필요한가** — `qemu:///system` 이면 뒤에서 만들 VM 과 지금 `virsh` 가 같은 곳을 본다. `qemu:///session` 이면 사용자 단위 하이퍼바이저를 보고 있어, VM 은 만들어졌는데 `virsh list` 에 안 나오는 상태가 된다.
이 실험대는 같은 줄을 편집기 없이 넣었다(observed).
```bash label="[lab host] 이 실험대가 실제로 친 형태"
echo 'export LIBVIRT_DEFAULT_URI=qemu:///system' >> ~/.bashrc
virsh uri
```
**rc 파일에만 넣으면 `ssh lab-host '명령'` 에서는 안 먹는다.** 그 형태는 비대화형 셸이라 `.bashrc`·`.zshrc` 를 안 읽는다. 뒤의 편들이 `ssh test-server 'virsh …'` 처럼 한 줄로 치는 일이 많으므로 여기서 갈라 둔다. 2026-09-17 에 확인했다(observed).
```text
ssh 로 한 줄 LIBVIRT_DEFAULT_URI=[(비어 있음)]
대화형 셸 LIBVIRT_DEFAULT_URI=qemu:///system
```
**셸과 무관하게 고정하려면 libvirt 자기 설정에 넣는다.** 이 실험대에는 그 파일도 있었다(observed).
```bash label="[lab host] 셸을 안 타는 자리에 고정한다"
mkdir -p ~/.config/libvirt
echo 'uri_default = "qemu:///system"' >> ~/.config/libvirt/libvirt.conf
virsh uri
```
```text
/home/donghyeon/.config/libvirt/libvirt.conf:uri_default = "qemu:///system"
qemu:///system
```
**둘 중 하나만 있으면 어디선가 어긋난다.** rc 만 있으면 `ssh` 한 줄에서 `qemu:///session` 을 보게 되고 — 그쪽에는 VM 이 없으므로 **목록이 빈 채로 나와 「VM 이 죽었다」로 읽힌다** — libvirt 설정만 있으면 대화형 셸의 `echo $LIBVIRT_DEFAULT_URI` 가 비어서 「설정이 안 됐다」로 읽힌다. 둘 다 넣어 두는 편이 낫다.
따라 하는 사람에게는 편집기 쪽이 맞다. `echo >>` 는 같은 가이드를 두 번 따라 하면 같은 줄을 한 번 더 붙이고, 파일에 이미 무엇이 들어 있는지도 보여 주지 않는다. 파일을 열면 둘 다 해결된다.
**문제가 생기면** — `.bashrc` 에 넣은 것은 새로 여는 셸에만 적용된다. 지금 셸에서 값이 안 바뀌었으면 `source ~/.bashrc` 를 치거나 새 셸을 연다.
`sudo virsh` 와 그냥 `virsh` 를 섞어 치지 않는다. 이 문제는 한 번 고쳐도 반복해서 재발한다. `sudo` 는 환경 변수를 물려주지 않아 여기서 넣은 줄이 전달되지 않는데, root 로 도니 결과적으로 `qemu:///system` 이 되어 그쪽도 동작한다. 둘 다 되기 때문에 섞어 쓰면 어떤 명령은 되고 어떤 명령은 `Network not found: no network with matching name 'default'` 가 나온다. 네트워크가 없어서가 아니라 두 명령이 서로 다른 인스턴스에 물어본 것이다.
### 5. 기본 네트워크를 켠다
**목적** — VM 이 붙을 `virbr0` 와 `192.168.122.0/24` 를 지금도 재부팅 뒤에도 있게 한다.
```bash label="[lab host] ① 가상 네트워크가 살아 있는지 본다"
virsh net-list --all
```
**예상 결과**(observed)
```text
Name State Autostart Persistent
--------------------------------------------
default active yes yes
```
② `default` 행의 State 와 Autostart 두 칸 중 하나라도 어긋나면 아래 두 줄로 맞춘다.
```bash label="[lab host] ② 꺼져 있거나 autostart 가 no 일 때만 친다"
virsh net-start default
virsh net-autostart default
```
두 줄을 `&&` 로 잇지 않는다. `virsh net-start default` 는 이미 `active` 면 `error: network is already active` 로 실패한다. `A && B` 는 A 가 성공했을 때만 B 를 실행하므로, 두 번째로 칠 때는 `net-autostart` 가 아예 돌지 않는다. 화면에는 둘 다 실패한 것처럼 보이지만 앞선 실행에서 이미 목적을 이룬 상태다. 이 가이드를 두 번 이상 따라 한다면 `;` 로 잇고 앞엣것의 실패를 삼킨다.
```bash label="[lab host] 두 번 이상 따라 할 때 쓰는 형태"
virsh net-start default 2>/dev/null; virsh net-autostart default
```
**왜 필요한가** — `active` 이면서 autostart 가 `yes` 면 지금도, 호스트를 재부팅한 뒤에도 `virbr0` 와 `192.168.122.0/24` 가 있다. `inactive` 면 VM 을 만들어도 DHCP 가 없어 IP 를 못 받는다. autostart 가 `no` 면 지금은 되고 호스트를 재부팅한 다음 게스트의 SSH 가 전부 실패하는데, 그때 원인을 게스트에서 찾게 된다.
**문제가 생기면** — `--all` 을 주는 까닭이 여기 있다. 빼면 `inactive` 인 네트워크는 아예 목록에 안 나와서 없는 것과 꺼진 것을 구분할 수 없다. `net-start default` 가 `already active` 말고 다른 사유로 실패하면 dnsmasq 가 안 깔린 것이므로 2번으로 돌아간다.
## 구성 값
| 무엇이 서나 | 어떤 이름과 값으로 |
|---|---|
| 저장소 | `~/workspace/keycloak-pattern` |
| 패키지 (Arch) | `qemu-full` · `libvirt` · `virt-install` · `dnsmasq` |
| 유닛 | `libvirtd.socket` |
| 보조 그룹 | `libvirt` |
| 연결 URI | `qemu:///system` — `~/.bashrc` 의 `LIBVIRT_DEFAULT_URI` |
| 가상 네트워크 | `default` · `active` · autostart `yes` |
| 브리지와 대역 | `virbr0` · `192.168.122.0/24` |
대역이 `192.168.122.0/24` 로 굳으면 다음 단계의 DHCP 예약 세 줄과 그 뒤 모든 단계의 upstream 주소가 그 안에서 정해진다. 게스트 주소를 바꾸고 싶으면 여기서 바꾸는 것이지 게스트 안에서 바꾸는 것이 아니다.
## 끝났는지 판정한다
### 확인 ① virsh 가 sudo 없이 통과하는가
**무엇을 확인하는가** — 이 셸에서 VM 을 만들 수 있는 상태인지.
```bash label="[lab host] 권한과 연결과 네트워크를 차례로 본다"
virsh uri
virsh net-list --all
virsh list --all
```
**어디를 봐야 하는가** — 첫 줄이 `qemu:///system` 인가, 둘째에서 `default` 가 `active` 이고 autostart 가 `yes` 인가, 셋째가 `sudo` 없이 오류 없이 끝나는가.
**이 결과가 의미하는 것** — 셋이 다 통과하면 이 단계는 끝났다. `virsh list --all` 이 `sudo` 없이 오류 없이 끝나는 것이 이 단계의 통과 조건 전부이고, 표의 내용은 아직 볼 것이 없다. 빈 표를 「호스트에 아무것도 없다」로 읽지 않는다 — 지금은 만들지 않았으니 비어 있는 것이고, 다음 단계가 끝난 뒤에 같은 명령이 세 줄을 내놓는다.
### 확인 ② 그룹이 지금 셸에 반영됐는가
**무엇을 확인하는가** — `usermod` 가 파일에만 들어간 것인지, 지금 세션이 들고 있는 목록에도 들어간 것인지.
```bash label="[lab host] 지금 세션이 들고 있는 그룹 목록"
groups
```
**어디를 봐야 하는가** — 출력에 `libvirt` 가 끼어 있는가.
**이 결과가 의미하는 것** — 끼어 있으면 소켓에 붙을 권한이 지금 셸에 있다. 없는데 `virsh` 가 도는 일은 없으므로, 확인 ① 이 `Permission denied` 로 끝났다면 먼저 여기를 본다.
## 통과 조건을 한 번에 다시 본다
| 무엇 | 명령 | 통과 |
|---|---|---|
| 가상화 확장 | `grep -Eo 'vmx\|svm' /proc/cpuinfo \| head -1` | `vmx` 또는 `svm` 한 줄 |
| 그룹 | `groups` | `libvirt` 가 끼어 있다 |
| 연결 URI | `virsh uri` | `qemu:///system` |
| 네트워크 | `virsh net-list --all` | `default` 가 `active` · autostart `yes` |
| 빈 목록 | `virsh list --all` | sudo 없이 통과. 표가 비어 있어도 된다 |
다섯 칸이 다 맞으면 다음 단계로 넘어간다.
## 막히면
| 증상 | 원인 | 확인 |
|---|---|---|
| `virsh list` 에 permission denied | 그룹 반영 안 됨 | 로그아웃과 로그인을 했는가. `groups` 에 libvirt 가 있는가 |
| 만든 VM 이 목록에 없다 | URI 가 `session` | `virsh uri` |
| `sudo` 로는 되는데 그냥은 안 된다 | 세션 인스턴스를 보고 있다 | `virsh uri` |
| `net-start` 가 `already active` | 앞서 켜 두었다 | `virsh net-list --all` 의 State |
| VM 이 극단적으로 느리다 | KVM 미사용 | `lsmod \| grep kvm` · BIOS |
| `net-start default` 실패 | dnsmasq 없음 | 패키지 설치 확인 |
| `cp: cannot stat 'deploy/...'` | lab host 에 저장소가 없다 | `ls ~/workspace` |
## 무엇이 관측이고 무엇이 아닌가
- (observed) libvirt `12.7.0` 과 `QEMU emulator version 11.1.1`, `groups` 의 세 이름, `default` 네트워크가 `active` 이고 autostart 가 `yes` 인 것, `virsh uri` 가 내놓은 `qemu:///system`.
- (unknown) `lsmod | grep kvm` 의 출력은 캡처해 두지 않았다. 가이드도 줄 모양만 적고 값을 싣지 않았다.
- (unknown) 가이드의 실측 줄은 이 호스트가 16 코어 전부에서 지원한다고 적었는데 대상 환경 쪽은 논리 코어 8(i5-1135G7)로 적혀 있다. 두 값이 어긋나고 어느 쪽이 이 호스트의 값인지는 재지 않았다. 세 게스트의 vCPU 합이 5 라 8 에서도 16 에서도 CPU overcommit 이 아니므로 이 단계의 판정은 어느 쪽이어도 바뀌지 않는다.
- (unknown) 원본 가이드에 되돌리는 절차가 없다. 패키지와 그룹과 유닛과 `~/.bashrc` 와 네트워크 autostart 를 걷어내 본 적이 없다.
- (unknown) `sudo modprobe kvm_intel` 과 `systemctl status libvirtd.socket` 은 막혔을 때 치라고 가이드가 적어 둔 명령이고, 이 실험대에서는 막히지 않아 치지 않았다.
- (unknown) `nano ~/.bashrc` 로 여는 형태는 이 실험대가 치지 않았다. 이 실험대는 `echo >>` 로 넣었고, 편집기 쪽은 같은 상태에 닿는 형태로 적었다.
- (external) Debian 과 Ubuntu 의 패키지 이름은 가이드가 참고로 적어 둔 것이고 이 실험대는 Arch 로 세웠다.
- (external) 보조 그룹이 로그인 시점에 고정된다는 것, `virsh net-start` 가 이미 `active` 면 실패한다는 것, `sudo` 가 환경 변수를 물려주지 않는다는 것은 리눅스와 libvirt 의 동작이다. 이 실험대가 그 세 가지를 따로 재 보지는 않았다.
- (unknown) 만드는 명령은 구축할 때 친 것을 옮긴 것이라, 지금 다시 쳐도 같은 상태가 되는지는 확인되지 않았다.
<!-- body:end -->