chore: 실행 환경 구성 문서 추가 및 수정
This commit is contained in:
+128
-19
@@ -1,12 +1,14 @@
|
||||
# 03 — 호스트 nginx 라우팅
|
||||
# 03 — 엣지 nginx 라우팅 (kc-lab-edge)
|
||||
|
||||
## 이 단계가 끝나면
|
||||
|
||||
밖에서 보낸 요청이 **nginx → Traefik → 파드**로 닿는다. 아직 TLS 는 없다.
|
||||
밖에서 보낸 요청이 **호스트 DNAT → 엣지 nginx → Traefik → 파드**로 닿는다.
|
||||
아직 TLS 는 없다.
|
||||
|
||||
## 전제
|
||||
|
||||
[02](../02-k3s/) 가 끝나 두 노드가 `Ready`.
|
||||
[02](../02-k3s/) 가 끝나 두 노드가 `Ready` 이고, [01](../01-vms/) 에서
|
||||
`kc-lab-edge`(192.168.122.10) 까지 세 대가 떠 있다.
|
||||
|
||||
## 왜 프록시가 두 겹인가
|
||||
|
||||
@@ -14,20 +16,36 @@ nginx 와 Traefik 이 하는 일이 다르다.
|
||||
|
||||
| | 맡는 것 |
|
||||
|---|---|
|
||||
| 호스트 nginx | 바깥세상과의 접점 — TLS 종단 · 인증서 · `X-Forwarded-*` |
|
||||
| 엣지 nginx (`kc-lab-edge`) | 바깥세상과의 접점 — TLS 종단 · 인증서 · `X-Forwarded-*` |
|
||||
| Traefik | 클러스터 안의 동적 라우팅 — Ingress 를 보고 서비스를 고른다 |
|
||||
|
||||
**이 2홉이 운영 구조와 같다는 것이 이 배치의 핵심**이고, 동시에 B-4 의 헤더
|
||||
실험이 성립하는 이유다. 1홉을 가정하고 쓴 계약이 2홉에서도 유효한지를
|
||||
재려면 두 겹이 있어야 한다.
|
||||
|
||||
## 왜 엣지가 물리 호스트가 아니라 VM 인가
|
||||
|
||||
**L7 홉 수는 그대로 2홉이다.** 늘어난 것은 커널이 하는 L4 전달 한 번뿐이다.
|
||||
|
||||
바뀐 것은 **더러워지는 층이 어디냐**다. nginx 설정 · 인증서 · certbot · deploy
|
||||
훅은 자주 고치고 자주 갈아엎는 것들인데, 그것이 물리 호스트에 있으면
|
||||
「깨끗하게 초기화하고 다시」가 불가능하다. 엣지가 VM 이면 초기화가
|
||||
`virsh undefine kc-lab-edge --remove-all-storage` 한 줄이 된다.
|
||||
|
||||
물리 호스트에 남는 실험대 설정은 **DNAT 규칙 하나와 DHCP 예약 세 줄**뿐이고,
|
||||
둘 다 한 번 쓰고 다시 안 건드린다.
|
||||
|
||||
덤으로 **엣지 장애를 실험할 수 있게 된다.** 엣지가 물리 호스트일 때는
|
||||
`systemctl stop nginx` 가 진입 경로(SSH)까지 위험하게 만들어서 A층 실험 9건
|
||||
어디에도 엣지 장애가 없었다. VM 이면 A-4 와 똑같이 `virsh destroy` 로 뽑는다.
|
||||
|
||||
---
|
||||
|
||||
## 1. 설정을 쓴다
|
||||
|
||||
원본은 [`deploy/lab/host/nginx-keycloak-lab.conf`](../../../deploy/lab/host/nginx-keycloak-lab.conf).
|
||||
원본은 [`deploy/lab/edge/nginx-keycloak-lab.conf`](../../../deploy/lab/edge/nginx-keycloak-lab.conf).
|
||||
|
||||
**하기**
|
||||
**하기** — `[kc-lab-edge]`. lab host 에서 원격 실행해도 된다.
|
||||
```bash
|
||||
sudo tee /etc/nginx/sites-available/keycloak-lab > /dev/null <<'EOF'
|
||||
upstream k3s_traefik {
|
||||
@@ -69,22 +87,53 @@ EOF
|
||||
> **인증서 경로는 아직 없다.** [04](../04-tls/) 에서 만든다. 그전까지는 443
|
||||
> 블록을 주석 처리하고 80 만 `proxy_pass` 로 두면 이 단계를 먼저 확인할 수 있다.
|
||||
|
||||
Arch 는 `sites-available` 관례가 없다. 직접 만들고 `nginx.conf` 의 `http` 블록
|
||||
안에서 include 한다.
|
||||
**엣지가 Debian 이라 `sites-available` 관례가 기본으로 있다.** 물리 호스트
|
||||
(Arch) 였을 때는 디렉터리를 직접 만들고 `nginx.conf` 에 include 를 넣어야
|
||||
했는데, 그 손질이 없어졌다. 운영도 Debian 계열이라 관례가 맞아떨어진다.
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /etc/nginx/sites-{available,enabled}
|
||||
sudo ln -s /etc/nginx/sites-available/keycloak-lab /etc/nginx/sites-enabled/
|
||||
# nginx.conf 의 http { } 안에: include /etc/nginx/sites-enabled/*;
|
||||
ssh kc-lab-edge '
|
||||
sudo ln -sf /etc/nginx/sites-available/keycloak-lab /etc/nginx/sites-enabled/
|
||||
sudo rm -f /etc/nginx/sites-enabled/default'
|
||||
```
|
||||
|
||||
`default` 를 지우는 것을 빠뜨리지 않는다. Debian 기본 사이트가 `:80` 에
|
||||
`default_server` 로 붙어 있어서, 우리 설정의 `listen 80 default_server` 와
|
||||
**충돌해 `nginx -t` 가 실패한다.**
|
||||
|
||||
> **★ `http2 on;` 을 쓰지 않는다.** 그 지시어는 nginx 1.25.1 이상이다.
|
||||
>
|
||||
> ```
|
||||
> 엣지 (Debian 12): nginx version: nginx/1.22.1
|
||||
> 물리 호스트 (Arch): nginx version: nginx/1.30.4
|
||||
> ```
|
||||
>
|
||||
> 물리 호스트에서 쓰던 설정을 그대로 옮기면 **실측으로 이렇게 막힌다.**
|
||||
>
|
||||
> ```
|
||||
> [emerg] 1339#1339: unknown directive "http2" in /etc/nginx/sites-enabled/keycloak-lab:29
|
||||
> nginx: configuration file /etc/nginx/nginx.conf test failed
|
||||
> ```
|
||||
>
|
||||
> `listen 443 ssl http2;` 형태를 쓴다 — 1.22 와 1.30 양쪽에서 다 돈다.
|
||||
|
||||
## 2. 문법을 보고 적용한다
|
||||
|
||||
**하기**
|
||||
**하기** — `[kc-lab-edge]`
|
||||
```bash
|
||||
sudo nginx -t && sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
**실측** — 04 를 아직 안 했다면 여기서 이렇게 막히는 것이 **정상**이다.
|
||||
설정은 맞는데 참조하는 파일이 아직 없는 것뿐이다.
|
||||
|
||||
```
|
||||
[emerg] 1431#1431: cannot load certificate
|
||||
"/etc/letsencrypt/live/auth.hyeonworks.com/fullchain.pem": BIO_new_file() failed
|
||||
(SSL: ... No such file or directory ...)
|
||||
nginx: configuration file /etc/nginx/nginx.conf test failed
|
||||
```
|
||||
|
||||
**어디를 봐야 하는가** — `nginx -t` 가 내놓는 **마지막 줄**이다. `syntax is
|
||||
ok` 와 `test is successful` 두 마디가 다 나와야 통과다. 앞에 나오는
|
||||
`[warn]` 줄(예: `types_hash_max_size`)은 통과를 막지 않는다 — 04 에서 이
|
||||
@@ -113,7 +162,58 @@ systemctl status nginx --no-pager | head -20
|
||||
것이고, 안 바뀌었으면 `-t` 는 통과했는데 reload 가 안 간 것이다.
|
||||
04 에서 인증서 갱신이 서빙까지 닿았는지를 정확히 이 방법으로 판정한다.
|
||||
|
||||
## 3. 층별로 확인한다 — 아래에서 위로
|
||||
## 3. 호스트에서 엣지로 넘긴다 (DNAT)
|
||||
|
||||
여기까지 하면 엣지 nginx 는 살아 있는데 **아무도 거기로 안 보낸다.** tailnet
|
||||
주소 `100.83.212.4:443` 을 받는 것은 여전히 물리 호스트다. 그 트래픽을
|
||||
엣지로 넘기는 것이 이 단계고, **물리 호스트가 실험대를 위해 하는 일의
|
||||
전부**다.
|
||||
|
||||
**하기** — `[lab host]`. 원본은
|
||||
[`deploy/lab/edge/lab-edge-dnat.nft`](../../../deploy/lab/edge/lab-edge-dnat.nft)
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /etc/nftables.d
|
||||
sudo cp deploy/lab/edge/lab-edge-dnat.nft /etc/nftables.d/
|
||||
sudo cp deploy/lab/edge/lab-edge-dnat.service /etc/systemd/system/
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable --now lab-edge-dnat.service
|
||||
```
|
||||
|
||||
규칙의 알맹이는 두 줄이다.
|
||||
|
||||
```
|
||||
iifname "tailscale0" tcp dport { 80, 443 } dnat to 192.168.122.10
|
||||
ip daddr 192.168.122.10 tcp dport { 80, 443 } ct state new accept
|
||||
```
|
||||
|
||||
**★ SNAT 을 걸지 않는다.** 게스트의 기본 게이트웨이가 호스트라 응답은
|
||||
어차피 여기로 돌아오고, conntrack 이 알아서 되돌린다. masquerade 를 붙이면
|
||||
출발지가 덮여서 **엣지가 모든 클라이언트를 `192.168.122.1` 로 본다** — 이
|
||||
실험대가 재고 있는 `X-Forwarded-For` 계약이 통째로 무의미해진다.
|
||||
|
||||
**두 번째 줄이 왜 필요한가** — libvirt 의 기본 네트워크 규칙은 게스트 대역으로
|
||||
들어가는 `RELATED,ESTABLISHED` 만 허용한다. **밖에서 새로 들어오는 연결은
|
||||
막는다.** 그래서 명시적으로 열어 준다.
|
||||
|
||||
**확인**
|
||||
```bash
|
||||
sudo nft list table ip lab_edge
|
||||
```
|
||||
|
||||
**어디를 봐야 하는가** — `dnat to 192.168.122.10` 한 줄과 `accept` 한 줄이
|
||||
다 있는가. 그리고 **`masquerade` 나 `snat` 이 없는가.**
|
||||
|
||||
**이 결과가 의미하는 것** — PREROUTING nat 은 라우팅 결정보다 먼저 돌기
|
||||
때문에 이 규칙이 **호스트 자신의 `:443` 소켓보다 우선한다.** 그래서 물리
|
||||
호스트에 nginx 가 아직 떠 있어도 트래픽은 엣지로 간다 — 전환이 원자적이고,
|
||||
되돌리기도 한 줄이다.
|
||||
|
||||
```bash
|
||||
sudo nft delete table ip lab_edge # rollback
|
||||
```
|
||||
|
||||
## 4. 층별로 확인한다 — 아래에서 위로
|
||||
|
||||
한 번에 밖에서 치지 말고, **가까운 층부터** 본다. 어디서 끊겼는지가 바로 나온다.
|
||||
|
||||
@@ -163,7 +263,16 @@ curl -s -o /dev/null -w '%{http_code}\n' http://192.168.122.12
|
||||
결과가 나온다. 한쪽만 다르면 upstream 두 개 중 하나가 죽은 것이고, 그
|
||||
상태에서는 **요청의 절반만 실패**해서 「가끔 안 된다」로 보인다.
|
||||
|
||||
**확인 ②** nginx 가 80 에서 리다이렉트하나
|
||||
**확인 ②** 엣지 nginx 가 직접 응답하나 (DNAT 을 건너뛴다)
|
||||
```bash
|
||||
curl -s -o /dev/null -w '%{http_code} %{redirect_url}\n' http://192.168.122.10
|
||||
```
|
||||
|
||||
**어디를 봐야 하는가** — `301` 과 `Location` 이 나오는가. 여기서 막히면
|
||||
문제는 **엣지 안**이다(설정·기동). 통과하는데 아래 ③ 이 안 되면 문제는
|
||||
**DNAT** 이다. 이 한 층을 끼워 두면 그 둘을 헷갈리지 않는다.
|
||||
|
||||
**확인 ③** 밖에서, 즉 DNAT 을 거쳐 닿나
|
||||
```bash
|
||||
curl -I http://auth.hyeonworks.com
|
||||
```
|
||||
@@ -179,9 +288,9 @@ Location: https://auth.hyeonworks.com/
|
||||
`https://` 로 시작하고 원래 호스트명을 그대로 들고 있는가. `$host` 대신
|
||||
설정에 이름을 박아 두면 여기서 엉뚱한 호스트가 나온다.
|
||||
|
||||
**이 결과가 의미하는 것** — 301 이 나왔다는 것은 **바깥 요청이 호스트 nginx
|
||||
까지 닿았다**는 뜻이다(①은 게스트에 직접 친 것이므로 nginx 를 안 거쳤다).
|
||||
DNS·방화벽·80 리스너가 전부 살아 있다. 응답이 아예 없으면 nginx 가 안 떴거나
|
||||
**이 결과가 의미하는 것** — 301 이 나왔다는 것은 **바깥 요청이 DNAT 을 거쳐
|
||||
엣지 nginx 까지 닿았다**는 뜻이다(①은 Traefik 에 직접, ②는 엣지에 직접 친
|
||||
것이라 DNAT 을 안 거쳤다). DNS·DNAT·80 리스너가 전부 살아 있다. 응답이 아예 없으면 nginx 가 안 떴거나
|
||||
80 이 막힌 것이다. 리다이렉트를 따라가 끝까지 보려면 `-L` 을 붙인다.
|
||||
|
||||
값을 반복해서 잴 때의 형태는 이것이고, 아래가 이 실험대의 실측이다.
|
||||
@@ -193,7 +302,7 @@ curl -s -o /dev/null -w '%{http_code} %{redirect_url}\n' http://auth.hyeonworks.
|
||||
301 https://auth.hyeonworks.com/
|
||||
```
|
||||
|
||||
**확인 ③** 끝까지 닿나 (TLS 이후)
|
||||
**확인 ④** 끝까지 닿나 (TLS 이후)
|
||||
```bash
|
||||
curl -s -o /dev/null -w '%{http_code}\n' https://auth.hyeonworks.com/realms/master
|
||||
```
|
||||
@@ -211,7 +320,7 @@ curl -s -o /dev/null -w '%{http_code}\n' https://auth.hyeonworks.com/realms/mast
|
||||
본다), `curl: (60)` 같은 인증서 오류는 아직 04 를 안 한 것이다. TLS 단계에서
|
||||
막히면 코드만 보지 말고 `curl -v` 로 협상 과정을 읽는다 — 04 에서 그렇게 한다.
|
||||
|
||||
## 4. upstream 이 둘인 이유
|
||||
## 5. upstream 이 둘인 이유
|
||||
|
||||
```
|
||||
upstream k3s_traefik {
|
||||
|
||||
Reference in New Issue
Block a user