Skip to content

필사 모드: Ingress Controller 교체 마이그레이션 — 무중단 전환 전략

한국어
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.
원문 렌더가 준비되기 전까지 텍스트 가이드로 표시합니다.

들어가며

Ingress Controller는 한번 정하면 좀처럼 바꾸지 않는 인프라입니다. 그런데 2026년 들어 교체를 검토하는 조직이 부쩍 늘었습니다. 가장 큰 이유는 가장 널리 쓰이던 ingress-nginx가 유지보수 모드로 전환되고, 어노테이션 기반 설정 주입(snippet)에서 여러 보안 이슈(CVE)가 보고되면서 운영 리스크가 커졌기 때문입니다. 거기에 Ingress API가 frozen되어 Gateway API로 흐름이 넘어가는 큰 추세까지 겹쳤습니다.

문제는 Ingress Controller가 외부 트래픽의 진입점이라는 점입니다. 잘못 교체하면 전체 서비스가 한꺼번에 끊깁니다. 따라서 교체는 "한 방에 갈아끼우기"가 아니라 "두 컨트롤러를 공존시키며 트래픽을 조금씩 옮기는" 무중단 전략으로 접근해야 합니다.

이 글에서는 교체 동기 정리부터 인벤토리 작성, 어노테이션 매핑, IngressClass 분리 공존, DNS 가중치 기반 전환, 단계별 롤아웃과 롤백, 검증, 흔한 함정, 최종 체크리스트까지 실전 절차를 단계적으로 다룹니다.

교체 동기 정리

먼저 "왜 바꾸는가"를 명확히 해야 전환 목표와 검증 기준이 잡힙니다. 흔한 동기는 다음과 같습니다.

- **유지보수/보안**: ingress-nginx의 유지보수 모드 전환, snippet 관련 CVE 대응

- **Gateway API 전환**: Ingress frozen 흐름에 맞춰 Gateway API 네이티브 구현으로 이동

- **기능 요구**: API 게이트웨이 기능(인증, 레이트리밋, 변환)이나 멀티테넌시 위임 모델 필요

- **성능/관측성**: 동적 reload, 더 나은 메트릭/트레이싱

- **비용**: 로드밸런서 통합, 운영 단순화

동기에 따라 목표 컨트롤러가 달라집니다. 보안/유지보수가 동기라면 HAProxy나 Traefik 같은 Ingress 호환 컨트롤러로의 비교적 단순한 교체가, Gateway API 전환이 동기라면 Envoy Gateway 등으로의 모델 변경이 목표가 됩니다.

인벤토리 작성

전환의 첫 실무 단계는 현재 무엇이 배포되어 있는지 빠짐없이 파악하는 것입니다. 다음 명령으로 Ingress 자원을 모두 수집합니다.

모든 네임스페이스의 Ingress 목록

kubectl get ingress -A -o wide

어노테이션까지 포함한 전체 정의를 백업

kubectl get ingress -A -o yaml > ingress-backup.yaml

사용 중인 IngressClass 확인

kubectl get ingressclass

어떤 어노테이션이 쓰이는지 집계

kubectl get ingress -A -o json \

| jq -r '.items[].metadata.annotations | keys[]' \

| sort | uniq -c | sort -rn

마지막 명령으로 클러스터 전체에서 어떤 어노테이션이 얼마나 쓰이는지 집계하면, 어떤 기능을 새 컨트롤러에서 반드시 재현해야 하는지가 보입니다. TLS Secret, cert-manager 발급 인증서, 외부 DNS 레코드도 함께 목록화합니다.

어노테이션 매핑 표

가장 손이 많이 가는 작업이 어노테이션을 새 컨트롤러의 표현으로 옮기는 것입니다. 대표적인 ingress-nginx 어노테이션의 대응 관계를 정리합니다.

| 기능 | ingress-nginx | Traefik | Contour |

|---|---|---|---|

| 경로 rewrite | rewrite-target | Middleware(ReplacePathRegex) | pathRewritePolicy |

| SSL 리다이렉트 | ssl-redirect | Middleware(RedirectScheme) | virtualhost.tls 자동 |

| 바디 크기 제한 | proxy-body-size | Middleware(Buffering) | 글로벌 설정 |

| 레이트리밋 | limit-rps | Middleware(RateLimit) | 글로벌/외부 |

| 백엔드 프로토콜 | backend-protocol | serversTransport | 서비스 protocol |

| 화이트리스트 | whitelist-source-range | Middleware(IPWhiteList) | authorization/외부 |

표에서 보듯 ingress-nginx의 단일 어노테이션 하나가 Traefik에서는 별도 Middleware CRD로, Contour에서는 HTTPProxy의 필드나 글로벌 설정으로 흩어집니다. 즉 일대일 변환이 아니라 모델 변환임을 인지해야 합니다.

공존 전략: IngressClass 분리

무중단 전환의 핵심은 두 컨트롤러를 동시에 띄워 두고, IngressClass로 누가 어떤 Ingress를 처리할지 명확히 구분하는 것입니다.

