본문으로 건너뛰기

ADR-0028: ingress-nginx EOL 대응 — Gateway API 마이그레이션 경로

  • 상태: 완료 (2026-08-08 이행 종료 — nginx 해체). 승인 2026-07-23
  • 날짜: 2026-07-23 (같은 날 정정 — 업그레이드 경로 부재 실측 반영)
  • 관련: Epic #2600, ADR-0022(Linkerd mTLS)

배경

prod(aks-genos-prod)의 ingress-nginx 컨트롤러가 v1.15.1(chart ingress-nginx-4.15.1)이며 EOL(2026-03-24)을 4개월 경과했다. 보안 패치가 더 이상 공급되지 않는 컴포넌트가 클러스터의 모든 외부 진입점을 처리하고 있다.

클러스터 실측 (2026-07-23)

항목실측값
컨트롤러registry.k8s.io/ingress-nginx/controller:v1.15.1, Helm 릴리스 ingress-nginxDataX repo 관리 밖
업스트림 릴리스v1.15.1 이 마지막. 2026-03-19 에 세 라인(1.13.9/1.14.5/1.15.1) 동시 배포 후 릴리스 없음 = 종료 시 마지막 패치 일괄 배포. 우리는 이미 최종 버전 사용 중 → 업그레이드 경로 없음
후속 프로젝트kubernetes-retired/ingate아카이브됨(2026-06-30 최종 push). 전환 대상 아님
WAF 실제 모드configmap modsecurity-snippet = SecRuleEngine On (DetectionOnly 아님) + OWASP CRS on → 실제 차단 중
외부 노출ingress-nginx-controller = Service type LoadBalancer(20.214.90.38). 앞단 WAF/CDN 없음
공유 단위컨트롤러 replicas=1(파드 1개) + LB IP 20.214.90.38 단일gend 8 · llmops 6 · monitoring 2 가 전부 통과. VM 은 8노드로 분산돼 있으나 진입점은 하나
nginx Ingress16개 / 3 네임스페이스: gend 8 · llmops 6 (GenOS 소유) · monitoring 2
IngressClassnginx 단 하나만 등록 (controller k8s.io/ingress-nginx)
nginx 전용 annotation21종 사용 중
Istioistio-system/istiod 상주(109일) — 단 istio IngressClass 미등록, istio 클래스 Ingress 4개(KServe)는 status.loadBalancer 가 비어 실제로 서빙되지 않음
Gateway API CRD부분 설치httproutes.gateway.networking.k8s.io 만 존재, GatewayClass/Gateway CRD 없음
서비스 메시Linkerd (ADR-0022). Linkerd 는 ingress 컨트롤러가 아니므로 nginx 대체 불가
인증서cert-manager 상주 (trust-manager 포함)

소유 구조 — 이행 순서를 결정하는 실제 변수

정정(2026-07-23): 초판은 "llmops 6개가 GenOS 소유라 단일 팀 결정 불가"로 판단했으나, llmops(azure GenOS)는 실사용자 없는 소유자 전용 환경임이 확인됐다. 중단 허용 범위가 다르므로 팀 간 합의 제약은 해소되고, 대신 blast radius 가 이행 순서의 기준이 된다.

대상규모중단 허용역할
llmops6 (표준 4 · WAF 2)허용 — 소유자 전용실전 검증대. 표준 경로와 WAF 경로를 실트래픽으로 검증
monitoring2 (표준)낮음관측 도구 — 저위험 후속
gend8 (전부 WAF · 2는 ForwardAuth 동반)낮음 — 타 운영자 사용최종 이행 대상

핵심 함의 세 가지: 0. llmops 가 자유롭다고 컨트롤러가 자유로운 것은 아니다. 컨트롤러는 replicas=1 단일 파드이고 LB IP 도 하나이며 gend 8개가 같이 탄다. 따라서 이행 방식은 컨트롤러 교체가 아니라 Gateway 병행 기동 → Ingress 단위 전환 → 맨 마지막에 nginx 해체 여야 한다.

  1. WAF 등가를 합성 환경이 아니라 실서비스(llmops 2개)에서 검증할 수 있다. 랩 PoC 보다 훨씬 강한 증거이고, 실패해도 영향 범위가 소유자 본인으로 한정된다.
  2. llmops 에는 ForwardAuth 사용 Ingress 가 0개다. oauth2-proxy 연동(gend 의 kafka-ui· mlflow 2개)은 자유 실험대에서 사전 검증할 수 없는 유일한 영역이며, 여기만 별도 랩이 필요하다.

마이그레이션 난이도 — annotation 의존성

