The experiment documents record what was found. These record what to type to
reproduce it, in folders per stage.
Two kinds of command are kept apart. 하기/확인 is what somebody actually types
at a terminal — short, one at a time. 근거를 재려면 is the long measuring form
this lab used to put evidence in a document, marked as not needed day to day.
The same split applies to curl: -I to look once, -w '%{http_code}' only when
comparing across repetitions.
No placeholders. Where a value is needed the command that produces it is
given, and secrets are checked by length rather than printed:
TOKEN=$(ssh kc-lab-1 'sudo cat /var/lib/rancher/k3s/server/node-token')
echo "${#TOKEN} 자"
Stage 05 verifies resources in layers, because a Secret existing and a pod
having received it are different facts: keys, then length, then the value
inside the container, then which env var came from which Secret. Same for
workloads — Deployment to ReplicaSet to Pod, with the seven ReplicaSets this
cluster actually carries as the worked example.
Two commands were wrong and re-running them caught it. kubectl get endpoints
prints a deprecation warning on v1.33+, so the guide uses describe svc and
EndpointSlice. And the Keycloak image has no curl, so reading metrics from
inside the container fails with exit 127 — the guide asks Prometheus instead,
or runs a throwaway curl pod.
Read-only checks were executed against the running lab and their output is
quoted verbatim. Creating commands could not be re-run without destroying the
lab, so they are the ones used at build time; the README says which is which.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
171 lines
5.2 KiB
Markdown
171 lines
5.2 KiB
Markdown
# 03 — 호스트 nginx 라우팅
|
|
|
|
## 이 단계가 끝나면
|
|
|
|
밖에서 보낸 요청이 **nginx → Traefik → 파드**로 닿는다. 아직 TLS 는 없다.
|
|
|
|
## 전제
|
|
|
|
[02](../02-k3s/) 가 끝나 두 노드가 `Ready`.
|
|
|
|
## 왜 프록시가 두 겹인가
|
|
|
|
nginx 와 Traefik 이 하는 일이 다르다.
|
|
|
|
| | 맡는 것 |
|
|
|---|---|
|
|
| 호스트 nginx | 바깥세상과의 접점 — TLS 종단 · 인증서 · `X-Forwarded-*` |
|
|
| Traefik | 클러스터 안의 동적 라우팅 — Ingress 를 보고 서비스를 고른다 |
|
|
|
|
**이 2홉이 운영 구조와 같다는 것이 이 배치의 핵심**이고, 동시에 B-4 의 헤더
|
|
실험이 성립하는 이유다. 1홉을 가정하고 쓴 계약이 2홉에서도 유효한지를
|
|
재려면 두 겹이 있어야 한다.
|
|
|
|
---
|
|
|
|
## 1. 설정을 쓴다
|
|
|
|
원본은 [`deploy/lab/host/nginx-keycloak-lab.conf`](../../../deploy/lab/host/nginx-keycloak-lab.conf).
|
|
|
|
**하기**
|
|
```bash
|
|
sudo tee /etc/nginx/sites-available/keycloak-lab > /dev/null <<'EOF'
|
|
upstream k3s_traefik {
|
|
server 192.168.122.11:80;
|
|
server 192.168.122.12:80;
|
|
}
|
|
|
|
server {
|
|
listen 80 default_server;
|
|
server_name _;
|
|
return 301 https://$host$request_uri;
|
|
}
|
|
|
|
server {
|
|
listen 443 ssl default_server;
|
|
http2 on;
|
|
server_name _;
|
|
|
|
ssl_certificate /etc/letsencrypt/live/auth.hyeonworks.com/fullchain.pem;
|
|
ssl_certificate_key /etc/letsencrypt/live/auth.hyeonworks.com/privkey.pem;
|
|
ssl_protocols TLSv1.2 TLSv1.3;
|
|
|
|
location / {
|
|
proxy_pass http://k3s_traefik;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Forwarded-Host $host;
|
|
proxy_set_header X-Forwarded-Proto https;
|
|
proxy_set_header X-Forwarded-Port 443;
|
|
proxy_set_header X-Forwarded-For $remote_addr;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
proxy_read_timeout 3600s;
|
|
proxy_send_timeout 3600s;
|
|
}
|
|
}
|
|
EOF
|
|
```
|
|
|
|
> **인증서 경로는 아직 없다.** [04](../04-tls/) 에서 만든다. 그전까지는 443
|
|
> 블록을 주석 처리하고 80 만 `proxy_pass` 로 두면 이 단계를 먼저 확인할 수 있다.
|
|
|
|
Arch 는 `sites-available` 관례가 없다. 직접 만들고 `nginx.conf` 의 `http` 블록
|
|
안에서 include 한다.
|
|
|
|
```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/*;
|
|
```
|
|
|
|
## 2. 문법을 보고 적용한다
|
|
|
|
**하기**
|
|
```bash
|
|
sudo nginx -t && sudo systemctl reload nginx
|
|
```
|
|
|
|
**`&&` 가 중요하다.** 설정이 깨진 상태에서 reload 하면 nginx 가 새 워커를
|
|
띄우지 못한다. `-t` 를 먼저 통과시키고 그때만 reload 한다.
|
|
|
|
## 3. 층별로 확인한다 — 아래에서 위로
|
|
|
|
한 번에 밖에서 치지 말고, **가까운 층부터** 본다. 어디서 끊겼는지가 바로 나온다.
|
|
|
|
**확인 ①** Traefik 이 듣고 있나 (nginx 를 건너뛴다)
|
|
```bash
|
|
curl -s -o /dev/null -w '%{http_code}\n' http://192.168.122.11
|
|
```
|
|
```
|
|
404
|
|
```
|
|
|
|
**`404` 가 성공 신호다.** Traefik 까지 닿았는데 매칭되는 Ingress 규칙이 없다는
|
|
뜻이다. `502` 나 연결 거부면 그 아래에서 끊긴 것이다.
|
|
|
|
**확인 ②** nginx 가 80 에서 리다이렉트하나
|
|
```bash
|
|
curl -s -o /dev/null -w '%{http_code} %{redirect_url}\n' http://auth.hyeonworks.com
|
|
```
|
|
```
|
|
301 https://auth.hyeonworks.com/
|
|
```
|
|
|
|
**확인 ③** 끝까지 닿나 (TLS 이후)
|
|
```bash
|
|
curl -s -o /dev/null -w '%{http_code}\n' https://auth.hyeonworks.com/realms/master
|
|
```
|
|
```
|
|
200
|
|
```
|
|
|
|
## 4. upstream 이 둘인 이유
|
|
|
|
```
|
|
upstream k3s_traefik {
|
|
server 192.168.122.11:80;
|
|
server 192.168.122.12:80;
|
|
}
|
|
```
|
|
|
|
두 노드 모두 Traefik 이 뜨므로 어느 쪽으로 보내도 된다. nginx 는 기본
|
|
라운드로빈으로 번갈아 보내고, **한쪽이 죽으면 자동으로 뺀다.**
|
|
|
|
그 「빼는」 동작이 로그에 이렇게 남는다.
|
|
|
|
```
|
|
connect() failed (113: No route to host) ← 호스트에 못 닿는다
|
|
connect() failed (111: Connection refused) ← 포트에 아무도 없다
|
|
no live upstreams ← 둘 다 죽었다고 판단
|
|
```
|
|
|
|
**113 과 111 은 대응이 다르다.** 113 은 네트워크, 111 은 프로세스다.
|
|
A-4 에서 노드를 잃었을 때 이 세 줄이 1분 안에 순서대로 나왔다.
|
|
|
|
---
|
|
|
|
## 막히면
|
|
|
|
| 증상 | 어디서 끊겼나 | 확인 |
|
|
|---|---|---|
|
|
| ① 이 연결 거부 | Traefik 이 안 떴거나 게스트가 죽음 | `kubectl get pods -n kube-system` |
|
|
| ① 이 `502` | Traefik 은 떴는데 백엔드가 없음 | Ingress 확인 |
|
|
| ② 가 응답 없음 | nginx 가 안 떴거나 방화벽 | `systemctl status nginx` |
|
|
| ③ 이 `502` | 인증서 문제 또는 upstream 다운 | [04](../04-tls/) · 아래 로그 |
|
|
|
|
**로그를 볼 때** — 실무자가 치는 형태다.
|
|
```bash
|
|
journalctl -u nginx -p err -n 5 # 최근 에러만
|
|
journalctl -u nginx -f # 지금 벌어지는 것
|
|
```
|
|
|
|
**★ nginx 에러 로그는 2048바이트에서 잘린다.** 긴 URL 이 끝에서 단어 중간에
|
|
끊겨 보이면 그것이다. 되찾으려면 **access 로그를 본다** — 거기엔 제한이 없다.
|
|
|
|
```bash
|
|
grep oauth2/callback /var/log/nginx/access.log | tail -1
|
|
```
|
|
|
|
이 실험대에서 B-7 의 502 원인이 error 로그에 있었는데 잘려 있었고, access
|
|
로그에는 3492자로 온전히 남아 있었다.
|