Files
document-haness/docs/virtualization/tech-log-studio/lab-environment-build/setup/setup-power-cycle-the-lab-and-reallocate-guest-memory.md
T
DongHyeonkaandClaude Opus 5 2109f726fe feat(pipeline): keycloak-session-store 25편·virtualization 59편을 S3→S5→S6 으로 돌린다
기록 84편을 계약 에이전트로 다시 썼다. 기존 71편(kss 25 · virt 46)과, 계약에만
있고 안 쓰여 있던 새 글감 13편이다. 원장 84개를 열어 단계마다 스킬 영수증과 관문
종료 코드를 적었고 verify-pipeline-run.py 가 error 0 으로 닫는다.

SSOT 결함 둘을 고쳤다.

- kss 의 `약 58일` 이 반입 중 `약 59일` 로 바뀌어 있었다. 원 증거 파일이
  「남은 일수: 88일 … 실제 갱신까지 약 58일」로 산수를 직접 적는다. D-4a 쪽
  `약 59일` 은 강제 갱신 뒤(`VALID: 89 days`)라 맞는 값이라 그대로 뒀다.
- virt §198 의 `11.6GB` 는 §178 의 원 측정 `Mem: 11648`(MiB)과 어긋나는데
  원 가이드의 표기 그대로라 고치지 않고 쓰이는 자리에 대조를 적었다.

기록의 수치 오류 셋을 고쳤다 — CASE 요약의 「게스트 셋에 8240MB」(5120+3120 은
둘이다), k3s 편이 같은 것을 여섯·일곱·여덟로 세던 것, no-docker 편의 「셋을 더
든다」(§281 의 표는 네 행이고 디스크 행이 빠져 있었다).

계약을 셋 고쳤다.