정정 3 (2026-08-03, 단계 2 착수 시 실측): 초기 인벤토리가 annotation 키만 세고 값을 보지 않았다. 값 기준 재분류 결과 per-ingress enable-modsecuritytrue 인 인그레스는 0개gend 7개 전부 명시적 false(opt-out), 나머지는 미지정. 실제 WAF 구조는 컨트롤러 전역(configmap SecRuleEngine On)이 opt-out 하지 않은 인그레스에만 적용되는 형태다. 함의:

  • gend 이행에 WAF 등가가 필요 없다 (원래부터 opt-out). 잔여 난제는 ForwardAuth(kafka-ui·mlflow) 뿐.
  • 전역 WAF 의 실제 적용 대상 = llmops 표준 4(단계 1 이행으로 WAF 미적용 상태가 됨 — 소유자 전용이라 수용, 복원 원하면 Coraza WASM 등 EG 확장 별도 검토) + monitoring 2(단계 3 이행 시 동일 손실 발생 — 착수 전 결정 필요).
  • 또한 gend-ingress-trino-external 은 목록에서 소실됨(2026-07-23 이후 삭제 관측, 본 작업과 무관) — gend 는 7개.

Gateway API 표준(HTTPRoute 필터)으로 직접 대응되지 않는 기능이 존재한다.

등급사용 annotation (건수)Gateway API 대응
표준 대응proxy-read/send-timeout(13/11) · proxy-body-size(12) · ssl-redirect(7) · rewrite-target+use-regex(5/6) · CORS 계열(3+2+2+1) · proxy-bufferingHTTPRoute 필터 + 구현체 정책 CRD
표준 부재enable-modsecurity(10) · OWASP CRS(4+2) = WAFGateway API 표준 없음 → 구현체 확장 또는 별도 WAF 계층
표준 부재auth-url/auth-signin/auth-response-headers(2/2/2) = oauth2-proxy ForwardAuth구현체별 ExtAuth 정책 (Istio AuthorizationPolicy/Envoy SecurityPolicy 등)

Ingress 별 난이도 (이행 순서의 근거)

정정(2026-07-23): 초판은 annotation 난이도만으로 순서를 정했으나, 소유 구조 확인 후 blast radius(중단 허용) 우선 · 난이도 차선으로 재정렬한다. 위험한 것을 먼저 안전한 곳에서 해보는 편이, 쉬운 것을 위험한 곳에서 먼저 하는 것보다 낫다.

단계대상검증 가치
1 (llmops 표준 4)genos-ingress, genos-api-rewrite, genos-default-ingress, genos-flowise-ingress중단 자유. 라우팅·TLS·rewrite 실트래픽 검증
2 (llmops WAF 2)codespace-1-external, genos-minio-ingress중단 자유. WAF 등가를 실서비스에서 확증 — PoC 의 (a) 를 실증으로 대체
3 (monitoring 2)grafana-ingress, grafana-ingress-ip표준·저위험. 타 사용자 노출 첫 단계
4 (gend WAF 6)gend-ingress, -keycloak, -trino-external, -mcp, -dagster, -weaviate1~2 단계 결과를 그대로 적용. 무중단 전환 필요
5 (gend ForwardAuth 2)gend-ingress-kafka-ui, gend-ingress-mlflow사전 검증 불가 영역 — 별도 랩 검증 필수

대안 검토

정정(2026-07-23): 초판은 "지원 버전으로 업그레이드해 시간을 번다"를 1차 조치로 제시했으나, 실측 결과 v1.15.1 이 마지막 릴리스이고 우리가 이미 그것을 쓰고 있어 실행 불가능하다. 후속 프로젝트 InGate 도 아카이브됐다. 시간을 벌 옵션이 없으며 마이그레이션이 유일한 해법이다.

옵션장점단점판단
A. 지원 버전 업그레이드v1.15.1 이 최종 릴리스 = 상위 버전 부재불가(정정)
D. InGate 전환kubernetes-retired/ingate 아카이브불가(정정)
B. Istio Gateway APIistiod 가 이미 상주(신규 컨트롤플레인 불필요), ExtAuth/WAF 확장 경로 존재메시가 Linkerd 라 데이터플레인 2종 운영 부담, istio IngressClass 미등록 = 실사용 검증 필요PoC 후보 1
C. Envoy GatewayGateway API 네이티브, 정책 CRD 성숙, ingress 전용이라 메시와 역할 비중복신규 컴포넌트 학습·운영 비용PoC 후보 2
E. 앞단 Azure WAF(Front Door/App Gateway) 추가방어층 즉시 확보클라우드 비용·DNS 전환, GenOS 트래픽 포함 → 협의 확대미채택(아래 결정 1)

결정

결정 1 — EOL 노출 자세: 단계적 이행 + 현행 WAF 를 보상 통제로 인정 (2026-07-23 확정)

