diff --git a/infra/apisix.md b/infra/apisix.md index 6d98905..9a1702b 100644 --- a/infra/apisix.md +++ b/infra/apisix.md @@ -52,31 +52,32 @@ BunnyCDN(inouter, ID 5316471) → apisix-osaka(172.233.93.180) → 백엔드 - 배포: K3s apisix 네임스페이스, **Deployment replica 2** (2026-04-04 HA 업그레이드) - APISIX: 3.15.0-ubuntu - SafeLine WAF 연동: `plugin_attr.chaitin-waf` → `safeline-detector:8000` (10.43.253.244) -- global_rules (id=1): `http-logger` (CrowdSec 로그 전송) + `limit-req` (rate 20, burst 10). **`chaitin-waf`는 global_rules에 없음** — 라우트별 적용 (2026-03-15 git push 500 사건 이후) +- global_rules: `http-logger` (CrowdSec 로그 전송) + `limit-req` (rate 20, burst 10). **`chaitin-waf`는 global_rule에 없음** — 라우트별 적용 (2026-03-15 git push 500 사건 이후). 두 global_rule 모두 GatewayProxy CR(`spec.plugins`)로 선언되어 ingress controller가 관리. - 서비스: apisix-gateway LoadBalancer 192.168.9.50 (80→9080, 443→9443) -- etcd: **외부 통합 etcd 클러스터** (192.168.9.100, 10.100.2.214, 10.253.101.233), prefix `/apisix/seoul`. K3s 내부 apisix-etcd StatefulSet은 통합 이전 시 삭제됨 (2026-04-06). 자세한 내용은 [[postgresql-ha#etcd-클러스터-patroni-dcs|postgresql-ha]] 참고 -- Admin API: `apisix-admin` ClusterIP :9180 (`adminKey: edd1c9f034335f136f87ad84b625c8f1`) +- etcd: **K3s 내부 apisix-etcd StatefulSet 3 replicas** (Bitnami etcd 차트, Longhorn PVC 5Gi × 3), prefix `/apisix`. ClusterIP `apisix-etcd.apisix.svc.cluster.local:2379`. 외부 통합 etcd로 잠시 이전했다가(2026-04-06) Patroni와의 장애 격리 + 네트워크 단순화를 위해 K3s 내부로 복귀(2026-04-08). +- Admin API: `apisix-admin` ClusterIP :9180 (`adminKey: edd1c9f034335f136f87ad84b625c8f1`). admin allow IPs: `127.0.0.1/24`, `10.42.0.0/16`(pod), `10.43.0.0/16`(svc), `192.168.9.0/24`(LAN), `100.64.0.0/10`(Tailscale) - HAProxy: OpenWrt에서 :9080→192.168.9.50:80, :9443→192.168.9.50:443 (MetalLB) - 2026-03-25 메인 라우팅 역할을 Traefik으로 이전, APISIX는 SafeLine WAF 전용으로 축소 -- 2026-04-08 ApisixRoute CRD 사용을 위해 ingress controller 복구 +- 2026-04-08 ApisixRoute CRD 사용을 위해 ingress controller 복구 + K3s 내부 etcd 복귀 -#### plugin_metadata (etcd 직접 등록) +#### plugin_metadata (GatewayProxy CR로 관리) -chaitin-waf 플러그인은 `plugin_attr`(config.yaml)이 아닌 **`plugin_metadata`(etcd)**에서 detector 노드를 읽음. 반드시 etcd에 등록해야 함. +chaitin-waf 플러그인은 `plugin_attr`(config.yaml)이 아닌 **`plugin_metadata`(etcd)**에서 detector 노드를 읽음. 옛날에는 etcd에 직접 PUT했으나, 2026-04-08부터 ingress controller가 destructive sync로 미관리 객체를 1분마다 삭제하므로 **반드시 GatewayProxy CR의 `spec.pluginMetadata`로 선언**해야 함. helm values의 `gatewayProxy.pluginMetadata` 항목 참고. -``` -etcdctl put /apisix/plugin_metadata/chaitin-waf '{"id":"chaitin-waf","nodes":[{"host":"10.43.253.244","port":8000}],"config":{"connect_timeout":1000,"send_timeout":1000,"read_timeout":1000,"req_body_size":1024,"real_client_ip":true}}' -``` +#### 등록된 라우트 / TLS (ApisixRoute / ApisixTls CRD) -#### 등록된 라우트 (etcd 직접 등록) +모든 라우트와 SSL은 K8s CRD로 선언됨. ingress controller가 watch하여 APISIX admin API로 push. -| Route ID | 호스트 | upstream | chaitin-waf | 비고 | -|----------|--------|----------|-------------|------| -| juiceshop | juiceshop.keepanker.cv | juiceshop:3000 (juiceshop ns) | block | WAF 테스트용 OWASP Juice Shop | +| Kind | Name | Namespace | 설명 | +|------|------|-----------|------| +| ApisixRoute | juiceshop | juiceshop | `juiceshop.keepanker.cv` → juiceshop:3000, plugins.chaitin-waf (block) | +| ApisixTls | wildcard-keepanker-cv | apisix | `*.keepanker.cv` + `keepanker.cv`, secret `wildcard-keepanker-cv-tls` (cert-manager 발급) | -#### real_ip 설정 (ConfigMap 직접 수정) +⚠️ **etcd 직접 등록 금지** (1분 이내 controller가 삭제). 모든 신규 객체는 CRD로 선언해야 함. -`real_ip_header: X-Forwarded-For`, `real_ip_from: 0.0.0.0/0` (BunnyCDN 경유이므로 전체 허용). Helm values로는 반영 안 되어 ConfigMap 직접 패치. +#### real_ip 설정 + +helm values `apisix.nginx.http.realIpFrom` / `realIpHeader`로 관리. 차트 정상 반영됨. #### 이전 사유 (2026-03-25) - Ingress Controller 2.0 초기 시도에서 GatewayProxy 모드 + ApisixRoute CRD 연동 실패 (당시 helm values에 v1.x 형식의 `config.apisix.serviceName` 사용 → 차트 1.x 스키마와 불일치) @@ -137,6 +138,48 @@ spec: WAF가 문제 시 `plugins` 항목만 빼면 즉시 비활성화됨. +#### Destructive sync 주의사항 (v2.x controller) + +새 controller는 **CRD/GatewayProxy로 선언되지 않은 모든 APISIX 객체를 1분마다 자동 삭제**함. 영향: + +| 객체 | 관리 방법 | +|------|----------| +| routes | `ApisixRoute` CRD (또는 HTTPRoute) | +| upstreams | `ApisixUpstream` CRD | +| ssls | `ApisixTls` CRD | +| consumers | `ApisixConsumer` CRD | +| plugin_metadata | `GatewayProxy.spec.pluginMetadata` (helm values) | +| global_rules | `GatewayProxy.spec.plugins` (helm values) | + +⚠️ **etcd 직접 PUT은 임시 디버깅 외에는 금지.** 1분 안에 삭제됨. 옛 운영 메모에 있는 `etcdctl put` 예제는 모두 deprecated. + +#### K3s 내부 etcd 복귀 (2026-04-08) + +기존: 외부 통합 etcd 클러스터(192.168.9.100/10.100.2.214/10.253.101.233, prefix `/apisix/seoul`) — Patroni DCS와 etcd 공유. + +복귀 후: K3s 내부 `apisix-etcd` StatefulSet 3 replicas (Bitnami etcd, Longhorn PVC 5Gi × 3, ClusterIP `apisix-etcd.apisix.svc:2379`, prefix `/apisix`). + +이유: +- **장애 격리**: Patroni 이슈가 APISIX 라우팅에 영향 주지 않게 +- **네트워크 단순화**: K3s 내부 통신만으로 충분 +- **운영 일관성**: helm 한 곳에서 etcd + apisix + ingress controller 관리 + +helm values 핵심: +```yaml +etcd: + enabled: true + replicaCount: 3 + persistence: + enabled: true + size: 5Gi +``` + +업그레이드 시 주의: +- Bitnami etcd 차트의 pre-upgrade hook은 기존 etcd 멤버 list를 시도함. 멤버가 없는 상황에서는 무한 retry 실패 → `helm upgrade --no-hooks` 사용 또는 helm rollback 후 재시도 +- helm upgrade 전 release values nesting 점검 필수: `apisix.admin.allow.ipList`(O) vs `admin.allow.ipList`(X), `apisix.nginx.http.realIpFrom`(O) vs `nginx.http.realIpFrom`(X), `service.http.containerPort`(O) vs `gateway.http.containerPort`(X). 잘못된 nesting은 차트가 조용히 무시함. +- 이전 후 ingress controller는 자동으로 모든 K8s CRD 객체를 새 etcd에 재push (rollout restart로 즉시 동기화 가능) +- 외부 통합 etcd의 stale `/apisix/seoul/*` 키는 수동 삭제 (postgresql-ha.md의 etcd cleanup 명령 참고) + ### BunnyCDN Pull Zone 매핑 | Zone | ID | Origin | 방향 | 주요 Hostnames | diff --git a/infra/postgresql-ha.md b/infra/postgresql-ha.md index 2d84b02..aa4cd75 100644 --- a/infra/postgresql-ha.md +++ b/infra/postgresql-ha.md @@ -140,18 +140,29 @@ pgcat는 풀링 전용으로만 쓰고, leader 탐지는 OpenWrt HAProxy에 위 Patroni failover 발생 → pgcat가 옛 primary IP(`10.100.2.5`)를 hardcoded 참조 → nocodb 마이그레이션 시 `cannot execute UPDATE in a read-only transaction` 에러로 4시간 가량 CrashLoopBackOff. n8n은 마이그레이션이 없어서 표면화되지는 않았으나 동일한 잠재 문제 존재. 위의 단일 백엔드 구조로 변경하여 항구 해결. -## APISIX etcd 통합 (2026-04-06) +## APISIX etcd 사용 현황 -기존 각 사이트별 독립 etcd를 통합 etcd 클러스터로 이전. +| 사이트 | etcd | prefix | 비고 | +|--------|------|--------|------| +| osaka | 통합 클러스터 (192.168.9.100, ...) | `/apisix/osaka` | Docker APISIX (waf-apisix), [[apisix#오사카-apisix-osaka]] | +| sandbox-tokyo | (미가동) | `/apisix/tokyo` | 2026-04-08 NixOS 전환으로 APISIX 자체 폐기, etcd 데이터만 보존 | +| 서울 K3s | **K3s 내부 apisix-etcd StatefulSet** (apisix.apisix.svc:2379) | `/apisix` | 2026-04-08 외부 통합에서 K3s 내부로 복귀 | -| 사이트 | 기존 etcd | 이전 후 | prefix | -|--------|----------|---------|--------| -| osaka | Docker waf-etcd (로컬) | 통합 클러스터 (192.168.9.100) | `/apisix/osaka` | -| sandbox-tokyo | Docker apisix-etcd (로컬) | 통합 클러스터 (10.253.101.233) | `/apisix/tokyo` (2026-04-08 NixOS 전환으로 sandbox-tokyo APISIX 자체가 미가동) | -| 서울 K3s | StatefulSet 3노드 (K3s 내부) | 통합 클러스터 (192.168.9.100) | `/apisix/seoul` | +### 2026-04-06 → 2026-04-08 변경 이력 -- 데이터 이전: `etcdctl make-mirror --prefix /apisix/ --dest-prefix /apisix-{site}/` -- K3s 내 apisix-etcd StatefulSet + PVC 삭제 완료 +1. **2026-04-06**: 서울 K3s APISIX의 K3s 내부 apisix-etcd StatefulSet을 삭제하고 외부 통합 etcd로 이전 (`/apisix/seoul` prefix). 통합 운영 + 컴포넌트 수 절감 의도. +2. **2026-04-08**: 다시 K3s 내부로 복귀. Patroni DCS와 같은 etcd 클러스터를 공유할 때 장애 전파 위험(Patroni 이슈 → APISIX 라우팅 영향)이 비직관적이라 격리. 외부 통합 etcd의 `/apisix/seoul/*` 20개 키 삭제 완료. **현재 외부 통합 etcd는 Patroni DCS + osaka APISIX 전용.** + +### Stale prefix 삭제 명령 (참고) + +```bash +# HTTP API v3 사용 (heimdall에서 직접 호출) +KEY=$(echo -n "/apisix/seoul/" | base64) +RANGE_END=$(echo -n "/apisix/seoul0" | base64) +curl -s -X POST "http://192.168.9.100:2379/v3/kv/deleterange" \ + -H "Content-Type: application/json" \ + -d "{\"key\":\"$KEY\",\"range_end\":\"$RANGE_END\"}" +``` ## 관련 문서