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>
This commit is contained in:
co-authored by
Claude Opus 5
parent
ab59130196
commit
024362d096
@@ -22,7 +22,7 @@
|
||||
|
||||
| 무엇 | 지금 |
|
||||
|---|---|
|
||||
| 증거 | `evidence/raw` 1 · `evidence/meta` 1 — 두 폴더에 있는 것은 폴더를 설명하는 `README.txt` 한 장씩이고 캡처한 원문은 0건이다 (`rendered`·`browser` 도 같다) |
|
||||
| 증거 | **2026-09-17 기준** `evidence/raw` 43 · `evidence/meta` 1. 2026-09-16 까지는 두 폴더에 `README.txt` 한 장씩뿐이고 캡처한 원문이 0건이었다. 2026-09-17 에 실험대를 철거하고 다시 세우며 `raw/lab-state-before-rebuild/` 에 42건을 남겼다 (`rendered`·`browser` 는 여전히 0건이다) |
|
||||
|
||||
제5~9부의 코드 블록에 있는 명령 출력은 전부 **사람이 문서에 옮겨 적은 것**이다. 실행한 명령·cwd·실행 시각·종료 코드를 짝지어 남긴 `evidence/meta/` 항목이 하나도 없으므로, 그 값들이 언제 어느 상태에서 나왔는지는 **문서가 스스로 밝힌 날짜**(제7부 2026-09-10, §218 2026-09-03)까지만 확인된다. 반입한 `source/` 에도 캡처 원문은 없었다 — 원본 문서들도 출력을 본문에 옮겨 적는 방식이었다.
|
||||
|
||||
@@ -8277,6 +8277,34 @@ echo 'export LIBVIRT_DEFAULT_URI=qemu:///system' >> ~/.bashrc
|
||||
virsh uri
|
||||
```
|
||||
|
||||
**★ rc 파일에만 넣으면 `ssh lab-host '명령'` 에서는 안 먹는다**(2026-09-17, observed).
|
||||
그 형태는 비대화형 셸이라 `.bashrc`·`.zshrc` 를 안 읽는다.
|
||||
|
||||
```
|
||||
ssh 로 한 줄 LIBVIRT_DEFAULT_URI=[(비어 있음)]
|
||||
대화형 셸 LIBVIRT_DEFAULT_URI=qemu:///system
|
||||
```
|
||||
|
||||
셸과 무관하게 고정하려면 libvirt 자기 설정에 넣는다. 이 실험대에는 그 파일도
|
||||
있었다(observed).
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.config/libvirt
|
||||
echo 'uri_default = "qemu:///system"' >> ~/.config/libvirt/libvirt.conf
|
||||
virsh uri
|
||||
```
|
||||
|
||||
```
|
||||
/home/donghyeon/.config/libvirt/libvirt.conf:uri_default = "qemu:///system"
|
||||
qemu:///system
|
||||
```
|
||||
|
||||
**둘 중 하나만 있으면 어디선가 어긋난다.** rc 만 있으면 `ssh` 한 줄에서
|
||||
`qemu:///session` 을 보게 되고 — 그쪽에는 VM 이 없으므로 목록이 빈 채로 나와
|
||||
「VM 이 죽었다」로 읽힌다 — libvirt 설정만 있으면 대화형 셸의
|
||||
`echo $LIBVIRT_DEFAULT_URI` 가 비어서 「설정이 안 됐다」로 읽힌다.
|
||||
|
||||
|
||||
**따라 하는 사람은** 편집기로 연다. `echo >>` 는 가이드를 두 번 따라 하면 같은
|
||||
줄을 한 번 더 붙이고, 파일에 이미 무엇이 들어 있는지도 보여 주지 않는다.
|
||||
|
||||
@@ -9297,8 +9325,40 @@ rm ~/node-token
|
||||
|
||||
토큰이 `/tmp/token` 대신 자기 홈에 놓이고, `sudo tee` 대신 `scp` 와 `chmod 600`
|
||||
이 그 파일을 만든다. 설치 스크립트는 `sudo` 아래 root 로 도니 홈의 `600` 파일도
|
||||
읽는다. 설치가 끝나면 지우는 것은 양쪽이 같다. 이 여덟 줄 형태는 이 실험대에서
|
||||
치지 않았다(unknown).
|
||||
읽는다.
|
||||
|
||||
**★ 그런데 마지막 줄의 `rm ~/node-token` 만 치면 이 노드는 다시 못 뜬다.** 설치
|
||||
스크립트가 `--token-file` 에 준 경로를 유닛의 `ExecStart` 에 **그대로 굽기**
|
||||
때문에, 파일이 사라지면 agent 가 영원히 그 파일을 기다린다. 2026-09-17 에 A-4 를
|
||||
돌려 노드 전원을 뽑았다 다시 켜면서 그대로 겪었다(observed) — VM 은 떴는데 노드가
|
||||
`NotReady` 에서 안 돌아왔다.
|
||||
|
||||
```
|
||||
k3s[522]: level=info msg="Waiting for file \"/home/donghyeon/node-token\" to be created"
|
||||
k3s-agent.service: Scheduled restart job, restart counter is at 1.
|
||||
```
|
||||
|
||||
**지우기 전에 토큰을 root 만 읽는 자리로 옮기고 유닛을 그쪽으로 돌린다.** 그렇게
|
||||
하고 홈의 사본을 지우자 agent 를 완전히 세웠다 다시 켜도 `active` 였고 노드도
|
||||
`Ready` 로 돌아왔다(observed).
|
||||
|
||||
```bash
|
||||
sudo install -m 600 -o root -g root ~/node-token /etc/rancher/node-token
|
||||
sudo ls -l /etc/rancher/node-token
|
||||
sudo sed -i "s|$HOME/node-token|/etc/rancher/node-token|" /etc/systemd/system/k3s-agent.service
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl restart k3s-agent
|
||||
rm ~/node-token
|
||||
```
|
||||
|
||||
```
|
||||
-rw------- 1 root root 109 /etc/rancher/node-token
|
||||
'--token-file' \
|
||||
'/etc/rancher/node-token' \
|
||||
```
|
||||
|
||||
이 여덟 줄 형태는 2026-09-17 에 쳤다 — 설치 자체는 이 판으로 다시 돌리지 않았고,
|
||||
유닛을 고쳐 다시 시작하는 쪽만 확인했다(observed).
|
||||
|
||||
**확인** — agent 가 클러스터에 들어왔는가, 그리고 **제 주소로** 들어왔는가
|
||||
|
||||
@@ -9627,7 +9687,7 @@ kubectl get nodes
|
||||
**확인 ① nginx 가 깔려 있는가** — `[kc-lab-edge]`
|
||||
|
||||
```bash
|
||||
which nginx
|
||||
ls /etc/nginx/
|
||||
```
|
||||
|
||||
**관측** — 2026-09-11, 새로 만든 `kc-lab-edge` 에서 실제로 나온 것(observed)
|
||||
@@ -9637,10 +9697,25 @@ donghyeon@kc-lab-edge:~$ cd /etc/nginx/
|
||||
-bash: cd: /etc/nginx/: No such file or directory
|
||||
```
|
||||
|
||||
**어디를 봐야 하는가** — `which nginx` 가 **아무것도 안 찍으면** 미설치다.
|
||||
**어디를 봐야 하는가** — `No such file or directory` 가 나오면 미설치다.
|
||||
`/etc/nginx` 디렉터리는 패키지가 만든다. 그것이 없다는 것은 **경로를 잘못 찾은
|
||||
것이 아니라 설치가 안 된 것**이다.
|
||||
|
||||
**★ 여기에 `which nginx` 를 쓰지 않는다.** 처음에는 그렇게 적어 두었는데 **깔려
|
||||
있을 때도 똑같이 아무것도 안 찍힌다.** 2026-09-17 에 nginx 가 돌고 있는
|
||||
`kc-lab-edge` 에서 직접 쳐서 확인했다(observed).
|
||||
|
||||
```
|
||||
which nginx -> (아무것도 안 찍고 exit 1)
|
||||
PATH -> /usr/local/bin:/usr/bin:/bin:/usr/games
|
||||
sudo nginx -v -> nginx version: nginx/1.22.1
|
||||
```
|
||||
|
||||
실행 파일은 `/usr/sbin/nginx` 인데 데비안의 일반 사용자 `PATH` 에 `/usr/sbin` 이
|
||||
없다. 그래서 `which nginx` 의 빈손은 **「안 깔렸다」가 아니라 「이 PATH 에서는 안
|
||||
보인다」**이고, 깔린 경우와 안 깔린 경우가 같은 출력이라 **판정에 쓸 수 없다.**
|
||||
판 번호를 볼 때도 `nginx -v` 가 아니라 `sudo nginx -v` 로 친다.
|
||||
|
||||
**실행 절차**
|
||||
|
||||
번호는 가이드 03 의 것을 그대로 쓴다.
|
||||
@@ -9783,7 +9858,8 @@ ls -l /etc/nginx/sites-enabled/
|
||||
sudo nginx -t && sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
통과하면 이런 형태다. Debian 12 는 `[warn]` 한 줄을 늘 같이 내놓는다(observed).
|
||||
통과하면 이런 형태다. 2026-09-11 의 엣지에서는 `[warn]` 한 줄이 앞에 붙어 세
|
||||
줄이었다(observed).
|
||||
|
||||
```
|
||||
nginx: [warn] could not build optimal types_hash, you should increase either
|
||||
@@ -9792,6 +9868,20 @@ nginx: configuration file /etc/nginx/nginx.conf syntax is ok
|
||||
nginx: configuration file /etc/nginx/nginx.conf test is successful
|
||||
```
|
||||
|
||||
**★ 그 `[warn]` 은 늘 나오지는 않는다.** 처음에는 「Debian 12 는 늘 같이
|
||||
내놓는다」고 적었는데, 2026-09-17 에 같은 Debian 12 · 같은 `nginx/1.22.1` 엣지를
|
||||
새로 세우고 `sudo nginx -t` 를 세 번 쳤더니 **한 번도 안 나왔다**(observed). 두
|
||||
줄뿐이었다.
|
||||
|
||||
```
|
||||
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
|
||||
nginx: configuration file /etc/nginx/nginx.conf test is successful
|
||||
```
|
||||
|
||||
그러니 이 줄이 안 보인다고 설정이 덜 읽힌 것이 아니다. **판정은 `syntax is ok`
|
||||
와 `test is successful` 두 마디로만 하고**, `[warn]` 줄은 나오면 무시하고 안
|
||||
나오면 없는 대로 둔다.
|
||||
|
||||
**어디를 봐야 하는가** — `nginx -t` 가 내놓는 **마지막 줄**이다. `syntax is ok`
|
||||
와 `test is successful` 두 마디가 다 나와야 통과다. 앞에 나오는 `[warn]` 줄(예:
|
||||
`types_hash_max_size`)은 통과를 막지 않는다 — **04 에서 이 경고를 실패로
|
||||
@@ -10035,9 +10125,32 @@ curl -s -o /dev/null -w '%{http_code}\n' http://192.168.122.12
|
||||
curl -s -o /dev/null -w '%{http_code} %{redirect_url}\n' http://192.168.122.10
|
||||
```
|
||||
|
||||
**어디를 봐야 하는가** — `301` 과 `Location` 이 나오는가. 여기서 막히면 문제는
|
||||
**엣지 안**이다(설정·기동). 통과하는데 아래 ③ 이 안 되면 문제는 **DNAT** 이다.
|
||||
이 한 층을 끼워 두면 그 둘을 헷갈리지 않는다.
|
||||
**어디를 봐야 하는가** — **엣지가 답을 하는가.** 여기서 막히면 문제는 **엣지
|
||||
안**이다(설정·기동). 통과하는데 아래 ③ 이 안 되면 문제는 **DNAT** 이다. 이 한
|
||||
층을 끼워 두면 그 둘을 헷갈리지 않는다.
|
||||
|
||||
**★ 이 단계에서 나오는 코드는 `301` 이 아니라 `404` 다.** 처음에는 `301` 이라고
|
||||
적었는데, 2026-09-17 에 이 절을 그대로 따라 엣지를 다시 세우고 재 보니 이렇게
|
||||
나왔다(observed).
|
||||
|
||||
```
|
||||
HTTP/1.1 404 Not Found
|
||||
Server: nginx/1.22.1
|
||||
Content-Type: text/plain; charset=utf-8
|
||||
Content-Length: 19
|
||||
```
|
||||
|
||||
```
|
||||
404 page not found
|
||||
```
|
||||
|
||||
`Server:` 가 `nginx/1.22.1` 이고 본문이 Traefik 의 `404 page not found` 라는 것이
|
||||
**엣지가 받아서 Traefik 으로 넘겼다**는 증거다. 이 단계의 설정에는 리다이렉트를 낼
|
||||
블록이 없다 — 위 「② 에 쓸 내용」은 `listen 80` 블록 하나뿐이고 그 안은
|
||||
`proxy_pass` 다. `301` 은 **04 가 443 블록과 80→443 리다이렉트를 얹은 뒤**의
|
||||
값이고, 처음 적은 `301` 은 다 끝난 실험대에서 잰 것을 이 자리로 옮겨 적은
|
||||
것이었다. 그래서 이 층의 통과 기준은 코드값이 아니라 **「`curl: (7)` 없이 응답이
|
||||
온다」와 `Server: nginx`** 두 가지다.
|
||||
|
||||
**확인 ③ 밖에서, 즉 DNAT 을 거쳐 닿나**
|
||||
|
||||
@@ -10073,6 +10186,21 @@ curl -s -o /dev/null -w '%{http_code} %{redirect_url}\n' http://auth.hyeonworks.
|
||||
301 https://auth.hyeonworks.com/
|
||||
```
|
||||
|
||||
**★ 이 `301` 도 04 이후의 값이다.** 03 까지만 한 상태라면 ② 와 같은 `404` 가
|
||||
정상이고, 이 층이 묻는 것은 코드값이 아니라 **「밖에서 친 것이 엣지까지
|
||||
닿았는가」**다 — ② 와 같은 응답이 밖에서도 오면 DNAT 과 구멍이 살아 있다는 뜻이다.
|
||||
|
||||
2026-09-17 에 밖에서 재 보니 **닿지 않았다**(observed).
|
||||
|
||||
```
|
||||
curl: (7) Failed to connect to auth.hyeonworks.com port 80 after 54 ms
|
||||
```
|
||||
|
||||
같은 시각에 호스트 안에서 친 `http://192.168.122.10` 은 `404` 로 답했다. 아래
|
||||
「막히면」 표의 마지막 줄이 가리키는 상태 그대로이고, 원인도 거기 적힌 대로였다 —
|
||||
**이 호스트에 실제로 깔려 있는 유닛에 `ExecStartPost` 줄이 없다.** 아래 「배포된
|
||||
것과 적어 둔 것이 다르다」를 본다.
|
||||
|
||||
**확인 ④ 끝까지 닿나** (TLS 이후)
|
||||
|
||||
```bash
|
||||
@@ -10153,10 +10281,10 @@ error 로그에 있었는데 잘려 있었고, access 로그에는 **3492자**
|
||||
| ① 이 `502` | Traefik 은 떴는데 백엔드가 없음 | Ingress 확인 |
|
||||
| ② 가 응답 없음 | nginx 가 안 떴거나 방화벽 | `systemctl status nginx` |
|
||||
| ③ 이 `502` | 인증서 문제 또는 upstream 다운 | 04 · 위 로그 |
|
||||
| `/etc/nginx: No such file or directory` | **nginx 미설치.** cloud-init 은 안 깐다 | `which nginx` |
|
||||
| `/etc/nginx: No such file or directory` | **nginx 미설치.** cloud-init 은 안 깐다 | `ls /etc/nginx/` — `which nginx` 는 깔려 있어도 빈손이라 못 가른다 |
|
||||
| `nginx -t` 가 `duplicate default server` | `sites-enabled/default` 가 살아 있다 | `ls -l /etc/nginx/sites-enabled/` |
|
||||
| 설정을 썼는데 아무 변화가 없다 | 경로 오타 — `site-available`(단수)에 썼다 | `ls /etc/nginx/sites-available/` |
|
||||
| `unknown directive "http2"` | nginx < 1.25.1. Debian 12 는 1.22 다 | `nginx -v` → `listen 443 ssl http2` 형태로 |
|
||||
| `unknown directive "http2"` | nginx < 1.25.1. Debian 12 는 1.22 다 | `sudo nginx -v` → `listen 443 ssl http2` 형태로 |
|
||||
| `systemctl reload` 가 실패한다 | 설정이 `nginx -t` 를 못 통과했다 | **reload 말고 `sudo nginx -t` 를 먼저** 친다 |
|
||||
| `cp: cannot stat 'deploy/...'` | 엣지에서 쳤거나, lab host 에 리포가 없다 | `ls ~/workspace` → 없으면 00 의 0번으로 |
|
||||
| `Unit lab-edge-dnat.service does not exist` | 유닛 파일이 없거나, `/etc/systemd/` 에 썼거나, `daemon-reload` 를 안 했다 | `find /etc/systemd -name 'lab-edge-dnat*'` — `system/` 아래여야 한다 |
|
||||
@@ -10169,7 +10297,8 @@ error 로그에 있었는데 잘려 있었고, access 로그에는 **3492자**
|
||||
- (observed) 2026-09-11 새로 만든 엣지에서 `/etc/nginx` 가 없던 것, `nginx -t`
|
||||
출력 세 줄, 층별 확인 ①~④ 의 코드, `301 https://auth.hyeonworks.com/`,
|
||||
upstream 실패 errno 세 줄, access 로그 3492자
|
||||
- (observed) `.nft` 와 유닛 파일의 내용은 저장소 원본과 같다
|
||||
- (observed) 위에 실은 `.nft` 와 유닛의 내용은 **저장소 원본**과 같다. 다만
|
||||
**호스트에 실제로 깔려 있는 두 파일은 그것이 아니다** — 바로 아래를 본다
|
||||
- (unknown) `curl -I http://192.168.122.11` 의 전체 출력은 캡처해 두지 않았다.
|
||||
가이드도 봐야 할 줄만 적었다
|
||||
- (unknown) `systemctl status nginx` 두 번, `ls -l /etc/nginx/sites-enabled/`,
|
||||
@@ -10181,6 +10310,43 @@ error 로그에 있었는데 잘려 있었고, access 로그에는 **3492자**
|
||||
- **되돌리기는 가이드 7편 가운데 이 편에만 있다.** 위 네 줄이 원문 그대로이고,
|
||||
그 네 줄을 실제로 쳐서 걷어내 본 기록은 없다(unknown)
|
||||
|
||||
**배포된 것과 적어 둔 것이 다르다**
|
||||
|
||||
2026-09-17 에 이 절을 따라 실험대를 다시 세우다 호스트의 두 파일을 열어 봤더니
|
||||
**위에 실은 내용과 달랐다**(observed). 저장소 원본을 고친 뒤 호스트에 다시
|
||||
배포하지 않았다 — 파일 시각이 그렇게 말한다.
|
||||
|
||||
| 파일 | 호스트에 깔린 것 (`11:00` · `10:52`) | 저장소 원본과 위 본문 (`13:30`) |
|
||||
|---|---|---|
|
||||
| `lab-edge-dnat.nft` | `prerouting` 뒤에 `forward` 체인이 **그대로 있다** (`priority filter - 10` · `ct state new accept`) | 그 체인이 **없다.** 자리에 「왜 뺐는지」 주석만 남겼다 |
|
||||
| `lab-edge-dnat.service` | `ExecStartPost` 줄이 **없다** | `ExecStartPost=-…insert rule ip libvirt_network guest_input…` 이 있다 |
|
||||
|
||||
**이것이 위 확인 ③ 의 `curl: (7)` 을 그대로 설명한다.** 고친 쪽의 요점이 「앞
|
||||
체인의 `accept` 로는 뒤 체인의 `reject` 를 못 막으니 구멍은 libvirt 체인 안에
|
||||
뚫는다」(§180)인데, 호스트에는 **효과가 없는 쪽만 깔려 있고 효과가 있는 쪽은 안
|
||||
깔려 있다.** 게다가 그 구멍은 런타임 상태라 libvirt 가 네트워크를 다시 세우면
|
||||
날아가고, 다시 넣어 줄 `ExecStartPost` 가 유닛에 없으니 **실험대를 다시 세운
|
||||
직후에는 밖에서 못 들어온다.**
|
||||
|
||||
**그래서 03 을 다시 밟을 때 4·5 번을 「이미 있으니 건너뛴다」로 넘기지 않는다.**
|
||||
파일이 있는 것과 그 파일이 위 본문의 내용인 것은 다르다. 두 파일을 열어 위와 한
|
||||
줄씩 대조하거나, 갈린 두 자리만 세어 본다.
|
||||
|
||||
```bash
|
||||
grep -c 'chain forward' /etc/nftables.d/lab-edge-dnat.nft
|
||||
grep -c ExecStartPost /etc/systemd/system/lab-edge-dnat.service
|
||||
```
|
||||
|
||||
`0` 과 `1` 이 나와야 맞다. 반대로 나오면 고치기 전 판이 깔려 있는 것이고, 다시
|
||||
써서 `sudo systemctl daemon-reload && sudo systemctl restart lab-edge-dnat.service`
|
||||
로 올린다.
|
||||
|
||||
**그런데 고쳤는지는 이번에 확인하지 못했다**(unknown). 이 호스트의 `sudo` 는
|
||||
비밀번호를 묻고 이번 작업에는 그 비밀번호가 없었다. 그래서 `nft list table ip
|
||||
lab_edge` 도 `guest_input` 체인 조회도 못 돌렸고, 두 파일을 다시 배포하지도
|
||||
않았다. 위 표는 **파일 내용과 타임스탬프만으로** 적은 것이고, `reject` 줄의
|
||||
카운터가 실제로 올라갔는지는 안 봤다.
|
||||
|
||||
## 190. 단계 04 — Let's Encrypt 와 인증서 갱신
|
||||
|
||||
**어디서 치는가** — 이 단계도 셸이 갈린다. 1~3·5 번은 전부 `[kc-lab-edge]` 에서
|
||||
@@ -10278,13 +10444,29 @@ DNS 연동도 없어 관리할 것이 적다. 감수한 비용은 Cloudflare API
|
||||
**확인 ① 이 도메인이 무엇으로 풀리는가**
|
||||
|
||||
```bash
|
||||
dig +short auth.hyeonworks.com
|
||||
getent hosts auth.hyeonworks.com
|
||||
```
|
||||
|
||||
**실측**(observed)
|
||||
|
||||
```
|
||||
100.83.212.4
|
||||
100.83.212.4 auth.hyeonworks.com
|
||||
```
|
||||
|
||||
**★ 여기에 `dig` 를 바로 쓰지 않는다.** 처음에는 `dig +short auth.hyeonworks.com`
|
||||
이라고 적었는데 **엣지 게스트에 `dig` 가 없다.** 2026-09-17 에 cloud-init 으로
|
||||
갓 만든 `kc-lab-edge` 에서 쳐서 확인했다(observed).
|
||||
|
||||
```
|
||||
dig: command not found
|
||||
```
|
||||
|
||||
cloud-init 이 까는 것은 `curl` 과 `nftables` 뿐이라 `dnsutils` 가 안 들어간다.
|
||||
`getent hosts` 는 `libc` 가 주는 것이라 어디서나 있고, 주소와 이름을 한 줄로 낸다.
|
||||
`dig` 의 형태가 꼭 필요하면 먼저 깐다 — 둘 다 같은 `100.83.212.4` 를 냈다(observed).
|
||||
|
||||
```bash
|
||||
sudo apt install -y dnsutils && dig +short auth.hyeonworks.com
|
||||
```
|
||||
|
||||
**어디를 봐야 하는가** — 주소 한 줄. 그 주소가 공개 인터넷 대역인가,
|
||||
@@ -10299,9 +10481,14 @@ dig +short auth.hyeonworks.com
|
||||
**확인 ② nginx 판 번호는 몇인가**
|
||||
|
||||
```bash
|
||||
nginx -v
|
||||
sudo nginx -v
|
||||
```
|
||||
|
||||
**★ `sudo` 가 붙는 까닭은 §189 확인 ① 과 같다.** 실행 파일이 `/usr/sbin/nginx`
|
||||
인데 데비안 일반 사용자 `PATH` 에 그 디렉터리가 없다. `sudo` 없이 치면
|
||||
`nginx: command not found` 다 — 안 깔린 것처럼 보이지만 아니다(2026-09-17,
|
||||
observed).
|
||||
|
||||
**실측**(observed) — 같은 설정인데 배포판에서 갈린다.
|
||||
|
||||
```
|
||||
@@ -10899,7 +11086,7 @@ timedatectl show -p NTP -p NTPSynchronized
|
||||
|
||||
| 증상 | 원인 | 확인 |
|
||||
|---|---|---|
|
||||
| certbot 검증 실패 | DNS 가 이 호스트를 안 가리킴 · 80 이 막힘 | `dig +short auth.hyeonworks.com` · 밖에서 `curl -I http://auth.hyeonworks.com` |
|
||||
| certbot 검증 실패 | DNS 가 이 호스트를 안 가리킴 · 80 이 막힘 | `getent hosts auth.hyeonworks.com` · 밖에서 `curl -I http://auth.hyeonworks.com` |
|
||||
| 체인 단계가 1개 | `cert.pem` 을 씀 | 위 확인 ② |
|
||||
| 갱신은 됐는데 옛 인증서가 나감 | **deploy 훅 없음** | 워커 PID · `sudo ls /etc/letsencrypt/renewal-hooks/deploy/` |
|
||||
| 훅이 실패한 것처럼 보임 | stderr 경고를 error 로 표시 | 문구 말고 **워커 PID** |
|
||||
@@ -10966,11 +11153,19 @@ timedatectl show -p NTP -p NTPSynchronized
|
||||
| PostgreSQL | Deployment `postgres` — ReplicaSet `postgres-7b474b88c8` |
|
||||
| Service | `keycloak` → Endpoints `10.42.0.67:8080,10.42.1.155:8080` |
|
||||
| Secret | `keycloak-lab-secrets` — `KC_BOOTSTRAP_ADMIN_PASSWORD` 19 bytes · `POSTGRES_PASSWORD` 22 bytes |
|
||||
| PVC | StorageClass `local-path`, 이름 끝에 파드 번호가 붙는다 |
|
||||
| PVC | `postgres-data` 하나. StorageClass `local-path` — **파드 번호가 붙지 않는다**(아래) |
|
||||
| Ingress | HOSTS 가 `auth.hyeonworks.com` |
|
||||
| 클러스터링 | JGroups — 디스커버리 테이블 `jgroups_ping`, 메시지 포트 7800 |
|
||||
| 관리 포트 | `9000` — `/metrics` 가 거기 있다 |
|
||||
|
||||
**★ 이미지 태그는 이제 안다.** 2026-09-17 에 `keycloak-pattern` @ `9465582b` 의
|
||||
`deploy/lab/k8s/keycloak-cluster.yaml` 을 열어 읽었다(observed).
|
||||
|
||||
```
|
||||
image: postgres:16-alpine
|
||||
image: quay.io/keycloak/keycloak:26.7.0
|
||||
```
|
||||
|
||||
**판 번호는 여기 없다.** 가이드 05 는 Keycloak 과 PostgreSQL 의 이미지 태그를 적지
|
||||
않았고 `keycloak-cluster.yaml` 원문은 `source/` 에 반입되지 않았다. 이 두 판 번호는
|
||||
이 문서에서 대조하지 못한다(unknown). 가이드 안에 태그가 찍힌 이미지는 아래 임시
|
||||
@@ -11012,8 +11207,20 @@ ls deploy/lab/k8s/
|
||||
curl -s -o /dev/null -w '%{http_code} tls=%{ssl_verify_result}\n' https://auth.hyeonworks.com/
|
||||
```
|
||||
|
||||
**어디를 봐야 하는가** — 04 에서 잰 `404 tls=0` 이 그대로인가. `tls=0` 이 아니면
|
||||
05 를 시작할 때가 아니라 04 로 돌아갈 때다.
|
||||
**어디를 봐야 하는가** — 04 에서 잰 `404 tls=0` 이 그대로인가. **앞의 코드를 같이
|
||||
본다.**
|
||||
|
||||
**★ `tls=0` 만 보면 안 된다.** 연결이 **아예 안 됐을 때도** `tls=0` 이 나온다 —
|
||||
`%{ssl_verify_result}` 는 TLS 검증까지 갔을 때만 뜻이 있고 못 갔으면 초기값 `0` 이
|
||||
그대로 찍힌다. 2026-09-17 에 04 를 건너뛴 상태에서 재 보니 이렇게 나왔다(observed).
|
||||
|
||||
```
|
||||
000 tls=0
|
||||
```
|
||||
|
||||
`000` 은 응답이 없었다는 뜻이다. **판정은 코드가 `404` 인 것과 `tls=0` 이 같이
|
||||
나오는 것으로 한다.** `000 tls=0` 이면 04 가 아직 안 됐거나 밖에서 들어오는 길이
|
||||
막힌 것이라 05 를 시작할 때가 아니다.
|
||||
|
||||
**이 결과가 의미하는 것** — 가이드 04 가 이 값 옆에 한 줄을 적어 두었다 —
|
||||
「앞의 `404` 는 05 이후에 `200` 으로 바뀐다.」 지금 재 두면 아래 5번의 `200` 이
|
||||
@@ -11047,9 +11254,21 @@ kubectl -n keycloak-lab rollout status statefulset/keycloak --timeout=300s
|
||||
partitioned roll out complete: 2 new pods have been updated...
|
||||
```
|
||||
|
||||
**어디를 봐야 하는가** — 이 명령은 **끝날 때까지 아무것도 안 찍고 멈춰 있다.** 그
|
||||
침묵이 정상이다. 마지막에 나오는 한 줄에서 `complete` 라는 낱말과 파드 개수 `2` 를
|
||||
본다. 300초를 다 쓰고 타임아웃으로 끝나면 그것도 답이다 — 「안 떴다」가 확정된 것이니
|
||||
**어디를 봐야 하는가** — 마지막에 나오는 한 줄에서 `complete` 라는 낱말과 파드 개수
|
||||
`2` 를 본다. 그 줄이 나올 때까지 명령이 안 끝나고 멈춰 있는 것이 정상이다.
|
||||
|
||||
**★ 다만 아무것도 안 찍고 멈춰 있지는 않다.** 처음에는 「끝날 때까지 아무것도 안
|
||||
찍는다」고 적었는데, 2026-09-17 에 갓 세운 클러스터에서 재 보니 기다리는 동안 줄이
|
||||
먼저 나왔다(observed).
|
||||
|
||||
```
|
||||
Waiting for 2 pods to be ready...
|
||||
Waiting for 1 pods to be ready...
|
||||
partitioned roll out complete: 2 new pods have been updated...
|
||||
```
|
||||
|
||||
`Waiting for …` 줄은 진행 상황이지 오류가 아니다. 숫자가 줄어들수록 파드가 하나씩
|
||||
Ready 가 된다. 300초를 다 쓰고 타임아웃으로 끝나면 그것도 답이다 — 「안 떴다」가 확정된 것이니
|
||||
아래 「막히면」으로 간다.
|
||||
|
||||
**이 결과가 의미하는 것** — `complete` 면 두 파드가 다 Ready 가 됐다는 뜻이라 2번의
|
||||
@@ -11326,8 +11545,24 @@ kubectl -n keycloak-lab get pvc
|
||||
```
|
||||
|
||||
**어디를 봐야 하는가** — STATUS 칸(`Bound`/`Pending`)과 VOLUME 칸(비어 있는지),
|
||||
그리고 STORAGECLASS 칸이 `local-path` 인가. StatefulSet 이면 PVC 이름 끝에 파드
|
||||
번호가 붙어 있어(`...-keycloak-0`) 어느 파드 것인지 바로 보인다.
|
||||
그리고 STORAGECLASS 칸이 `local-path` 인가.
|
||||
|
||||
**★ 이 실험대에는 파드 번호가 붙은 PVC 가 없다.** 처음에는 「StatefulSet 이면 PVC
|
||||
이름 끝에 파드 번호가 붙어 있어 어느 파드 것인지 바로 보인다」고 적었는데, 그것은
|
||||
`volumeClaimTemplates` 를 쓰는 StatefulSet 의 이야기이고 **이 매니페스트의
|
||||
`keycloak` StatefulSet 에는 그 칸이 없다.** PVC 는 PostgreSQL 이 쓰는
|
||||
`postgres-data` 하나뿐이고 그것도 StatefulSet 이 아니라 별도 문서로 선언되어
|
||||
Deployment 가 `claimName` 으로 가리킨다. 2026-09-17 에 매니페스트와 클러스터
|
||||
양쪽에서 확인했다(observed).
|
||||
|
||||
```
|
||||
NAME STATUS VOLUME CAPACITY STORAGECLASS
|
||||
postgres-data Bound pvc-fe269834-3dd1-4b1f-82f6-a53413310a30 5Gi local-path
|
||||
```
|
||||
|
||||
그래서 **Keycloak 파드는 볼륨을 안 갖는다.** 세션이 어디 사는지를 묻는 이 실험대의
|
||||
질문에서 그것이 중요하다 — 파드가 죽으면 그 안의 것은 사라지고
|
||||
`postgres-data` 만 남는다.
|
||||
|
||||
**이 결과가 의미하는 것** — `Bound` 면 볼륨이 붙었다. `Pending` 이면 StorageClass 가
|
||||
없거나 노드에 자리가 없다. **`local-path` 는 파드가 스케줄될 때까지
|
||||
@@ -11364,6 +11599,17 @@ ISPN000094: Received new cluster view for channel ISPN:
|
||||
**이름 목록**, 그리고 `|1` 이 **뷰 번호**(멤버가 들고 날 때마다 올라간다).
|
||||
`tail -1` 을 붙였으므로 지금 보고 있는 것은 **가장 최근 뷰** 하나다.
|
||||
|
||||
**★ 이름 뒤에 `(v=…)` 가 붙어 나오기도 한다.** 2026-09-17 에 새로 세운 클러스터에서
|
||||
같은 줄을 뽑아 보니 이랬다(observed).
|
||||
|
||||
```
|
||||
[keycloak-1-3159(v=16.0.12)|1] (2) [keycloak-1-3159(v=16.0.12), keycloak-0-13057(v=16.0.12)]
|
||||
```
|
||||
|
||||
`(v=16.0.12)` 는 JGroups 가 붙인 판 번호이고, 볼 것은 그대로 `(2)` 와 이름 목록과
|
||||
`|1` 이다. **맨 앞의 이름이 `keycloak-0` 이 아니어도 정상이다** — 맨 앞에는 코디네이터가 오고
|
||||
먼저 뜬 쪽이 그 일을 맡는다. 이 판에서는 `keycloak-1` 이었다.
|
||||
|
||||
**이 결과가 의미하는 것** — `(2)` 면 이 노드는 상대를 봤다. `(1)` 이면 혼자 있다고
|
||||
알고 있다. 뷰 번호가 계속 오르고 있으면 멤버가 붙었다 떨어지기를 반복하는 중이라,
|
||||
「지금 2 다」라는 스냅숏보다 그 사실이 더 중요하다. **이 줄은 과거형이다** — 지금
|
||||
@@ -11466,6 +11712,26 @@ Service 뒤에 파드가 있는지(2-4), 파드가 Ready 인지(2-2) 순서다.
|
||||
curl -I https://auth.hyeonworks.com/realms/master
|
||||
```
|
||||
|
||||
**★ 이 `200` 은 04 를 끝냈을 때의 값이다.** 04 를 건너뛰고 05 만 했다면 443 을 듣는
|
||||
것이 없으므로 여기는 `000` 이다. 2026-09-17 에 그 상태에서 재 보니 이렇게
|
||||
나왔다(observed).
|
||||
|
||||
```
|
||||
curl: (7) Failed to connect to auth.hyeonworks.com:443 after 2 ms: Could not connect to server
|
||||
```
|
||||
|
||||
**그때도 05 가 세운 것까지는 잴 수 있다.** TLS 를 빼고 `Host` 헤더를 실어 80 으로
|
||||
치면 nginx → Traefik → Ingress → Service → 파드가 이어졌는지가 그대로 나온다. 둘 다
|
||||
`200` 이었다(observed).
|
||||
|
||||
```bash
|
||||
curl -s -o /dev/null -w '%{http_code}\n' -H 'Host: auth.hyeonworks.com' http://192.168.122.10/realms/master
|
||||
curl -s -o /dev/null -w '%{http_code}\n' -H 'Host: auth.hyeonworks.com' http://192.168.122.11/realms/master
|
||||
```
|
||||
|
||||
앞엣것은 엣지 nginx 를 지나고 뒤엣것은 Traefik 에 바로 친다. 둘이 같으면 남은 것은
|
||||
TLS 뿐이다.
|
||||
|
||||
**확인 ⑤ 관리 콘솔에 로그인된다**
|
||||
|
||||
브라우저로 `https://auth.hyeonworks.com/admin` 에 들어가 관리자로 로그인한다.
|
||||
@@ -11478,7 +11744,7 @@ curl -I https://auth.hyeonworks.com/realms/master
|
||||
|---|---|---|
|
||||
| 롤아웃 | `kubectl -n keycloak-lab rollout status statefulset/keycloak --timeout=300s` | `complete` · 파드 `2` |
|
||||
| Service 뒤 | `kubectl -n keycloak-lab describe svc keycloak \| grep -i endpoints` | 주소 두 개 |
|
||||
| 클러스터 | `… query=vendor_cluster_size` | 원소 둘 · 값 둘 다 2 |
|
||||
| 클러스터 | `… query=vendor_cluster_size` | 원소 둘 · 값 둘 다 2 — **`health` 가 `up` 이 된 뒤에 잰다** |
|
||||
| 밖에서 | `curl -s -o /dev/null -w '%{http_code}\n' https://auth.hyeonworks.com/realms/master` | `200` |
|
||||
|
||||
**근거를 재려면 (선택)** — 세션이 실제로 어디 저장되는지는 DB 를 직접 본다.
|
||||
@@ -11573,9 +11839,17 @@ kubectl -n keycloak-lab exec -it keycloak-0 -- sh
|
||||
- (observed) 디스커버리 테이블에는 둘 다 있는데 메시지가 안 가는 상태를 실제로 겪었다
|
||||
- (external) `kubectl get endpoints` 의 v1.33 deprecation 은 쿠버네티스 쪽 규격이고
|
||||
이 실험대가 잰 값이 아니다
|
||||
- (unknown) `keycloak-cluster.yaml` 원문은 `source/` 에 없다. **Keycloak 과
|
||||
PostgreSQL 의 이미지 태그**, 파드 자원 한도, 프로브 설정, `persistent-user-sessions`
|
||||
설정값은 여기서 대조하지 못했다
|
||||
- (observed) 2026-09-17 에 이 절차를 갓 세운 클러스터에서 처음부터 다시 밟았다.
|
||||
같은 값이 다시 나온 것 — `postgres-7b474b88c8` 이라는 ReplicaSet 해시, Secret 의
|
||||
19·22 bytes 와 주입된 길이 19·복호 길이 22, Endpoints 두 개와 `ready` 두 줄,
|
||||
`jgroups_ping` 2 행, 임시 파드가 읽은 `vendor_cluster_size … 2.0`, Keycloak
|
||||
이미지에 `curl` 이 없다는 것과 exit code 127, `kubectl get endpoints` 의 deprecation
|
||||
경고. **파드 IP 와 Infinispan 접미사만 달랐다** — 그것들은 매번 새로 붙는다
|
||||
- (observed) `keycloak-cluster.yaml` 원문을 `keycloak-pattern` @ `9465582b` 에서
|
||||
읽었다. 이미지 태그는 `postgres:16-alpine` 과 `quay.io/keycloak/keycloak:26.7.0`
|
||||
이다
|
||||
- (unknown) 파드 자원 한도, 프로브 설정, `persistent-user-sessions` 설정값은 아직
|
||||
이 문서로 옮기지 않았다
|
||||
- (unknown) `get all`·`get deploy`·`get pvc`·`describe pvc`·`jgroups_ping` 조회·
|
||||
`get events`·`describe pod`·`logs` 의 출력은 가이드가 봐야 할 줄만 적었고 값이 남아
|
||||
있지 않다
|
||||
@@ -11731,6 +12005,19 @@ kubectl -n observability exec deploy/prometheus -- \
|
||||
준다(연결 거부·타임아웃·404). 확인 ③ 에서 값이 한 노드만 나오는 증상의 원인이 대개
|
||||
여기 있고, 그때 **클러스터가 아니라 스크레이프가 문제다.**
|
||||
|
||||
**★ `down` 말고 `unknown` 도 있다.** 스택을 막 올린 직후에는 `health` 가 `unknown`
|
||||
이고 `lastError` 는 빈 문자열이다 — 실패한 것이 아니라 **아직 한 번도 안 긁은**
|
||||
것이다. 2026-09-17 에 `rollout status` 직후에 잡힌 모습이다(observed).
|
||||
|
||||
```
|
||||
"job":"keycloak"
|
||||
"lastError":""
|
||||
"health":"unknown"
|
||||
```
|
||||
|
||||
한 주기 뒤에 같은 줄이 `"health":"up"` 으로 바뀌었다. `unknown` 을 보고 권한이나
|
||||
네트워크를 뒤지기 전에 한 번 더 친다.
|
||||
|
||||
**확인 ③ 클러스터 상태 — 두 노드가 각각 몇 명을 보고 있는가**
|
||||
|
||||
```bash
|
||||
@@ -11746,9 +12033,40 @@ keycloak-0 → 2
|
||||
```
|
||||
|
||||
**어디를 봐야 하는가** — 응답은 **줄바꿈 없는 JSON 한 줄**이다. `data.result` 배열의
|
||||
원소가 **몇 개인가**(보고하는 노드 수), 각 원소에서 두 군데만 읽는다 — `"metric"`
|
||||
안의 `node` 라벨과 `"value"` 배열의 **둘째 원소**(따옴표에 싸인 값). `jq` 가 없으므로
|
||||
눈으로 읽는다.
|
||||
원소가 **몇 개인가**(보고하는 노드 수), 각 원소에서 두 군데만 읽는다 — 어느 Keycloak
|
||||
파드가 보고한 것인지와 `"value"` 배열의 **둘째 원소**(따옴표에 싸인 값). `jq` 가
|
||||
없으므로 눈으로 읽는다.
|
||||
|
||||
**★ 어느 파드인지는 `node` 라벨이 아니라 `pod` 라벨이 말한다.** 처음에는 「`node`
|
||||
라벨을 읽는다」고 적었는데, Prometheus 를 거쳐 나온 응답에서 `node` 는 **쿠버네티스
|
||||
노드 이름**(`kc-lab-1`)이다. Keycloak 이 자기 지표에 붙인 `node` 는 스크레이프할 때
|
||||
이름이 겹쳐 `exported_node` 로 밀린다. 2026-09-17 에 원소 하나를 그대로 옮기면
|
||||
이렇다(observed).
|
||||
|
||||
```
|
||||
"metric":{"__name__":"vendor_cluster_size","cache_manager":"keycloak",
|
||||
"exported_node":"keycloak-1-3159","instance":"10.42.0.9:9000","job":"keycloak",
|
||||
"node":"kc-lab-1","pod":"keycloak-1"},"value":[1789620021.097,"2"]
|
||||
```
|
||||
|
||||
세 라벨이 각각 다른 것을 말한다 — `pod` 가 Keycloak 파드(`keycloak-1`),
|
||||
`exported_node` 가 Infinispan 멤버 식별자(`keycloak-1-3159`), `node` 가 그 파드가 떠
|
||||
있는 쿠버네티스 노드(`kc-lab-1`)다. 앞의 확인에서 본 `ISPN000094` 의 이름과 견줄
|
||||
것은 `exported_node` 이고, 어느 노드를 끊었을 때 무엇이 바뀌는지를 볼 때는 `node` 다.
|
||||
|
||||
**★ 스택을 막 올린 직후에는 원소가 모자란다.** 1번의 `rollout status` 가 성공한
|
||||
직후에 이 질의를 치면 Keycloak 대상이 아직 한 번도 안 긁혀서 원소가 하나뿐이거나
|
||||
아예 없다. 2026-09-17 에 38초 시점과 약 2분 시점에 같은 질의를 쳐서
|
||||
확인했다(observed).
|
||||
|
||||
| 언제 | 확인 ② 의 keycloak `health` | 이 질의의 원소 |
|
||||
|---|---|---|
|
||||
| `rollout status` 직후(38초) | `"health":"unknown"` | 1 개 |
|
||||
| 약 2분 뒤 | `"health":"up"` | 2 개 · 둘 다 `2` |
|
||||
|
||||
**`unknown` 은 실패가 아니라 아직 안 긁었다는 뜻이다.** 원소가 모자라면 분단을
|
||||
의심하기 전에 확인 ② 의 `health` 를 먼저 보고, `unknown` 이면 한 주기 기다렸다 다시
|
||||
친다.
|
||||
|
||||
**이 결과가 의미하는 것** — **두 노드가 각각 자기가 아는 멤버 수를 보고한다.** 둘 다
|
||||
2 면 클러스터가 온전하다. 분단되면 한쪽은 2, 다른 쪽은 1 이 된다 — **한 노드만 보면
|
||||
@@ -11838,6 +12156,11 @@ kubectl -n observability port-forward svc/grafana 3000:3000
|
||||
|
||||
**이 절차가 성립하는 범위**
|
||||
|
||||
- (observed) 2026-09-17 에 이 절차를 갓 세운 클러스터에서 다시 밟았다. 파드 네 줄과
|
||||
job 네 개가 그대로 나왔고 ReplicaSet 해시 `845b5678cf`·`6774f94f7c` 까지 같았다.
|
||||
달랐던 것 — `rollout status` 직후에는 Grafana 가 `0/1` 이었고 Keycloak 대상의
|
||||
`health` 가 `unknown` 이라 `vendor_cluster_size` 의 원소가 하나뿐이었다. 약 2분
|
||||
뒤에 둘 다 `2` 가 됐다
|
||||
- (observed) 파드 네 줄과 job 네 개, `vendor_cluster_size` 2·2, 503 중에도 `up` 이
|
||||
1 이었던 것
|
||||
- (observed) Redis·BFF·PostgreSQL 이 스크레이프 대상에 없다는 것. 「안 찍은 것」이
|
||||
@@ -16770,6 +17093,16 @@ kubectl auth can-i get nodes/proxy --as=system:serviceaccount:observability:prom
|
||||
kubectl describe clusterrole prometheus
|
||||
```
|
||||
|
||||
**★ 첫 명령은 답 앞에 경고 한 줄을 같이 낸다**(2026-09-17, observed). 실패가 아니라
|
||||
`nodes` 가 네임스페이스에 속한 자원이 아니라는 안내이고, 볼 것은 그 아래의 `yes`
|
||||
또는 `no` 다.
|
||||
|
||||
```
|
||||
Warning: resource 'nodes' is not namespace scoped
|
||||
|
||||
yes
|
||||
```
|
||||
|
||||
## 312. 배치 제어 — nodeSelector · 라벨 · taint
|
||||
|
||||
```yaml
|
||||
@@ -17131,6 +17464,23 @@ virsh setmem kc-lab-1 5120M --config
|
||||
`setmaxmem --live`는 대개 거부된다 — 게스트가 부팅 시 메모리 맵을 정하기
|
||||
때문이다. **상한을 바꾸려면 게스트를 껐다 켜야 한다.**
|
||||
|
||||
**★ 바꾼 것이 들어갔는지는 `dominfo` 로 판정하지 않는다.** `--config` 는 다음 기동부터
|
||||
쓸 설정만 바꾸는데 `dominfo` 는 **지금 돌고 있는 값**을 보여 준다. 그래서 명령이 제대로
|
||||
먹어도 `dominfo` 의 숫자는 안 바뀌고, 그 화면을 「안 먹었다」로 읽게 된다. 2026-09-17 에
|
||||
같은 순간을 두 명령으로 나란히 찍었다(observed).
|
||||
|
||||
```
|
||||
virsh dominfo kc-lab-1 | grep -i memory
|
||||
Max memory: 5242880 KiB ← 돌고 있는 값
|
||||
|
||||
virsh dumpxml kc-lab-1 --inactive | grep -E '<memory|<currentMemory'
|
||||
<memory unit='KiB'>4194304</memory> ← 방금 바꾼 설정
|
||||
<currentMemory unit='KiB'>4194304</currentMemory>
|
||||
```
|
||||
|
||||
같은 도메인, 같은 시각, 다른 숫자다. **바꾼 것이 들어갔는지는 `--inactive` 가 답하고,
|
||||
`dominfo` 는 지금 무엇으로 돌고 있는지를 답한다.**
|
||||
|
||||
```bash
|
||||
virsh dominfo kc-lab-1 | grep -i memory
|
||||
ssh kc-lab-1 free -m # 게스트가 실제로 인식한 값
|
||||
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
### virsh list --all
|
||||
Id Name State
|
||||
-----------------------------
|
||||
9 kc-lab-edge running
|
||||
10 kc-lab-1 running
|
||||
12 kc-lab-2 running
|
||||
|
||||
|
||||
### virsh vol-list default
|
||||
Name Path
|
||||
----------------------------------------------------------------------
|
||||
base.qcow2 /var/lib/libvirt/images/base.qcow2
|
||||
kc-lab-1.qcow2 /var/lib/libvirt/images/kc-lab-1.qcow2
|
||||
kc-lab-2.qcow2 /var/lib/libvirt/images/kc-lab-2.qcow2
|
||||
kc-lab-edge.qcow2 /var/lib/libvirt/images/kc-lab-edge.qcow2
|
||||
seed-kc-lab-1.iso /var/lib/libvirt/images/seed-kc-lab-1.iso
|
||||
seed-kc-lab-2.iso /var/lib/libvirt/images/seed-kc-lab-2.iso
|
||||
seed-kc-lab-edge.iso /var/lib/libvirt/images/seed-kc-lab-edge.iso
|
||||
|
||||
|
||||
### virsh net-dumpxml default
|
||||
<network>
|
||||
<name>default</name>
|
||||
<uuid>a14c3b01-2c56-4181-8310-e6cba888e2a8</uuid>
|
||||
<forward mode='nat'>
|
||||
<nat>
|
||||
<port start='1024' end='65535'/>
|
||||
</nat>
|
||||
</forward>
|
||||
<bridge name='virbr0' stp='on' delay='0'/>
|
||||
<mac address='52:54:00:6c:a6:a8'/>
|
||||
<ip address='192.168.122.1' netmask='255.255.255.0'>
|
||||
<dhcp>
|
||||
<range start='192.168.122.2' end='192.168.122.254'/>
|
||||
<host mac='52:54:00:aa:bb:11' name='kc-lab-1' ip='192.168.122.11'/>
|
||||
<host mac='52:54:00:aa:bb:12' name='kc-lab-2' ip='192.168.122.12'/>
|
||||
<host mac='52:54:00:aa:bb:10' name='kc-lab-edge' ip='192.168.122.10'/>
|
||||
</dhcp>
|
||||
</ip>
|
||||
</network>
|
||||
|
||||
|
||||
### df -h /
|
||||
Filesystem Size Used Avail Use% Mounted on
|
||||
/dev/nvme0n1p3 226G 16G 199G 8% /
|
||||
|
||||
### free -m
|
||||
total used free shared buff/cache available
|
||||
Mem: 11648 10444 191 4 1345 1204
|
||||
Swap: 8191 959 7232
|
||||
|
||||
### virsh dominfo (셋)
|
||||
-- kc-lab-1
|
||||
Name: kc-lab-1
|
||||
State: running
|
||||
CPU(s): 2
|
||||
Max memory: 5242880 KiB
|
||||
Used memory: 5242880 KiB
|
||||
Persistent: yes
|
||||
Autostart: disable
|
||||
Autostart Once: disable
|
||||
-- kc-lab-2
|
||||
Name: kc-lab-2
|
||||
State: running
|
||||
CPU(s): 2
|
||||
Max memory: 4194304 KiB
|
||||
Used memory: 4194304 KiB
|
||||
Persistent: yes
|
||||
Autostart: disable
|
||||
Autostart Once: disable
|
||||
-- kc-lab-edge
|
||||
Name: kc-lab-edge
|
||||
State: running
|
||||
CPU(s): 1
|
||||
Max memory: 1048576 KiB
|
||||
Used memory: 1048576 KiB
|
||||
Persistent: yes
|
||||
Autostart: disable
|
||||
Autostart Once: disable
|
||||
|
||||
### virsh domblklist (셋)
|
||||
-- kc-lab-1
|
||||
Target Source
|
||||
-----------------------------------------------------
|
||||
vda /var/lib/libvirt/images/kc-lab-1.qcow2
|
||||
vdb /var/lib/libvirt/images/seed-kc-lab-1.iso
|
||||
|
||||
-- kc-lab-2
|
||||
Target Source
|
||||
-----------------------------------------------------
|
||||
vda /var/lib/libvirt/images/kc-lab-2.qcow2
|
||||
vdb /var/lib/libvirt/images/seed-kc-lab-2.iso
|
||||
|
||||
-- kc-lab-edge
|
||||
Target Source
|
||||
--------------------------------------------------------
|
||||
vda /var/lib/libvirt/images/kc-lab-edge.qcow2
|
||||
vdb /var/lib/libvirt/images/seed-kc-lab-edge.iso
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
Id Name State
|
||||
-----------------------------
|
||||
9 kc-lab-edge running
|
||||
10 kc-lab-1 running
|
||||
12 kc-lab-2 running
|
||||
|
||||
Name Path
|
||||
----------------------------------------------------------------------
|
||||
base.qcow2 /var/lib/libvirt/images/base.qcow2
|
||||
kc-lab-1.qcow2 /var/lib/libvirt/images/kc-lab-1.qcow2
|
||||
kc-lab-2.qcow2 /var/lib/libvirt/images/kc-lab-2.qcow2
|
||||
kc-lab-edge.qcow2 /var/lib/libvirt/images/kc-lab-edge.qcow2
|
||||
seed-kc-lab-1.iso /var/lib/libvirt/images/seed-kc-lab-1.iso
|
||||
seed-kc-lab-2.iso /var/lib/libvirt/images/seed-kc-lab-2.iso
|
||||
seed-kc-lab-edge.iso /var/lib/libvirt/images/seed-kc-lab-edge.iso
|
||||
|
||||
<dhcp>
|
||||
<range start='192.168.122.2' end='192.168.122.254'/>
|
||||
<host mac='52:54:00:aa:bb:11' name='kc-lab-1' ip='192.168.122.11'/>
|
||||
<host mac='52:54:00:aa:bb:12' name='kc-lab-2' ip='192.168.122.12'/>
|
||||
<host mac='52:54:00:aa:bb:10' name='kc-lab-edge' ip='192.168.122.10'/>
|
||||
</dhcp>
|
||||
</ip>
|
||||
</network>
|
||||
|
||||
Filesystem Size Used Avail Use% Mounted on
|
||||
/dev/nvme0n1p3 226G 16G 199G 8% /
|
||||
@@ -0,0 +1,18 @@
|
||||
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.
|
||||
|
||||
Domain 'kc-lab-2' destroyed
|
||||
|
||||
Domain 'kc-lab-2' has been undefined
|
||||
Volume 'vda'(/var/lib/libvirt/images/kc-lab-2.qcow2) removed.
|
||||
Volume 'vdb'(/var/lib/libvirt/images/seed-kc-lab-2.iso) removed.
|
||||
|
||||
Domain 'kc-lab-1' destroyed
|
||||
|
||||
Domain 'kc-lab-1' has been undefined
|
||||
Volume 'vda'(/var/lib/libvirt/images/kc-lab-1.qcow2) removed.
|
||||
Volume 'vdb'(/var/lib/libvirt/images/seed-kc-lab-1.iso) removed.
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
Updated network default persistent config and live state
|
||||
Updated network default persistent config and live state
|
||||
Updated network default persistent config and live state
|
||||
--- 확인 ---
|
||||
<range start='192.168.122.2' end='192.168.122.254'/>
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
Id Name State
|
||||
--------------------
|
||||
|
||||
Name Path
|
||||
--------------------------------------------------
|
||||
base.qcow2 /var/lib/libvirt/images/base.qcow2
|
||||
|
||||
<dhcp>
|
||||
<range start='192.168.122.2' end='192.168.122.254'/>
|
||||
</dhcp>
|
||||
</ip>
|
||||
</network>
|
||||
|
||||
Filesystem Size Used Avail Use% Mounted on
|
||||
/dev/nvme0n1p3 226G 7.9G 206G 4% /
|
||||
virbr0 DOWN 192.168.122.1/24
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
xorriso 1.5.8.pl02 : RockRidge filesystem manipulator, libburnia project.
|
||||
|
||||
xorriso : FAILURE : Cannot determine attributes of source file '/home/donghyeon/workspace/cloud/meta-kc-lab-1' : No such file or directory
|
||||
종료 코드: 5
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
xorriso 1.5.8.pl02 : RockRidge filesystem manipulator, libburnia project.
|
||||
|
||||
kc-lab-1 → 종료 0
|
||||
xorriso 1.5.8.pl02 : RockRidge filesystem manipulator, libburnia project.
|
||||
|
||||
kc-lab-2 → 종료 0
|
||||
xorriso 1.5.8.pl02 : RockRidge filesystem manipulator, libburnia project.
|
||||
|
||||
kc-lab-edge → 종료 0
|
||||
-rw-r--r-- 1 donghyeon donghyeon 378880 Sep 17 13:23 seed-kc-lab-1.iso
|
||||
-rw-r--r-- 1 donghyeon donghyeon 378880 Sep 17 13:23 seed-kc-lab-2.iso
|
||||
-rw-r--r-- 1 donghyeon donghyeon 378880 Sep 17 13:23 seed-kc-lab-edge.iso
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
Vol seed-kc-lab-1.iso created
|
||||
|
||||
|
||||
Vol seed-kc-lab-2.iso created
|
||||
|
||||
|
||||
Vol seed-kc-lab-edge.iso created
|
||||
|
||||
|
||||
--- 확인 ---
|
||||
Name Path
|
||||
----------------------------------------------------------------------
|
||||
base.qcow2 /var/lib/libvirt/images/base.qcow2
|
||||
seed-kc-lab-1.iso /var/lib/libvirt/images/seed-kc-lab-1.iso
|
||||
seed-kc-lab-2.iso /var/lib/libvirt/images/seed-kc-lab-2.iso
|
||||
seed-kc-lab-edge.iso /var/lib/libvirt/images/seed-kc-lab-edge.iso
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
Updated network default persistent config and live state
|
||||
Updated network default persistent config and live state
|
||||
Updated network default persistent config and live state
|
||||
--- 확인 ---
|
||||
<range start='192.168.122.2' end='192.168.122.254'/>
|
||||
<host mac='52:54:00:aa:bb:10' name='kc-lab-edge' ip='192.168.122.10'/>
|
||||
<host mac='52:54:00:aa:bb:11' name='kc-lab-1' ip='192.168.122.11'/>
|
||||
<host mac='52:54:00:aa:bb:12' name='kc-lab-2' ip='192.168.122.12'/>
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
|
||||
Starting install...
|
||||
|
||||
Allocating 'kc-lab-1.qcow2' | 20 GB 00:00
|
||||
|
||||
Creating domain... | 00:00
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
|
||||
Starting install...
|
||||
|
||||
Allocating 'kc-lab-2.qcow2' | 20 GB 00:00
|
||||
|
||||
Creating domain... | 00:00
|
||||
Domain creation completed.
|
||||
kc-lab-2 종료 0
|
||||
|
||||
Starting install...
|
||||
|
||||
Allocating 'kc-lab-edge.qcow2' | 10 GB 00:00
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
=== virsh list --all ===
|
||||
Id Name State
|
||||
-----------------------------
|
||||
13 kc-lab-1 running
|
||||
14 kc-lab-2 running
|
||||
15 kc-lab-edge running
|
||||
|
||||
|
||||
=== DHCP 리스 ===
|
||||
Expiry Time MAC address Protocol IP address Hostname Client ID or DUID
|
||||
--------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
2026-09-17 14:24:12 52:54:00:aa:bb:10 ipv4 192.168.122.10/24 kc-lab-edge ff:56:50:4d:98:00:02:00:00:ab:11:d7:90:6d:17:fc:bf:ab:64
|
||||
2026-09-17 14:24:06 52:54:00:aa:bb:11 ipv4 192.168.122.11/24 kc-lab-1 ff:56:50:4d:98:00:02:00:00:ab:11:96:13:63:32:a6:4c:7b:ed
|
||||
2026-09-17 14:24:11 52:54:00:aa:bb:12 ipv4 192.168.122.12/24 kc-lab-2 ff:56:50:4d:98:00:02:00:00:ab:11:ef:d1:ff:8b:ff:65:e4:56
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
=== kc-lab-1 ===
|
||||
kc-lab-1
|
||||
NAME SIZE FSTYPE LABEL MOUNTPOINT
|
||||
vda 20G
|
||||
├─vda1 19.9G ext4 /
|
||||
├─vda14 3M
|
||||
└─vda15 124M vfat /boot/efi
|
||||
vdb 370K iso9660 CIDATA
|
||||
=== kc-lab-2 ===
|
||||
kc-lab-2
|
||||
NAME SIZE FSTYPE LABEL MOUNTPOINT
|
||||
vda 20G
|
||||
├─vda1 19.9G ext4 /
|
||||
├─vda14 3M
|
||||
└─vda15 124M vfat /boot/efi
|
||||
vdb 370K iso9660 CIDATA
|
||||
=== kc-lab-edge ===
|
||||
kc-lab-edge
|
||||
NAME SIZE FSTYPE LABEL MOUNTPOINT
|
||||
vda 10G
|
||||
├─vda1 9.9G ext4 /
|
||||
├─vda14 3M
|
||||
└─vda15 124M vfat /boot/efi
|
||||
vdb 370K iso9660 CIDATA
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
=== 게스트에서 sudo 가 되나 ===
|
||||
uid=0(root) gid=0(root) groups=0(root)
|
||||
|
||||
=== 실제로 쓴 kc-lab-1.yaml 을 게스트의 cloud-init schema 에 건다 ===
|
||||
Error:
|
||||
Cloud config schema errors: users.0: {'name': 'donghyeon', 'groups': ['sudo'], 'shell': '/bin/bash', 'sudo': ['ALL=(ALL) NOPASSWD:ALL'], 'lock_passwd': False, 'plain_text_passwd': '<REDACTED 5자>', 'ssh_authorized_keys': ['ssh-ed25519 <REDACTED> test-server -> kc-lab', 'ssh-ed25519 <REDACTED> donghyeon@donghyeon-960XGK']} is not valid under any of the given schemasCloud config schema deprecations:
|
||||
|
||||
종료=1
|
||||
@@ -0,0 +1,19 @@
|
||||
Id Name State
|
||||
-----------------------------
|
||||
13 kc-lab-1 running
|
||||
14 kc-lab-2 running
|
||||
15 kc-lab-edge running
|
||||
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
|
||||
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
|
||||
It is also possible that a host key has just been changed.
|
||||
The fingerprint for the ED25519 key sent by the remote host is
|
||||
SHA256:4crUKIfcfmBFDkk5ARCqZ0z/m1AIuSjZ8Ys6ZMoNxBI.
|
||||
Please contact your system administrator.
|
||||
Add correct host key in /home/donghyeon/.ssh/known_hosts to get rid of this message.
|
||||
Offending ED25519 key in /home/donghyeon/.ssh/known_hosts:1
|
||||
Host key for 192.168.122.11 has changed and you have requested strict checking.
|
||||
Host key verification failed.
|
||||
@@ -0,0 +1,13 @@
|
||||
=== 별칭으로는 되나 ===
|
||||
kc-lab-1
|
||||
PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
|
||||
|
||||
=== known_hosts 에 무엇이 있나 ===
|
||||
# Host 192.168.122.11 found: line 1
|
||||
192.168.122.11 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBG+/wya7fn6K845Mh717dDobbgUbpTqyK1hi2qLyMGm
|
||||
|
||||
=== config 에서 IP 직접 지정에 걸리는 규칙 ===
|
||||
Host kc-lab-*
|
||||
StrictHostKeyChecking no
|
||||
UserKnownHostsFile /dev/null
|
||||
LogLevel ERROR
|
||||
@@ -0,0 +1,4 @@
|
||||
옛 키 셋 제거
|
||||
Warning: Permanently added '192.168.122.11' (ED25519) to the list of known hosts.
|
||||
kc-lab-1
|
||||
PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
[INFO] Finding release for channel stable
|
||||
[INFO] Using v1.36.4+k3s1 as release
|
||||
[INFO] Downloading hash https://github.com/k3s-io/k3s/releases/download/v1.36.4%2Bk3s1/sha256sum-amd64.txt
|
||||
[INFO] Downloading binary https://github.com/k3s-io/k3s/releases/download/v1.36.4%2Bk3s1/k3s
|
||||
[INFO] Verifying binary download
|
||||
[INFO] Installing k3s to /usr/local/bin/k3s
|
||||
[INFO] Skipping installation of SELinux RPM
|
||||
[INFO] Creating /usr/local/bin/kubectl symlink to k3s
|
||||
[INFO] Creating /usr/local/bin/crictl symlink to k3s
|
||||
[INFO] Creating /usr/local/bin/ctr symlink to k3s
|
||||
[INFO] Creating killall script /usr/local/bin/k3s-killall.sh
|
||||
[INFO] Creating uninstall script /usr/local/bin/k3s-uninstall.sh
|
||||
[INFO] env: Creating environment file /etc/systemd/system/k3s.service.env
|
||||
[INFO] systemd: Creating service file /etc/systemd/system/k3s.service
|
||||
[INFO] systemd: Enabling k3s unit
|
||||
Created symlink /etc/systemd/system/multi-user.target.wants/k3s.service → /etc/systemd/system/k3s.service.
|
||||
[INFO] systemd: Starting k3s
|
||||
=== 종료 0 ===
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
active
|
||||
NAME STATUS ROLES AGE VERSION
|
||||
kc-lab-1 Ready control-plane 44s v1.36.4+k3s1
|
||||
@@ -0,0 +1,7 @@
|
||||
=== 고치기 전 server 줄 ===
|
||||
server: https://127.0.0.1:6443
|
||||
=== 고친 뒤 ===
|
||||
server: https://192.168.122.11:6443
|
||||
=== kubectl get nodes ===
|
||||
NAME STATUS ROLES AGE VERSION
|
||||
kc-lab-1 Ready control-plane 52s v1.36.4+k3s1
|
||||
@@ -0,0 +1,4 @@
|
||||
109 node-token
|
||||
scp OK
|
||||
lab host 사본 지움
|
||||
109 /home/donghyeon/node-token
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
[INFO] Finding release for channel stable
|
||||
[INFO] Using v1.36.4+k3s1 as release
|
||||
[INFO] Downloading hash https://github.com/k3s-io/k3s/releases/download/v1.36.4%2Bk3s1/sha256sum-amd64.txt
|
||||
[INFO] Downloading binary https://github.com/k3s-io/k3s/releases/download/v1.36.4%2Bk3s1/k3s
|
||||
[INFO] Verifying binary download
|
||||
[INFO] Installing k3s to /usr/local/bin/k3s
|
||||
[INFO] Skipping installation of SELinux RPM
|
||||
[INFO] Creating /usr/local/bin/kubectl symlink to k3s
|
||||
[INFO] Creating /usr/local/bin/crictl symlink to k3s
|
||||
[INFO] Creating /usr/local/bin/ctr symlink to k3s
|
||||
[INFO] Creating killall script /usr/local/bin/k3s-killall.sh
|
||||
[INFO] Creating uninstall script /usr/local/bin/k3s-agent-uninstall.sh
|
||||
[INFO] env: Creating environment file /etc/systemd/system/k3s-agent.service.env
|
||||
[INFO] systemd: Creating service file /etc/systemd/system/k3s-agent.service
|
||||
[INFO] systemd: Enabling k3s-agent unit
|
||||
Created symlink /etc/systemd/system/multi-user.target.wants/k3s-agent.service → /etc/systemd/system/k3s-agent.service.
|
||||
[INFO] systemd: Starting k3s-agent
|
||||
=== 종료 0 ===
|
||||
토큰파일 지움
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
=== kubectl get nodes -o wide ===
|
||||
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
|
||||
kc-lab-1 Ready control-plane 2m8s v1.36.4+k3s1 192.168.122.11 <none> Debian GNU/Linux 12 (bookworm) 6.1.0-53-cloud-amd64 (amd64) containerd://2.3.4-k3s1.36
|
||||
kc-lab-2 Ready <none> 52s v1.36.4+k3s1 192.168.122.12 <none> Debian GNU/Linux 12 (bookworm) 6.1.0-53-cloud-amd64 (amd64) containerd://2.3.4-k3s1.36
|
||||
|
||||
=== kube-system ===
|
||||
coredns-54996dc9b4-x8c24 Running
|
||||
helm-install-traefik-crd-v5m5f Completed
|
||||
helm-install-traefik-rjnbm Completed
|
||||
local-path-provisioner-77b9867795-j8qkb Running
|
||||
metrics-server-6dc596dfb8-4dkmg Running
|
||||
svclb-traefik-72a92206-5jpxg Running
|
||||
svclb-traefik-72a92206-lpswx Running
|
||||
traefik-59b7647586-t8fc7 Running
|
||||
|
||||
=== StorageClass ===
|
||||
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
|
||||
local-path (default) rancher.io/local-path Delete WaitForFirstConsumer false 2m3s
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
설치 종료 0
|
||||
● nginx.service - A high performance web server and a reverse proxy server
|
||||
Loaded: loaded (/lib/systemd/system/nginx.service; enabled; preset: enabled)
|
||||
Active: active (running) since Thu 2026-09-17 04:27:39 UTC; 1s ago
|
||||
Docs: man:nginx(8)
|
||||
Process: 1041 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
|
||||
--- ls /etc/nginx/ ---
|
||||
conf.d
|
||||
fastcgi.conf
|
||||
fastcgi_params
|
||||
koi-utf
|
||||
koi-win
|
||||
mime.types
|
||||
modules-available
|
||||
modules-enabled
|
||||
nginx.conf
|
||||
proxy_params
|
||||
scgi_params
|
||||
sites-available
|
||||
sites-enabled
|
||||
snippets
|
||||
uwsgi_params
|
||||
win-utf
|
||||
--- sites-enabled ---
|
||||
total 0
|
||||
lrwxrwxrwx 1 root root 34 Sep 17 04:27 default -> /etc/nginx/sites-available/default
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
--- sites-enabled ---
|
||||
total 0
|
||||
lrwxrwxrwx 1 root root 39 Sep 17 04:27 keycloak-lab -> /etc/nginx/sites-available/keycloak-lab
|
||||
--- nginx -t ---
|
||||
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
|
||||
nginx: configuration file /etc/nginx/nginx.conf test is successful
|
||||
reload-OK
|
||||
--- 프로세스 트리 ---
|
||||
root 1065 1 0 04:27 ? 00:00:00 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
|
||||
www-data 1066 1065 0 04:27 ? 00:00:00 nginx: worker process
|
||||
donghye+ 1093 1092 0 04:27 ? 00:00:00 bash -c sudo rm /etc/nginx/sites-enabled/default sudo cp /tmp/keycloak-lab /etc/nginx/sites-available/keycloak-lab sudo ln -s /etc/nginx/sites-available/keycloak-lab /etc/nginx/sites-enabled/keycloak-lab echo "--- sites-enabled ---"; ls -l /etc/nginx/sites-enabled/ echo "--- nginx -t ---"; sudo nginx -t 2>&1 sudo systemctl reload nginx && echo reload-OK echo "--- 프로세스 트리 ---"; ps -ef | grep "[n]ginx" | head -4
|
||||
www-data 1106 1065 0 04:27 ? 00:00:00 nginx: worker process
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
=== 3-③ nginx -t (문서는 [warn] types_hash 줄이 늘 같이 나온다고 적었다) ===
|
||||
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
|
||||
nginx: configuration file /etc/nginx/nginx.conf test is successful
|
||||
=== nginx -v ===
|
||||
bash: line 5: nginx: command not found
|
||||
=== 3-② ls -l sites-enabled ===
|
||||
total 0
|
||||
lrwxrwxrwx 1 root root 39 Sep 17 04:27 keycloak-lab -> /etc/nginx/sites-available/keycloak-lab
|
||||
=== 3-④ systemctl status nginx | head -20 ===
|
||||
● nginx.service - A high performance web server and a reverse proxy server
|
||||
Loaded: loaded (/lib/systemd/system/nginx.service; enabled; preset: enabled)
|
||||
Active: active (running) since Thu 2026-09-17 04:27:39 UTC; 1min 23s ago
|
||||
Docs: man:nginx(8)
|
||||
Process: 1041 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
|
||||
Process: 1042 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
|
||||
Process: 1105 ExecReload=/usr/sbin/nginx -g daemon on; master_process on; -s reload (code=exited, status=0/SUCCESS)
|
||||
Main PID: 1065 (nginx)
|
||||
Tasks: 2 (limit: 1141)
|
||||
Memory: 1.8M
|
||||
CPU: 12ms
|
||||
CGroup: /system.slice/nginx.service
|
||||
├─1065 "nginx: master process /usr/sbin/nginx -g daemon on; master_process on;"
|
||||
└─1106 "nginx: worker process"
|
||||
=== 확인 ① Traefik 직접 (기대 404) ===
|
||||
% Total % Received % Xferd Average Speed Time Time Time Current
|
||||
Dload Upload Total Spent Left Speed
|
||||
|
||||
0 0 0 0 0 0 0 0 0HTTP/1.1 404 Not Found
|
||||
Content-Type: text/plain; charset=utf-8
|
||||
X-Content-Type-Options: nosniff
|
||||
Date: Thu, 17 Sep 2026 04:29:02 GMT
|
||||
Content-Length: 19
|
||||
|
||||
=== 두 노드 비교 ===
|
||||
404
|
||||
404
|
||||
=== 확인 ② 엣지 nginx 직접 (기대 301) ===
|
||||
404
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
=== 엣지 응답 헤더 전체 (Server: 가 누구인가) ===
|
||||
HTTP/1.1 404 Not Found
|
||||
Server: nginx/1.22.1
|
||||
Date: Thu, 17 Sep 2026 04:29:13 GMT
|
||||
Content-Type: text/plain; charset=utf-8
|
||||
Content-Length: 19
|
||||
Connection: keep-alive
|
||||
X-Content-Type-Options: nosniff
|
||||
|
||||
|
||||
=== 엣지 응답 본문 ===
|
||||
404 page not found
|
||||
|
||||
=== 문서 전제: which nginx 가 미설치 판정에 쓰인다. 설치된 지금 무엇이 나오나 ===
|
||||
which nginx -> (exit 1)
|
||||
nginx -v -> bash: line 3: nginx: command not found
|
||||
sudo nginx -v-> nginx version: nginx/1.22.1
|
||||
PATH -> /usr/local/bin:/usr/bin:/bin:/usr/games
|
||||
dpkg 판 ->
|
||||
=== nginx -t 를 세 번 더 — [warn] 이 정말 안 나오나 ===
|
||||
0
|
||||
0
|
||||
0
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
=== 4번 치기 전 호스트 상태 ===
|
||||
--- tailscale0 있나 ---
|
||||
3: tailscale0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1280 qdisc fq_codel state UNKNOWN group default qlen 500
|
||||
inet 100.83.212.4/32 scope global tailscale0
|
||||
valid_lft forever preferred_lft forever
|
||||
--- 80/443 을 듣는 프로세스 ---
|
||||
sudo: a terminal is required to read the password; either use ssh's -t option or configure an askpass helper
|
||||
sudo: a password is required
|
||||
--- 기존 lab_edge 테이블 ---
|
||||
sudo: a terminal is required to read the password; either use ssh's -t option or configure an askpass helper
|
||||
sudo: a password is required
|
||||
--- 기존 파일 ---
|
||||
-rw-r--r-- 1 root root 384 Sep 11 11:00 /etc/nftables.d/lab-edge-dnat.nft
|
||||
-rw-r--r-- 1 root root 332 Sep 11 10:52 /etc/systemd/system/lab-edge-dnat.service
|
||||
--- 유닛 상태 ---
|
||||
active
|
||||
enabled
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
=== /etc/nftables.d/lab-edge-dnat.nft (실물) ===
|
||||
#!/usr/sbin/nft -f
|
||||
table ip lab_edge
|
||||
delete table ip lab_edge
|
||||
|
||||
table ip lab_edge {
|
||||
chain prerouting {
|
||||
type nat hook prerouting priority dstnat; policy accept;
|
||||
iifname "tailscale0" tcp dport {80, 443} dnat to 192.168.122.10
|
||||
}
|
||||
|
||||
chain forward {
|
||||
type filter hook forward priority filter - 10; policy accept;
|
||||
ip daddr 192.168.122.10 tcp dport {80, 443} ct state new accept
|
||||
}
|
||||
}
|
||||
=== /etc/systemd/system/lab-edge-dnat.service (실물) ===
|
||||
[Unit]
|
||||
Description=Lab edge DNAT (tailnet :80/:443 -> kc-lab-edge)
|
||||
After=network-online.target libvirtd.service
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
ExecStart=/usr/sbin/nft -f /etc/nftables.d/lab-edge-dnat.nft
|
||||
ExecStop=/usr/sbin/nft delete table ip lab_edge
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
=== 유닛 ===
|
||||
active
|
||||
enabled
|
||||
=== sudo 는 비밀번호를 요구한다 (4·5번을 그대로 칠 수 없다) ===
|
||||
sudo: a password is required
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
=== [워크스테이션] auth.hyeonworks.com 이 어디로 도나 ===
|
||||
100.83.212.4 auth.hyeonworks.com
|
||||
=== 확인 ③ 밖에서 도메인으로 (문서 기대: 301 https://auth.hyeonworks.com/) ===
|
||||
curl: (7) Failed to connect to auth.hyeonworks.com port 80 after 54 ms: Couldn't connect to server
|
||||
000
|
||||
=== 같은 것을 tailnet 주소로 직접 (DNS 를 건너뛴다) ===
|
||||
curl: (7) Failed to connect to 100.83.212.4 port 80 after 2 ms: Couldn't connect to server
|
||||
000
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
=== 04 확인 ① dig ===
|
||||
bash: line 3: dig: command not found
|
||||
(dig 이 없다: 127)
|
||||
=== 04 확인 ② nginx -v (문서가 그대로 적은 형태) ===
|
||||
bash: line 5: nginx: command not found
|
||||
=== 같은 것을 sudo 로 ===
|
||||
nginx version: nginx/1.22.1
|
||||
=== 04 1-① certbot 설치 ===
|
||||
Created symlink /etc/systemd/system/timers.target.wants/certbot.timer → /lib/systemd/system/certbot.timer.
|
||||
|
||||
Setting up python3-lxml:amd64 (4.9.2-1+b1) ...
|
||||
Setting up python3-certbot-dns-cloudflare (2.0.0-1) ...
|
||||
Processing triggers for man-db (2.11.2-2) ...
|
||||
Processing triggers for libc-bin (2.36-9+deb12u14) ...
|
||||
=== 04 1-② 문서가 적은 형태 그대로 (일반 사용자) ===
|
||||
* dns-cloudflare
|
||||
* standalone
|
||||
* webroot
|
||||
(exit 0)
|
||||
=== 같은 것을 sudo 로 ===
|
||||
* dns-cloudflare
|
||||
* standalone
|
||||
* webroot
|
||||
(exit 0)
|
||||
=== 일반 사용자로 쳤을 때 stderr 에 무엇이 나오나 ===
|
||||
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
* dns-cloudflare
|
||||
Description: Obtain certificates using a DNS TXT record (if you are using
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
=== 처방 후보 A: getent (기본 설치) ===
|
||||
100.83.212.4 auth.hyeonworks.com
|
||||
(exit 0)
|
||||
=== 처방 후보 B: dnsutils 를 깔고 dig ===
|
||||
Setting up dnsutils (1:9.18.49-1~deb12u2) ...
|
||||
Processing triggers for man-db (2.11.2-2) ...
|
||||
100.83.212.4
|
||||
(exit 0)
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
=== 05 확인 ① 저장소 루트에서 매니페스트가 보이나 ===
|
||||
a1-block-jgroups-transport.yaml
|
||||
b7-oauth2-proxy.yaml
|
||||
bff-redis.yaml
|
||||
echo-network-policy.yaml
|
||||
echo.yaml
|
||||
keycloak-cluster.yaml
|
||||
observability.yaml
|
||||
traefik-forwarded-headers.yaml
|
||||
=== 05 확인 ② 앞 단계의 404 tls=0 이 그대로인가 ===
|
||||
000 tls=0
|
||||
=== kubectl 이 도나 ===
|
||||
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
|
||||
kc-lab-1 Ready control-plane 9m12s v1.36.4+k3s1 192.168.122.11 <none> Debian GNU/Linux 12 (bookworm) 6.1.0-53-cloud-amd64 (amd64) containerd://2.3.4-k3s1.36
|
||||
kc-lab-2 Ready <none> 7m56s v1.36.4+k3s1 192.168.122.12 <none> Debian GNU/Linux 12 (bookworm) 6.1.0-53-cloud-amd64 (amd64) containerd://2.3.4-k3s1.36
|
||||
@@ -0,0 +1,14 @@
|
||||
=== 05 1-② kubectl apply ===
|
||||
namespace/keycloak-lab created
|
||||
secret/keycloak-lab-secrets created
|
||||
persistentvolumeclaim/postgres-data created
|
||||
deployment.apps/postgres created
|
||||
service/postgres created
|
||||
statefulset.apps/keycloak created
|
||||
service/keycloak-headless created
|
||||
service/keycloak created
|
||||
ingress.networking.k8s.io/keycloak created
|
||||
=== 05 2-① rollout status (문서: partitioned roll out complete: 2 new pods...) ===
|
||||
Waiting for 2 pods to be ready...
|
||||
Waiting for 1 pods to be ready...
|
||||
partitioned roll out complete: 2 new pods have been updated...
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
=== 05 판정 ①-① get all ===
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
pod/keycloak-0 1/1 Running 0 68s
|
||||
pod/keycloak-1 1/1 Running 0 68s
|
||||
pod/postgres-7b474b88c8-s6vdh 1/1 Running 0 68s
|
||||
|
||||
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||
service/keycloak ClusterIP 10.43.173.152 <none> 8080/TCP 69s
|
||||
service/keycloak-headless ClusterIP None <none> 8080/TCP,9000/TCP 69s
|
||||
service/postgres ClusterIP 10.43.38.64 <none> 5432/TCP 69s
|
||||
|
||||
NAME READY UP-TO-DATE AVAILABLE AGE
|
||||
deployment.apps/postgres 1/1 1 1 69s
|
||||
|
||||
NAME DESIRED CURRENT READY AGE
|
||||
replicaset.apps/postgres-7b474b88c8 1 1 1 69s
|
||||
|
||||
NAME READY AGE
|
||||
statefulset.apps/keycloak 2/2 69s
|
||||
=== 05 판정 ①-② get secret,configmap,pvc,ingress ===
|
||||
NAME TYPE DATA AGE
|
||||
secret/keycloak-lab-secrets Opaque 2 69s
|
||||
|
||||
NAME DATA AGE
|
||||
configmap/kube-root-ca.crt 1 69s
|
||||
|
||||
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
|
||||
persistentvolumeclaim/postgres-data Bound pvc-fe269834-3dd1-4b1f-82f6-a53413310a30 5Gi RWO local-path <unset> 69s
|
||||
|
||||
NAME CLASS HOSTS ADDRESS PORTS AGE
|
||||
ingress.networking.k8s.io/keycloak traefik auth.hyeonworks.com 192.168.122.11,192.168.122.12 80 69s
|
||||
=== 05 판정 ② postgres 사슬 ===
|
||||
NAME DESIRED CURRENT READY AGE
|
||||
replicaset.apps/postgres-7b474b88c8 1 1 1 69s
|
||||
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
pod/postgres-7b474b88c8-s6vdh 1/1 Running 0 69s
|
||||
--- Deployment 는 라벨 없이 ---
|
||||
NAME READY UP-TO-DATE AVAILABLE AGE
|
||||
postgres 1/1 1 1 69s
|
||||
--- keycloak 쪽 ---
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
pod/keycloak-0 1/1 Running 0 69s
|
||||
pod/keycloak-1 1/1 Running 0 69s
|
||||
=== 05 판정 ③ Secret 길이 (값은 안 찍는다) ===
|
||||
Data
|
||||
====
|
||||
KC_BOOTSTRAP_ADMIN_PASSWORD: 19 bytes
|
||||
POSTGRES_PASSWORD: 22 bytes
|
||||
--- ② 길이만 ---
|
||||
22
|
||||
--- ③ 파드 안에서 길이만 ---
|
||||
길이=19
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
=== 판정 ③ 끝 — 환경변수가 어느 Secret 에서 왔나 ===
|
||||
KC_DB
|
||||
KC_DB_URL
|
||||
KC_DB_USERNAME
|
||||
KC_DB_PASSWORD keycloak-lab-secrets
|
||||
KC_HOSTNAME
|
||||
KC_HOSTNAME_STRICT
|
||||
KC_PROXY_HEADERS
|
||||
KC_HTTP_ENABLED
|
||||
KC_HEALTH_ENABLED
|
||||
KC_METRICS_ENABLED
|
||||
JAVA_OPTS_KC_HEAP
|
||||
KC_BOOTSTRAP_ADMIN_USERNAME
|
||||
KC_BOOTSTRAP_ADMIN_PASSWORD keycloak-lab-secrets
|
||||
=== 판정 ④ Endpoints ===
|
||||
Endpoints: 10.42.0.9:8080,10.42.1.3:8080
|
||||
--- EndpointSlice ---
|
||||
NAME ADDRESSTYPE PORTS ENDPOINTS AGE
|
||||
keycloak-lxk8h IPv4 8080 10.42.0.9,10.42.1.3 82s
|
||||
--- ready 까지 ---
|
||||
10.42.0.9 true
|
||||
10.42.1.3 true
|
||||
--- 문서가 쓰지 말라는 형태를 실제로 쳐 본다 ---
|
||||
Warning: v1 Endpoints is deprecated in v1.33+; use discovery.k8s.io/v1 EndpointSlice
|
||||
NAME ENDPOINTS AGE
|
||||
keycloak 10.42.0.9:8080,10.42.1.3:8080 82s
|
||||
keycloak-headless 10.42.0.9:8080,10.42.1.3:8080,10.42.0.9:9000 + 1 more... 82s
|
||||
=== 판정 ⑤ PVC ===
|
||||
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
|
||||
postgres-data Bound pvc-fe269834-3dd1-4b1f-82f6-a53413310a30 5Gi RWO local-path <unset> 82s
|
||||
=== 판정 ⑥ 로그 ===
|
||||
2026-09-17 04:35:25,386 INFO [org.infinispan.CLUSTER] (executor-thread-1) ISPN000094: Received new cluster view for channel ISPN: [keycloak-1-3159(v=16.0.12)|1] (2) [keycloak-1-3159(v=16.0.12), keycloak-0-13057(v=16.0.12)]
|
||||
=== 판정 ⑥ 테이블 ===
|
||||
name | ip
|
||||
------------------+----------------
|
||||
keycloak-1-3159 | 10.42.0.9:7800
|
||||
keycloak-0-13057 | 10.42.1.3:7800
|
||||
(2 rows)
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
=== 판정 ⑥ 지표 — Prometheus 가 아직 없을 때의 임시 파드 형태 ===
|
||||
K0=10.42.1.3
|
||||
vendor_cluster_size{cache_manager="keycloak",node="keycloak-0-13057"} 2.0
|
||||
=== 문서가 적은 대로 Keycloak 컨테이너 안에는 curl 이 없나 ===
|
||||
sh: line 1: curl: command not found
|
||||
command terminated with exit code 127
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
=== 판정 ⑦ 밖에서 (문서 기대 200) — 04 를 안 했으니 443 이 없다 ===
|
||||
000
|
||||
--- 같은 것을 읽는 형태로 ---
|
||||
curl: (7) Failed to connect to auth.hyeonworks.com:443 after 2 ms: Could not connect to server
|
||||
=== 04 를 건너뛴 상태에서 Ingress~파드까지는 이어졌나 (엣지에 80 으로, Host 를 실어) ===
|
||||
200
|
||||
--- Traefik 에 직접 ---
|
||||
200
|
||||
=== 판정 ⑧ 세션 표 (로그인 전) ===
|
||||
offline_flag | count
|
||||
--------------+-------
|
||||
(0 rows)
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
=== 06 1-② apply + rollout ===
|
||||
namespace/observability created
|
||||
serviceaccount/prometheus created
|
||||
clusterrole.rbac.authorization.k8s.io/prometheus created
|
||||
clusterrolebinding.rbac.authorization.k8s.io/prometheus created
|
||||
configmap/prometheus-config created
|
||||
persistentvolumeclaim/prometheus-data created
|
||||
deployment.apps/prometheus created
|
||||
service/prometheus created
|
||||
daemonset.apps/node-exporter created
|
||||
service/node-exporter created
|
||||
deployment.apps/grafana created
|
||||
configmap/grafana-datasources created
|
||||
service/grafana created
|
||||
ingress.networking.k8s.io/grafana created
|
||||
Waiting for deployment "prometheus" rollout to finish: 0 of 1 updated replicas are available...
|
||||
deployment "prometheus" successfully rolled out
|
||||
=== 06 판정 ①-① get pods ===
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
grafana-845b5678cf-bblgz 0/1 Running 0 30s
|
||||
node-exporter-82mmn 1/1 Running 0 30s
|
||||
node-exporter-g688j 1/1 Running 0 30s
|
||||
prometheus-6774f94f7c-pj8w9 1/1 Running 0 30s
|
||||
=== 06 판정 ①-② get pods -o wide ===
|
||||
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
|
||||
grafana-845b5678cf-bblgz 0/1 Running 0 30s 10.42.0.11 kc-lab-1 <none> <none>
|
||||
node-exporter-82mmn 1/1 Running 0 30s 192.168.122.12 kc-lab-2 <none> <none>
|
||||
node-exporter-g688j 1/1 Running 0 30s 192.168.122.11 kc-lab-1 <none> <none>
|
||||
prometheus-6774f94f7c-pj8w9 1/1 Running 0 30s 10.42.0.12 kc-lab-1 <none> <none>
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
=== 06 판정 ①-① 다시 (grafana 가 Ready 가 됐나) ===
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
grafana-845b5678cf-bblgz 1/1 Running 0 38s
|
||||
node-exporter-82mmn 1/1 Running 0 38s
|
||||
node-exporter-g688j 1/1 Running 0 38s
|
||||
prometheus-6774f94f7c-pj8w9 1/1 Running 0 38s
|
||||
=== 06 판정 ① 스크레이프 job 이름 ===
|
||||
"job":"keycloak"
|
||||
"job":"kubelet"
|
||||
"job":"node-exporter"
|
||||
"job":"prometheus"
|
||||
=== 06 판정 ② health 와 lastError ===
|
||||
"job":"keycloak"}
|
||||
"job":"keycloak"
|
||||
"lastError":""
|
||||
"health":"unknown"
|
||||
"job":"keycloak"}
|
||||
"job":"keycloak"
|
||||
"lastError":""
|
||||
"health":"unknown"
|
||||
"job":"kubelet"}
|
||||
"job":"kubelet"
|
||||
"lastError":""
|
||||
"health":"up"
|
||||
"job":"kubelet"}
|
||||
"job":"kubelet"
|
||||
"lastError":""
|
||||
"health":"up"
|
||||
"job":"node-exporter"}
|
||||
"job":"node-exporter"
|
||||
"lastError":""
|
||||
"health":"up"
|
||||
"job":"node-exporter"}
|
||||
"job":"node-exporter"
|
||||
"lastError":""
|
||||
"health":"up"
|
||||
"job":"prometheus"}
|
||||
"job":"prometheus"}
|
||||
"lastError":""
|
||||
"health":"up"
|
||||
"job":"keycloak"}}
|
||||
"job":"keycloak"}}
|
||||
=== 06 판정 ② 권한 ===
|
||||
Warning: resource 'nodes' is not namespace scoped
|
||||
|
||||
yes
|
||||
=== 06 판정 ③ vendor_cluster_size ===
|
||||
{"status":"success","data":{"resultType":"vector","result":[{"metric":{"__name__":"vendor_cluster_size","cache_manager":"keycloak","exported_node":"keycloak-1-3159","instance":"10.42.0.9:9000","job":"keycloak","node":"kc-lab-1","pod":"keycloak-1"},"value":[1789619921.770,"2"]}]}}
|
||||
=== 06 판정 ④ up ===
|
||||
{"status":"success","data":{"resultType":"vector","result":[{"metric":{"__name__":"up","instance":"192.168.122.12:9100","job":"node-exporter","node":"kc-lab-2"},"value":[1789619921.874,"1"]},{"metric":{"__name__":"up","beta_kubernetes_io_arch":"amd64","beta_kubernetes_io_instance_type":"k3s","beta_kubernetes_io_os":"linux","instance":"kc-lab-2","job":"kubelet","kubernetes_io_arch":"amd64","kubernetes_io_hostname":"kc-lab-2","kubernetes_io_os":"linux","node_kubernetes_io_instance_type":"k3s"},"value":[1789619921.874,"1"]},{"metric":{"__name__":"up","beta_kubernetes_io_arch":"amd64","beta_kubernetes_io_instance_type":"k3s","beta_kubernetes_io_os":"linux","instance":"kc-lab-1","job":"kubelet","kubernetes_io_arch":"amd64","kubernetes_io_hostname":"kc-lab-1","kubernetes_io_os":"linux","node_kubernetes_io_instance_type":"k3s","node_role_kubernetes_io_control_plane":"true"},"value":[1789619921.874,"1"]},{"metric":{"__name__":"up","instance":"192.168.122.11:9100","job":"node-exporter","node":"kc-lab-1"},"value":[1789619921.874,"1"]},{"metric":{"__name__":"up","instance":"localhost:9090","job":"prometheus"},"value":[1789619921.874,"1"]},{"metric":{"__name__":"up","instance":"10.42.0.9:9000","job":"keycloak","node":"kc-lab-1","pod":"keycloak-1"},"value":[1789619921.874,"1"]}]}}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
=== 90초 뒤 다시 — 스크레이프가 한 바퀴 더 돈 뒤 ===
|
||||
--- targets 의 keycloak health ---
|
||||
"job":"keycloak"}
|
||||
"job":"keycloak"
|
||||
"health":"up"
|
||||
"job":"keycloak"}
|
||||
"job":"keycloak"
|
||||
"health":"up"
|
||||
--
|
||||
"job":"keycloak"}}
|
||||
"job":"keycloak"}}
|
||||
"job":"keycloak"}}
|
||||
"job":"keycloak"}}
|
||||
"job":"keycloak"}}
|
||||
--- vendor_cluster_size ---
|
||||
{"status":"success","data":{"resultType":"vector","result":[{"metric":{"__name__":"vendor_cluster_size","cache_manager":"keycloak","exported_node":"keycloak-1-3159","instance":"10.42.0.9:9000","job":"keycloak","node":"kc-lab-1","pod":"keycloak-1"},"value":[1789620021.097,"2"]},{"metric":{"__name__":"vendor_cluster_size","cache_manager":"keycloak","exported_node":"keycloak-0-13057","instance":"10.42.1.3:9000","job":"keycloak","node":"kc-lab-2","pod":"keycloak-0"},"value":[1789620021.097,"2"]}]}}
|
||||
--- up{job=keycloak} ---
|
||||
{"status":"success","data":{"resultType":"vector","result":[{"metric":{"__name__":"up","instance":"10.42.0.9:9000","job":"keycloak","node":"kc-lab-1","pod":"keycloak-1"},"value":[1789620021.182,"1"]},{"metric":{"__name__":"up","instance":"10.42.1.3:9000","job":"keycloak","node":"kc-lab-2","pod":"keycloak-0"},"value":[1789620021.182,"1"]}]}}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
=== 07 1-① 상한을 올리고 현재 할당을 맞춘다 ===
|
||||
Max memory: 5242880 KiB
|
||||
Used memory: 5242880 KiB
|
||||
|
||||
setmaxmem OK
|
||||
|
||||
setmem OK
|
||||
=== ② 도메인이 보는 값과 게스트가 인식한 값 ===
|
||||
Max memory: 5242880 KiB
|
||||
Used memory: 5242880 KiB
|
||||
total used free shared buff/cache available
|
||||
Mem: 4929 2587 126 2 2507 2342
|
||||
=== 되돌린다 (원래 값으로) ===
|
||||
|
||||
|
||||
원복 OK
|
||||
Max memory: 5242880 KiB
|
||||
Used memory: 5242880 KiB
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
=== dominfo 는 무엇을 보여 주나 (도메인이 돌고 있을 때) ===
|
||||
Max memory: 5242880 KiB
|
||||
Used memory: 5242880 KiB
|
||||
=== 같은 순간의 설정값 (--inactive) ===
|
||||
<memory unit='KiB'>4194304</memory>
|
||||
<currentMemory unit='KiB'>4194304</currentMemory>
|
||||
=== 돌고 있는 값 ===
|
||||
<memory unit='KiB'>5242880</memory>
|
||||
<currentMemory unit='KiB'>5242880</currentMemory>
|
||||
=== 다시 5120M 으로 맞춰 둔다 (원래 상태) ===
|
||||
|
||||
|
||||
<memory unit='KiB'>5242880</memory>
|
||||
<currentMemory unit='KiB'>5242880</currentMemory>
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
=== 00 확인 ① CPU 가 확장을 내놓나 ===
|
||||
vmx
|
||||
=== 확인 ② 커널이 잡고 있나 ===
|
||||
kvm_intel 524288 11
|
||||
kvm 1490944 6 kvm_intel
|
||||
irqbypass 16384 1 kvm
|
||||
=== 1. 저장소 ===
|
||||
cloud
|
||||
keycloak-pattern
|
||||
a1-block-jgroups-transport.yaml
|
||||
b7-oauth2-proxy.yaml
|
||||
bff-redis.yaml
|
||||
echo-network-policy.yaml
|
||||
=== 2. virsh 판 ===
|
||||
12.7.0
|
||||
=== 3. libvirt 소켓과 그룹 ===
|
||||
● libvirtd.socket - libvirt legacy monolithic daemon socket
|
||||
Loaded: loaded (/usr/lib/systemd/system/libvirtd.socket; enabled; preset: disabled)
|
||||
Active: active (running) since Thu 2026-09-03 19:00:35 KST; 1 week 6 days ago
|
||||
Invocation: e330532b350d4d79ad7f31f7c6512a6c
|
||||
libvirt
|
||||
libvirt:x:962:donghyeon
|
||||
=== 4. 연결 URI ===
|
||||
LIBVIRT_DEFAULT_URI=[(비어 있음)]
|
||||
/home/donghyeon/.zshrc:2:export LIBVIRT_DEFAULT_URI=qemu:///system
|
||||
/home/donghyeon/.bashrc:11:export LIBVIRT_DEFAULT_URI=qemu:///system
|
||||
qemu:///system
|
||||
|
||||
=== 5. 기본 네트워크 ===
|
||||
Name State Autostart Persistent
|
||||
--------------------------------------------
|
||||
default active yes yes
|
||||
|
||||
=== 확인 ① virsh 가 sudo 없이 통과하나 ===
|
||||
Id Name State
|
||||
-----------------------------
|
||||
13 kc-lab-1 running
|
||||
15 kc-lab-edge running
|
||||
16 kc-lab-2 running
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
=== rc 파일이 비대화형 ssh 에서 읽히나 ===
|
||||
비대화형: LIBVIRT_DEFAULT_URI=[(비어 있음)]
|
||||
=== 그런데도 virsh 가 system 을 보는 까닭 ===
|
||||
-rw-r--r-- 1 root root 547 Sep 1 19:16 /etc/libvirt/libvirt.conf
|
||||
-rw-r--r-- 1 donghyeon donghyeon 31 Sep 9 13:42 /home/donghyeon/.config/libvirt/libvirt.conf
|
||||
/home/donghyeon/.config/libvirt/libvirt.conf:uri_default = "qemu:///system"
|
||||
/etc/libvirt/libvirt.conf:# (@uri_default also prevents probing of the hypervisor driver).
|
||||
/etc/libvirt/libvirt.conf:#uri_default = "qemu:///system"
|
||||
=== virsh 가 지금 무엇을 보나 ===
|
||||
qemu:///system
|
||||
|
||||
=== 대화형 셸에서는 rc 가 읽힌다 ===
|
||||
대화형: LIBVIRT_DEFAULT_URI=qemu:///system
|
||||
@@ -0,0 +1,51 @@
|
||||
2026-09-17 에 test-server 를 문서대로 철거하고 다시 세운 기록.
|
||||
|
||||
문서가 맞는지 실물로 확인하는 것이 목적이라, 명령을 고쳐 치지 않고 **문서에 적힌
|
||||
그대로** 쳤다. 그래서 실패한 출력도 그대로 들어 있다 — 06 이 그것이다.
|
||||
|
||||
| 파일 | 무엇 |
|
||||
|---|---|
|
||||
| 01 | 철거 전 호스트 상태 전부 |
|
||||
| 02 | 철거 문서의 「지우기 전에 먼저 본다」 |
|
||||
| 03 | 철거 1단계 — 게스트 셋 |
|
||||
| 04 | 철거 2단계 — DHCP 예약 셋 |
|
||||
| 05 | 철거 확인표 다섯 줄 |
|
||||
| 06 | **구축 5번을 문서 그대로 쳤다 → 실패.** meta 파일 이름에 `.yaml` 이 빠져 있다 |
|
||||
| 07 | 이름을 고치고 세 장을 구웠다. 문서에는 한 장 굽는 절만 있다 |
|
||||
| 08 | 시드 볼륨 셋 업로드 |
|
||||
| 09 | DHCP 예약 셋 |
|
||||
| 10~11 | `virt-install` 세 대 |
|
||||
| 12 | 부팅 뒤 리스 — 셋 다 예약 주소를 받았다 |
|
||||
| 13 | 게스트 SSH 와 `vdb` 가 `iso9660 CIDATA` 인 것 |
|
||||
| 14 | `cloud-init schema` 는 `sudo:` 리스트형을 거부하는데 런타임은 받는다 |
|
||||
| 15 | **k3s 사전 확인이 실패.** `REMOTE HOST IDENTIFICATION HAS CHANGED!` |
|
||||
| 16 | 별칭은 되고 IP 직접 지정은 막힌다 — `Host kc-lab-*` 가 IP 에 안 걸린다 |
|
||||
| 17 | 옛 키를 지우고 다시 |
|
||||
| 18~19 | k3s server 설치와 확인 |
|
||||
| 20 | kubeconfig 를 lab host 로 |
|
||||
| 21~22 | 토큰 전달과 agent 설치 |
|
||||
| 23 | 2노드 클러스터 확인 |
|
||||
|
||||
비밀은 옮기지 않았다. 14 에 평문 비밀번호와 공개키가 찍혀 나와 `<REDACTED>` 로
|
||||
가렸고, 가린 자리에 길이만 남겼다.
|
||||
25-edge-nginx-config.txt 03 1-④·2·3 — sites-enabled/default 를 지우고 keycloak-lab 을 써서 링크·nginx -t·reload 까지
|
||||
26-edge-nginx-verify.txt 03 3-②③④ 와 층 ①② — nginx -t 가 두 줄이다([warn] 없음), nginx -v 가 command not found, 층 ② 가 404
|
||||
27-edge-doc-mismatch.txt which nginx 가 깔린 상태에서도 빈손인 것 · PATH · sudo nginx -v · nginx -t 3회 warn 0
|
||||
28-host-state-before-dnat.txt 03 4번 치기 전 호스트 — tailscale0 있음, DNAT 파일과 유닛이 2026-09-11 자로 이미 있음, 호스트 sudo 는 비밀번호를 묻는다
|
||||
29-host-dnat-files.txt 호스트에 깔려 있는 .nft 와 유닛 원문 — forward 체인이 남아 있고 ExecStartPost 가 없다 (문서·저장소 원본과 다르다)
|
||||
30-layer3-from-workstation.txt 03 층 ③ 을 밖에서 — curl: (7) Failed to connect. 29 의 ExecStartPost 누락이 그대로 드러난 자리
|
||||
31-cert-doc-precheck.txt 04 확인 ①② 와 1번 — dig 이 게스트에 없다, nginx -v 가 sudo 없이는 안 된다, certbot plugins 세 줄은 문서대로
|
||||
32-cert-doc-dig-remedy.txt 처방 검증 — getent hosts 와 dnsutils 설치 뒤의 dig 이 같은 주소를 낸다
|
||||
33-keycloak-precheck.txt 05 확인 ①② — 매니페스트 목록, 000 tls=0 (연결이 안 돼도 tls=0 이 찍힌다), 2노드 Ready
|
||||
34-keycloak-apply.txt 05 1-②·2-① — apply 출력 9줄과 rollout status. Waiting for … 줄이 먼저 나온다
|
||||
35-keycloak-verify-1-3.txt 05 판정 ①~③ — get all·get secret,configmap,pvc,ingress·사슬·Secret 길이 19/22/19 (값은 안 찍었다)
|
||||
36-keycloak-verify-4-6.txt 05 판정 ④~⑥ — Endpoints 둘, PVC 가 postgres-data 하나, ISPN000094 에 (v=16.0.12), jgroups_ping 2행
|
||||
37-keycloak-cluster-size.txt 05 판정 ⑥ 지표 — 임시 파드가 읽은 vendor_cluster_size 2.0, Keycloak 안에 curl 이 없다(exit 127)
|
||||
38-keycloak-verify-7-8.txt 05 판정 ⑦⑧ — 밖에서는 000(04 미실시), Host 헤더를 실은 80 은 엣지·Traefik 둘 다 200, 세션 표 0행
|
||||
39-observability-apply.txt 06 1·2 — apply 출력, rollout, 파드 네 줄(38초에 grafana 가 0/1)
|
||||
40-observability-verify.txt 06 판정 ①~④ — job 네 개, health 가 unknown 인 keycloak 둘, can-i 의 경고 줄, 원소 하나뿐인 cluster_size
|
||||
41-observability-cluster-size-retry.txt 90초 뒤 다시 — health 가 up 이 되고 원소 둘 다 2. node/pod/exported_node 세 라벨이 다른 것을 말한다
|
||||
42-power-cycle-memory.txt 07 1번 — setmaxmem/setmem --config 뒤 dominfo 가 안 바뀐다. 게스트 free -m 도 옛 값
|
||||
43-dominfo-vs-config.txt 같은 순간의 dominfo(5242880) 와 dumpxml --inactive(4194304). 바꾼 것이 들어갔는지는 --inactive 가 답한다
|
||||
44-host-prep-checks.txt 00 의 확인 명령 전부 — vmx, kvm 모듈 셋, 저장소, virsh 12.7.0, libvirtd.socket, libvirt 그룹, default 네트워크, sudo 없는 virsh list
|
||||
45-libvirt-uri-mechanism.txt rc 파일은 비대화형 ssh 에서 안 읽힌다. 그런데도 되는 까닭은 ~/.config/libvirt/libvirt.conf 의 uri_default
|
||||
+83
-20
@@ -123,11 +123,11 @@ ssh kc-lab-edge
|
||||
|
||||
**무엇을 확인하는가** — 엣지에 nginx 가 이미 깔려 있는지.
|
||||
|
||||
```bash label="[kc-lab-edge] 실행 파일이 있는지 본다"
|
||||
which nginx
|
||||
```bash label="[kc-lab-edge] 패키지가 만드는 디렉터리를 본다"
|
||||
ls /etc/nginx/
|
||||
```
|
||||
|
||||
**어디를 봐야 하는가** — 아무것도 안 찍히면 미설치다. 2026-09-11 에 새로 만든 `kc-lab-edge` 에서는 이렇게 나왔다(observed).
|
||||
**어디를 봐야 하는가** — `No such file or directory` 가 나오면 미설치다. 2026-09-11 에 새로 만든 `kc-lab-edge` 에서는 이렇게 나왔다(observed).
|
||||
|
||||
```text
|
||||
donghyeon@kc-lab-edge:~$ cd /etc/nginx/
|
||||
@@ -136,6 +136,16 @@ donghyeon@kc-lab-edge:~$ cd /etc/nginx/
|
||||
|
||||
**이 결과가 의미하는 것** — `/etc/nginx` 디렉터리는 패키지가 만든다. 그것이 없다는 것은 경로를 잘못 짚었다는 뜻이 아니라 설치가 안 됐다는 뜻이다. 앞 단계의 cloud-init 목록에 nginx 가 없으므로 1번에서 깐다.
|
||||
|
||||
**여기에 `which nginx` 를 쓰지 않는다.** 깔려 있을 때도 똑같이 아무것도 안 찍힌다 — 2026-09-17 에 nginx 가 돌고 있는 엣지에서 직접 쳐서 확인했다(observed).
|
||||
|
||||
```text
|
||||
which nginx -> (아무것도 안 찍고 exit 1)
|
||||
PATH -> /usr/local/bin:/usr/bin:/bin:/usr/games
|
||||
sudo nginx -v -> nginx version: nginx/1.22.1
|
||||
```
|
||||
|
||||
실행 파일은 `/usr/sbin/nginx` 인데 데비안의 일반 사용자 `PATH` 에 `/usr/sbin` 이 없다. 그래서 이 명령의 빈손은 「안 깔렸다」가 아니라 「이 PATH 에서는 안 보인다」이고, **깔린 경우와 안 깔린 경우의 출력이 같아 판정에 쓸 수 없다.** 판 번호를 볼 때도 `nginx -v` 가 아니라 `sudo nginx -v` 다.
|
||||
|
||||
## 실행 절차
|
||||
|
||||
### 1. 엣지에 nginx 를 깔고 기본 사이트를 끈다
|
||||
@@ -239,7 +249,7 @@ sudo nginx -t && sudo systemctl reload nginx
|
||||
systemctl status nginx --no-pager | head -20
|
||||
```
|
||||
|
||||
**예상 결과** — Debian 12 는 `[warn]` 한 줄을 늘 같이 내놓는다(observed).
|
||||
**예상 결과** — 2026-09-11 의 엣지에서는 `[warn]` 한 줄이 앞에 붙어 세 줄이었다(observed).
|
||||
|
||||
```text
|
||||
nginx: [warn] could not build optimal types_hash, you should increase either
|
||||
@@ -248,7 +258,14 @@ nginx: configuration file /etc/nginx/nginx.conf syntax is ok
|
||||
nginx: configuration file /etc/nginx/nginx.conf test is successful
|
||||
```
|
||||
|
||||
`syntax is ok` 와 `test is successful` 두 마디가 다 나와야 통과다. 앞의 `[warn]` 줄은 통과를 막지 않는다. 통과했으면 `&&` 뒤의 reload 가 이어서 돌고 `systemctl reload` 는 아무 말 없이 끝난다.
|
||||
**그 `[warn]` 이 늘 나오지는 않는다.** 2026-09-17 에 같은 Debian 12 · 같은 `nginx/1.22.1` 엣지를 새로 세우고 세 번 쳤더니 한 번도 안 나왔다(observed). 두 줄뿐이었다.
|
||||
|
||||
```text
|
||||
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
|
||||
nginx: configuration file /etc/nginx/nginx.conf test is successful
|
||||
```
|
||||
|
||||
**판정은 `syntax is ok` 와 `test is successful` 두 마디로만 한다.** `[warn]` 줄은 나오면 무시하고 안 나오면 없는 대로 둔다 — 없다고 설정이 덜 읽힌 것이 아니다. 통과했으면 `&&` 뒤의 reload 가 이어서 돌고 `systemctl reload` 는 아무 말 없이 끝난다.
|
||||
|
||||
**왜 필요한가** — `&&` 로 이은 것은 문법이 깨진 설정으로 reload 하지 않으려는 것이다. 실패면 `[emerg]` 줄에 파일과 줄 번호가 찍히고 reload 는 아예 안 돌아, 지금 돌고 있는 nginx 는 옛 설정 그대로 멀쩡하다. 경고와 오류를 여기서 갈라 두면 다음 단계에서 같은 `types_hash` 경고를 실패로 오독하지 않는다. ④ 의 프로세스 트리에서 워커 줄의 PID 를 눈에 담아 둔다 — reload 는 마스터를 그대로 두고 워커만 갈아 끼우므로, 전후로 워커 PID 가 바뀌면 새 설정이 적용된 것이다. 다음 단계에서 인증서 갱신이 서빙까지 닿았는지를 똑같은 방법으로 판정한다.
|
||||
|
||||
@@ -399,12 +416,14 @@ PREROUTING nat 은 라우팅 결정보다 먼저 도므로 이 규칙이 호스
|
||||
|
||||
한 번에 밖에서 치지 말고 가까운 층부터 본다. 네 명령이 각각 다른 층을 건너뛰므로 어디서 끊겼는지가 바로 나온다.
|
||||
|
||||
| # | 무엇을 건너뛰나 | 실측 |
|
||||
|---|---|---|
|
||||
| ① `192.168.122.11` | nginx 를 건너뛴다 | `404` |
|
||||
| ② `192.168.122.10` | DNAT 을 건너뛴다 | `301` |
|
||||
| ③ 도메인 | 밖에서 | `301 https://auth.hyeonworks.com/` |
|
||||
| ④ 도메인 · TLS 이후 | — | `200` |
|
||||
| # | 무엇을 건너뛰나 | 이 단계까지 했을 때 | 다 끝난 실험대에서 |
|
||||
|---|---|---|---|
|
||||
| ① `192.168.122.11` | nginx 를 건너뛴다 | `404` | `404` |
|
||||
| ② `192.168.122.10` | DNAT 을 건너뛴다 | `404` | `301` |
|
||||
| ③ 도메인 | 밖에서 | `404` | `301 https://auth.hyeonworks.com/` |
|
||||
| ④ 도메인 · TLS 이후 | — | (아직 안 된다) | `200` |
|
||||
|
||||
**칸이 둘인 까닭.** 이 단계가 쓰는 설정에는 `listen 80` 블록 하나뿐이고 그 안은 `proxy_pass` 다. 리다이렉트를 낼 블록이 없으니 **`301` 은 여기서 나오지 않는다.** 다음 단계가 443 블록과 80→443 리다이렉트를 얹어야 그 값이 나온다. 오른쪽 칸을 그대로 기준으로 삼으면 정상인 `404` 를 실패로 읽는다.
|
||||
|
||||
처음 볼 때는 응답을 눈으로 읽는 형태로 치고, 같은 것을 여러 번 재거나 두 노드를 나란히 비교할 때만 값만 뽑는 형태로 바꾼다.
|
||||
|
||||
@@ -437,9 +456,20 @@ curl -s -o /dev/null -w '%{http_code}\n' http://192.168.122.12
|
||||
curl -s -o /dev/null -w '%{http_code} %{redirect_url}\n' http://192.168.122.10
|
||||
```
|
||||
|
||||
**어디를 봐야 하는가** — `301` 과 `Location` 이 나오는가.
|
||||
**어디를 봐야 하는가** — 엣지가 답을 하는가. **이 단계에서 나오는 코드는 `404` 다.** 2026-09-17 에 이 절을 그대로 따라 엣지를 세우고 재 보니 이렇게 나왔다(observed).
|
||||
|
||||
**이 결과가 의미하는 것** — 여기서 막히면 문제는 엣지 안이다. 통과하는데 아래 ③ 이 안 되면 문제는 DNAT 이다. 이 한 층을 끼워 두면 그 둘을 헷갈리지 않는다.
|
||||
```text
|
||||
HTTP/1.1 404 Not Found
|
||||
Server: nginx/1.22.1
|
||||
Content-Type: text/plain; charset=utf-8
|
||||
Content-Length: 19
|
||||
```
|
||||
|
||||
```text
|
||||
404 page not found
|
||||
```
|
||||
|
||||
**이 결과가 의미하는 것** — `Server:` 가 `nginx/1.22.1` 이고 본문이 Traefik 의 `404 page not found` 라는 것이 엣지가 받아서 Traefik 으로 넘겼다는 증거다. **이 층의 통과 기준은 코드값이 아니라 「`curl: (7)` 없이 응답이 온다」와 `Server: nginx` 두 가지다.** 여기서 막히면 문제는 엣지 안이다. 통과하는데 아래 ③ 이 안 되면 문제는 DNAT 이다. 이 한 층을 끼워 두면 그 둘을 헷갈리지 않는다.
|
||||
|
||||
### 확인 ③ 밖에서, 즉 DNAT 을 거쳐 닿나
|
||||
|
||||
@@ -461,6 +491,16 @@ curl -s -o /dev/null -w '%{http_code} %{redirect_url}\n' http://auth.hyeonworks.
|
||||
301 https://auth.hyeonworks.com/
|
||||
```
|
||||
|
||||
**그 `301` 도 다음 단계 이후의 값이다.** 여기까지만 했다면 ② 와 같은 `404` 가 정상이고, 이 층이 묻는 것은 코드값이 아니라 밖에서 친 것이 엣지까지 닿았는가다.
|
||||
|
||||
2026-09-17 에 밖에서 재 보니 닿지 않았다(observed).
|
||||
|
||||
```text
|
||||
curl: (7) Failed to connect to auth.hyeonworks.com port 80 after 54 ms
|
||||
```
|
||||
|
||||
같은 시각에 호스트 안에서 친 `http://192.168.122.10` 은 `404` 로 답했다. 아래 「막히면」 표의 마지막 줄이 가리키는 상태 그대로였고, 원인도 거기 적힌 대로였다 — **그 호스트에 깔려 있는 유닛에 `ExecStartPost` 줄이 없다.** 아래 「배포된 것과 적어 둔 것이 다르다」를 본다.
|
||||
|
||||
### 확인 ④ 끝까지 닿나
|
||||
|
||||
**무엇을 확인하는가** — nginx 에서 Traefik 을 지나 파드까지 2홉이 다 이어졌는지.
|
||||
@@ -525,10 +565,10 @@ grep oauth2/callback /var/log/nginx/access.log | tail -1 | wc -c
|
||||
| 유닛 | `systemctl is-active lab-edge-dnat.service` | `active` |
|
||||
| 우리 테이블 | `sudo nft list table ip lab_edge` | `dnat to 192.168.122.10` · 포트 `80, 443` · SNAT 없음 |
|
||||
| 층 ① | `curl -I http://192.168.122.11` | `404` |
|
||||
| 층 ② | `curl -s -o /dev/null -w '%{http_code} %{redirect_url}\n' http://192.168.122.10` | `301` |
|
||||
| 층 ③ | `curl -I http://auth.hyeonworks.com` | `301` · `Location` 이 원래 호스트명 |
|
||||
| 층 ② | `curl -sS -D - -o /dev/null http://192.168.122.10` | `404` · `Server: nginx/1.22.1` |
|
||||
| 층 ③ | `curl -I http://auth.hyeonworks.com` | ② 와 같은 응답이 밖에서도 온다 |
|
||||
|
||||
층 ④ 는 다음 단계가 인증서를 얹은 뒤에 통과한다.
|
||||
층 ④ 는 다음 단계가 인증서를 얹은 뒤에 통과한다. **층 ②③ 의 `301` 도 그때 나온다** — 여기서는 `404` 가 통과다.
|
||||
|
||||
## 막히면
|
||||
|
||||
@@ -538,10 +578,10 @@ grep oauth2/callback /var/log/nginx/access.log | tail -1 | wc -c
|
||||
| ① 이 `502` | Traefik 은 떴는데 백엔드가 없음 | Ingress 확인 |
|
||||
| ② 가 응답 없음 | nginx 가 안 떴거나 방화벽 | `systemctl status nginx` |
|
||||
| ③ 이 `502` | 인증서 문제 또는 upstream 다운 | 다음 단계 · 위 확인 ⑤ |
|
||||
| `/etc/nginx: No such file or directory` | nginx 미설치. cloud-init 은 안 깐다 | `which nginx` |
|
||||
| `/etc/nginx: No such file or directory` | nginx 미설치. cloud-init 은 안 깐다 | `ls /etc/nginx/` — `which nginx` 는 깔려 있어도 빈손이라 못 가른다 |
|
||||
| `nginx -t` 가 `duplicate default server` | `sites-enabled/default` 가 살아 있다 | `ls -l /etc/nginx/sites-enabled/` |
|
||||
| 설정을 썼는데 아무 변화가 없다 | 경로 오타 — `site-available`(단수)에 썼다 | `ls /etc/nginx/sites-available/` |
|
||||
| `unknown directive "http2"` | nginx < 1.25.1. Debian 12 는 1.22 다 | `nginx -v` |
|
||||
| `unknown directive "http2"` | nginx < 1.25.1. Debian 12 는 1.22 다 | `sudo nginx -v` |
|
||||
| `systemctl reload` 가 실패한다 | 설정이 `nginx -t` 를 못 통과했다 | reload 말고 `sudo nginx -t` 를 먼저 |
|
||||
| `cp: cannot stat 'deploy/...'` | 엣지에서 쳤거나 lab host 에 저장소가 없다 | `ls ~/workspace` |
|
||||
| `Unit lab-edge-dnat.service does not exist` | 유닛이 없거나 `/etc/systemd/` 에 썼거나 `daemon-reload` 를 안 했다 | `find /etc/systemd -name 'lab-edge-dnat*'` |
|
||||
@@ -549,10 +589,33 @@ grep oauth2/callback /var/log/nginx/access.log | tail -1 | wc -c
|
||||
| 호스트 안에서는 404 인데 밖에서만 connection refused | libvirt `guest_input` 의 `reject`. 구멍이 빠졌거나 날아갔다 | `sudo nft -a list chain ip libvirt_network guest_input` — `reject` 줄의 카운터가 올라가면 여기다 |
|
||||
| `Error: syntax error, unexpected ct` | 셸이 `{80,443}` 을 펼쳤다 | `'{80,443}'` 로 따옴표 |
|
||||
|
||||
## 배포된 것과 적어 둔 것이 다르다
|
||||
|
||||
2026-09-17 에 이 절차를 따라 실험대를 다시 세우다 호스트의 두 파일을 열어 봤더니 **위에 실은 내용과 달랐다**(observed). 저장소 원본을 고친 뒤 호스트에 다시 배포하지 않았다 — 파일 시각이 그렇게 말한다.
|
||||
|
||||
| 파일 | 호스트에 깔린 것 (`11:00` · `10:52`) | 저장소 원본과 위 본문 (`13:30`) |
|
||||
|---|---|---|
|
||||
| `lab-edge-dnat.nft` | `prerouting` 뒤에 `forward` 체인이 그대로 있다 (`priority filter - 10` · `ct state new accept`) | 그 체인이 없다. 자리에 왜 뺐는지 주석만 남겼다 |
|
||||
| `lab-edge-dnat.service` | `ExecStartPost` 줄이 없다 | `ExecStartPost=-…insert rule ip libvirt_network guest_input…` 이 있다 |
|
||||
|
||||
**이것이 위 확인 ③ 의 `curl: (7)` 을 그대로 설명한다.** 고친 쪽의 요점은 앞 체인의 `accept` 로는 뒤 체인의 `reject` 를 못 막으니 구멍은 libvirt 체인 안에 뚫는다는 것인데, 호스트에는 **효과가 없는 쪽만 깔려 있고 효과가 있는 쪽은 안 깔려 있다.** 게다가 그 구멍은 런타임 상태라 libvirt 가 네트워크를 다시 세우면 날아가고, 다시 넣어 줄 `ExecStartPost` 가 유닛에 없으니 **실험대를 다시 세운 직후에는 밖에서 못 들어온다.**
|
||||
|
||||
**그래서 4번과 5번은 「이미 있으니 건너뛴다」로 넘기지 않는다.** 파일이 있는 것과 그 파일이 이 문서가 싣는 내용인 것은 다르다. 두 파일을 연 다음 위 본문과 한 줄씩 대조한다.
|
||||
|
||||
```bash label="[lab host] 깔려 있는 것이 이 문서가 싣는 내용인지 본다"
|
||||
grep -c 'chain forward' /etc/nftables.d/lab-edge-dnat.nft
|
||||
grep -c ExecStartPost /etc/systemd/system/lab-edge-dnat.service
|
||||
```
|
||||
|
||||
`0` 과 `1` 이 나와야 맞다. 반대로 나오면 고치기 전 판이 깔려 있는 것이고, 다시 써서 `sudo systemctl daemon-reload && sudo systemctl restart lab-edge-dnat.service` 로 올린다.
|
||||
|
||||
**고쳤는지는 확인하지 못했다**(unknown). 그 호스트의 `sudo` 는 비밀번호를 묻고 이번 작업에는 그 비밀번호가 없었다. `nft list table ip lab_edge` 도 `guest_input` 체인 조회도 못 돌렸고 두 파일을 다시 배포하지도 않았다. 위 표는 **파일 내용과 타임스탬프만으로** 적은 것이고 `reject` 줄의 카운터가 실제로 올라갔는지는 안 봤다.
|
||||
|
||||
## 무엇이 관측이고 무엇이 아닌가
|
||||
|
||||
- (observed) 2026-09-11 새로 만든 엣지에서 `/etc/nginx` 가 없던 것, `nginx -t` 출력 세 줄, 층별 확인 ①~④ 의 코드, `301 https://auth.hyeonworks.com/`, upstream 실패 errno 세 줄, access 로그 3492자.
|
||||
- (observed) `.nft` 와 유닛 파일의 내용은 저장소 원본과 같다.
|
||||
- (observed) 2026-09-11 새로 만든 엣지에서 `/etc/nginx` 가 없던 것, `nginx -t` 출력 세 줄, 다 끝난 실험대에서 잰 층별 확인 ①~④ 의 코드, `301 https://auth.hyeonworks.com/`, upstream 실패 errno 세 줄, access 로그 3492자.
|
||||
- (observed) 2026-09-17 에 이 절차를 따라 엣지를 다시 세우고 잰 것 — `nginx -t` 가 두 줄인 것, `which nginx` 가 깔린 상태에서도 빈손인 것, 층 ① 이 `.11`·`.12` 둘 다 `404` 인 것, 층 ② 가 `404` · `Server: nginx/1.22.1` 인 것, 층 ③ 이 `curl: (7)` 인 것, 호스트의 두 파일이 위 본문과 다른 것.
|
||||
- (observed) 위에 실은 `.nft` 와 유닛의 내용은 저장소 원본과 같다. 다만 **호스트에 실제로 깔려 있는 두 파일은 그것이 아니다** — 「배포된 것과 적어 둔 것이 다르다」를 본다.
|
||||
- (inferred) 이 이동으로 L7 홉 수가 2홉 그대로라는 판정이 이 배치의 전제다. 늘어난 것은 커널이 하는 L4 전달 한 번뿐이라 `X-Forwarded-*` 계약이 그대로 성립한다.
|
||||
- (unknown) `curl -I http://192.168.122.11` 의 전체 출력은 캡처해 두지 않았다. 가이드도 봐야 할 줄만 적었다.
|
||||
- (unknown) `systemctl status nginx` 두 번과 `ls -l /etc/nginx/sites-enabled/`, 파일 찾기, 체인 조회, `journalctl -u nginx`, access 로그 두 줄은 가이드가 적어 둔 명령이고 출력이 남아 있지 않다.
|
||||
|
||||
+32
-2
@@ -261,11 +261,41 @@ curl -sfL https://get.k3s.io | sudo sh -s - agent \
|
||||
--node-ip 192.168.122.12
|
||||
```
|
||||
|
||||
```bash label="[kc-lab-2] ⑥ 설치가 끝나면 토큰 파일을 지운다"
|
||||
```bash label="[kc-lab-2] ⑥ 토큰을 root 만 읽는 자리로 옮긴다"
|
||||
sudo install -m 600 -o root -g root ~/node-token /etc/rancher/node-token
|
||||
sudo ls -l /etc/rancher/node-token
|
||||
```
|
||||
|
||||
```bash label="[kc-lab-2] ⑦ 유닛이 그 자리를 보게 고치고 다시 읽는다"
|
||||
sudo sed -i "s|$HOME/node-token|/etc/rancher/node-token|" /etc/systemd/system/k3s-agent.service
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl restart k3s-agent
|
||||
```
|
||||
|
||||
```bash label="[kc-lab-2] ⑧ 이제 홈의 사본을 지운다"
|
||||
rm ~/node-token
|
||||
```
|
||||
|
||||
```bash label="[kc-lab-2] ⑦ lab host 로 나온다"
|
||||
:::warning
|
||||
|
||||
**⑥⑦ 을 건너뛰고 `rm ~/node-token` 만 치면 이 노드는 다시 못 뜬다.** 설치 스크립트가 `--token-file` 에 준 경로를 유닛의 `ExecStart` 에 그대로 굽기 때문에, 파일이 사라지면 agent 가 영원히 그 파일을 기다린다. 2026-09-17 에 A-4 를 돌려 노드 전원을 뽑았다 다시 켜면서 그대로 겪었다(observed) — VM 은 떴는데 노드가 `NotReady` 에서 안 돌아왔다.
|
||||
|
||||
:::
|
||||
|
||||
```text
|
||||
k3s[522]: level=info msg="Waiting for file \"/home/donghyeon/node-token\" to be created"
|
||||
k3s-agent.service: Scheduled restart job, restart counter is at 1.
|
||||
```
|
||||
|
||||
토큰을 `/etc/rancher/node-token` 으로 옮기고 유닛을 그쪽으로 돌린 뒤 홈의 사본을 지우자, agent 를 완전히 세웠다 다시 켜도 `active` 였고 노드도 `Ready` 로 돌아왔다(observed).
|
||||
|
||||
```text
|
||||
-rw------- 1 root root 109 /etc/rancher/node-token
|
||||
'--token-file' \
|
||||
'/etc/rancher/node-token' \
|
||||
```
|
||||
|
||||
```bash label="[kc-lab-2] ⑨ lab host 로 나온다"
|
||||
exit
|
||||
```
|
||||
|
||||
|
||||
+70
-8
@@ -71,12 +71,19 @@ source:
|
||||
| PostgreSQL | Deployment `postgres` — ReplicaSet `postgres-7b474b88c8` |
|
||||
| Service | `keycloak` → Endpoints `10.42.0.67:8080,10.42.1.155:8080` |
|
||||
| Secret | `keycloak-lab-secrets` — `KC_BOOTSTRAP_ADMIN_PASSWORD` 19 bytes · `POSTGRES_PASSWORD` 22 bytes |
|
||||
| PVC | StorageClass `local-path`, 이름 끝에 파드 번호가 붙는다 |
|
||||
| PVC | `postgres-data` 하나. StorageClass `local-path` — **파드 번호가 붙지 않는다**(판정 ⑤) |
|
||||
| Ingress | HOSTS 가 `auth.hyeonworks.com` |
|
||||
| 클러스터링 | JGroups — 디스커버리 테이블 `jgroups_ping`, 메시지 포트 7800 |
|
||||
| 관리 포트 | `9000` — `/metrics` 가 거기 있다 |
|
||||
|
||||
**판 번호는 여기 없다**(unknown). 가이드 05 는 Keycloak 과 PostgreSQL 의 이미지 태그를 적지 않았고 `keycloak-cluster.yaml` 원문은 반입되지 않았다. 가이드 안에 태그가 찍힌 이미지는 아래 임시 파드의 `curlimages/curl:8.11.1` 하나뿐이다.
|
||||
**이미지 태그는 매니페스트가 갖는다.** 가이드 05 는 그것을 적지 않았지만 2026-09-17 에 `keycloak-pattern` @ `9465582b` 의 `deploy/lab/k8s/keycloak-cluster.yaml` 을 열어 읽었다(observed).
|
||||
|
||||
```text
|
||||
image: postgres:16-alpine
|
||||
image: quay.io/keycloak/keycloak:26.7.0
|
||||
```
|
||||
|
||||
파드 자원 한도와 프로브 설정과 `persistent-user-sessions` 설정값은 아직 이 기록으로 옮기지 않았다(unknown).
|
||||
|
||||
## 전제와 되돌리기
|
||||
|
||||
@@ -105,7 +112,15 @@ ls deploy/lab/k8s/
|
||||
curl -s -o /dev/null -w '%{http_code} tls=%{ssl_verify_result}\n' https://auth.hyeonworks.com/
|
||||
```
|
||||
|
||||
**어디를 봐야 하는가** — 앞 단계에서 잰 `404 tls=0` 이 그대로인가. `tls=0` 이 아니면 이 단계를 시작할 때가 아니라 앞 단계로 돌아갈 때다.
|
||||
**어디를 봐야 하는가** — 앞 단계에서 잰 `404 tls=0` 이 그대로인가. **앞의 코드를 같이 본다.**
|
||||
|
||||
**`tls=0` 만 보면 안 된다.** 연결이 아예 안 됐을 때도 `tls=0` 이 나온다 — `%{ssl_verify_result}` 는 TLS 검증까지 갔을 때만 뜻이 있고 못 갔으면 초기값 `0` 이 그대로 찍힌다. 2026-09-17 에 앞 단계를 건너뛴 상태에서 재 보니 이렇게 나왔다(observed).
|
||||
|
||||
```text
|
||||
000 tls=0
|
||||
```
|
||||
|
||||
`000` 은 응답이 없었다는 뜻이다. **판정은 코드가 `404` 인 것과 `tls=0` 이 같이 나오는 것으로 한다.**
|
||||
|
||||
**이 결과가 의미하는 것** — 가이드가 그 값 옆에 한 줄을 적어 두었다 — 앞의 `404` 는 이 단계 이후에 `200` 으로 바뀐다. 지금 재 두면 아래의 `200` 이 이 단계가 만든 변화인지가 분명해진다.
|
||||
|
||||
@@ -137,13 +152,15 @@ kubectl apply -f deploy/lab/k8s/keycloak-cluster.yaml
|
||||
kubectl -n keycloak-lab rollout status statefulset/keycloak --timeout=300s
|
||||
```
|
||||
|
||||
**예상 결과**
|
||||
**예상 결과** — 2026-09-17 에 갓 세운 클러스터에서 잰 것이다(observed).
|
||||
|
||||
```text
|
||||
Waiting for 2 pods to be ready...
|
||||
Waiting for 1 pods to be ready...
|
||||
partitioned roll out complete: 2 new pods have been updated...
|
||||
```
|
||||
|
||||
이 명령은 끝날 때까지 아무것도 안 찍고 멈춰 있다. 그 침묵이 정상이고, 마지막 한 줄에서 `complete` 라는 낱말과 파드 개수 `2` 를 본다. StatefulSet 은 파드를 하나씩 순서대로 띄우므로 중간에 `0/2` 로 한참 멈춰 있는 것도 정상이다.
|
||||
마지막 한 줄에서 `complete` 라는 낱말과 파드 개수 `2` 를 본다. 그 줄이 나올 때까지 명령이 안 끝나고 멈춰 있는 것이 정상이다. **다만 아무것도 안 찍고 멈춰 있지는 않다** — `Waiting for …` 줄은 진행 상황이지 오류가 아니다. 숫자가 줄어들수록 파드가 하나씩 Ready 가 된다. StatefulSet 은 파드를 하나씩 순서대로 띄우므로 중간에 `0/2` 로 한참 멈춰 있는 것도 정상이다.
|
||||
|
||||
**왜 필요한가** — 300초를 다 쓰고 타임아웃으로 끝나면 그것도 답이 된다. 「안 떴다」가 확정되고 진단으로 넘어간다. `rollout status` 를 쓰는 까닭은 `get pods` 를 반복해서 치는 것보다 나아서만이 아니라, 언제 끝났는지를 사람이 판정하지 않아도 되기 때문이다. 기다리지 않고 판정으로 가면 아직 안 뜬 것과 못 뜨는 것이 섞인다.
|
||||
|
||||
@@ -279,8 +296,19 @@ KC_DB
|
||||
KC_DB_URL
|
||||
KC_DB_USERNAME
|
||||
KC_DB_PASSWORD keycloak-lab-secrets
|
||||
KC_HOSTNAME
|
||||
KC_HOSTNAME_STRICT
|
||||
KC_PROXY_HEADERS
|
||||
KC_HTTP_ENABLED
|
||||
KC_HEALTH_ENABLED
|
||||
KC_METRICS_ENABLED
|
||||
JAVA_OPTS_KC_HEAP
|
||||
KC_BOOTSTRAP_ADMIN_USERNAME
|
||||
KC_BOOTSTRAP_ADMIN_PASSWORD keycloak-lab-secrets
|
||||
```
|
||||
|
||||
열세 줄이 나오고 **오른쪽이 채워진 줄은 둘**이다(2026-09-17, observed). 처음에는 앞의 네 줄만 실었는데 그러면 `KC_BOOTSTRAP_ADMIN_PASSWORD` 가 Secret 에서 오는 것이 안 보인다.
|
||||
|
||||
오른쪽 칸이 채워진 줄만 Secret 을 참조한다. 비밀이어야 할 변수의 오른쪽이 비어 있으면 그 값은 매니페스트에 평문으로 적혀 있다는 뜻이고, 그 파일은 대개 git 에 들어간다.
|
||||
|
||||
### 확인 ④ Service 뒤에 파드가 있나
|
||||
@@ -339,7 +367,16 @@ kubectl -n keycloak-lab get pods --show-labels
|
||||
kubectl -n keycloak-lab get pvc
|
||||
```
|
||||
|
||||
**어디를 봐야 하는가** — STATUS 칸이 `Bound` 인가 `Pending` 인가, VOLUME 칸이 비어 있는지, STORAGECLASS 칸이 `local-path` 인가. StatefulSet 이면 PVC 이름 끝에 파드 번호가 붙어 있어 어느 파드 것인지 바로 보인다.
|
||||
**어디를 봐야 하는가** — STATUS 칸이 `Bound` 인가 `Pending` 인가, VOLUME 칸이 비어 있는지, STORAGECLASS 칸이 `local-path` 인가.
|
||||
|
||||
**이 실험대에는 파드 번호가 붙은 PVC 가 없다.** 「StatefulSet 이면 PVC 이름 끝에 파드 번호가 붙는다」는 `volumeClaimTemplates` 를 쓰는 StatefulSet 의 이야기이고 **이 매니페스트의 `keycloak` StatefulSet 에는 그 칸이 없다.** PVC 는 PostgreSQL 이 쓰는 `postgres-data` 하나뿐이고 그것도 StatefulSet 이 아니라 별도 문서로 선언되어 Deployment 가 `claimName` 으로 가리킨다. 2026-09-17 에 매니페스트와 클러스터 양쪽에서 확인했다(observed).
|
||||
|
||||
```text
|
||||
NAME STATUS VOLUME CAPACITY STORAGECLASS
|
||||
postgres-data Bound pvc-fe269834-3dd1-4b1f-82f6-a53413310a30 5Gi local-path
|
||||
```
|
||||
|
||||
그래서 **Keycloak 파드는 볼륨을 안 갖는다.** 세션이 어디 사는지를 묻는 이 실험대의 질문에서 그것이 중요하다 — 파드가 죽으면 그 안의 것은 사라지고 `postgres-data` 만 남는다.
|
||||
|
||||
**이 결과가 의미하는 것** — `Bound` 면 볼륨이 붙었다. `Pending` 이면 StorageClass 가 없거나 노드에 자리가 없다. `local-path` 는 파드가 스케줄될 때까지 기다리므로, 파드가 안 뜨면 PVC 도 `Pending` 인 것이 정상이다. 둘이 서로를 기다리는 것처럼 보이지만 파드 쪽 원인을 먼저 본다. 사유는 PVC 의 이벤트에 적혀 있다.
|
||||
|
||||
@@ -364,6 +401,14 @@ ISPN000094: Received new cluster view for channel ISPN:
|
||||
|
||||
**어디를 봐야 하는가** — 세 군데다. 괄호 안의 `(2)` 가 멤버 수, 대괄호 안의 이름 목록, 그리고 `|1` 이 뷰 번호다. 뷰 번호는 멤버가 들고 날 때마다 올라간다.
|
||||
|
||||
**이름 뒤에 `(v=…)` 가 붙어 나오기도 한다.** 2026-09-17 에 새로 세운 클러스터에서 같은 줄을 뽑아 보니 이랬다(observed).
|
||||
|
||||
```text
|
||||
[keycloak-1-3159(v=16.0.12)|1] (2) [keycloak-1-3159(v=16.0.12), keycloak-0-13057(v=16.0.12)]
|
||||
```
|
||||
|
||||
`(v=16.0.12)` 는 JGroups 가 붙인 판 번호이고, 볼 것은 그대로 `(2)` 와 이름 목록과 `|1` 이다. **맨 앞의 이름이 `keycloak-0` 이 아니어도 정상이다** — 맨 앞에는 코디네이터가 오고 먼저 뜬 쪽이 그 일을 맡는다. 이 판에서는 `keycloak-1` 이었다.
|
||||
|
||||
이름 목록의 `keycloak-0-10001` 은 파드 이름 그대로가 아니다. Infinispan 이 파드 이름 뒤에 임의의 접미사를 붙여 클러스터 노드 식별자로 쓰기 때문에, 아래 지표에 나오는 `keycloak-0-46674` 와는 `keycloak-0` 까지만 같다. 그래서 대조할 때 맞춰 보는 것은 접미사 앞의 파드 이름이다. Keycloak 만 StatefulSet 이라 그 앞부분이 고정이고, 덕분에 로그와 `jgroups_ping` 테이블과 지표를 같은 이름으로 견줄 수 있다.
|
||||
|
||||
**이 결과가 의미하는 것** — `(2)` 면 이 노드는 상대를 봤다. `(1)` 이면 혼자 있다고 알고 있다. 뷰 번호가 계속 오르고 있으면 멤버가 붙었다 떨어지기를 반복하는 중이라, 「지금 2 다」라는 스냅숏보다 그 사실이 더 중요하다. 이 줄은 과거형이므로 지금 상태는 지표로 본다.
|
||||
@@ -435,6 +480,21 @@ curl -s -o /dev/null -w '%{http_code}\n' https://auth.hyeonworks.com/realms/mast
|
||||
curl -I https://auth.hyeonworks.com/realms/master
|
||||
```
|
||||
|
||||
**이 `200` 은 앞 단계를 끝냈을 때의 값이다.** 인증서 단계를 건너뛰고 이 단계만 했다면 443 을 듣는 것이 없으므로 여기는 `000` 이다. 2026-09-17 에 그 상태에서 재 보니 이렇게 나왔다(observed).
|
||||
|
||||
```text
|
||||
curl: (7) Failed to connect to auth.hyeonworks.com:443 after 2 ms: Could not connect to server
|
||||
```
|
||||
|
||||
**그때도 이 단계가 세운 것까지는 잴 수 있다.** TLS 를 빼고 `Host` 헤더를 실어 80 으로 치면 nginx → Traefik → Ingress → Service → 파드가 이어졌는지가 그대로 나온다. 둘 다 `200` 이었다(observed).
|
||||
|
||||
```bash label="[lab host] TLS 를 빼고 사슬만 잰다"
|
||||
curl -s -o /dev/null -w '%{http_code}\n' -H 'Host: auth.hyeonworks.com' http://192.168.122.10/realms/master
|
||||
curl -s -o /dev/null -w '%{http_code}\n' -H 'Host: auth.hyeonworks.com' http://192.168.122.11/realms/master
|
||||
```
|
||||
|
||||
앞엣것은 엣지 nginx 를 지나고 뒤엣것은 Traefik 에 바로 친다. 둘이 같으면 남은 것은 TLS 뿐이다.
|
||||
|
||||
### 확인 ⑧ 관리 콘솔에 로그인된다
|
||||
|
||||
**무엇을 확인하는가** — 가이드가 적은 「이 단계가 끝나면」의 앞 절반.
|
||||
@@ -511,10 +571,12 @@ kubectl -n keycloak-lab exec -it keycloak-0 -- sh
|
||||
- (observed) 디스커버리 테이블에는 둘 다 있는데 메시지가 안 가는 상태를 실제로 겪었다.
|
||||
- (external) `kubectl get endpoints` 의 v1.33 deprecation 은 쿠버네티스 쪽 규격이고 이 실험대가 잰 값이 아니다.
|
||||
- (external) Infinispan 이 파드 이름에 임의의 접미사를 붙여 클러스터 노드 식별자로 쓴다는 것은 Infinispan 의 동작이다. 이 기록에 실린 `keycloak-0-10001` 과 `keycloak-0-46674` 가 그렇게 생긴 이름이다(observed).
|
||||
- (unknown) `keycloak-cluster.yaml` 원문이 반입되지 않아 Keycloak 과 PostgreSQL 의 이미지 태그, 파드 자원 한도, 프로브 설정, `persistent-user-sessions` 설정값은 대조하지 못했다. 위에 적은 객체 이름과 해시와 바이트 수는 돌고 있던 실험대에서 읽은 것이고, 매니페스트가 그것들을 어떤 값으로 선언했는지는 여기서 확인할 수 없다.
|
||||
- (unknown) `keycloak-cluster.yaml` 원문은 `source/` 에 없다. 파드 자원 한도와 프로브 설정과 `persistent-user-sessions` 설정값은 아직 이 기록으로 옮기지 않았다.
|
||||
- (unknown) `get all` 과 `get deploy` 와 `get pvc` 와 `describe pvc` 와 디스커버리 테이블 조회와 `get events` 와 `describe pod` 와 `logs` 의 출력은 가이드가 봐야 할 줄만 적었고 값이 남아 있지 않다.
|
||||
- (unknown) 원본 가이드 05 에 되돌리는 절차가 없다. `local-path` PVC 가 노드 디스크에 남긴 데이터가 네임스페이스를 지울 때 어떻게 되는지도 확인하지 않았다.
|
||||
- (unknown) 이 단계에는 「이 실험대는 이렇게 했다」와 「따라 하는 사람은」 두 갈래가 없다. 05 는 파일을 직접 쓰지 않고 저장소의 매니페스트를 적용한다.
|
||||
- (unknown) 만드는 명령은 구축할 때 친 것을 옮긴 것이라 다시 쳐서 같은 상태가 되는지는 확인되지 않았다.
|
||||
- (observed) 2026-09-17 에 갓 세운 클러스터에서 이 절차를 처음부터 다시 밟았다. 같은 값이 다시 나온 것 — `postgres-7b474b88c8` 이라는 ReplicaSet 해시, Secret 의 19·22 bytes 와 주입된 길이 19·복호 길이 22, Endpoints 두 개와 `ready` 두 줄, `jgroups_ping` 2 행, 임시 파드가 읽은 `vendor_cluster_size … 2.0`, Keycloak 이미지에 `curl` 이 없다는 것과 exit code 127, `kubectl get endpoints` 의 deprecation 경고. **파드 IP 와 Infinispan 접미사만 달랐다** — 그것들은 매번 새로 붙는다.
|
||||
- (observed) 같은 날 잰 것 가운데 문서와 달랐던 것 — `rollout status` 가 `Waiting for …` 를 찍는다는 것, PVC 가 `postgres-data` 하나라는 것, 환경변수 목록이 열세 줄이라는 것, `ISPN000094` 이름에 `(v=16.0.12)` 가 붙는다는 것, 인증서 단계를 건너뛰면 밖에서가 `000` 이라는 것.
|
||||
- (observed) `keycloak-cluster.yaml` 을 `keycloak-pattern` @ `9465582b` 에서 읽었다. 이미지 태그는 `postgres:16-alpine` 과 `quay.io/keycloak/keycloak:26.7.0` 이다.
|
||||
|
||||
<!-- body:end -->
|
||||
|
||||
+15
-2
@@ -79,12 +79,25 @@ virsh setmaxmem kc-lab-1 5120M --config
|
||||
virsh setmem kc-lab-1 5120M --config
|
||||
```
|
||||
|
||||
```bash label="[lab host] ② 도메인이 보는 값과 게스트가 인식한 값을 함께 본다"
|
||||
```bash label="[lab host] ② 바꾼 값이 설정에 들어갔는지 본다"
|
||||
virsh dumpxml kc-lab-1 --inactive | grep -E '<memory|<currentMemory'
|
||||
```
|
||||
|
||||
```bash label="[lab host] ③ 지금 돌고 있는 값과 게스트가 인식한 값도 함께 본다"
|
||||
virsh dominfo kc-lab-1 | grep -i memory
|
||||
ssh kc-lab-1 free -m
|
||||
```
|
||||
|
||||
**예상 결과** — `dominfo` 의 `Max memory` 가 바뀐 상한으로 나온다. 게스트의 `free -m` 은 재부팅 전까지 옛 값을 보여 준다.
|
||||
**예상 결과** — ② 의 두 줄이 방금 준 값으로 바뀐다. ③ 의 `dominfo` 와 게스트의 `free -m` 은 **재부팅 전까지 옛 값 그대로**다.
|
||||
|
||||
**`dominfo` 로 판정하지 않는다.** `--config` 는 다음 기동부터 쓸 설정만 바꾸는데 `dominfo` 는 **지금 돌고 있는 값**을 보여 준다. 그래서 `setmaxmem … --config` 가 제대로 먹어도 `dominfo` 의 숫자는 안 바뀌고, 그 화면을 보고 「안 먹었다」로 읽게 된다. 2026-09-17 에 같은 순간을 두 명령으로 나란히 찍었다(observed).
|
||||
|
||||
```text
|
||||
virsh dominfo Max memory: 5242880 KiB ← 돌고 있는 값
|
||||
virsh dumpxml --inactive <memory unit='KiB'>4194304</memory> ← 방금 바꾼 설정
|
||||
```
|
||||
|
||||
같은 도메인, 같은 시각, 다른 숫자다. **바꾼 것이 들어갔는지는 `--inactive` 가 답하고, `dominfo` 는 지금 무엇으로 돌고 있는지를 답한다.**
|
||||
|
||||
**왜 필요한가** — 두 명령이 다른 것을 바꾼다.
|
||||
|
||||
|
||||
+22
@@ -274,6 +274,28 @@ 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` 를 치거나 새 셸을 연다.
|
||||
|
||||
+41
-3
@@ -195,6 +195,16 @@ kubectl -n observability exec deploy/prometheus -- \
|
||||
|
||||
**이 결과가 의미하는 것** — `down` 인 대상이 있으면 `lastError` 가 까닭을 그대로 말해 준다. 연결 거부인지 타임아웃인지 404 인지가 거기 적혀 있다. 확인 ③ 에서 값이 한 노드만 나오는 증상의 원인이 대개 여기 있고, 그때는 클러스터가 아니라 스크레이프가 문제다.
|
||||
|
||||
**`down` 말고 `unknown` 도 있다.** 스택을 막 올린 직후에는 `health` 가 `unknown` 이고 `lastError` 는 빈 문자열이다 — 실패한 것이 아니라 **아직 한 번도 안 긁은** 것이다. 2026-09-17 에 `rollout status` 직후에 잡힌 모습이다(observed).
|
||||
|
||||
```text
|
||||
"job":"keycloak"
|
||||
"lastError":""
|
||||
"health":"unknown"
|
||||
```
|
||||
|
||||
한 주기 뒤에 같은 줄이 `"health":"up"` 으로 바뀌었다. `unknown` 을 보고 권한이나 네트워크를 뒤지기 전에 한 번 더 친다.
|
||||
|
||||
권한이 모자라 대상 하나만 빠지는 일이 이 실험대에서 실제로 있었다. Prometheus 는 타깃을 적어 두지 않고 쿠버네티스 API 에 물어서 찾으므로 읽기 권한이 필요하다. 그 권한을 담는 `ClusterRole` 에서 `nodes/proxy` 를 빠뜨리자 kubelet 타깃만 `403 Forbidden` 으로 실패하고 나머지 잡은 전부 정상이었다. `nodes` 와 `nodes/metrics` 와 `nodes/proxy` 는 서로 다른 권한이라, 노드 지표를 긁는 경로 `/api/v1/nodes/<name>/proxy/metrics` 에는 셋째 것이 따로 있어야 한다. 부분 실패라 1번의 `rollout status` 는 성공이라고 말하고, 타깃 목록을 직접 봐야 드러난다. 권한만 따로 물을 수도 있다.
|
||||
|
||||
```bash label="[lab host] 그 ServiceAccount 가 그 동사를 쓸 수 있는지 묻는다"
|
||||
@@ -202,6 +212,14 @@ kubectl auth can-i get nodes/proxy --as=system:serviceaccount:observability:prom
|
||||
kubectl describe clusterrole prometheus
|
||||
```
|
||||
|
||||
첫 명령은 답 앞에 경고 한 줄을 같이 낸다(2026-09-17, observed). 실패가 아니라 `nodes` 가 네임스페이스에 속한 자원이 아니라는 안내이고, 볼 것은 그 아래의 `yes` 또는 `no` 다.
|
||||
|
||||
```text
|
||||
Warning: resource 'nodes' is not namespace scoped
|
||||
|
||||
yes
|
||||
```
|
||||
|
||||
### 확인 ③ 클러스터 상태 — 두 노드가 각각 몇 명을 보고 있는가
|
||||
|
||||
**무엇을 확인하는가** — 두 Keycloak 이 서로를 보고 있는지.
|
||||
@@ -218,7 +236,26 @@ keycloak-1 → 2
|
||||
keycloak-0 → 2
|
||||
```
|
||||
|
||||
**어디를 봐야 하는가** — `data.result` 배열의 원소가 몇 개인가, 각 원소에서 `metric` 안의 `node` 라벨과 `value` 배열의 둘째 원소만 읽는다. `jq` 가 없으므로 눈으로 읽는다.
|
||||
**어디를 봐야 하는가** — `data.result` 배열의 원소가 몇 개인가, 각 원소에서 어느 Keycloak 파드가 보고한 것인지와 `value` 배열의 둘째 원소만 읽는다. `jq` 가 없으므로 눈으로 읽는다.
|
||||
|
||||
**어느 파드인지는 `node` 라벨이 아니라 `pod` 라벨이 말한다.** Prometheus 를 거쳐 나온 응답에서 `node` 는 **쿠버네티스 노드 이름**이다. Keycloak 이 자기 지표에 붙인 `node` 는 스크레이프할 때 이름이 겹쳐 `exported_node` 로 밀린다. 2026-09-17 에 원소 하나를 그대로 옮기면 이렇다(observed).
|
||||
|
||||
```text
|
||||
"metric":{"__name__":"vendor_cluster_size","cache_manager":"keycloak",
|
||||
"exported_node":"keycloak-1-3159","instance":"10.42.0.9:9000","job":"keycloak",
|
||||
"node":"kc-lab-1","pod":"keycloak-1"},"value":[1789620021.097,"2"]
|
||||
```
|
||||
|
||||
세 라벨이 각각 다른 것을 말한다 — `pod` 가 Keycloak 파드, `exported_node` 가 Infinispan 멤버 식별자, `node` 가 그 파드가 떠 있는 쿠버네티스 노드다. 앞 단계의 `ISPN000094` 이름과 견줄 것은 `exported_node` 이고, 어느 노드를 끊었을 때 무엇이 바뀌는지를 볼 때는 `node` 다.
|
||||
|
||||
**스택을 막 올린 직후에는 원소가 모자란다.** 1번의 `rollout status` 가 성공한 직후에 이 질의를 치면 Keycloak 대상이 아직 한 번도 안 긁혀서 원소가 하나뿐이거나 아예 없다. 2026-09-17 에 38초 시점과 약 2분 시점에 같은 질의를 쳐서 확인했다(observed).
|
||||
|
||||
| 언제 | 확인 ② 의 keycloak `health` | 이 질의의 원소 |
|
||||
|---|---|---|
|
||||
| `rollout status` 직후(38초) | `"health":"unknown"` | 1 개 |
|
||||
| 약 2분 뒤 | `"health":"up"` | 2 개 · 둘 다 `2` |
|
||||
|
||||
원소가 모자라면 분단을 의심하기 전에 확인 ② 의 `health` 를 먼저 보고, `unknown` 이면 한 주기 기다렸다 다시 친다.
|
||||
|
||||
**이 결과가 의미하는 것** — 두 노드가 각각 자기가 아는 멤버 수를 보고한다. 둘 다 2 면 클러스터가 온전하다. 분단되면 한쪽은 2, 다른 쪽은 1 이 된다 — 한 노드만 보면 분단을 놓친다. 원소가 하나뿐이면 분단이 아니라 스크레이프 실패일 수 있으므로 확인 ② 의 `health` 를 먼저 본다. 값이 아예 안 나오면 `"result":[]` 로 빈 배열이 오는데, 이것은 「0 이다」가 아니라 「그런 지표가 없다」는 뜻이다. 지표 이름을 잘못 쳤거나 그 대상을 긁고 있지 않은 것이므로 확인 ① 로 돌아간다.
|
||||
|
||||
@@ -260,7 +297,7 @@ kubectl -n observability port-forward svc/grafana 3000:3000
|
||||
| 파드 | `kubectl -n observability get pods` | 네 줄 · `node-exporter` 가 둘 |
|
||||
| 대상 | `… /api/v1/targets \| grep -o '"job":"[^"]*"' \| sort -u` | `keycloak` 이 목록에 있다 |
|
||||
| 상태 | `… /api/v1/targets \| tr ',' '\n' \| grep -E …` | `"health":"up"` 아닌 줄이 없다 |
|
||||
| 클러스터 | `… query=vendor_cluster_size` | 원소 둘 · 값 둘 다 2 |
|
||||
| 클러스터 | `… query=vendor_cluster_size` | 원소 둘 · 값 둘 다 2 — **`health` 가 `up` 이 된 뒤에 잰다** |
|
||||
|
||||
## 막히면
|
||||
|
||||
@@ -285,6 +322,7 @@ kubectl -n observability port-forward svc/grafana 3000:3000
|
||||
- (unknown) 원본 가이드 06 에 되돌리는 절차가 없다. DaemonSet 이 노드마다 남긴 것을 걷어내 본 적이 없다.
|
||||
- (unknown) 이 단계에는 「이 실험대는 이렇게 했다」와 「따라 하는 사람은」 두 갈래가 없다. 06 은 파일을 직접 쓰지 않고 저장소의 매니페스트를 적용한다.
|
||||
- (inferred) node-exporter 가 재는 것은 게스트 안에서 본 값이다. 호스트에서 같은 것을 재면 다른 수가 나올 수 있는데, 스크레이프 대상 넷이 전부 클러스터 안이라 이 실험대는 호스트 쪽 지표를 긁지 않는다.
|
||||
- (unknown) 만드는 명령은 구축할 때 친 것을 옮긴 것이라 다시 쳐서 같은 상태가 되는지는 확인되지 않았다.
|
||||
- (observed) 2026-09-17 에 이 절차를 갓 세운 클러스터에서 다시 밟았다. 파드 네 줄과 job 네 개가 그대로 나왔고 ReplicaSet 해시 `845b5678cf`·`6774f94f7c` 까지 같았다. 달랐던 것 — `rollout status` 직후에는 Grafana 가 `0/1` 이었고 Keycloak 대상의 `health` 가 `unknown` 이라 `vendor_cluster_size` 의 원소가 하나뿐이었다. 약 2분 뒤에 둘 다 `2` 가 됐다.
|
||||
- (observed) `kubectl auth can-i get nodes/proxy --as=…` 는 `yes` 앞에 `Warning: resource 'nodes' is not namespace scoped` 한 줄을 같이 낸다. 경고이지 실패가 아니다.
|
||||
|
||||
<!-- body:end -->
|
||||
|
||||
+21
-5
@@ -133,13 +133,25 @@ ssh kc-lab-edge
|
||||
### 확인 ① 이 도메인이 무엇으로 풀리는가
|
||||
|
||||
```bash label="[kc-lab-edge] 도메인이 어느 주소로 풀리는지 본다"
|
||||
dig +short auth.hyeonworks.com
|
||||
getent hosts auth.hyeonworks.com
|
||||
```
|
||||
|
||||
**실측**(observed)
|
||||
|
||||
```text
|
||||
100.83.212.4
|
||||
100.83.212.4 auth.hyeonworks.com
|
||||
```
|
||||
|
||||
**여기에 `dig` 를 바로 쓰지 않는다.** 엣지 게스트에는 `dig` 가 없다 — 2026-09-17 에 cloud-init 으로 갓 만든 `kc-lab-edge` 에서 쳐서 확인했다(observed).
|
||||
|
||||
```text
|
||||
dig: command not found
|
||||
```
|
||||
|
||||
cloud-init 이 까는 것은 `curl` 과 `nftables` 뿐이라 `dnsutils` 가 안 들어간다. `getent hosts` 는 `libc` 가 주는 것이라 어디서나 있고 주소와 이름을 한 줄로 낸다. `dig` 의 형태가 꼭 필요하면 먼저 깐다 — 둘 다 같은 `100.83.212.4` 를 냈다(observed).
|
||||
|
||||
```bash label="[kc-lab-edge] dig 를 쓰려면 먼저 깐다"
|
||||
sudo apt install -y dnsutils && dig +short auth.hyeonworks.com
|
||||
```
|
||||
|
||||
**어디를 봐야 하는가** — 주소 한 줄. 그 주소가 공개 인터넷 대역인가, `100.64.0.0/10` 안인가.
|
||||
@@ -149,9 +161,11 @@ dig +short auth.hyeonworks.com
|
||||
### 확인 ② nginx 판 번호는 몇인가
|
||||
|
||||
```bash label="[kc-lab-edge] 이 게스트의 nginx 판 번호"
|
||||
nginx -v
|
||||
sudo nginx -v
|
||||
```
|
||||
|
||||
**`sudo` 가 붙는 까닭은 앞 단계와 같다.** 실행 파일이 `/usr/sbin/nginx` 인데 데비안 일반 사용자 `PATH` 에 그 디렉터리가 없다. `sudo` 없이 치면 `nginx: command not found` 가 나오고, 안 깔린 것처럼 보이지만 아니다(2026-09-17, observed).
|
||||
|
||||
**실측**(observed) — 같은 설정인데 배포판에서 갈린다.
|
||||
|
||||
```text
|
||||
@@ -548,7 +562,7 @@ ps -eo pid,lstart,args | grep 'nginx: worker' | grep -v grep
|
||||
|
||||
| 증상 | 원인 | 확인 |
|
||||
|---|---|---|
|
||||
| certbot 검증 실패 | DNS 가 이 호스트를 안 가리킴 · 80 이 막힘 | `dig +short auth.hyeonworks.com` · 밖에서 `curl -I http://auth.hyeonworks.com` |
|
||||
| certbot 검증 실패 | DNS 가 이 호스트를 안 가리킴 · 80 이 막힘 | `getent hosts auth.hyeonworks.com` · 밖에서 `curl -I http://auth.hyeonworks.com` |
|
||||
| 체인 단계가 1개 | `cert.pem` 을 씀 | 확인 ② |
|
||||
| 갱신은 됐는데 옛 인증서가 나감 | deploy 훅 없음 | 워커 PID · `sudo ls /etc/letsencrypt/renewal-hooks/deploy/` |
|
||||
| 훅이 실패한 것처럼 보임 | stderr 경고를 error 로 표시 | 문구 말고 워커 PID |
|
||||
@@ -562,7 +576,9 @@ ps -eo pid,lstart,args | grep 'nginx: worker' | grep -v grep
|
||||
|
||||
## 무엇이 관측이고 무엇이 아닌가
|
||||
|
||||
- (observed) `dig` 가 낸 `100.83.212.4`, `certbot plugins` 세 줄, `nginx -v` 두 줄, 체인 4단계와 `Verify return code: 0 (ok)`, `404 tls=0`, 배포판 기본 유닛에 훅이 없다는 것, 갱신에서 서빙까지 2305초 대 1~2초, reload 무중단 측정값.
|
||||
- (observed) 이름 풀이가 낸 `100.83.212.4`, `certbot plugins` 세 줄, `nginx -v` 두 줄, 체인 4단계와 `Verify return code: 0 (ok)`, `404 tls=0`, 배포판 기본 유닛에 훅이 없다는 것, 갱신에서 서빙까지 2305초 대 1~2초, reload 무중단 측정값.
|
||||
- (observed) 2026-09-17 에 새로 만든 엣지에서 잰 것 — `dig` 가 없다는 것, `getent hosts` 와 `dnsutils` 설치 뒤의 `dig` 가 같은 주소를 낸다는 것, `nginx -v` 가 `sudo` 없이는 `command not found` 라는 것, `certbot plugins` 세 줄이 문서에 적힌 것과 같다는 것.
|
||||
- (unknown) 2번부터 4번까지(토큰 발급 · 자격증명 파일 · 시험 발급 · 실제 발급)는 2026-09-17 에 다시 밟지 않았다. Cloudflare 토큰이 필요한 구간이라 토큰 없이는 칠 수 없고, 값을 옮겨 오지도 않았다. 그래서 **5번의 443 블록과 6번의 훅도 재확인하지 못했다** — 인증서 파일이 없으면 그 설정은 `nginx -t` 를 못 지난다.
|
||||
- (external) `100.64.0.0/10` 이 CGNAT 예약 대역이라는 것과 Let's Encrypt 의 발급 한도와 유효기간 90일은 규격이고 이 실험대가 잰 값이 아니다.
|
||||
- (external) Cloudflare 계정에 남는 토큰은 엣지를 지운다고 없어지지 않는다. 가이드는 폐기를 적지 않았다.
|
||||
- (inferred) 2305초 측정은 훅이 물리 호스트에만 있던 시절의 기록이라, 엣지 게스트에서 다시 잰 값이 아니다. 지금 배치에서 같은 수가 나오는지는 재지 않았다.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"schemaVersion": 4,
|
||||
"project": "virtualization",
|
||||
"ssot": "final/document.md",
|
||||
"ssotSha256": "7f6b40ab80434185a4bcd206ff1bf59b616b8ddcbb916fad1b9a82c0a854ad22",
|
||||
"ssotSha256": "2b860da308e69e0803c5bf2a6f911c1bafa01c1ef5e0e121100567d2ffdc353e",
|
||||
"sourceRevision": "no-commit · 밖에서 반입한 문서 한 편. 고정할 저장소 리비전이 없다",
|
||||
"generatedAt": "2026-09-17",
|
||||
"sourceRepository": [
|
||||
|
||||
Reference in New Issue
Block a user