- kss 의 sourceRepository 리비전이 cdac9b8 이었는데 그 커밋에는 docs/guides/**
  28개가 아예 없다. 9465582b 로 바꾸고, 반입한 바이트가 어느 커밋과도 같지 않다는
  것을 측정값과 함께 적었다 — 반입은 커밋이 아니라 그 시점의 작업 트리에서 떠 온
  것이다(kss 297/306 · virt 12/14 가 작업 트리와 같고, 200 커밋을 거슬러 전수
  대조했을 때 가장 가까운 커밋도 28개가 어긋났다).
- virt 계약이 「2026-09-11 재배분」이라고 적는데 SSOT 는 재배분 날짜를 적지 않고
  재배분 뒤 값은 이미 2026-09-10 측정에 찍혀 있다.
- kss 후보 대장이 지나친 절 아홉에 처분을 적었다(warn 9 → 0). 새 글감은 0건이고
  넷은 앵커가 h3 슬러그의 접두가 아니라 중간 토막이라 검사기가 못 본 것이었다.

style_profile.mjs 의 결함 둘을 고쳤다 — frontmatter 가 문장으로 세어져
(실측 398자짜리 「문장」 하나) 평균 길이를 기준 안으로 밀어 올리고 있었고,
engPerSent 의 분자는 목록을 포함한 글에서, 분모는 목록을 걷어낸 글에서 세고
있었다(Question 기록에서 11.94 → 3.86).

verify-pipeline.py 전 항목 PASS · error 0 · unittest 334건 OK.

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

12 KiB

kind, slug, title, topic, topicName, project, status, pinnedVersions, source, sourceRevision
kind slug title topic topicName project status pinnedVersions source sourceRevision
SETUP power-cycle-the-lab-and-reallocate-guest-memory 실험대를 껐다 켜고 게스트 메모리를 다시 나눈다 lab-environment-build 실험대 환경 구성 virtualization 게시 전
name version
libvirt 12.7.0
name version
QEMU 11.1.1
name version
k3s v1.36.4+k3s1
name version
게스트 Debian 12 genericcloud
name version
기준 배치 2026-09-03 · 호스트 RAM 증설 뒤 재배분, 그 뒤 값은 2026-09-10 실측
final/document.md#332-vm-메모리-재배분-게스트를-다시-만들지-않는다
final/document.md#333-안전한-종료-순서
final/document.md#334-복구-순서-종료의-역순
final/document.md#313-k3s-server와-agent-죽였을-때가-다르다
9465582b5d1630eb4ae7c4e078021486919bf6b6

실험대를 껐다 켜고 게스트 메모리를 다시 나눈다

게스트를 다시 만들지 않고 메모리를 재배분하고, 노트북을 끄기 전에 워크로드를 위에서부터 내리고, 켤 때 그 역순으로 되살리는 절차다. 순서를 틀리면 PostgreSQL 이 강제 종료되어 다음 기동에 crash recovery 가 돈다.

관계

  • 실험대를 철거하고 무엇이 남는지 확인한다 지우는 쪽이다. 거기서는 virsh destroy 로 전원을 뽑고, 여기서는 virsh shutdown 으로 정상 종료한다.
  • Keycloak 2노드와 PostgreSQL 을 k3s 에 올리고 클러스터가 묶였는지 확인한다 여기서 내리고 올리는 워크로드를 그 절차가 세운다. 네임스페이스와 리소스 이름도 거기서 정해졌다.
  • k3s server 와 agent 를 게스트 두 대에 깔고 lab host 에서 kubectl 로 본다 kubectl 이 lab host 에서 도는 까닭이 그 절차에 있다. 이 편의 명령도 전부 거기서 친다.
  • 호스트에 직접 깔지 않고 게스트 VM 두 대로 간다 — 관측자가 실험 대상과 함께 죽으면 안 된다 server 와 agent 를 죽였을 때가 왜 다른지, 그래서 관측 스택을 어디에 두는지를 그 결정이 받는다.
  • 5120MB 를 줬는데 353MB 를 쓰고 있었다 — 선언한 양과 실제로 드는 양 재배분한 값은 상한이지 점유가 아니다. 상한과 실제 점유를 가르는 방법이 그 기록에 있다.
  • 이 호스트의 가상 머신들은 설정한 메모리와 지금 잡고 있는 메모리가 얼마나 다른가 여기서 준 값이 그 물음이 견줄 설정 값이 된다.

본문

읽기 전에 — 어디서 치는가

전부 [lab host] 다. virshkubectl 도 거기서 돌고, 게스트 안을 봐야 할 때만 ssh kc-lab-1 ... 형태로 원격 실행한다. 게스트에 로그인해서 치지 않는다.

이 절차는 메모리 값을 바꾸고 워크로드를 올렸다 내리는 일뿐이라 설정 파일을 손대지 않고, 편집기를 여는 곳도 없다.

이 절차가 다루는 일 셋

세우는 편 일곱과 지우는 편 하나 사이에 있는 일이다. 실험대를 계속 쓰면 실제로 자주 하는 쪽이 이쪽이다.

언제 무엇을
호스트 RAM 을 늘렸거나 게스트가 좁을 때 게스트를 다시 만들지 않고 메모리를 재배분한다
노트북을 끄기 전에 워크로드와 게스트를 위에서부터 내린다
다시 켤 때 종료의 역순으로 되살린다

셋 다 게스트를 지우지 않아서, 디스크도 시드 ISO 도 그대로 두고 도메인의 값과 파드 수만 바꾼다.

실행 절차

1. 게스트 메모리를 다시 나눈다

목적 — 게스트를 다시 만들지 않고 도메인이 쓸 메모리 상한과 현재 할당을 바꾼다.

상한을 먼저 올리고 그다음에 현재 할당을 맞춘 뒤, 도메인과 게스트 양쪽에서 값을 확인한다.

virsh setmaxmem kc-lab-1 5120M --config
virsh setmem kc-lab-1 5120M --config
virsh dominfo kc-lab-1 | grep -i memory
ssh kc-lab-1 free -m

예상 결과dominfoMax memory 가 바뀐 상한으로 나온다. 게스트의 free -m 은 재부팅 전까지 옛 값을 보여 준다.

왜 필요한가 — 두 명령이 다른 것을 바꾼다.

setmaxmem : 상한. 부팅할 때 게스트가 보는 총량 setmem : 현재 할당. 상한 이하여야 한다

현재값을 상한보다 크게 줄 수 없으므로 setmaxmem 이 먼저다. 거꾸로 치면 두 번째 명령이 상한을 넘는 값을 받아 거부된다. 플래그도 갈린다 — --config 는 영구 정의라 다음 부팅부터 먹고, --live 는 실행 중인 도메인에 즉시 먹는다. 다만 setmaxmem --live 는 게스트가 부팅할 때 메모리 맵을 정하기 때문에 대개 거부된다. 그래서 상한을 바꾸려면 게스트를 껐다 켠다.

문제가 생기면 — ② 의 free -m 이 옛 값이면 아직 재부팅하지 않은 것이다. dominfo 쪽도 안 바뀌었으면 --config 를 빠뜨렸는지 본다.

2. 위에서부터 내린다

목적 — 애플리케이션과 데이터베이스와 게스트와 호스트를 순서대로 멈춰서 다음 기동에 복구 절차가 돌지 않게 한다.

Keycloak 을 먼저 0 으로 내려 클러스터에서 정상 탈퇴시키고, PostgreSQL 을 그다음에 내리고, 게스트를 정상 종료한 뒤 호스트를 끈다.

kubectl -n keycloak-lab scale statefulset/keycloak --replicas=0
kubectl -n keycloak-lab wait --for=delete pod -l app=keycloak --timeout=120s
kubectl -n keycloak-lab scale deployment/postgres --replicas=0
kubectl -n keycloak-lab wait --for=delete pod -l app=postgres --timeout=120s
virsh shutdown kc-lab-1 && virsh shutdown kc-lab-2
sudo systemctl poweroff

예상 결과 — ① 과 ② 의 wait 가 각각 파드 삭제를 확인하고 돌아온다. ③ 뒤에는 virsh list --all 에서 두 게스트가 shut off 로 바뀐다.

왜 필요한가 — ③ 의 virsh shutdown 은 게스트 systemd 가 k3s 를 멈추고 k3s 가 컨테이너에 SIGTERM 을 보내는 연쇄다. 유예 시간이 짧으면 PostgreSQL 이 강제 종료되어 다음 기동에 crash recovery 가 돈다. ① 과 ② 로 미리 내려 두면 그 연쇄가 데이터베이스까지 닿지 않는다. 순서를 뒤집어 PostgreSQL 을 먼저 내리면 Keycloak 이 데이터베이스 없이 남아 기동 실패와 재시작을 반복한다.

문제가 생기면wait120s 안에 안 끝나면 파드가 종료 중에 걸린 것이다. kubectl -n keycloak-lab get pods -o wide 로 어느 파드가 어느 노드에서 Terminating 인지 보고, 그 상태로 ③ 을 치지 않는다.

3. 종료의 역순으로 되살린다

목적 — 게스트와 클러스터와 워크로드를 반대 순서로 올려 Keycloak 이 데이터베이스를 찾을 수 있게 한다.

게스트를 띄워 노드가 Ready 가 되기를 기다린 뒤, PostgreSQL 을 먼저 올리고 Keycloak 을 나중에 올린다.

virsh start kc-lab-1 && virsh start kc-lab-2
kubectl get nodes
kubectl -n keycloak-lab scale deployment/postgres --replicas=1
kubectl -n keycloak-lab rollout status deployment/postgres
kubectl -n keycloak-lab scale statefulset/keycloak --replicas=2

예상 결과 — ① 의 kubectl get nodes 에 두 노드가 Ready 로 나온다. ② 의 rollout status 가 배포 완료로 돌아온 뒤에 ③ 을 친다.

왜 필요한가 — PostgreSQL 이 먼저다. Keycloak 이 데이터베이스 없이 뜨면 기동에 실패하기 때문이다. 그리고 스케일을 0 으로 내려 둔 것은 자동으로 복구되지 않는다. 게스트를 켜고 노드가 Ready 가 돼도 파드 수는 0 그대로이므로 ② 와 ③ 을 명시적으로 쳐야 한다.

문제가 생기면 — ① 에서 노드가 Ready 로 안 올라오면 게스트 안의 k3s 유닛부터 본다. ② 의 rollout status 가 멈춰 있으면 PostgreSQL 이 crash recovery 중일 수 있으니 아래 확인 방법의 postmaster.pid 를 함께 본다.

구성 값

기준 배치는 2026-09-03 에 구축이 끝난 상태다.

게스트 역할 vCPU · 메모리
kc-lab-1 k3s server 2 · 3584M
kc-lab-2 k3s agent 2 · 2560M

이 실험대는 그 뒤에 호스트를 8GB 에서 12GB 로 물리 증설했고, 위 1번의 방법으로 게스트 메모리를 다시 나눴다. 게스트를 다시 만들거나 디스크를 손댈 일은 전혀 없었다. 1번의 예에 쓴 5120M 이 그렇게 올린 값이다.

내리고 올리는 워크로드는 둘이다.

리소스 내릴 때 올릴 때
statefulset/keycloak 0 (먼저) 2 (나중)
deployment/postgres 0 (나중) 1 (먼저)

네임스페이스는 keycloak-lab 이고 wait 의 제한 시간은 양쪽 다 120s 다.

확인 방법

무엇을 확인하는가 — 어느 게스트가 server 이고 어느 게스트가 agent 인지, 그리고 PostgreSQL 이 깨끗이 내려갔는지.

kubectl get nodes -o custom-columns=\
'NODE:.metadata.name,CP:.metadata.labels.node-role\.kubernetes\.io/control-plane'
ssh kc-lab-2 'sudo ls /var/lib/rancher/k3s/storage/*postgres-data*/pgdata/postmaster.pid'