외부 DNS

┌────────────┴────────────┐

│ (가중치/레코드로 분배) │

▼ ▼

┌──────────────┐ ┌──────────────┐

│ LB (old) │ │ LB (new) │

│ nginx 컨트롤러 │ │ traefik 컨트롤러│

└──────┬───────┘ └──────┬───────┘

│ class: nginx │ class: traefik

▼ ▼

┌───────────────────────────────────────────┐

│ 동일 backend Service/Pod │

└───────────────────────────────────────────┘

새 컨트롤러는 별도 IngressClass로 설치합니다.

apiVersion: networking.k8s.io/v1

kind: IngressClass

metadata:

name: traefik

spec:

controller: traefik.io/ingress-controller

이렇게 하면 기존 Ingress(ingressClassName: nginx)는 그대로 nginx 컨트롤러가 처리하고, 새로 만든 리소스(ingressClassName: traefik)만 새 컨트롤러가 처리합니다. 둘은 서로 간섭하지 않으므로 안전하게 병행 운영할 수 있습니다.

어노테이션 → CRD/미들웨어 변환

Traefik으로 옮긴다면, ingress-nginx의 rewrite 어노테이션은 Middleware로 변환합니다.

apiVersion: traefik.io/v1alpha1

kind: Middleware

metadata:

name: strip-api-prefix

spec:

replacePathRegex:

regex: ^/api/(.*)

replacement: /$1

apiVersion: traefik.io/v1alpha1

kind: IngressRoute

metadata:

name: web

spec:

entryPoints:

- websecure

routes:

- match: Host(`app.example.com`) && PathPrefix(`/api`)

kind: Rule

services:

- name: api

port: 80

middlewares:

- name: strip-api-prefix

Contour로 옮긴다면 HTTPProxy로 변환합니다.

apiVersion: projectcontour.io/v1

kind: HTTPProxy

metadata:

name: web

spec:

virtualhost:

fqdn: app.example.com

tls:

secretName: app-tls

routes:

- conditions:

- prefix: /api

pathRewritePolicy:

replacePrefix:

- replacement: /

services:

- name: api

port: 80

핵심은 변환된 리소스를 새 IngressClass로 만들어, 기존 트래픽과 격리된 상태에서 먼저 검증하는 것입니다.

DNS 가중치 기반 전환

리소스를 새 컨트롤러에 복제하고 검증까지 마쳤다면, 이제 실제 트래픽을 옮길 차례입니다. 두 컨트롤러는 각자의 LoadBalancer를 가지므로, DNS에서 가중치를 조절해 점진적으로 트래픽을 이동합니다.

단계 1: new 0% ─ 새 컨트롤러에 합성 트래픽만 (내부 검증)

단계 2: new 5% ─ 카나리. 에러율/지연 비교

단계 3: new 25% ─ 메트릭 안정 확인

단계 4: new 50% ─ 양쪽 균등, 부하 비교

단계 5: new 100% ─ 전면 전환

단계 6: old 제거 ─ 안정화 기간 후 구 컨트롤러 정리

가중치 기반 DNS(예: 라우팅 정책)를 쓰거나, 두 LB 앞에 공유 진입점을 두는 방식 모두 가능합니다. 각 단계 사이에 충분한 관찰 기간을 두고, 이상 시 즉시 이전 가중치로 되돌립니다.

단계별 롤아웃과 롤백

전환 절차를 명령 단위로 정리하면 다음과 같습니다.

1) 새 컨트롤러 설치 (별도 IngressClass)

helm install traefik traefik/traefik \

--namespace traefik --create-namespace \

--set ingressClass.name=traefik

2) 변환된 리소스 적용 (새 class)

kubectl apply -f converted-routes/

3) 새 LB의 엔드포인트로 직접 검증

curl -H "Host: app.example.com" http://<NEW_LB_IP>/healthz

4) DNS 가중치 단계 상향 (5 → 25 → 50 → 100)

각 단계마다 메트릭 관찰

5) 롤백이 필요하면 DNS 가중치를 즉시 0으로

리소스는 그대로 두므로 트래픽만 구 컨트롤러로 회귀

롤백 설계의 핵심은 "구 컨트롤러와 그 Ingress를 전환 완료 후 안정화 기간까지 절대 지우지 않는 것"입니다. DNS 가중치만 되돌리면 즉시 원상복구되도록 만들어야 안전합니다.

검증: 트래픽 비교와 합성 모니터

전환 단계마다 다음을 비교 검증합니다.

- **상태 코드 분포**: 2xx/4xx/5xx 비율이 구 컨트롤러와 동일한가

- **지연(latency)**: p50/p95/p99 분위 지연이 악화되지 않았는가

- **TLS 동작**: 인증서 체인, SNI, 리다이렉트가 동일한가

- **경로 매칭**: rewrite 결과 URL이 백엔드 기대와 일치하는가