업그레이드 경로가 없으므로 "패치 수급 복구"라는 선택지 자체가 없다. 대신 컨트롤러가 SecRuleEngine On + OWASP CRS 로 실제 차단 중이라는 사실을 보상 통제로 인정하고, 난이도 1단계부터 분기 단위로 단계 이행한다. 앞단 Azure WAF 추가(옵션 E)는 비용·DNS 전환과 GenOS 트래픽 포함으로 협의 범위가 커져 현 시점 미채택한다 — 단, 이행이 2분기 이상 지연되거나 EOL 관련 실 취약점이 공표되면 재검토한다.

결정 2 — 구현체: 2주 PoC 후 확정 (2026-07-23 확정)

Istio·Envoy Gateway 어느 쪽도 WAF·ForwardAuth 등가 확보 여부가 미검증이라 지금 확정하면 근거 없는 선택이 된다. 다만 소유 구조 확인으로 검증 방식이 갈린다: (a) WAF 등가 — 랩 대신 llmops 실이행(단계 2)으로 확증. 중단이 허용되는 실서비스에서 CRS 차단 동작을 직접 확인하는 편이 합성 테스트보다 증거력이 높다. (b) ForwardAuth 등가 — 랩 검증 필수. llmops 에 사용처가 0개라 사전 실증이 불가능하고, 유일한 사용처(gend kafka-ui·mlflow)는 타 사용자 영향이 있어 실험대로 못 쓴다. 둘 다 충족하는 후보 중 운영 부담이 낮은 쪽을 택한다. 양쪽 다 실패하면 앞단 WAF(옵션 E)로 방어층을 분리한 뒤 재설계한다. 결정 3 — 단계 이행: 위 난이도 표 순서(1→4단계)로 진행하고, 각 단계는 Gateway 병행 운영 → 트래픽 전환 → 관측 → 구 Ingress 제거 로 나눈다. Gateway API CRD 는 현재 HTTPRoute 만 있으므로 전체 CRD 세트 설치가 Phase 1 선행 조건이다. 결정 4 — 정리 대상: istio 클래스 KServe Ingress 4개는 서빙되지 않는 사문 리소스다. 마이그레이션과 별개로 삭제 또는 실사용 전환을 결정한다.

이행 완료 (2026-08-08)

ingress-nginx 해체 = EOL 노출 종결. helm uninstall ingress-nginx + 네임스페이스 삭제까지 마쳤고, 구 LB IP 20.214.90.38 은 응답하지 않는다(000) — 해체 실증.

단계대상완료일
1llmops 표준 42026-07-25 (DNS 컷오버)
2llmops codespace·minio2026-08-03 (사고 1호 복구 겸)
3monitoring grafana2026-08-03 (사고 2호 복구 겸)
4·5gend 7종 (ForwardAuth 포함)2026-08-03 (DNS 컷오버)
정리구 Ingress 15개 제거 → nginx 해체2026-08-08

해체 후 12경로 전건 정상(UI·API·Keycloak·dagster·weaviate·mcp·grafana·minio·flowise·codespace).

이행이 가르쳐 준 것

  1. 컷오버 단위는 인그레스가 아니라 호스트네임 — 같은 호스트의 일부만 옮기면 나머지가 catch-all 로 빨려들어간다(사고 2건, 각 9일 오라우팅). 컷오버 전 get ingress -A 로 호스트 전수 조사가 의무.
  2. DNS 를 옮기면 ACME HTTP01 검증 경로도 함께 옮겨진다 — 체크리스트에 인증서 갱신 경로가 빠져 있어 갱신이 조용히 멈췄다(#3091). 컷오버 항목에 영구 편입.
  3. 인벤토리는 키가 아니라 값enable-modsecurity 키만 세고 값(false)을 안 봐서 WAF 난이도를 과대평가했다. per-ingress WAF 사용은 실제로 0건이었다.
  4. 시맨틱 차이를 그대로 이식하지 말 것 — nginx read-timeout(유휴) vs Gateway API request(총 지속시간). 3600s 이식은 활성 WS·업로드를 자른다.

결과

  • 긍정: EOL 노출 해소, 표준 API 로 이동, 라우팅 정책의 선언적 관리
  • 부담: 데이터플레인 2종(Linkerd 메시 + Gateway) 운영, WAF/ForwardAuth 재구성
  • 미결: 구현체 확정(단계 12 + ForwardAuth 랩 결과로 판단), WAF 정책 이관 범위 (동등 룰셋 유지 vs 재정의), gend 단계(45) 전환 점검창
  • 해소됨: 팀 간 조율llmops 는 소유자 전용 환경으로 확인. 컨트롤러 업그레이드 시점 — 상위 버전 부재로 해당 없음

참고

  • 인벤토리 재현: kubectl --context aks-genos-prod get ingress -A -o json → ingressClass=nginx 필터 후 nginx.ingress.kubernetes.io/* annotation 집계
  • 본 ADR 은 실행 승인이 아니다. 실제 변경은 GenOS 합의 후 별도 이슈로 진행한다.