ingress-nginx 지원 종료와 F5 NGINX Ingress Controller 전환기
2025년 11월, 쿠버네티스 SIG Network와 Security Response Committee가 ingress-nginx의 지원 종료를 발표했다. 2026년 3월까지만 최소한의 유지보수를 하고, 그 이후로는 버그 수정도 보안 패치도 없다는 내용이었다. 그리고 실제로 올해 3월, 저장소는 읽기 전용으로 바뀌었다.
이 소식이 돌았을 때 주변에서 이제 NGINX는 못 쓰는 것이냐는 질문을 꽤 많이 받았다. 지원이 종료된 것은 커뮤니티의 ingress-nginx 컨트롤러이고, NGINX 자체는 계속 쓸 수 있다. 우리 팀도 컨트롤러를 F5 NGINX Ingress Controller로 바꿔 NGINX를 계속 쓰고 있다. 이번 글에서는 이름 때문에 헷갈리기 쉬운 두 컨트롤러를 먼저 비교해 보고, 전환하면서 겪은 일을 기록해 두려고 한다.
1. ingress-nginx와 NGINX Ingress Controller 비교
쿠버네티스 생태계에는 NGINX 기반 Ingress Controller가 두 개 있다.
- ingress-nginx: 쿠버네티스 커뮤니티가 관리하던 컨트롤러로, 이번에 지원이 종료된 쪽이다.
- NGINX Ingress Controller: NGINX를 만든 F5가 직접 관리하는 컨트롤러로, 지원 종료와 상관없이 지금도 개발이 이어지고 있다.
둘 다 데이터 플레인으로 NGINX를 쓴다는 점만 같고, 설정 방식과 동작은 생각보다 많이 다르다. 전환하면서 체감한 차이를 표로 정리하면 다음과 같다.
| 구분 | ingress-nginx (커뮤니티) | NGINX Ingress Controller (F5) |
|---|---|---|
| 관리 주체 | 쿠버네티스 커뮤니티 | F5 |
| 저장소 | kubernetes/ingress-nginx |
nginx/kubernetes-ingress |
| 현재 상태 | 2026년 3월 지원 종료, 저장소 archived | 개발 진행 중 |
| 라이선스 | Apache 2.0 | Apache 2.0 (NGINX Plus 기반 상용판 별도) |
| annotation 접두사 | nginx.ingress.kubernetes.io/* |
nginx.org/*, nginx.com/* |
| 고급 설정 방식 | annotation과 ConfigMap 위주 | annotation 외에 VirtualServer, Policy 같은 CRD 제공 |
| snippet | 많이 쓰였지만 보안 이슈의 원인이 됨 | 기본값이 꺼짐, 켜려면 별도 옵션 필요 |
| 같은 host를 여러 리소스에 선언 | 자동으로 합쳐 줌 | 가장 먼저 만든 리소스만 반영, 나머지는 거부 |
| 리소스 하나에 여러 host | Ingress에서 가능, server-alias도 지원 |
Ingress에서는 가능, VirtualServer는 host 하나만 가능 |
annotation 접두사가 다른 것은 찾아 바꾸면 그만이지만, 표 아래쪽 두 줄처럼 host를 다루는 방식이 다른 것은 설정 구조를 다시 짜야 한다. 이 부분은 아래에서 다뤄보려고 한다.
참고로 지원 종료의 배경은 크게 두 가지였다. 오랫동안 메인테이너 한두 명이 퇴근 후와 주말 시간을 쪼개 프로젝트를 유지해 왔다는 구조적인 한계, 그리고 2025년 3월에 공개된 IngressNightmare(CVE-2025-1974) 같은 심각한 보안 문제다. 클러스터 절반 가까이에서 쓰이던 컨트롤러가 이렇게 유지되고 있었다는 사실이 오픈소스에 관심이 많은 입장에서 꽤 씁쓸했다.
2. F5를 선택한 이유
커뮤니티가 권고한 선택지는 크게 두 갈래였다. Gateway API로 넘어가거나, 다른 Ingress Controller로 바꾸거나. 후보로는 Traefik, HAProxy, Envoy 계열, 그리고 F5 NGINX가 있었다.
Gateway API 문서를 한글화하면서 그 설계 사상에 꽤 공감했던 터라 개인적으로는 Gateway API로 바로 가고 싶은 마음도 있었다. 하지만 운영 중인 서비스의 Ingress를 한 번에 Gateway API 리소스로 바꾸기에는 작업 범위가 너무 컸다. 지원 종료 일정은 정해져 있었고, 우리에게는 변경을 최소화하면서 안전하게 넘어가는 방법이 필요했다.
F5 NGINX Ingress Controller를 고른 이유는 다음과 같다.
- 데이터 플레인이 똑같이 NGINX라서 기존 운영 경험을 그대로 살릴 수 있다.
- 기존 Ingress 리소스를 상당 부분 그대로 쓸 수 있다. 5.3.0 버전부터는 ingress-nginx 호환 annotation도 일부 지원한다.
- 오픈소스로 시작하고, 필요하면 NGINX Plus 기반 상용 지원으로 넘어갈 수 있다.
- 회사가 관리하는 프로젝트라서 이번처럼 메인테이너가 부족해 프로젝트가 멈출 걱정이 상대적으로 적다.
3. 전환하면서 겪은 문제
공식 문서와 쿠버네티스 Steering Committee의 성명에서 똑같이 강조한 내용이 있다. 어떤 대안도 이미지만 바꾸면 끝나는 drop-in 교체품은 아니라는 것이다. 우리도 아래와 같은 부분은 설정을 다시 잡아야 했다.
3-1. AWS NLB 연결
우리는 AWS에서 NLB를 앞단에 두고 Ingress Controller로 트래픽을 넘기는 구조를 쓰고 있다. 다행히 이 구조 자체는 바꿀 필요가 없었다. NLB 관련 설정은 컨트롤러의 Service에 붙는 annotation이라서, 기존 ingress-nginx Service에 달려 있던 annotation을 F5 컨트롤러의 Helm values로 그대로 가져오면 됐다.
controller:
service:
type: LoadBalancer
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: "external"
service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: "ip"
service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"
다만 새 컨트롤러를 띄우면 NLB도 새로 만들어진다. 그래서 두 컨트롤러를 잠시 함께 띄워 두고, 새 NLB로 트래픽이 정상적으로 흐르는지 확인한 뒤 DNS를 넘기는 순서로 진행했다. ingressClassName을 서로 다르게 두면 두 컨트롤러가 같은 클러스터에서 충돌 없이 함께 동작한다.
3-2. 여러 Ingress에 같은 host가 들어간 경우
전환 작업에서 시간이 가장 많이 걸린 부분이다. 우리는 그동안 Ingress를 host 단위가 아니라 서비스 컴포넌트 단위로 만들어 왔다. 컴포넌트마다 Ingress를 하나씩 두다 보니 Ingress 하나에 여러 host가 들어가기도 했고, 같은 host가 path만 달리해서 여러 Ingress에 나뉘어 들어가기도 했다. 예를 들면 이런 식이다.
# web 컴포넌트
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: shop-web
spec:
ingressClassName: nginx
rules:
- host: shop.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: shop-web
port:
number: 80
- host: m.shop.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: shop-web
port:
number: 80
---
# api 컴포넌트
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: shop-api
spec:
ingressClassName: nginx
rules:
- host: shop.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: shop-api
port:
number: 80
- host: m.shop.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: shop-api
port:
number: 80
ingress-nginx에서는 이 구성이 문제없이 동작했다. 같은 host를 가진 Ingress가 여러 개 있으면 컨트롤러가 path를 모아 NGINX server 블록 하나로 합쳐 주기 때문이다.
F5는 다르다. 같은 host를 선언한 리소스가 여러 개 있으면 합치지 않고, 생성 시점이 가장 오래된 리소스 하나만 반영한다. 나머지는 거부되고, 이벤트에 All hosts are taken by other resources라는 메시지가 남는다. 위 예시라면 먼저 만든 shop-web만 반영되고 shop-api의 /api 경로는 빠진다. 판정은 host별로 이뤄지기 때문에, host를 여러 개 가진 Ingress는 일부 host만 반영되고 나머지는 거부되는 상태가 될 수도 있다. 에러가 크게 나지 않고 특정 path만 404가 나는 식으로 드러나서 원인을 찾기도 쉽지 않다.
결국 Ingress를 컴포넌트 단위에서 host 단위로 다시 묶었다. host 하나에 Ingress 하나를 두고, 그 host로 들어오는 path를 모두 한 곳에 모으는 방식이다.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: shop-example-com
spec:
ingressClassName: nginx
rules:
- host: shop.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: shop-web
port:
number: 80
- path: /api
pathType: Prefix
backend:
service:
name: shop-api
port:
number: 80
m.shop.example.com도 같은 방식으로 Ingress를 하나 따로 만들었다. Ingress 개수는 비슷하지만, 이제는 도메인 하나에 어떤 path가 어느 서비스로 가는지 Ingress 하나만 보면 알 수 있다.
컴포넌트마다 annotation을 다르게 줘야 해서 Ingress를 꼭 나눠야 하는 경우도 있다. F5에는 이런 경우를 위해 mergeable Ingress라는 기능이 있다. host와 TLS를 맡는 master Ingress 하나에, path를 맡는 minion Ingress 여러 개를 annotation(nginx.org/mergeable-ingress-type)으로 묶어 주는 방식이다. VirtualServer라는 F5 전용 CRD에 VirtualServerRoute를 붙여 path별로 나누는 방법도 있지만, F5에 그만큼 더 묶이게 된다.
3-3. annotation 변환
annotation은 이름만 바꾸면 되는 것도 있고, 아예 다른 방식으로 바꿔야 하는 것도 있다. 우리는 클러스터에서 쓰던 annotation을 전부 뽑아 대응표를 먼저 만들고 작업했다.
| ingress-nginx | F5 NGINX Ingress Controller |
|---|---|
nginx.ingress.kubernetes.io/proxy-body-size |
nginx.org/client-max-body-size 또는 VirtualServer의 client-max-body-size |
nginx.ingress.kubernetes.io/rewrite-target |
nginx.org/rewrites 또는 VirtualServer의 action.proxy.rewritePath |
nginx.ingress.kubernetes.io/server-alias |
대응 기능 없음, host마다 리소스를 분리 |
nginx.ingress.kubernetes.io/configuration-snippet |
snippet 옵션을 켜야 사용 가능, 가능하면 Policy나 전용 필드로 대체 |
snippet은 이번 기회에 최대한 걷어 냈다. IngressNightmare도 결국 snippet 계열 annotation이 공격 경로였던 만큼, 편하다는 이유로 계속 쓰기에는 부담이 컸다.
4. 사용 후기
좋았던 점은 이번 기회에 Ingress 구성이 정리됐다는 것이다. 컴포넌트 단위로 흩어져 있던 라우팅을 host 단위로 모으고 나니, 도메인별로 트래픽이 어디로 가는지 한눈에 보인다. 같은 host를 둘러싼 충돌도 조용히 합쳐지는 대신 명확하게 거부되니, 처음에는 불편해도 길게 보면 실수를 줄여 줄 것 같다.
아쉬운 점은 역시 전환 비용이다. 이름이 비슷하니 금방 끝날 줄 알았는데, host 구성과 annotation을 하나씩 다시 보는 데 생각보다 오래 걸렸다. 그리고 nginx.org/* annotation은 결국 F5 전용이라서, 나중에 다른 구현체로 넘어갈 일이 생기면 그때 또 다시 작업해야 한다.
마치며
이번 작업을 하면서 "Ingress API 다음은 Gateway API"라는 말이 더 와닿았다. Ingress API 자체는 없어지지 않지만 기능 추가는 멈췄고, 앞으로의 개발은 Gateway API에서 이뤄진다. F5도 NGINX Gateway Fabric이라는 Gateway API 구현체를 따로 만들고 있으니, 언젠가 한 번 더 바꾸게 될 것 같다. 그때는 이번처럼 일정에 쫓기지 않고 미리 준비해 두고 싶다.
혹시 아직 ingress-nginx를 그대로 쓰고 있다면, 당장 서비스가 멈추지는 않더라도 보안 패치가 더 이상 나오지 않는다는 점은 꼭 기억했으면 한다.
다음 전환은 조금 더 여유롭게 해 보자.
참고 자료