합성 모니터(synthetic check)로 주요 경로를 두 컨트롤러에 동시에 던져 응답을 diff하면 동작 차이를 조기에 발견할 수 있습니다.

같은 요청을 양쪽 LB에 보내 응답 비교

for path in / /api/users /login /static/app.js; do

old=$(curl -s -o /dev/null -w "%{http_code}" -H "Host: app.example.com" http://<OLD_LB_IP>$path)

new=$(curl -s -o /dev/null -w "%{http_code}" -H "Host: app.example.com" http://<NEW_LB_IP>$path)

echo "$path old=$old new=$new"

done

함정: 동작 차이

겉보기에 같은 설정이라도 컨트롤러마다 미묘한 동작 차이가 있어 사고가 납니다. 대표적인 것들입니다.

- **rewrite 정규식 차이**: ingress-nginx의 rewrite-target과 capture group 동작이 Traefik/Contour와 다릅니다. 슬래시 처리, 후행 슬래시 유무를 반드시 확인합니다.

- **경로 매칭 우선순위**: Prefix vs Exact, 가장 긴 매칭 우선 규칙이 구현마다 다를 수 있습니다.

- **기본 타임아웃**: 백엔드 응답 타임아웃, idle 타임아웃 기본값이 다릅니다. 긴 요청이 새 컨트롤러에서만 끊길 수 있습니다.

- **헤더 처리**: X-Forwarded-* 헤더 추가/덮어쓰기 정책 차이.

- **바디 크기 제한**: 기본 제한이 달라 업로드가 새 컨트롤러에서만 막힐 수 있습니다.

- **정규식 호스트 매칭**: 와일드카드 호스트 처리 방식 차이.

이런 차이는 명세 비교만으로는 잡기 어렵고, 위의 합성 모니터 diff로 실제 트래픽을 흘려 봐야 드러납니다.

마이그레이션 체크리스트

전환 전후로 점검할 항목을 정리합니다.

[ ] 모든 Ingress 인벤토리 백업 (kubectl get ingress -A -o yaml)

[ ] 사용 중인 어노테이션 전수 집계 및 매핑표 작성

[ ] TLS Secret / cert-manager 발급 인증서 목록화

[ ] 새 컨트롤러를 별도 IngressClass로 설치 (공존)

[ ] 어노테이션 → CRD/Middleware 변환 리소스 작성

[ ] 새 LB 엔드포인트 직접 검증 (Host 헤더로)

[ ] 합성 모니터로 양쪽 응답 diff

[ ] DNS 가중치 단계 전환 (5→25→50→100), 단계마다 관찰

[ ] 상태코드/지연/TLS/경로 매칭 비교 통과

[ ] 롤백 시나리오(가중치 0 복귀) 리허설 완료

[ ] 안정화 기간 후 구 컨트롤러/구 Ingress 정리

[ ] 장기적으로 Gateway API 전환 경로 계획

마치며

Ingress Controller 교체는 외부 진입점을 바꾸는 위험한 작업이지만, IngressClass 분리로 두 컨트롤러를 공존시키고 DNS 가중치로 점진 전환하면 무중단으로 안전하게 해낼 수 있습니다. 핵심은 세 가지입니다. 첫째, 정확한 인벤토리와 어노테이션 매핑. 둘째, 일대일 변환이 아닌 모델 변환이라는 인식과 동작 차이에 대한 합성 모니터 검증. 셋째, DNS 가중치만 되돌리면 즉시 복구되는 롤백 설계입니다.

마지막으로, 어차피 컨트롤러를 교체하는 김에 장기적으로는 Ingress frozen 흐름을 고려해 Gateway API로의 전환 경로까지 함께 계획해 두면, 다음번 큰 전환을 한 번 더 겪지 않아도 됩니다.

참고 자료

- Kubernetes Ingress 공식 문서: https://kubernetes.io/docs/concepts/services-networking/ingress/

- Kubernetes IngressClass 문서: https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class

- ingress-nginx 어노테이션 문서: https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/

- Traefik Kubernetes Ingress 문서: https://doc.traefik.io/traefik/providers/kubernetes-ingress/

- Traefik Middleware 문서: https://doc.traefik.io/traefik/middlewares/overview/

- Project Contour HTTPProxy 문서: https://projectcontour.io/docs/main/config/fundamentals/

- HAProxy Kubernetes Ingress 문서: https://www.haproxy.com/documentation/kubernetes-ingress/

- Gateway API 마이그레이션(ingress2gateway): https://gateway-api.sigs.k8s.io/guides/migrating-from-ingress/

- cert-manager 공식 문서: https://cert-manager.io/docs/

현재 단락 (1/153)

Ingress Controller는 한번 정하면 좀처럼 바꾸지 않는 인프라입니다. 그런데 2026년 들어 교체를 검토하는 조직이 부쩍 늘었습니다. 가장 큰 이유는 가장 널리 쓰이던...

작성 글자: 0원문 글자: 6,391작성 단락: 0/153