refactor: 문서 개선 중
This commit is contained in:
+6
-2
@@ -6,7 +6,11 @@ topic: build-completion-judgment
|
||||
topicName: 끝났다는 판정
|
||||
project: virtualization
|
||||
status: 게시 전
|
||||
sourceRevision: 9465582b5d1630eb4ae7c4e078021486919bf6b6
|
||||
sourceRevision: import-head 9465582b5d1630eb4ae7c4e078021486919bf6b6 · uncommitted-working-tree snapshot
|
||||
evidence:
|
||||
- ../../../final/evidence/raw/lab-state-before-rebuild/15-k3s-precheck.txt
|
||||
- ../../../final/evidence/raw/lab-state-before-rebuild/21-k3s-token.txt
|
||||
- ../../../final/evidence/raw/lab-state-before-rebuild/22-k3s-agent-install.txt
|
||||
source:
|
||||
- final/document.md#185-가이드-묶음이-스스로-정한-규약
|
||||
- final/document.md#188-단계-02-k3s-server-와-agent
|
||||
@@ -156,7 +160,7 @@ ssh kc-lab-2 "curl -sfL https://get.k3s.io | sudo sh -s - agent \
|
||||
|
||||
## 확인하지 못한 것
|
||||
|
||||
설치 출력도 `journalctl` 출력도 `final/evidence/` 에 남기지 않았다. `level=fatal msg="Error: --token is required"` 와 `Host key verification failed.` 는 SSOT 본문에서 옮긴 것이고 명령 출력 파일이 근거가 아니다. 토큰 108자도 같다.
|
||||
원래 빈 토큰 사건의 `level=fatal msg="Error: --token is required"` 와 당시 설치 출력은 여전히 직접 raw가 없다. 다만 2026-09-17 후속 원문은 현재 정상 경로를 보강한다. `15-k3s-precheck.txt` 에는 별도 precheck에서 `Host key verification failed.`가 남아 있고, `21-k3s-token.txt` 는 재구축 시점의 **비어 있지 않은 토큰**을 길이로 확인했으며, `22-k3s-agent-install.txt` 는 그 토큰으로 agent 설치가 성공한 출력을 담는다. 이 셋은 원래 빈 토큰 실패의 재현이 아니라 **follow-up corroboration**이다. 과거 사건의 토큰 108자와 후속 실행의 길이가 다르더라도 같은 측정으로 덮어쓰지 않는다.
|
||||
|
||||
이 실패를 일부러 다시 만들어 본 기록도 없어서, 빈 토큰으로 설치하면 설치 출력이 성공으로 끝난다는 것은 한 번의 관측이다. 재현이 쉽지 않은 까닭은 이 부의 검증 방식에 있다 — 만드는 명령은 다시 치면 돌고 있는 실험대가 없어지므로 구축할 때 쓴 것을 옮기고 결과 상태를 확인하는 것으로 대신했다. 재현 없이 남길 수 있는 것은 `journalctl` 쪽이고, 그 출력을 `final/evidence/raw/` 에 남기면 진단 절에 증거가 붙는다.
|
||||
|
||||
|
||||
+8
-3
@@ -6,7 +6,10 @@ topic: build-completion-judgment
|
||||
topicName: 끝났다는 판정
|
||||
project: virtualization
|
||||
status: 게시 전
|
||||
sourceRevision: 9465582b5d1630eb4ae7c4e078021486919bf6b6
|
||||
sourceRevision: import-head 9465582b5d1630eb4ae7c4e078021486919bf6b6 · uncommitted-working-tree snapshot
|
||||
evidence:
|
||||
- ../../../final/evidence/raw/lab-state-before-rebuild/14-cloudinit-schema-check.txt
|
||||
- ../../../final/evidence/raw/lab-state-before-rebuild/15-k3s-precheck.txt
|
||||
source:
|
||||
- final/document.md#187-단계-01-게스트-세-대
|
||||
- final/document.md#186-단계-00-lab-host-가상화-준비
|
||||
@@ -178,18 +181,20 @@ python3 -c 'import yaml,sys; yaml.safe_load(open("kc-lab-1.yaml")); print("YAML
|
||||
|
||||
`0` 과 `2` 와 `YAML OK` 셋이 맞아야 시드를 만든다. 셋이 맞아도 cloud-config 로 유효하지는 않다 — 키 이름을 `users` 대신 `user` 로 친 오타는 이 세 줄을 그냥 통과한다. 그래서 게스트가 한 대라도 떠 있으면 cloud-init 자신의 스키마 검사기를 쓴다.
|
||||
|
||||
```bash
|
||||
```bash label="[lab host] [reference] historical command"
|
||||
ssh donghyeon@192.168.122.11 'umask 077; cat > ~/kc-lab-2.yaml' < kc-lab-2.yaml
|
||||
ssh donghyeon@192.168.122.11 'cloud-init schema -c ~/kc-lab-2.yaml; rm -f ~/kc-lab-2.yaml'
|
||||
```
|
||||
|
||||
이 두 줄은 **당시 문서에 남은 historical command**다. 둘째 줄은 스키마 검사가 실패해도 `; rm` 때문에 파일을 지운다. 따라 하는 절차에서는 검사와 삭제를 분리하고, 검사 성공 여부를 본 뒤 삭제한다.
|
||||
|
||||
이 파일에는 콘솔 비밀번호가 평문으로 들어 있어 `/tmp` 가 아니라 자기 홈에 `600` 으로 두고, 검사가 끝나면 바로 지운다.
|
||||
|
||||
## 확인하지 못한 것
|
||||
|
||||
넷 가운데 SSOT 가 관측으로 표시한 것은 시드를 SATA 로 붙였을 때 AHCI 장치가 보이지 않는 것과 스키마 검사기의 거부 문구뿐이다. YAML 파싱 실패와 `vol-upload` 누락과 네트워크 autostart 셋은 가이드가 막히면 표에 적어 둔 항목이라, 이 실험대에서 실제로 그 증상을 본 것인지 예상해 적은 것인지 SSOT 가 가르지 않았다. 이 글에서 그 셋은 그렇게 되는 구조까지이고 그렇게 됐다가 아니다.
|
||||
|
||||
명령 출력은 원문으로 남아 있지 않다. `Permission denied (publickey)` 도 `cloud-init status: done` 도 약 50초도 SSOT 본문이 근거다. `final/evidence/` 에 그 출력을 담은 파일이 없다. `virsh screenshot` 으로 뜬 화면도 저장해 두지 않았다.
|
||||
후속 원문이 생겼다. `14-cloudinit-schema-check.txt` 는 cloud-init 22.4.2가 `sudo` 리스트 표기를 실제로 거부한 출력을 다시 담아 **스키마 거부 claim을 직접 보강한다**. `15-k3s-precheck.txt` 의 `Host key verification failed.` 는 2026-09-17 재구축 중 나온 후속 SSH 관측으로, 원래 사건의 `Permission denied (publickey)`를 재현한 것은 아니다. 원래 `Permission denied (publickey)`, `cloud-init status: done`, 약 50초, `virsh screenshot` 화면은 여전히 SSOT 본문만 근거이고 직접 raw는 없다.
|
||||
|
||||
만드는 명령은 재실행으로 검증되지 않았다. 게스트를 다시 만들면 돌고 있는 실험대가 없어지므로 시드와 관련된 셋은 다시 재현하기 어렵다. 네트워크 autostart 만은 호스트를 재부팅해 확인할 수 있는데 그 기록도 없다.
|
||||
|
||||
|
||||
+2
-2
@@ -6,7 +6,7 @@ topic: build-completion-judgment
|
||||
topicName: 끝났다는 판정
|
||||
project: virtualization
|
||||
status: 게시 전
|
||||
sourceRevision: 9465582b5d1630eb4ae7c4e078021486919bf6b6
|
||||
sourceRevision: import-head 9465582b5d1630eb4ae7c4e078021486919bf6b6 · uncommitted-working-tree snapshot
|
||||
source:
|
||||
- final/document.md#190-단계-04-let's-encrypt-와-인증서-갱신
|
||||
- final/document.md#189-단계-03-엣지-nginx-라우팅과-호스트-dnat
|
||||
@@ -63,7 +63,7 @@ post/ 가 아니라 deploy/ 인 까닭 : post/ 는 갱신이 없어도 매번
|
||||
nginx : nginx/1.22.1
|
||||
certbot 플러그인 : dns-cloudflare
|
||||
발급 대상 : -d hyeonworks.com -d '*.hyeonworks.com'
|
||||
lineage 디렉터리 : /etc/letsencrypt/live/hyeonworks.com/
|
||||
lineage 디렉터리 : /etc/letsencrypt/live/auth.hyeonworks.com/ (2026-09-17 현재 관측)
|
||||
nginx 가 읽는 파일 : fullchain.pem · privkey.pem
|
||||
타이머 : certbot-renew.timer → certbot-renew.service
|
||||
인증서 유효기간 : 오늘 + 90일
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ topicName: 끝났다는 판정
|
||||
project: virtualization
|
||||
status: 게시 전
|
||||
questionStatus: OPEN
|
||||
sourceRevision: 9465582b5d1630eb4ae7c4e078021486919bf6b6
|
||||
sourceRevision: import-head 9465582b5d1630eb4ae7c4e078021486919bf6b6 · uncommitted-working-tree snapshot
|
||||
source:
|
||||
- final/document.md#194-이-부에서-파생될-open-question
|
||||
- final/document.md#184-이-부의-출처와-범위
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ topic: build-completion-judgment
|
||||
topicName: 끝났다는 판정
|
||||
project: virtualization
|
||||
status: 게시 전
|
||||
sourceRevision: 9465582b5d1630eb4ae7c4e078021486919bf6b6
|
||||
sourceRevision: import-head 9465582b5d1630eb4ae7c4e078021486919bf6b6 · uncommitted-working-tree snapshot
|
||||
source:
|
||||
- final/document.md#189-단계-03-엣지-nginx-라우팅과-호스트-dnat
|
||||
- final/document.md#185-가이드-묶음이-스스로-정한-규약
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ topic: build-completion-judgment
|
||||
topicName: 끝났다는 판정
|
||||
project: virtualization
|
||||
status: 게시 전
|
||||
sourceRevision: 9465582b5d1630eb4ae7c4e078021486919bf6b6
|
||||
sourceRevision: import-head 9465582b5d1630eb4ae7c4e078021486919bf6b6 · uncommitted-working-tree snapshot
|
||||
source:
|
||||
- final/document.md#191-단계-05-keycloak-2노드와-postgresql
|
||||
- final/document.md#192-단계-06-prometheus-와-grafana
|
||||
|
||||
+6
-1
@@ -7,7 +7,10 @@ topicName: 실험대의 진입 경로
|
||||
project: virtualization
|
||||
status: 게시 전
|
||||
basisVersion: 이 실험대의 2026-09-03 배치 · 호스트 nginx 1.30.4 (Arch) · k3s v1.36.4 의 기본 ingress 인 Traefik
|
||||
sourceRevision: 9465582b5d1630eb4ae7c4e078021486919bf6b6
|
||||
assets:
|
||||
- key: two-l7-hops-entry-recursion
|
||||
file: ../../../final/assets/diagrams/two-l7-hops-entry-recursion/two-l7-hops-entry-recursion.svg
|
||||
sourceRevision: import-head 9465582b5d1630eb4ae7c4e078021486919bf6b6 · uncommitted-working-tree snapshot
|
||||
source:
|
||||
- final/document.md#261-진입점-자체가-죽으면-로드밸런서의-재귀-문제
|
||||
- final/document.md#275-호스트-nginx와-traefik은-무엇이-다른가-둘-다-필요한-이유
|
||||
@@ -38,6 +41,8 @@ source:
|
||||
|
||||
<!-- body:start -->
|
||||
|
||||

|
||||
|
||||
## 요청 하나가 L7 을 두 번 지난다
|
||||
|
||||
브라우저가 보낸 요청은 파드에 닿기까지 HTTP 를 읽는 서버를 두 번 지난다.
|
||||
|
||||
+4
-4
@@ -7,7 +7,7 @@ topicName: 실험대의 진입 경로
|
||||
project: virtualization
|
||||
status: 게시 전
|
||||
decisionStatus: ADOPTED
|
||||
sourceRevision: 9465582b5d1630eb4ae7c4e078021486919bf6b6
|
||||
sourceRevision: import-head 9465582b5d1630eb4ae7c4e078021486919bf6b6 · uncommitted-working-tree snapshot
|
||||
source:
|
||||
- final/document.md#305-tunnel-채택하지-않은-이유를-남긴-자산
|
||||
- final/document.md#302-왜-적용하지-않는-것을-남겨두는가
|
||||
@@ -32,7 +32,7 @@ Cloudflare named tunnel 을 앞에 붙이면 브라우저에서 파드까지가
|
||||
- **엣지 nginx 를 호스트에서 게스트 VM 으로 옮긴다 — 성능이 아니라 더러워지는 층의 격리**
|
||||
같은 2홉을 지킨 채 엣지의 위치만 바꾼 결정이라 홉 수를 건드리지 않는다.
|
||||
- **이 실험대의 certbot 은 HTTP-01 로 받고 있는가 DNS-01 로 받고 있는가**
|
||||
재구축 때 인증서 디렉터리를 지워도 되는지가 그 물음에 걸려 있다.
|
||||
이 물음은 2026-09-17 `dns-cloudflare` 관측으로 닫혔다. 남은 것은 현재 Cloudflare 자격증명이 실제 재발급에 쓸 수 있는지다.
|
||||
|
||||
## 결정문
|
||||
|
||||
@@ -56,11 +56,11 @@ Cloudflare named tunnel 은 아웃바운드 연결만 쓰므로 포트포워딩
|
||||
|
||||
그 대가가 두 곳에서 청구됐다.
|
||||
인증서 발급 : HTTP-01 로 받을 수 없어 DNS-01 로 갔다
|
||||
재구축 : 인증서 디렉터리 `/etc/letsencrypt/` 를 지워도 되는지가 미확정이다
|
||||
재구축 : DNS-01 등록은 확인됐지만 Cloudflare 자격증명 유효성과 dry-run 성공을 확인하기 전에는 `/etc/letsencrypt/` 를 보존한다
|
||||
|
||||
기각한 파일을 지우지 않는 방침에도 근거가 셋 있다.
|
||||
① 저장소의 목적이 비교다 : 선택지를 나란히 두고 트레이드오프를 적는 것 자체가 산출물이라, 하나만 남기면 왜 이것을 골랐는지를 뒷받침할 근거가 없어진다
|
||||
② 죽은 코드가 아니라 테스트되는 코드다 : `scripts/verify-public-tunnel-config.sh` 가 붙어 있어 실행되지 않을 뿐 깨지면 드러난다
|
||||
③ 실험대 전용 설정은 `lab/` 아래로 분리했다 : 일반 배포 설정과 섞이지 않는다
|
||||
|
||||
재지 않은 것도 있다. 터널을 붙인 상태와 지금을 같은 방법으로 잰 비교는 이 저장소에 없다. 기각의 근거는 홉 수와 섞이는 헤더이지 두 구성을 재서 견준 값이 아니다. 인증서를 지금 어느 방식으로 받고 있는지도 이 실험대에서 아직 재지 않았다.
|
||||
재지 않은 것도 있다. 터널을 붙인 상태와 지금을 같은 방법으로 잰 비교는 이 저장소에 없다. 기각의 근거는 홉 수와 섞이는 헤더이지 두 구성을 재서 견준 값이 아니다. 인증 방식은 2026-09-17 `dns-cloudflare` 로 확인됐다. 다만 같은 후속 원문에서 Cloudflare 토큰 길이는 0자로 측정돼 재발급 가능성까지 확인된 것은 아니다.
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ topic: lab-entry-path-and-measurement-integrity
|
||||
topicName: 실험대의 진입 경로
|
||||
project: virtualization
|
||||
status: 게시 전
|
||||
sourceRevision: 9465582b5d1630eb4ae7c4e078021486919bf6b6
|
||||
sourceRevision: import-head 9465582b5d1630eb4ae7c4e078021486919bf6b6 · uncommitted-working-tree snapshot
|
||||
source:
|
||||
- final/document.md#209-nginx-keycloak-lab-conf-스티키-스위치와-신뢰-경계
|
||||
- final/document.md#259-x-forwarded--와-신뢰-경계
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ topicName: 실험대 환경 구성
|
||||
project: virtualization
|
||||
status: 게시 전
|
||||
lastVerifiedOn: 2026-09-10
|
||||
sourceRevision: 9465582b5d1630eb4ae7c4e078021486919bf6b6
|
||||
sourceRevision: import-head 9465582b5d1630eb4ae7c4e078021486919bf6b6 · uncommitted-working-tree snapshot
|
||||
source:
|
||||
- final/document.md#198-자원-할당과-실사용은-다르다
|
||||
- final/document.md#199-디스크-오버레이는-얼마나-쓰나
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ status: 게시 전
|
||||
assets:
|
||||
- key: nftables-forward-hook-chain-order
|
||||
file: ../../../final/assets/diagrams/nftables-forward-hook-chain-order/nftables-forward-hook-chain-order.svg
|
||||
sourceRevision: 9465582b5d1630eb4ae7c4e078021486919bf6b6
|
||||
sourceRevision: import-head 9465582b5d1630eb4ae7c4e078021486919bf6b6 · uncommitted-working-tree snapshot
|
||||
source:
|
||||
- final/document.md#180-nftables-는-앞-체인의-accept-로-뒤-체인의-reject-를-막지-못한다
|
||||
- final/document.md#179-엣지를-물리-호스트에서-vm-으로-옮기면-무엇이-새로-필요해지나
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ topicName: 실험대 환경 구성
|
||||
project: virtualization
|
||||
status: 게시 전
|
||||
basisVersion: Debian 12 genericcloud 위의 cloud-init 22.4.2 · NoCloud 데이터소스 · 호스트는 QEMU 11.1.1 · libvirt 12.7.0
|
||||
sourceRevision: 9465582b5d1630eb4ae7c4e078021486919bf6b6
|
||||
sourceRevision: import-head 9465582b5d1630eb4ae7c4e078021486919bf6b6 · uncommitted-working-tree snapshot
|
||||
source:
|
||||
- final/document.md#229-왜-os를-설치하지-않아도-vm이-뜨는가
|
||||
- final/document.md#236-클라우드-이미지와-cloud-init
|
||||
|
||||
+6
-1
@@ -7,7 +7,10 @@ topicName: 실험대 환경 구성
|
||||
project: virtualization
|
||||
status: 게시 전
|
||||
basisVersion: QEMU 11.1.1 의 qcow2 v3 · cluster_size 65536(기본값) · 실측은 Debian 12 genericcloud base.qcow2 한 장 · 압축은 zlib
|
||||
sourceRevision: 9465582b5d1630eb4ae7c4e078021486919bf6b6
|
||||
assets:
|
||||
- key: qcow2-mapping-clusters
|
||||
file: ../../../final/assets/diagrams/qcow2-mapping-clusters/qcow2-mapping-clusters.svg
|
||||
sourceRevision: import-head 9465582b5d1630eb4ae7c4e078021486919bf6b6 · uncommitted-working-tree snapshot
|
||||
source:
|
||||
- final/document.md#231-qcow2-파일-내부는-어떻게-생겼나-매핑표가-전부다
|
||||
- final/document.md#230-디스크-이미지를-"복사한다"는-것의-실제-원리
|
||||
@@ -40,6 +43,8 @@ qcow2 는 raw 에 매핑표 하나를 더한 것이고, 표가 가리키는 값
|
||||
|
||||
<!-- body:start -->
|
||||
|
||||

|
||||
|
||||
## raw 는 배열을 그대로 담는다
|
||||
|
||||
디스크는 섹터가 0번부터 늘어선 1차원 배열로 보인다. 파티션 테이블도 파일시스템도 부트로더도 전부 그 배열 안의 특정 위치에 기록된 바이트이고, 디스크 바깥에 따로 보관되는 정보가 없다. 그래서 배열을 처음부터 끝까지 그대로 파일에 쓰면 raw 이미지가 되고, 되돌린 디스크는 원본과 바이트 단위로 같아 똑같이 부팅한다.
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ topicName: 실험대 환경 구성
|
||||
project: virtualization
|
||||
status: 게시 전
|
||||
basisVersion: QEMU 11.1.1 · libvirt 12.7.0 위의 qcow2 · 게스트는 Debian 12 genericcloud · 크기 계산은 클러스터 64KiB · L2 항목 8B 기준
|
||||
sourceRevision: 9465582b5d1630eb4ae7c4e078021486919bf6b6
|
||||
sourceRevision: import-head 9465582b5d1630eb4ae7c4e078021486919bf6b6 · uncommitted-working-tree snapshot
|
||||
source:
|
||||
- final/document.md#181-qcow2-가-담는-것과-담지-않는-것
|
||||
- final/document.md#178-이-부의-출처와-범위
|
||||
|
||||
+2
-2
@@ -7,7 +7,7 @@ topicName: 실험대 환경 구성
|
||||
project: virtualization
|
||||
status: 게시 전
|
||||
decisionStatus: ADOPTED
|
||||
sourceRevision: 9465582b5d1630eb4ae7c4e078021486919bf6b6
|
||||
sourceRevision: import-head 9465582b5d1630eb4ae7c4e078021486919bf6b6 · uncommitted-working-tree snapshot
|
||||
source:
|
||||
- final/document.md#190-단계-04-let's-encrypt-와-인증서-갱신
|
||||
- final/document.md#184-이-부의-출처와-범위
|
||||
@@ -67,6 +67,6 @@ DNS 공급자 의존 : 인증서를 받는 일이 Cloudflare 계정에 묶인다
|
||||
|
||||
발급 자체에도 순서가 붙는다. --dry-run 을 먼저 돌리는 것은 Let's Encrypt 의 주당 중복 인증서 5장 한도를 dry-run 이 쓰지 않기 때문이다. dry-run 은 인증서를 저장하지 않으므로 그 직후 certbot certificates 가 No certificates found 를 내는 것이 정상이다.
|
||||
|
||||
이 결정이 남긴 이름 규칙이 하나 있다. live/hyeonworks.com/ 은 certbot 이 이 묶음을 관리하려고 첫 번째 -d 에서 따온 라벨이고 서빙과 무관하다. 브라우저가 보는 유효 호스트명은 -d 로 준 이름 전부이므로 auth.hyeonworks.com 으로 다시 받을 필요가 없다. 대신 nginx 설정에는 그 디렉터리 경로를 한 글자도 다르지 않게 적어야 한다. live/auth.hyeonworks.com/ 이라고 적으면 cannot load certificate 로 막히고, §182 가 이것을 가이드 결함 여섯 중 하나로 셌다. 와일드카드는 한 단계만 덮는다. a.b.hyeonworks.com 도, apex 인 hyeonworks.com 자신도 와일드카드에 들어가지 않아 -d 를 둘 준다.
|
||||
이 결정 뒤에 lineage 이름을 추측하는 규칙을 두지 않는다. 2026-09-17 현재 실험대에서 `sudo ls /etc/letsencrypt/live/` 와 `certbot certificates` 로 확인한 실제 경로는 `live/auth.hyeonworks.com/` 이다(observed). 브라우저가 보는 유효 호스트명은 SAN 목록이고 lineage 디렉터리 이름과 별개다. 원본 가이드 04 에 남아 있던 `live/hyeonworks.com/` 을 nginx 에 쓰면 `cannot load certificate` 로 막혔고, §182 가 이것을 가이드 결함으로 셌다. 그래서 nginx 에는 이름을 추측해 적지 않고 `certbot certificates` 가 찍은 경로를 그대로 쓴다. 와일드카드는 한 단계만 덮으므로 `a.b.hyeonworks.com` 과 apex `hyeonworks.com` 은 별도 조건으로 본다.
|
||||
|
||||
이 결정이 끝내지 못한 것이 하나 있다. 받은 인증서가 갱신 뒤에 실제로 서빙되는지는 발급 방식과 별개이고, 근거로 건 첫 기록이 그것을 받는다.
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ topicName: 실험대 환경 구성
|
||||
project: virtualization
|
||||
status: 게시 전
|
||||
decisionStatus: ADOPTED
|
||||
sourceRevision: 9465582b5d1630eb4ae7c4e078021486919bf6b6
|
||||
sourceRevision: import-head 9465582b5d1630eb4ae7c4e078021486919bf6b6 · uncommitted-working-tree snapshot
|
||||
source:
|
||||
- final/document.md#179-엣지를-물리-호스트에서-vm-으로-옮기면-무엇이-새로-필요해지나
|
||||
- final/document.md#178-이-부의-출처와-범위
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ source:
|
||||
- final/document.md#245-libvirt-default-네트워크와-virbr0
|
||||
- final/document.md#246-dnsmasq-libvirt-내장-dhcp-dns
|
||||
- final/document.md#248---live---config
|
||||
sourceRevision: 9465582b5d1630eb4ae7c4e078021486919bf6b6
|
||||
sourceRevision: import-head 9465582b5d1630eb4ae7c4e078021486919bf6b6 · uncommitted-working-tree snapshot
|
||||
---
|
||||
|
||||
# 게스트 주소는 libvirt DHCP 예약으로 고정한다 — 바꿀 수 없어서가 아니라 되돌리기가 가장 비싸서
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ source:
|
||||
- final/document.md#281-docker를-lab-host에-설치하면-안-되는-이유
|
||||
- final/document.md#282-그러면-이미지는-어떻게-넣는가
|
||||
- final/document.md#280-무엇을-어디에-설치하는가
|
||||
sourceRevision: 9465582b5d1630eb4ae7c4e078021486919bf6b6
|
||||
sourceRevision: import-head 9465582b5d1630eb4ae7c4e078021486919bf6b6 · uncommitted-working-tree snapshot
|
||||
---
|
||||
|
||||
# lab host 에는 Docker 를 깔지 않는다 — 이미지 저장소가 갈리고, 그 위에 네트워크 변수가 하나 더 붙는다
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ source:
|
||||
- final/document.md#213-왜-호스트에-직접-깔지-않고-vm-2대인가
|
||||
- final/document.md#313-k3s-server와-agent-죽였을-때가-다르다
|
||||
- final/document.md#218-실험대-전체-배치-2026-09-03-구축-완료-실측값
|
||||
sourceRevision: 9465582b5d1630eb4ae7c4e078021486919bf6b6
|
||||
sourceRevision: import-head 9465582b5d1630eb4ae7c4e078021486919bf6b6 · uncommitted-working-tree snapshot
|
||||
---
|
||||
|
||||
# 호스트에 직접 깔지 않고 게스트 VM 두 대로 간다 — 관측자가 실험 대상과 함께 죽으면 안 된다
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ topicName: 실험대 환경 구성
|
||||
project: virtualization
|
||||
status: 게시 전
|
||||
questionStatus: OPEN
|
||||
sourceRevision: 9465582b5d1630eb4ae7c4e078021486919bf6b6
|
||||
sourceRevision: import-head 9465582b5d1630eb4ae7c4e078021486919bf6b6 · uncommitted-working-tree snapshot
|
||||
source:
|
||||
- final/document.md#183-이-부에서-파생될-open-question-oq-1
|
||||
- final/document.md#180-nftables-는-앞-체인의-accept-로-뒤-체인의-reject-를-막지-못한다
|
||||
|
||||
+41
-64
@@ -6,8 +6,10 @@ topic: lab-environment-build
|
||||
topicName: 실험대 환경 구성
|
||||
project: virtualization
|
||||
status: 게시 전
|
||||
questionStatus: OPEN
|
||||
sourceRevision: 9465582b5d1630eb4ae7c4e078021486919bf6b6
|
||||
questionStatus: RESOLVED
|
||||
sourceRevision: import-head 9465582b5d1630eb4ae7c4e078021486919bf6b6 · uncommitted-working-tree snapshot
|
||||
evidence:
|
||||
- ../../../final/evidence/raw/lab-state-before-rebuild/58-authenticator-and-token.txt
|
||||
source:
|
||||
- final/document.md#204-재구축할-때-무엇이-남아-있나
|
||||
- final/document.md#266-dns-01-은-언제-쓰는가-네-가지-경우
|
||||
@@ -16,100 +18,75 @@ source:
|
||||
|
||||
# 이 실험대의 certbot 은 HTTP-01 로 받고 있는가 DNS-01 로 받고 있는가
|
||||
|
||||
어느 방식으로 받고 있는지 아직 읽지 못했다. §266 의 결론과 §190 은 DNS-01 을 가리키는데 원본 가이드 04 는 HTTP-01 로 적혀 있다. `authenticator` 한 값이면 갈리지만 호스트의 `sudo` 가 비밀번호를 요구해 비대화식으로 못 읽었다.
|
||||
2026-09-17 renewal 설정을 직접 읽으면서 답이 나왔다. `authenticator = dns-cloudflare` 였다. 따라서 이 실험대의 certbot은 DNS-01로 등록되어 있다(observed). 이전에 HTTP-01과 DNS-01 중 어느 쪽인지 열어 둔 상태는 닫는다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **인증서는 DNS-01 로 받는다 — 실험대 주소가 공개 인터넷에 없어 HTTP-01 이 성립하지 않는다**
|
||||
그 결정이 적은 것이 의도인지 이 호스트의 실제 설정인지를 이 물음이 가른다.
|
||||
- **DNS-01 으로 와일드카드 인증서를 받고 갱신이 서빙까지 닿게 한다**
|
||||
그 절차가 `--dns-cloudflare` 로 받는 길을 적는다. 이 호스트가 그 길로 받았는지는 확인하지 않았다.
|
||||
- **실험대를 철거하고 무엇이 남는지 확인한다**
|
||||
그 절차의 「인증서를 지우지 않는다」가 이 물음의 답에 걸려 있다.
|
||||
- **갱신은 매번 SUCCESS 였고 옛 인증서가 계속 나갔다 — 2305초와 1~2초**
|
||||
갱신은 됐는데 서빙까지 안 간 사건이다. 이 물음이 캐는 것은 갱신 자체가 도는지다.
|
||||
- **공개 터널을 쓰지 않고 tailnet 직결로 둔다 — 홉이 하나 늘면 재려던 계약이 오염된다**
|
||||
공개 인터넷에서 이 호스트에 닿을 길을 두지 않기로 한 결정이고, HTTP-01 이 성립하지 않는 조건을 그 결정이 만든다.
|
||||
- **가이드대로 다시 쳐서 이 실험대가 같은 상태로 서는가**
|
||||
그 물음이 재는 7단계 가운데 04 의 통과 조건이 이 답으로 확정된다.
|
||||
- 인증서는 DNS-01 로 받는다 — 실험대 주소가 공개 인터넷에 없어 HTTP-01 이 성립하지 않는다
|
||||
이 결정이 설계 의도만 적은 것이 아니라 현재 실험대의 등록 상태와도 일치한다는 것이 확인됐다.
|
||||
- DNS-01 으로 와일드카드 인증서를 받고 갱신이 서빙까지 닿게 한다
|
||||
renewal 설정의 `authenticator`와 자격증명 경로를 실제 출력으로 확인한 Setup이다.
|
||||
- 실험대를 철거하고 무엇이 남는지 확인한다
|
||||
검증 방식은 닫혔지만 현재 Cloudflare 자격증명이 재발급에 쓸 수 있는지는 별도 확인이 필요해 인증서 보존 판단에는 그 조건이 남는다.
|
||||
- 갱신은 매번 SUCCESS 였고 옛 인증서가 계속 나갔다 — 2305초와 1~2초
|
||||
이 질문은 발급·갱신 방식만 닫는다. 갱신된 파일을 nginx가 다시 읽는 문제는 그 Case의 범위다.
|
||||
- 공개 터널을 쓰지 않고 tailnet 직결로 둔다 — 홉이 하나 늘면 재려던 계약이 오염된다
|
||||
공개 인터넷에서 HTTP-01이 성립하지 않는 조건은 그대로다.
|
||||
- 가이드대로 다시 쳐서 이 실험대가 같은 상태로 서는가
|
||||
7단계 중 04의 인증 방식 판정은 닫혔다. 재발급 자격증명과 dry-run 성공 여부는 별도 검증으로 남긴다.
|
||||
|
||||
## 사실
|
||||
|
||||
§266 이 문서 둘이 어긋나 있다고 직접 적었다. 그 절의 결론과 §190 은 DNS-01 을 가리킨다.
|
||||
2026-09-17 `sudo sh -c` 안에서 `/etc/letsencrypt/renewal/*.conf`를 읽었다. 후속 원문에는 다음 세 줄이 남아 있다.
|
||||
|
||||
원본 가이드 `docs/guides/04-tls/README.md` 는 `certbot certonly --webroot` 로 적혀 있다. 전제도 공개 DNS 에 이름 셋이 이 호스트를 가리켜야 한다는 것이다.
|
||||
- `authenticator = dns-cloudflare`
|
||||
- `dns_cloudflare_credentials = /etc/letsencrypt/cloudflare.ini`
|
||||
- `server = https://acme-v02.api.letsencrypt.org/directory`
|
||||
|
||||
`dig +short auth.hyeonworks.com` 이 `100.83.212.4` 를 낸다.
|
||||
일반 셸에서 `sudo grep ... /etc/letsencrypt/renewal/*.conf`로 친 첫 시도는 zsh가 글로브를 sudo보다 먼저 펼치면서 `no matches found`로 실패했다. 그래서 `sudo sh -c` 안에서 글로브를 펼쳤다.
|
||||
|
||||
`100.64.0.0/10` 은 CGNAT(Carrier-Grade NAT, 통신사 공용 주소 변환)용 예약 대역이라 공개 인터넷에서 라우팅 자체가 안 된다. 방화벽을 여는 문제가 아니라 그 주소가 인터넷에 존재하지 않는다. 이것은 규격이고 이 실험대가 잰 값이 아니다.
|
||||
같은 후속 원문에서 Cloudflare 자격증명 파일의 토큰 길이는 `0자`로 측정됐고 토큰 검증은 `Invalid request headers`로 실패했다. 따라서 DNS-01 등록 여부는 확인됐지만 현재 자격증명이 유효하다고 확인된 것은 아니다.
|
||||
|
||||
`certbot plugins` 를 돌린 실측이 `dns-cloudflare` · `standalone` · `webroot` 세 줄을 냈으니 플러그인은 깔려 있다.
|
||||
`auth.hyeonworks.com`은 tailnet 주소 `100.83.212.4`로 풀리고, 이 주소는 공개 인터넷에서 라우팅되지 않는 `100.64.0.0/10` 안이다. 이 조건 때문에 HTTP-01을 현재 진입 경로에 그대로 적용할 수 없다는 판단도 바뀌지 않는다.
|
||||
|
||||
§190 이 적은 발급 대상은 `-d hyeonworks.com -d '*.hyeonworks.com'` 이고 lineage 디렉터리는 `/etc/letsencrypt/live/hyeonworks.com/` 이다. 자격증명 파일은 `/etc/letsencrypt/cloudflare.ini` 이고 권한이 `600` 이다.
|
||||
|
||||
ACME 명세가 와일드카드를 DNS-01 로만 허용한다. 호스트 한 대에 파일을 놓는 것은 그 이름 하나를 통제한다는 증명이고, DNS 존의 TXT 레코드를 고칠 수 있다는 것은 도메인 전체를 통제한다는 증명이라 증명의 급이 다르다.
|
||||
|
||||
§204 가 이 항목을 「미측정」으로 적었다. 호스트의 `sudo` 가 비밀번호를 요구해 비대화식으로 읽지 못했다.
|
||||
2026-09-17 현재 lineage는 `/etc/letsencrypt/live/auth.hyeonworks.com/`이었다. `/etc/letsencrypt/live/hyeonworks.com/`은 원본 가이드에 남은 known-wrong 경로였고, 그대로 `nginx -t`를 실행하면 certificate file not found로 실패했다.
|
||||
|
||||
## 가정
|
||||
|
||||
지금 서빙되는 인증서가 와일드카드라면 발급은 DNS-01 로 이뤄졌을 수밖에 없다. 다만 그 인증서가 실제로 와일드카드인지는 이 저장소에 출력으로 남아 있지 않다.
|
||||
|
||||
플러그인이 보인다는 것과 그것으로 받았다는 것을 같게 읽지 않는다. `certbot plugins` 는 설치된 것을 세지 무엇으로 발급했는지를 세지 않는다.
|
||||
|
||||
갱신이 돌고 있다는 것도 아직 확인이 아니다. §218 의 구축 완료 판정 기준이 `systemctl is-active nginx certbot-renew.timer` 에 `active active` 를 요구하지만, §190 은 `systemctl list-timers certbot-renew.timer` 의 실제 출력이 남아 있지 않다고 적는다.
|
||||
|
||||
갱신 설정이 발급 시점의 방식을 그대로 물려받았다고 전제한다. 발급 뒤에 누가 `renewal/*.conf` 를 손으로 고쳤다면 그 전제가 깨진다.
|
||||
renewal 설정이 2026-09-17 관측 뒤에 수동으로 변경되지 않았다고 전제한다. 이후 설정을 바꾸면 이 기록의 “현재” 범위도 그 시점까지만 유효하다.
|
||||
|
||||
## 미지수
|
||||
|
||||
`/etc/letsencrypt/renewal/*.conf` 의 `authenticator` 가 무엇인가.
|
||||
이 질문의 핵심 미지수였던 `authenticator`는 해소됐다.
|
||||
|
||||
그 값이 `webroot` 나 `standalone` 이면 지금 갱신이 실제로 돌고 있는가. 검증이 성립하지 않는 주소에 HTTP-01 로 설정돼 있다면 갱신은 조용히 실패한다.
|
||||
남은 미지수는 다른 판단에 속한다.
|
||||
|
||||
그 값이 `dns-cloudflare` 라면 `/etc/letsencrypt/cloudflare.ini` 에 든 토큰의 권한이 `존 하나 + DNS:Edit` 으로 좁혀져 있는가. §266 이 그 범위를 값으로 치르는 것이라고 적었는데, 이 호스트의 토큰이 실제로 그 범위인지는 SSOT 에 없다.
|
||||
- `/etc/letsencrypt/cloudflare.ini`에 유효한 토큰을 다시 넣었는가.
|
||||
- 그 토큰의 권한이 필요한 zone과 DNS 편집 범위로 좁혀져 있는가.
|
||||
- 현재 `certbot renew --dry-run`이 성공하는가.
|
||||
|
||||
지금 서빙되는 인증서가 와일드카드인가, 그리고 lineage 이름이 무엇인가.
|
||||
이 셋은 DNS-01인지 HTTP-01인지를 다시 여는 근거가 아니다. 재발급·갱신 가능성을 확인하는 후속 조건이다.
|
||||
|
||||
## 제약
|
||||
|
||||
호스트의 `sudo` 가 비밀번호를 요구해서 비대화식으로는 읽을 수 없고, 콘솔에서 쳐야 한다. 이것이 §204 가 미측정으로 남긴 까닭이다.
|
||||
비밀 값은 증거에 남기지 않는다. 자격증명은 길이·존재 여부와 API 검증 결과까지만 기록한다.
|
||||
|
||||
비밀 값을 옮기지 않는다. `cloudflare.ini` 의 토큰은 길이와 존재 여부까지만 적고 값을 찍는 명령을 남기지 않는다.
|
||||
|
||||
Let's Encrypt 를 tailnet 에 초대할 방법이 없다. 검증 방식을 바꿔 가며 돌려 보는 실험으로 답을 대신할 수 없다.
|
||||
|
||||
발급 한도가 같은 이름 조합에 주당 중복 5장이다. 시험은 `--dry-run` 으로 먼저 한다.
|
||||
`sudo`가 필요한 디렉터리의 글로브는 일반 사용자 셸에서 먼저 펼치지 않는다. 이 실험대에서는 그 방식이 실제로 실패했다.
|
||||
|
||||
## 선택지
|
||||
|
||||
### 1. 갱신 설정 파일을 콘솔에서 직접 읽는다
|
||||
### 1. DNS-01 — 현재 관측과 일치
|
||||
|
||||
`authenticator` 한 값이 이 물음을 통째로 닫는다. 같은 콘솔 세션에서 쓸 수 있는 방식과 갱신 예행연습과 이름이 가리키는 주소까지 한 번에 찍어 같은 시각의 출력으로 묶는다. 값을 읽을 수 없는 경우는 파일이 없을 때뿐이고, 그때는 이 호스트가 인증서를 받은 적이 없다는 다른 답이 된다.
|
||||
renewal 설정이 `dns-cloudflare`를 직접 가리킨다. 이 질문의 답이다.
|
||||
|
||||
### 2. 지금 서빙되는 인증서가 와일드카드인지부터 본다
|
||||
### 2. HTTP-01 — 현재 관측과 불일치
|
||||
|
||||
`sudo` 없이도 밖에서 TLS 핸드셰이크만으로 도메인 목록을 볼 수 있다. 와일드카드로 나오면 발급이 DNS-01 이었다는 쪽으로 정황이 좁혀진다. 다만 좁혀질 뿐 `authenticator` 를 대신하지는 못한다. 발급은 와일드카드로 받고 갱신 설정만 다른 경우를 이 방법으로는 가르지 못한다.
|
||||
|
||||
### 3. 인증서를 지우고 다시 받아 본다 — 제외
|
||||
|
||||
다시 받아 보면 어느 방식이 성립하는지 바로 드러나지만, §204 가 「어느 쪽인지 모르는 채로는 지우지 않는다」로 그 순서를 막아 두었다. HTTP-01 로 설정돼 있으면 재발급이 안 되고, 그러면 재구축을 시작하자마자 검증 방식부터 손봐야 한다. 답을 얻으려고 답이 필요한 상태를 만드는 순서다.
|
||||
원본 가이드 04에는 `--webroot` 경로가 남아 있었지만, 현재 renewal 설정이 그것을 사용한다는 증거는 없다. historical document branch로만 남긴다.
|
||||
|
||||
## 다음 검증
|
||||
|
||||
§204 와 §266 이 적은 네 줄을 호스트 콘솔에서 그대로 친다.
|
||||
이 Question은 RESOLVED다. `authenticator`를 다시 읽는 것은 질문을 닫기 위한 검증이 아니라 설정 변경 감시다.
|
||||
|
||||
1. `sudo grep -H authenticator /etc/letsencrypt/renewal/*.conf` 으로 검증 방식을 읽는다.
|
||||
2. `certbot plugins` 를 돌려 앞에 `*` 가 붙은 줄을 적는다.
|
||||
3. `sudo certbot renew --dry-run` 으로 갱신이 실제로 되는지 본다.
|
||||
4. `dig +short auth.hyeonworks.com` 으로 Let's Encrypt 가 올 수 있는 주소인지를 같은 시각에 함께 남긴다.
|
||||
5. 지금 서빙되는 인증서가 와일드카드인지와 lineage 이름이 무엇인지를 `certbot certificates` 의 도메인 목록으로 적는다.
|
||||
6. 출력 원문을 `final/evidence/raw/` 에 남기고 `meta/` 에 명령과 cwd 와 실행 시각과 종료 코드를 적는다. `cloudflare.ini` 의 토큰 값은 찍지 않는다.
|
||||
재발급 가능성을 확인하려면 유효한 Cloudflare 자격증명을 준비한 뒤 `sudo certbot renew --dry-run`을 별도 검증한다. 그 결과가 실패하더라도 이 Question을 다시 `OPEN`으로 돌리지 않는다. 실패 원인은 자격증명·DNS 권한·ACME 경로 쪽의 새 문제로 분리한다.
|
||||
|
||||
닫는 조건 : `authenticator` 한 값과 `--dry-run` 결과가 나오면 닫는다.
|
||||
|
||||
`dns-cloudflare` 면 「인증서는 DNS-01 로 받는다」가 이 호스트의 현재 상태를 적은 것으로 확인되고, 철거 절차의 「인증서를 지우지 않는다」가 정책에서 선택으로 바뀐다. §204 가 적은 대로 그때는 백업이 헛수고이므로 지워도 되고 재구축 절차가 한 단계 짧아진다.
|
||||
|
||||
`webroot` 나 `standalone` 이면 그 결정이 적은 것은 의도이고 실제 설정은 다른 것이므로, §190 과 가이드 04 가운데 어느 쪽이 실재인지를 먼저 고친 뒤 그 기록을 다시 판정한다.
|
||||
|
||||
`--dry-run` 이 실패하면 갱신이 이미 멈춰 있다는 뜻이라 그 자체가 새 Case 다. 「갱신은 매번 SUCCESS 였고 옛 인증서가 계속 나갔다」가 갱신은 되는데 서빙까지 안 간 것을 다뤘다면, 이번 것은 갱신 자체가 안 되는 쪽이고 증상이 또 조용하다.
|
||||
닫는 조건 : 2026-09-17 raw evidence에서 `authenticator = dns-cloudflare`가 직접 관측되어 충족됐다.
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ topicName: 실험대 환경 구성
|
||||
project: virtualization
|
||||
status: 게시 전
|
||||
questionStatus: OPEN
|
||||
sourceRevision: 9465582b5d1630eb4ae7c4e078021486919bf6b6
|
||||
sourceRevision: import-head 9465582b5d1630eb4ae7c4e078021486919bf6b6 · uncommitted-working-tree snapshot
|
||||
source:
|
||||
- final/document.md#183-이-부에서-파생될-open-question-oq-3
|
||||
- final/document.md#181-qcow2-가-담는-것과-담지-않는-것
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ topicName: 실험대 환경 구성
|
||||
project: virtualization
|
||||
status: 게시 전
|
||||
questionStatus: OPEN
|
||||
sourceRevision: 9465582b5d1630eb4ae7c4e078021486919bf6b6
|
||||
sourceRevision: import-head 9465582b5d1630eb4ae7c4e078021486919bf6b6 · uncommitted-working-tree snapshot
|
||||
source:
|
||||
- final/document.md#183-이-부에서-파생될-open-question-oq-2
|
||||
- final/document.md#181-qcow2-가-담는-것과-담지-않는-것
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ topic: lab-environment-build
|
||||
topicName: 실험대 환경 구성
|
||||
project: virtualization
|
||||
status: 게시 전
|
||||
sourceRevision: 9465582b5d1630eb4ae7c4e078021486919bf6b6
|
||||
sourceRevision: import-head 9465582b5d1630eb4ae7c4e078021486919bf6b6 · uncommitted-working-tree snapshot
|
||||
source:
|
||||
- final/document.md#203-실측으로-드러난-함정-셋
|
||||
- final/document.md#284-nginx-설정-구조-sites-available-은-nginx-기능이-아니다
|
||||
|
||||
+3
-3
@@ -6,7 +6,7 @@ topic: lab-environment-build
|
||||
topicName: 실험대 환경 구성
|
||||
project: virtualization
|
||||
status: 게시 전
|
||||
sourceRevision: 9465582b5d1630eb4ae7c4e078021486919bf6b6
|
||||
sourceRevision: import-head 9465582b5d1630eb4ae7c4e078021486919bf6b6 · uncommitted-working-tree snapshot
|
||||
source:
|
||||
- final/document.md#182-이-구축에서-드러난-문서-결함의-공통-원인
|
||||
- final/document.md#178-이-부의-출처와-범위
|
||||
@@ -64,7 +64,7 @@ source:
|
||||
|
||||
## 예외
|
||||
|
||||
이 기준이 잡는 것은 순서와 위치이고 명령의 정확성은 아니다. §182 이 적었듯 개별 명령은 전부 실제로 돌았던 것이라, 이 검사를 통과해도 오타나 잘못된 플래그나 낡은 옵션은 그대로 지나간다. 04 단계의 인증서 경로가 그런 경우다. 와일드카드로 받은 인증서 묶음의 이름(lineage)은 live/hyeonworks.com/ 인데 문서에는 live/auth.hyeonworks.com/ 이라 적혀 있었다. 단계 순서를 맞춰도 그 줄은 틀린 채다.
|
||||
이 기준이 잡는 것은 순서와 위치이고 명령의 정확성은 아니다. §182 이 적었듯 개별 명령은 전부 실제로 돌았던 것이라, 이 검사를 통과해도 오타나 잘못된 플래그나 낡은 옵션은 그대로 지나간다. 04 단계의 인증서 경로가 그런 경우다. 2026-09-17 실제 lineage 는 `live/auth.hyeonworks.com/` 이었는데 원본 가이드에는 `live/hyeonworks.com/` 이라고 적혀 있었다. 단계 순서를 맞춰도 그 known-wrong 경로는 그대로 실패한다.
|
||||
|
||||
배포판 차이도 이 두 검사로는 안 잡힌다. 03 단계의 설정 블록에 있던 http2 on; 은 Debian 12 의 nginx 1.22 에서 unknown directive 가 됐다. 순서를 맞춰도 같은 오류가 나므로 대상 배포판에서 실제로 돌려야 드러난다.
|
||||
|
||||
@@ -78,7 +78,7 @@ source:
|
||||
|
||||
- 03 단계 : nginx 설치 단계가 없어 /etc/nginx: No such file or directory
|
||||
- 03 단계 : 설정 블록이 http2 on; 이라 Debian 12 의 nginx 1.22 에서 unknown directive
|
||||
- 04 단계 : 인증서 경로가 lineage 이름과 다르다. 와일드카드는 live/hyeonworks.com/ 인데 live/auth.hyeonworks.com/ 이라 적혀 있었다
|
||||
- 04 단계 : 인증서 경로가 실제 lineage 와 다르다. 실제는 `live/auth.hyeonworks.com/` 인데 원본 가이드는 `live/hyeonworks.com/` 을 적어 `nginx -t` 가 실패했다
|
||||
- 00·03·05·06 단계 : 저장소가 lab host 에 있다고 가정해 cp: cannot stat 'deploy/...'
|
||||
- 05 단계 : 그 단계에 없는 리소스를 -l app=bff 로 조회. BFF 는 한참 뒤에 뜬다
|
||||
- 04 단계 : 확인 명령을 칠 위치가 틀렸다. 엣지 VM 안에서 tailnet 주소를 치면 connection refused
|
||||
|
||||
+6
-6
@@ -9,17 +9,17 @@ project: virtualization
|
||||
status: 게시 전
|
||||
studio: "https://hyeonworks.com/studio/documents/f972ca27-7e18-41c1-9494-59cc6f676ae2/edit"
|
||||
pinnedVersions:
|
||||
- name: libvirt
|
||||
- name: libvirt (observed; 이 Setup이 설치 버전을 고정하지 않음)
|
||||
version: 12.7.0
|
||||
- name: Debian GNU/Linux
|
||||
version: 12 (bookworm)
|
||||
- name: cloud-init
|
||||
- name: cloud-init (observed; bookworm/latest 이미지라 exact version 미고정)
|
||||
version: 22.4.2
|
||||
source:
|
||||
- final/document.md#187-단계-01-게스트-세-대
|
||||
- final/document.md#185-가이드-묶음이-스스로-정한-규약
|
||||
- final/document.md#184-이-부의-출처와-범위
|
||||
sourceRevision: 9465582b5d1630eb4ae7c4e078021486919bf6b6
|
||||
sourceRevision: import-head 9465582b5d1630eb4ae7c4e078021486919bf6b6 · uncommitted-working-tree snapshot
|
||||
---
|
||||
|
||||
# cloud-init 시드로 게스트 세 대를 만들고 SSH 가 키로 붙게 한다
|
||||
@@ -227,7 +227,7 @@ grep -c 'ssh-ed25519\|ssh-rsa' kc-lab-1.yaml
|
||||
|
||||
**예상 결과** — `0` 과 `2`. 첫 줄이 `0` 이 아니면 `__LAB_HOST_KEY__` 같은 문자열이 남아 있고, 둘째 줄이 `2` 가 아니면 키 하나가 안 들어갔다. 두 숫자가 맞아야 시드를 만든다.
|
||||
|
||||
첫 파일에서는 여기까지다. cloud-config 스키마까지 보는 검증기는 `cloud-init schema` 인데, 그것은 게스트 안의 cloud-init `22.4.2` 이고 첫 파일을 쓰는 시점에는 게스트가 아직 없다. lab host 에 cloud-init 이 깔려 있는지는 원본 가이드에 적혀 있지 않고 재지도 않았으며(unknown), `yamllint` 은 이 실험대에 없다. 게스트가 한 대라도 뜬 뒤에는 둘째 파일부터 그 검증기로 본다. 파일을 보내고, 들어가고, 검사하고, 지운다.
|
||||
첫 파일에서는 여기까지다. cloud-config 스키마까지 보는 검증기는 `cloud-init schema` 인데, 2026-09-17 이 실험대에서 관측한 게스트의 cloud-init 은 `22.4.2` 였다. 하지만 base URL이 `bookworm/latest` 이므로 다음 다운로드도 같은 cloud-init 버전이라는 계약은 없다. 첫 파일을 쓰는 시점에는 게스트가 아직 없다. lab host 에 cloud-init 이 깔려 있는지는 원본 가이드에 적혀 있지 않고 재지도 않았으며(unknown), `yamllint` 은 이 실험대에 없다. 게스트가 한 대라도 뜬 뒤에는 둘째 파일부터 그 검증기로 본다. 파일을 보내고, 들어가고, 검사하고, 지운다.
|
||||
|
||||
```bash label="[lab host] ③ 검사할 파일을 게스트로 보낸다"
|
||||
scp kc-lab-2.yaml donghyeon@192.168.122.11:~/
|
||||
@@ -267,7 +267,7 @@ Valid cloud-config: /home/donghyeon/kc-lab-edge.yaml
|
||||
|
||||
이 실험대는 접속과 리다이렉션과 권한 설정을 두 줄에 몰아넣었다(observed).
|
||||
|
||||
```bash label="[lab host] 이 실험대가 실제로 친 형태"
|
||||
```bash label="[lab host] [reference] 이 실험대가 실제로 친 형태"
|
||||
ssh donghyeon@192.168.122.11 'umask 077; cat > ~/kc-lab-2.yaml' < kc-lab-2.yaml
|
||||
ssh donghyeon@192.168.122.11 'cloud-init schema -c ~/kc-lab-2.yaml; rm -f ~/kc-lab-2.yaml'
|
||||
```
|
||||
@@ -469,7 +469,7 @@ Domain creation completed.
|
||||
| 디스크 | base 위의 오버레이(`backing_store=`). 복사가 아니다 |
|
||||
| 시드 | `seed-<이름>.iso` — `CIDATA` 라벨 · `user-data`·`meta-data` · `bus=virtio` |
|
||||
| cloud-init 패키지 | `[curl, nftables]` |
|
||||
| 스키마 검사기 | 게스트의 cloud-init `22.4.2` |
|
||||
| 스키마 검사기 | 2026-09-17 관측: cloud-init `22.4.2` · `bookworm/latest` 사용으로 재다운로드 버전은 미고정 |
|
||||
|
||||
메모리는 처음 만들 때 세 대 다 3584MB 였고, 실험을 늘리며 5120 과 4096 으로 재배분했다(observed). 세 게스트의 배정 합은 5120+4096+1024 = 10,240MB 이고, 호스트 RAM 은 11,648MiB 다. 배정 합이 더 작아서 배정률은 10240/11648 = 87.9% 이고, 배정하지 않고 남은 것이 1,408MiB 다. 그래서 이 배치는 「Guest configured memory 총량이 Host physical RAM보다 크다」는 Memory Overcommit 에 해당하지 않는다.
|
||||
|
||||
|
||||
+2
-2
@@ -9,7 +9,7 @@ project: virtualization
|
||||
status: 게시 전
|
||||
studio: "https://hyeonworks.com/studio/documents/74a7bacf-e5d8-4129-926a-c8cf5cacb8c9/edit"
|
||||
pinnedVersions:
|
||||
- name: nginx (엣지 게스트)
|
||||
- name: nginx (엣지 게스트, observed; apt 설치 버전 미고정)
|
||||
version: 1.22.1
|
||||
- name: Debian GNU/Linux
|
||||
version: 12 (bookworm)
|
||||
@@ -20,7 +20,7 @@ source:
|
||||
- final/document.md#179-엣지를-물리-호스트에서-vm-으로-옮기면-무엇이-새로-필요해지나
|
||||
- final/document.md#180-nftables-는-앞-체인의-accept-로-뒤-체인의-reject-를-막지-못한다
|
||||
- final/document.md#184-이-부의-출처와-범위
|
||||
sourceRevision: 9465582b5d1630eb4ae7c4e078021486919bf6b6
|
||||
sourceRevision: import-head 9465582b5d1630eb4ae7c4e078021486919bf6b6 · uncommitted-working-tree snapshot
|
||||
---
|
||||
|
||||
# 엣지 게스트에 nginx 를 세우고 호스트 DNAT 으로 밖에서 들어오는 길을 연다
|
||||
|
||||
+9
-6
@@ -17,7 +17,10 @@ source:
|
||||
- final/document.md#188-단계-02-k3s-server-와-agent
|
||||
- final/document.md#185-가이드-묶음이-스스로-정한-규약
|
||||
- final/document.md#184-이-부의-출처와-범위
|
||||
sourceRevision: 9465582b5d1630eb4ae7c4e078021486919bf6b6
|
||||
sourceRevision: import-head 9465582b5d1630eb4ae7c4e078021486919bf6b6 · uncommitted-working-tree snapshot
|
||||
evidence:
|
||||
- ../../../final/evidence/raw/lab-state-before-rebuild/22-k3s-agent-install.txt
|
||||
- ../../../final/evidence/raw/lab-state-before-rebuild/23-k3s-cluster-verify.txt
|
||||
---
|
||||
|
||||
# k3s server 와 agent 를 게스트 두 대에 깔고 lab host 에서 kubectl 로 본다
|
||||
@@ -121,7 +124,7 @@ ssh donghyeon@192.168.122.11 'hostname; cat /etc/os-release | head -1'
|
||||
**목적** — `kc-lab-1` 을 control-plane 으로 세우고, 그 노드가 자기 주소를 `192.168.122.11` 로 알게 한다.
|
||||
|
||||
```bash label="[lab host] ① server 설치 스크립트를 원격으로 돌린다"
|
||||
ssh kc-lab-1 'curl -sfL https://get.k3s.io | sudo sh -s - server --node-ip 192.168.122.11'
|
||||
ssh kc-lab-1 "curl -sfL https://get.k3s.io | sudo env INSTALL_K3S_VERSION=v1.36.4+k3s1 sh -s - server --node-ip 192.168.122.11"
|
||||
```
|
||||
|
||||
```bash label="[lab host] ② 유닛이 떴고 자기 자신을 노드로 등록했는지 본다"
|
||||
@@ -255,7 +258,7 @@ chmod 600 ~/node-token
|
||||
```
|
||||
|
||||
```bash label="[kc-lab-2] ⑤ agent 를 깐다"
|
||||
curl -sfL https://get.k3s.io | sudo sh -s - agent \
|
||||
curl -sfL https://get.k3s.io | sudo env INSTALL_K3S_VERSION=v1.36.4+k3s1 sh -s - agent \
|
||||
--server https://192.168.122.11:6443 \
|
||||
--token-file ~/node-token \
|
||||
--node-ip 192.168.122.12
|
||||
@@ -315,9 +318,9 @@ exit
|
||||
|
||||
**왜 필요한가** — 설치 스크립트는 `sudo` 아래 root 로 도니 홈의 `600` 파일도 읽는다. `--token` 대신 `--token-file` 을 쓰면 토큰이 명령줄에 안 들어가므로 프로세스 목록과 셸 히스토리에 남지 않고, 토큰을 꺼낸 셸과 같은 셸에서 쳐야 한다는 제약도 없어진다.
|
||||
|
||||
이 실험대는 두 줄로 했다(observed).
|
||||
이 실험대는 두 줄로 했다(observed). 아래는 당시 실행을 보존한 historical/reference block이고, 따라 하는 절차는 위 ①~⑪처럼 검증과 삭제를 분리한다.
|
||||
|
||||
```bash label="[lab host] 이 실험대가 실제로 친 형태"
|
||||
```bash label="[lab host] [reference] 이 실험대가 실제로 친 형태"
|
||||
ssh kc-lab-1 'sudo cat /var/lib/rancher/k3s/server/node-token' \
|
||||
| ssh kc-lab-2 'sudo tee /tmp/token >/dev/null'
|
||||
ssh kc-lab-2 "curl -sfL https://get.k3s.io | sudo sh -s - agent \
|
||||
@@ -528,7 +531,7 @@ subject=O = system:nodes, CN = system:node:kc-lab-2
|
||||
|
||||
## 무엇이 관측이고 무엇이 아닌가
|
||||
|
||||
- (observed) `v1.36.4+k3s1`, 두 노드 `Ready`, INTERNAL-IP 두 개, 토큰 108자, `ExecStart` 두 줄, agent 의 `localhost:8080` 오류와 인증서 subject.
|
||||
- (observed) 2026-09-17 당시 stable installer가 선택한 `v1.36.4+k3s1`, 두 노드 `Ready`, INTERNAL-IP 두 개, 토큰 길이, `ExecStart` 두 줄, agent 의 `localhost:8080` 오류와 인증서 subject. 위 canonical 설치 명령은 재실행 때도 같은 k3s를 받도록 `INSTALL_K3S_VERSION=v1.36.4+k3s1`을 명시한다.
|
||||
- (observed) `kube-system` 에 뜬 파드 여덟 줄과 `local-path (default)` StorageClass. `svclb-traefik-` 두 줄이 DaemonSet 이 두 노드에 다 떴다는 증거가 된다.
|
||||
- (external) 쿠버네티스 1.20 이전의 평문 8080, Node authorizer 와 NodeRestriction 의 동작은 이 실험대에서 잰 값이 아니다.
|
||||
- (unknown) 3번과 4번의 나눈 형태는 이 실험대에서 치지 않았다. 이 실험대가 친 것은 원격 셸 둘을 파이프로 이은 두 줄이고, 나눈 형태로 같은 클러스터가 서는지는 다시 재지 않았다.
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ pinnedVersions:
|
||||
version: v1.36.4+k3s1
|
||||
- name: curlimages/curl
|
||||
version: 8.11.1
|
||||
sourceRevision: 9465582b5d1630eb4ae7c4e078021486919bf6b6
|
||||
sourceRevision: import-head 9465582b5d1630eb4ae7c4e078021486919bf6b6 · uncommitted-working-tree snapshot
|
||||
source:
|
||||
- final/document.md#191-단계-05-keycloak-2노드와-postgresql
|
||||
- final/document.md#184-이-부의-출처와-범위
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ source:
|
||||
- final/document.md#333-안전한-종료-순서
|
||||
- final/document.md#334-복구-순서-종료의-역순
|
||||
- final/document.md#313-k3s-server와-agent-죽였을-때가-다르다
|
||||
sourceRevision: 9465582b5d1630eb4ae7c4e078021486919bf6b6
|
||||
sourceRevision: import-head 9465582b5d1630eb4ae7c4e078021486919bf6b6 · uncommitted-working-tree snapshot
|
||||
---
|
||||
|
||||
# 실험대를 껐다 켜고 게스트 메모리를 다시 나눈다
|
||||
|
||||
+4
-4
@@ -9,15 +9,15 @@ project: virtualization
|
||||
status: 게시 전
|
||||
studio: "https://hyeonworks.com/studio/documents/7c66a553-0008-4294-a27a-687bd1bda0c1/edit"
|
||||
pinnedVersions:
|
||||
- name: libvirt
|
||||
- name: libvirt (observed; pacman 설치는 버전 미고정)
|
||||
version: 12.7.0
|
||||
- name: QEMU
|
||||
- name: QEMU (observed; pacman 설치는 버전 미고정)
|
||||
version: 11.1.1
|
||||
source:
|
||||
- final/document.md#186-단계-00-lab-host-가상화-준비
|
||||
- final/document.md#185-가이드-묶음이-스스로-정한-규약
|
||||
- final/document.md#184-이-부의-출처와-범위
|
||||
sourceRevision: 9465582b5d1630eb4ae7c4e078021486919bf6b6
|
||||
sourceRevision: import-head 9465582b5d1630eb4ae7c4e078021486919bf6b6 · uncommitted-working-tree snapshot
|
||||
---
|
||||
|
||||
# lab host 에 가상화 패키지를 깔고 virsh 가 sudo 없이 돌게 만든다
|
||||
@@ -185,7 +185,7 @@ virsh --version
|
||||
qemu-system-x86_64 --version
|
||||
```
|
||||
|
||||
**예상 결과** — 판 번호 두 줄. 이 실험대는 libvirt `12.7.0` 과 `QEMU emulator version 11.1.1` 이었다(observed).
|
||||
**예상 결과** — 판 번호 두 줄. 이 실험대는 libvirt `12.7.0` 과 `QEMU emulator version 11.1.1` 이었다(observed). 다만 위 `pacman -S --needed` 는 Arch 현재 저장소에서 받으므로 이 두 버전을 재설치에도 고정하는 명령이 아니다. frontmatter의 번호도 **tested/observed version**이지 package pin이 아니다.
|
||||
|
||||
**왜 필요한가** — 여기서 나오는 libvirt 판 번호가 뒤의 옵션 이름을 가른다. 훨씬 낮은 판이면 `virt-install --cloud-init` 같은 옵션의 동작이 달라질 수 있으니, 다음 단계에서 막힐 때 이 번호를 같이 본다.
|
||||
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ pinnedVersions:
|
||||
version: v1.36.4+k3s1
|
||||
- name: Debian GNU/Linux
|
||||
version: 12 (bookworm)
|
||||
sourceRevision: 9465582b5d1630eb4ae7c4e078021486919bf6b6
|
||||
sourceRevision: import-head 9465582b5d1630eb4ae7c4e078021486919bf6b6 · uncommitted-working-tree snapshot
|
||||
source:
|
||||
- final/document.md#192-단계-06-prometheus-와-grafana
|
||||
- final/document.md#184-이-부의-출처와-범위
|
||||
|
||||
+28
-22
@@ -23,7 +23,9 @@ source:
|
||||
- final/document.md#202-철거-실제-출력-전문
|
||||
- final/document.md#204-재구축할-때-무엇이-남아-있나
|
||||
- final/document.md#195-이-부의-출처와-범위
|
||||
sourceRevision: 9465582b5d1630eb4ae7c4e078021486919bf6b6
|
||||
sourceRevision: import-head 9465582b5d1630eb4ae7c4e078021486919bf6b6 · uncommitted-working-tree snapshot
|
||||
evidence:
|
||||
- ../../../final/evidence/raw/lab-state-before-rebuild/58-authenticator-and-token.txt
|
||||
---
|
||||
|
||||
# 실험대를 철거하고 무엇이 남는지 확인한다
|
||||
@@ -39,7 +41,7 @@ sourceRevision: 9465582b5d1630eb4ae7c4e078021486919bf6b6
|
||||
- **5120MB 를 줬는데 353MB 를 쓰고 있었다 — 선언한 양과 실제로 드는 양**
|
||||
회수된 디스크 3.1GB 를 읽는 방법이 그 기록과 같다. 선언한 크기와 실제로 차지한 크기는 다른 값이다.
|
||||
- **이 실험대의 certbot 은 HTTP-01 로 받고 있는가 DNS-01 로 받고 있는가**
|
||||
인증서를 지우지 않고 남기는 까닭이 이 물음이 안 닫혔기 때문이다. 답이 나오면 정책이 바뀐다.
|
||||
이 물음은 `dns-cloudflare` 관측으로 닫혔다. 여기서는 그 사실과 별개로 현재 자격증명이 재발급에 쓸 수 있는지를 철거 조건으로 본다.
|
||||
- **가이드대로 다시 쳐서 이 실험대가 같은 상태로 서는가**
|
||||
철거는 그 물음을 재기 위한 전제라, 지우고 다시 세워 봐야 답이 나온다.
|
||||
- **도구가 낸 출력은 대상의 상태가 아니다 — 그 명령이 무엇을 세는지부터 가른다**
|
||||
@@ -159,9 +161,9 @@ error: XML error: Cannot use host name '' in network 'default'
|
||||
|
||||
zsh 에서 루프로 돌리면 이 오류를 만난다. zsh 는 따옴표 없는 변수를 단어 분리하지 않아서, bash 에서 되던 `set -- $entry` 가 `$1` 에 문자열 전체를 넣고 `$2` 와 `$3` 을 비워서 `name=""` 이 된다. 세 줄을 값 그대로 쓰는 편이 안전하다.
|
||||
|
||||
## 인증서는 건드리지 않는다
|
||||
## 인증서는 edge guest에서 백업한다
|
||||
|
||||
**이 절의 전제가 03·04 뒤로 깨져 있다**(2026-09-17, observed). 아래 본문은 인증서가 랩 호스트의 `/etc/letsencrypt/` 에 있다고 보고 그 디렉터리를 정책으로 남긴다. 그런데 03 이 nginx 를 `kc-lab-edge` 로 옮겼고 04 가 certbot 을 거기 깔았다. **지금 서빙하는 인증서는 그 게스트 안에 산다.** 그리고 이 절차의 1번은 게스트를 `--remove-all-storage` 로 지운다 — 인증서는 그 안에서 같이 사라진다.
|
||||
2026-09-17 현재 서빙하는 인증서는 **`kc-lab-edge` 안의 `/etc/letsencrypt/`** 에 있다(observed). 03에서 nginx를 edge guest로 옮겼고 04에서 certbot도 그 guest에 설치했다. 이 절차의 1번은 guest를 `--remove-all-storage`로 지우므로, 인증서도 백업하지 않으면 guest 디스크와 함께 사라진다.
|
||||
|
||||
두 기계를 나란히 열어 보면 이렇다.
|
||||
|
||||
@@ -175,32 +177,34 @@ ssh kc-lab-edge 'sudo ls -la /etc/letsencrypt/'
|
||||
| 랩 호스트 | 있다 (읽으려면 `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가 같은 파일을 가리키게 한다.
|
||||
|
||||
```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 ~/
|
||||
```bash label="[lab host] 실제로 서빙하는 인증서를 edge guest에서 묶는다"
|
||||
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/"
|
||||
```
|
||||
|
||||
**「철거해도 남는 것」 표의 패키지 줄도 같은 이유로 틀렸다.** 그 줄은 `nginx` 와 `certbot` 을 남는 쪽에 적어 두었는데, 둘 다 게스트 안에 있으므로 게스트와 함께 사라진다. 랩 호스트에 남는 것은 `libvirt` · `qemu` · `kubectl` 과, 쓰이지 않는 호스트 쪽 `certbot` 이다.
|
||||
**예상 결과** — host의 `$HOME/letsencrypt-backup-$STAMP.tgz`가 생긴다. 이 파일은 edge guest를 `--remove-all-storage`로 지우기 전에 host 쪽으로 빠져나온 복구본이다.
|
||||
|
||||
`/etc/letsencrypt/` 는 정책으로 남긴다. 한도 때문이 아니다 — Let's Encrypt 의 「같은 이름 조합에 주당 중복 5장」 제한은 가끔 재구축하는 정도로는 근처에도 못 간다.
|
||||
패키지도 host와 guest를 나눠 본다. `kc-lab-edge` 안의 `nginx`와 `certbot`은 guest와 함께 사라지고, host에는 `libvirt`·`qemu`·`kubectl`과 현재 서빙에는 쓰이지 않는 host `certbot`이 남는다.
|
||||
|
||||
남기는 까닭은 지금 재발급이 되는지를 모르기 때문이다. 이 실험대의 이름 셋은 tailnet 주소를 가리키고, `100.64.0.0/10` 은 CGNAT(Carrier-Grade NAT, 통신사 공용 주소 변환)용 예약 대역이라 공개 인터넷에서 라우팅되지 않는다. HTTP-01 검증은 Let's Encrypt 가 우리 서버로 들어오는 방식이므로 그 주소로는 검증이 성립하지 않는다. 지금 설정이 DNS-01 이면 지우고 다시 받으면 끝이고, HTTP-01 이면 검증 방식부터 손봐야 한다. 어느 쪽인지는 certbot 설정을 읽는 열린 물음이 한 줄로 닫는다.
|
||||
정책으로 남기는 것은 guest 안의 디렉터리 자체가 아니라 **host로 복사한 인증서 backup tar**다. 한도 때문도 아니고 검증 방식이 미확정이어서도 아니다. 2026-09-17 renewal 설정에서 `authenticator = dns-cloudflare` 를 직접 읽어 **DNS-01 등록은 확인했다**.
|
||||
|
||||
§204 는 HTTP-01 인 경우를 「못 한다」가 아니라 「일이 하나 생긴다」로 적었다. 최악이라도 A 레코드를 공인 IP 로 잠깐 돌려 받거나 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를 보존한다.
|
||||
|
||||
```bash label="[lab host] 인증서를 통째로 묶어 둔다"
|
||||
sudo tar czf ~/letsencrypt-backup-$(date +%Y%m%d-%H%M%S).tgz -C /etc letsencrypt
|
||||
복원도 **새로 만든 `kc-lab-edge` 안으로** 되돌린다. host의 `/etc`에 풀지 않는다. `{{STAMP}}`에는 백업 파일 이름의 시각값을 넣는다.
|
||||
|
||||
```bash label="[lab host] 백업을 새 edge guest로 되돌린다"
|
||||
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'
|
||||
```
|
||||
|
||||
복원은 반대로 한 줄이다. `{{STAMP}}` 는 위 명령이 만든 파일 이름에 찍힌 시각을 그대로 옮겨 넣는다.
|
||||
|
||||
```bash label="[lab host] 묶어 둔 인증서를 되돌린다"
|
||||
sudo tar xzf ~/letsencrypt-backup-{{STAMP}}.tgz -C /etc
|
||||
```
|
||||
마지막 줄이 둘 다 성공해야 복원이 끝난 것이다. 인증서 파일이 돌아왔는지와 nginx가 그 경로를 실제로 읽을 수 있는지를 따로 확인한다.
|
||||
|
||||
## 구성 값
|
||||
|
||||
@@ -263,11 +267,13 @@ seed-kc-lab-{1,2,edge}.iso Capacity=370.00 KiB Allocation=372.00 KiB
|
||||
| 무엇 | 어떻게 되나 | 왜 |
|
||||
|---|---|---|
|
||||
| `base.qcow2` (335MB) | 남는다 | 다음 오버레이의 바닥 |
|
||||
| 패키지 (libvirt · qemu · nginx · certbot · kubectl) | 남는다 | 재설치가 무의미 |
|
||||
| host 패키지 (`libvirt` · `qemu` · `kubectl`, 쓰이지 않는 host `certbot`) | 남는다 | guest 삭제와 무관한 host 설치 상태 |
|
||||
| edge guest의 `nginx` · `certbot` | 사라진다 | `kc-lab-edge` 디스크와 함께 삭제된다 |
|
||||
| `~/workspace/cloud/kc-lab-{1,2}.yaml` | 남는다 | 키와 비밀번호가 들어 있다 |
|
||||
| `~/.ssh/config` 의 `kc-lab-*` 항목 | 남는다 | 재구축해도 IP 가 같다 |
|
||||
| libvirt `default` 네트워크 정의 | 남는다 | 예약만 지웠다 |
|
||||
| `/etc/letsencrypt/` | 남긴다 (정책) | 재발급이 되는지를 아직 모른다 |
|
||||
| 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 · 모든 워크로드 | 사라진다 | 게스트와 함께 |
|
||||
|
||||
+17
-14
@@ -9,13 +9,16 @@ project: virtualization
|
||||
status: 게시 전
|
||||
studio: "https://hyeonworks.com/studio/documents/975a6d61-4e34-4034-a0d2-01fea3b498a3/edit"
|
||||
pinnedVersions:
|
||||
- name: nginx (엣지 게스트)
|
||||
- name: nginx (엣지 게스트, observed; apt 설치 버전 미고정)
|
||||
version: 1.22.1
|
||||
- name: nginx (물리 호스트)
|
||||
- name: nginx (물리 호스트, observed; 이 Setup이 설치·버전을 고정하지 않음)
|
||||
version: 1.30.4
|
||||
- name: Debian GNU/Linux
|
||||
version: 12 (bookworm)
|
||||
sourceRevision: 9465582b5d1630eb4ae7c4e078021486919bf6b6
|
||||
sourceRevision: import-head 9465582b5d1630eb4ae7c4e078021486919bf6b6 · uncommitted-working-tree snapshot
|
||||
evidence:
|
||||
- ../../../final/evidence/raw/lab-state-before-rebuild/58-authenticator-and-token.txt
|
||||
- ../../../final/evidence/raw/lab-state-before-rebuild/60-doc04-step5-path-defect.txt
|
||||
source:
|
||||
- final/document.md#190-단계-04-let's-encrypt-와-인증서-갱신
|
||||
- final/document.md#184-이-부의-출처와-범위
|
||||
@@ -81,7 +84,7 @@ source:
|
||||
| 패키지 | `certbot` · `python3-certbot-dns-cloudflare` |
|
||||
| 자격증명 | `/etc/letsencrypt/cloudflare.ini` — `600`, root 만 읽기 |
|
||||
| 발급 대상 | `-d hyeonworks.com -d '*.hyeonworks.com'` |
|
||||
| lineage 디렉터리 | `/etc/letsencrypt/live/hyeonworks.com/` — 첫 번째 `-d` 에서 따온 라벨 |
|
||||
| lineage 디렉터리 | `/etc/letsencrypt/live/auth.hyeonworks.com/` — 2026-09-17 현재 실험대에서 관측한 경로 |
|
||||
| nginx 가 읽는 것 | `fullchain.pem` · `privkey.pem` |
|
||||
| 유효기간 | 오늘 + 90일 |
|
||||
| deploy 훅 | `/etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh` (`chmod +x`) |
|
||||
@@ -98,7 +101,7 @@ source:
|
||||
|
||||
가이드는 이 선택을 우열로 적지 않는다. 공개 서버라면 HTTP-01 이 맞고, 토큰도 DNS 연동도 없어 관리할 것이 적다.
|
||||
|
||||
아래 절차는 DNS-01 로 받는 형태다. 다만 **지금 돌고 있는 이 실험대의 certbot 이 어느 쪽으로 등록되어 있는지는 아직 읽지 못했다**(unknown) — 호스트의 `sudo` 가 비밀번호를 요구해 `/etc/letsencrypt/renewal/*.conf` 의 `authenticator` 를 비대화식으로 읽을 수 없었다. 이 실험대의 문서 두 개도 이 대목에서 서로 어긋나, 한쪽은 DNS-01 로 결론냈고 다른 한쪽은 같은 04 단계를 `certbot certonly --webroot` 로 적어 두었다. 어느 쪽이 실제로 등록되어 있는지는 그 한 줄을 읽어야 갈린다.
|
||||
아래 절차는 DNS-01 로 받는 형태이고 **이 실험대의 현재 등록 방식도 DNS-01 로 확인됐다**(2026-09-17, observed). `sudo sh -c` 안에서 renewal 설정을 읽자 `authenticator = dns-cloudflare` 와 `dns_cloudflare_credentials = /etc/letsencrypt/cloudflare.ini` 가 나왔다. 다만 같은 후속 원문에서 토큰 길이는 0자였고 Cloudflare 검증은 실패했다. 따라서 검증 방식은 확정됐지만 현재 자격증명이 재발급에 쓸 수 있는지는 별도 확인이 필요하다.
|
||||
|
||||
## 전제와 되돌리기
|
||||
|
||||
@@ -284,7 +287,7 @@ sudo certbot certificates
|
||||
```
|
||||
|
||||
```bash label="[kc-lab-edge] ④ 인증서가 실제로 어떤 이름에 유효한지 본다"
|
||||
sudo openssl x509 -noout -ext subjectAltName -in /etc/letsencrypt/live/hyeonworks.com/fullchain.pem
|
||||
sudo openssl x509 -noout -ext subjectAltName -in /etc/letsencrypt/live/auth.hyeonworks.com/fullchain.pem
|
||||
```
|
||||
|
||||
**예상 결과** — ① 의 마지막 줄이 `The dry run was successful.` 이고, ② 는 `Successfully received certificate.` 와 저장 경로를 찍는다. ③ 에서 볼 것은 네 줄이다.
|
||||
@@ -293,8 +296,8 @@ sudo openssl x509 -noout -ext subjectAltName -in /etc/letsencrypt/live/hyeonwork
|
||||
|---|---|
|
||||
| `Domains:` | `hyeonworks.com *.hyeonworks.com` — 한 줄에 둘 다 |
|
||||
| `Expiry Date:` | 오늘 + 90일, `VALID` |
|
||||
| `Certificate Path:` | `/etc/letsencrypt/live/hyeonworks.com/fullchain.pem` |
|
||||
| `Private Key Path:` | `/etc/letsencrypt/live/hyeonworks.com/privkey.pem` |
|
||||
| `Certificate Path:` | `/etc/letsencrypt/live/auth.hyeonworks.com/fullchain.pem` — 2026-09-17 현재 관측 |
|
||||
| `Private Key Path:` | `/etc/letsencrypt/live/auth.hyeonworks.com/privkey.pem` |
|
||||
|
||||
④ 는 `DNS:hyeonworks.com, DNS:*.hyeonworks.com` 두 개를 내놓고 `auth.hyeonworks.com` 은 두 번째에 걸린다.
|
||||
|
||||
@@ -304,8 +307,8 @@ sudo openssl x509 -noout -ext subjectAltName -in /etc/letsencrypt/live/hyeonwork
|
||||
|
||||
| 무엇 | 정해지는 방식 |
|
||||
|---|---|
|
||||
| 디렉터리 이름 `live/hyeonworks.com/` | certbot 이 이 묶음을 관리하려고 붙인 라벨. 첫 번째 `-d` 에서 따오고 서빙과 무관하다 |
|
||||
| SAN 목록 | 브라우저가 보는 실제 유효 호스트명. `-d` 로 준 이름 전부 |
|
||||
| 디렉터리 이름 `live/auth.hyeonworks.com/` | 2026-09-17 현재 실험대에서 실제로 존재한 lineage. `certbot certificates` 가 찍은 경로를 따른다 |
|
||||
| SAN 목록 | 브라우저가 보는 실제 유효 호스트명. `-d` 로 준 이름 전부이며 lineage 이름과 별개 |
|
||||
|
||||
**문제가 생기면** — 최초 실행이면 계정 등록 대화가 먼저 뜬다. 이메일을 비우면 `Invalid email address: .` 로 되묻고, 약관은 `Y`, 뉴스레터는 발급과 무관하므로 `N` 이다. 프롬프트 없이 돌리려면 `-m <이메일> --agree-tos --no-eff-email` 을 붙인다. `--register-unsafely-without-email` 은 쓰지 않는다 — 갱신 실패를 알려 줄 통로가 사라지는데, 6번이 재는 것이 바로 그 갱신이다. DNS-01 은 TXT 레코드가 퍼질 때까지 기다리느라 수십 초 걸리므로 중간에 끊지 않는다.
|
||||
|
||||
@@ -334,8 +337,8 @@ server {
|
||||
listen 443 ssl http2 default_server;
|
||||
server_name _;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/hyeonworks.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/hyeonworks.com/privkey.pem;
|
||||
ssl_certificate /etc/letsencrypt/live/auth.hyeonworks.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/auth.hyeonworks.com/privkey.pem;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
|
||||
location / {
|
||||
@@ -369,7 +372,7 @@ sudo nginx -t && sudo systemctl reload nginx
|
||||
|
||||
`ssl_certificate` 에 적는 것은 `cert.pem` 이 아니라 `fullchain.pem` 이다. 서버 인증서만 보내면 중간 인증서가 빠져 체인이 끊기는데, 브라우저는 대개 캐시나 AIA 로 보완해서 정상으로 보이고 캐시가 없는 클라이언트에서만 깨지므로 발견이 늦다. 4번 ③ 에서 certbot 이 찍어 준 경로와 한 글자도 다르면 안 된다.
|
||||
|
||||
**★ 위 ② 의 인증서 경로가 틀렸다**(2026-09-17, observed). `live/hyeonworks.com/` 이라고 적혀 있는데 certbot 이 만드는 디렉터리는 **`live/auth.hyeonworks.com/`** 이다. 이름을 여럿 담은 인증서라도 디렉터리 이름은 `-d` 로 처음 준 이름 하나를 쓴다. 그대로 치면 3번에서 nginx 가 안 뜬다.
|
||||
**★ 원본 가이드 04 의 5단계에는 known-wrong 경로가 있었다**(2026-09-17, observed). 가이드는 `live/hyeonworks.com/` 을 적었지만 실제 `live/` 아래에는 `auth.hyeonworks.com` 이 있었고, 그 잘못된 경로로 `nginx -t` 를 돌리면 아래처럼 실패했다. 위 canonical 절차는 이 관측을 반영해 처음부터 `live/auth.hyeonworks.com/` 을 쓴다.
|
||||
|
||||
```text label="문서 그대로 쳤을 때"
|
||||
[emerg] cannot load certificate "/etc/letsencrypt/live/hyeonworks.com/fullchain.pem":
|
||||
@@ -388,7 +391,7 @@ auth.hyeonworks.com
|
||||
Certificate Path: /etc/letsencrypt/live/auth.hyeonworks.com/fullchain.pem
|
||||
```
|
||||
|
||||
**아래 「문제가 생기면」의 진단은 방향이 거꾸로다.** `live/auth.hyeonworks.com/` 이 없는 경로가 아니라 **그쪽이 맞는 경로**이고, `live/hyeonworks.com/` 이 디스크에 없다. 4번 ③ 이 찍어 준 경로를 그대로 옮기라는 원칙은 옳고, 위 ② 가 그 원칙을 스스로 어겼다.
|
||||
현재 truth는 하나다 — **이 실험대의 lineage는 `live/auth.hyeonworks.com/`이고, `live/hyeonworks.com/`은 원본 가이드에 남았던 historical wrong path**다. 다른 실험대에서 이름을 추측하지 말고 `certbot certificates` 가 찍은 `Certificate Path`를 그대로 nginx에 옮긴다.
|
||||
|
||||
**문제가 생기면** — `cannot load certificate` 로 막히면 경로를 본다. 자기 실험대의 디렉터리 이름은 `sudo ls /etc/letsencrypt/live/` 가 답한다. 확인 ② 에서 본 판 번호가 1.25.1 미만인데 `http2` 를 지시어로 썼다면 여기서 `unknown directive "http2"` 가 나온다.
|
||||
|
||||
|
||||
+6
-1
@@ -9,6 +9,9 @@ project: virtualization
|
||||
status: 초안
|
||||
basisVersion: Linux memory reclaim · swap · OOM Killer · cgroup memory limit · QEMU 의 Guest RAM backing
|
||||
studio: "https://hyeonworks.com/studio/documents/647d6d11-5bb2-4028-a530-b10c8925aa11/edit"
|
||||
assets:
|
||||
- key: memory-pressure-reclaim-swap-oom
|
||||
file: ../../../final/assets/diagrams/memory-pressure-reclaim-swap-oom/memory-pressure-reclaim-swap-oom.svg
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
source:
|
||||
- final/document.md#57-memory-overcommit
|
||||
@@ -48,6 +51,8 @@ source:
|
||||
|
||||
<!-- body:start -->
|
||||
|
||||

|
||||
|
||||
## CPU 가 모자랄 때와 RAM 이 모자랄 때
|
||||
|
||||
가상 머신들에 설정한 메모리 총량이 호스트의 물리 RAM 보다 큰 구성을 메모리 초과 할당(memory overcommit)이라고 한다. 근거 문서는 이 구성을 예시 숫자로 설명한다.
|
||||
@@ -90,7 +95,7 @@ QEMU 가 게스트 RAM 으로 잡아 둔 호스트 물리 페이지가 스왑으
|
||||
|
||||
메모리는 CPU 와 네트워크, 스토리지 실행을 모두 받치는 계층이라 압박이 메모리 안에서 끝나지 않는다. CPU 사용률이 낮은 구간에도 이 경로가 돌고 있을 수 있다.
|
||||
|
||||
이 절에는 그림을 넣지 않았다. 근거 절이 「Guest와 Host가 동시에 memory pressure를 겪으면」이라고 적어 네 갈래에 순서가 없는데, 그림 도구가 이 문맥에서 허용한 구성 문법은 순서를 요구하는 `sequence` 하나뿐이었다. 그리려면 없는 순서를 지어내야 한다.
|
||||
이 흐름은 순서를 억지로 만드는 sequence가 아니라, **Host memory pressure가 reclaim 뒤 clean-page 회수·Host swap·storage contention·Host OOM으로 갈라지는 관계**를 component-flow로 그렸다. Guest OOM은 같은 원인선에 붙이면 계층을 섞게 되므로 그림에서는 합치지 않고 아래 절에서 따로 구분한다.
|
||||
|
||||
## 회수로도 모자라면 OOM
|
||||
|
||||
|
||||
+5
@@ -9,6 +9,9 @@ project: virtualization
|
||||
status: 초안
|
||||
basisVersion: QEMU block backend 세 형태 — qcow2 파일 · RAW 파일 · Host block device. libvirt 로 정의한 VM 기준이고 제4부가 QEMU 와 libvirt 판을 고정하지 않았다
|
||||
studio: "https://hyeonworks.com/studio/documents/892b6087-8961-486c-b4d9-d3dd46bc2605/edit"
|
||||
assets:
|
||||
- key: qemu-block-backend-forms
|
||||
file: ../../../final/assets/diagrams/qemu-block-backend-forms/qemu-block-backend-forms.svg
|
||||
sourceRevision: no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다
|
||||
source:
|
||||
- final/document.md#140-vm-boundary를-넘으면-qemu가-등장
|
||||
@@ -48,6 +51,8 @@ source:
|
||||
|
||||
<!-- body:start -->
|
||||
|
||||

|
||||
|
||||
## 가상 머신 경계를 넘으면 QEMU 가 받는다
|
||||
|
||||
게스트의 `virtio-blk` 가 `virtqueue` 에 게시한 요청은 가상 머신 경계를 넘어 호스트 사용자 공간의 QEMU 로 간다. QEMU 안에서는 `virtio-blk` 장치 모델과 블록 백엔드가 그 요청을 받는다. QEMU 는 게스트에게 가상 블록 장치를 노출하고, 게스트의 가상 I/O 를 호스트 백엔드에 연결한다.
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user