어디를 봐야 하는가 — 첫 명령에서 kc-lab-1CP 열에 값이 있고 kc-lab-2 는 비어 있다. 둘째 명령은 파일이 없다고 나와야 한다. postmaster.pid 가 보이면 비정상 종료였고 다음 기동에 복구 절차가 실행된다.

이 결과가 의미하는 것 — 둘째 명령은 게스트가 떠 있어야 읽을 수 있으므로 3번의 ① 뒤에 친다. 첫 명령이 필요한 까닭은 두 노드를 같은 것으로 다루면 안 되기 때문이다. kc-lab-2 를 죽이면 그 노드의 워크로드만 사라지고 클러스터 제어는 살아 있지만, kc-lab-1 을 죽이면 kubectl 이 안 되고 DNS 와 인그레스도 함께 사라진다. 노드 상실 실험은 agent 를 죽이는 것이고, server 를 죽이는 것은 컨트롤 플레인 상실이라 성격이 다르다.

이 절차가 감당하지 않는 것

관측 스택을 어느 노드에 둘지는 여기서 정하지 않는다. server 와 agent 가 왜 다른지만 위에서 한 번 적었고, 그래서 관측을 server 쪽에 두는 판단은 게스트 두 대를 고른 결정과 Prometheus 를 올리는 편이 받는다.

이 절차의 근거는 제9부의 개념 문서이고, 그 문서의 실측 스냅샷은 2026-09-03, 재배분 기록은 2026-09-11 이다. 제7부가 2026-09-10 에 잰 게스트 메모리 값과 날짜가 엇갈리므로, 게스트 메모리 수치는 각각 그 시점의 것으로 읽는다.

이 순서대로 다시 돌려 검증하지는 않았다. 위 명령들은 원본이 적어 둔 순서를 그대로 옮긴 것이고, 한 번 더 껐다 켜서 같은 출력이 나오는지는 확인하지 못했다.