K8s Ingress NGINX란?
Kubernetes 클러스터 외부 트래픽을 내부 서비스로 라우팅하는 Ingress Controller 중 가장 널리 사용되는 것이 NGINX Ingress Controller다. 단순 라우팅을 넘어 TLS 종료, 속도 제한, 카나리 배포, 커스텀 헤더 주입까지 Annotation 하나로 제어할 수 있다.
이 글에서는 실무에서 자주 쓰이는 NGINX Ingress 고급 설정 7가지를 코드와 함께 심층적으로 다룬다.
1. 기본 Ingress 리소스 구조
Ingress 리소스는 호스트·경로 기반 라우팅 규칙을 선언적으로 정의한다.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
ingressClassName: nginx
tls:
- hosts:
- api.example.com
secretName: api-tls
rules:
- host: api.example.com
http:
paths:
- path: /api(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: api-service
port:
number: 8080
- path: /web(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: web-service
port:
number: 3000
rewrite-target: /$2는 캡처 그룹을 활용해 /api/users → /users로 경로를 재작성한다. 정규식 기반 라우팅의 핵심이다.
2. Rate Limiting으로 API 보호
DDoS나 과도한 요청으로부터 서비스를 보호하려면 속도 제한이 필수다. NGINX Ingress는 Annotation만으로 이를 구현한다.
metadata:
annotations:
nginx.ingress.kubernetes.io/limit-rps: "10"
nginx.ingress.kubernetes.io/limit-rpm: "300"
nginx.ingress.kubernetes.io/limit-burst-multiplier: "5"
nginx.ingress.kubernetes.io/limit-connections: "5"
nginx.ingress.kubernetes.io/limit-whitelist-source-range: "10.0.0.0/8,172.16.0.0/12"
| Annotation | 설명 |
|---|---|
limit-rps |
초당 최대 요청 수 |
limit-rpm |
분당 최대 요청 수 |
limit-burst-multiplier |
버스트 허용 배수 (rps × 배수) |
limit-connections |
IP당 동시 연결 수 |
limit-whitelist-source-range |
제한 면제 IP 대역 |
내부 서비스 간 통신은 whitelist로 면제하고, 외부 트래픽에만 제한을 적용하는 것이 일반적이다. limit-burst-multiplier를 적절히 설정하면 순간적인 트래픽 스파이크도 수용할 수 있다.
3. 카나리 배포 (Canary Deployment)
새 버전을 전체 트래픽에 노출하기 전에 일부 트래픽만 라우팅하는 카나리 배포를 Ingress 레벨에서 구현할 수 있다.
# 카나리 Ingress (새 버전)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-canary
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "20"
spec:
ingressClassName: nginx
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-service-v2
port:
number: 8080
전체 트래픽의 20%가 v2로 라우팅된다. 헤더 기반 카나리도 가능하다:
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-by-header: "X-Canary"
nginx.ingress.kubernetes.io/canary-by-header-value: "always"
X-Canary: always 헤더가 포함된 요청만 v2로 라우팅된다. QA 팀이 프로덕션 환경에서 새 버전을 검증할 때 유용하다. 관련 배포 전략은 ArgoCD GitOps 배포 글도 참고하자.
4. 커스텀 에러 페이지와 Default Backend
404나 503 에러 시 기본 NGINX 에러 페이지 대신 커스텀 페이지를 보여줄 수 있다.
annotations:
nginx.ingress.kubernetes.io/custom-http-errors: "404,500,502,503"
nginx.ingress.kubernetes.io/default-backend: error-pages-service
별도의 error-pages-service를 배포하고, 해당 서비스가 에러 코드에 맞는 HTML을 반환하도록 구성한다. ConfigMap으로 전역 설정도 가능하다:
apiVersion: v1
kind: ConfigMap
metadata:
name: ingress-nginx-controller
namespace: ingress-nginx
data:
custom-http-errors: "404,500,502,503"
default-backend-service: "default/error-pages-service"
5. CORS·보안 헤더 주입
백엔드 코드 수정 없이 Ingress 레벨에서 CORS와 보안 헤더를 설정할 수 있다.
annotations:
# CORS
nginx.ingress.kubernetes.io/enable-cors: "true"
nginx.ingress.kubernetes.io/cors-allow-origin: "https://app.example.com"
nginx.ingress.kubernetes.io/cors-allow-methods: "GET,POST,PUT,DELETE,OPTIONS"
nginx.ingress.kubernetes.io/cors-allow-headers: "Authorization,Content-Type,X-Request-ID"
nginx.ingress.kubernetes.io/cors-max-age: "3600"
# 보안 헤더
nginx.ingress.kubernetes.io/configuration-snippet: |
more_set_headers "X-Frame-Options: DENY";
more_set_headers "X-Content-Type-Options: nosniff";
more_set_headers "Referrer-Policy: strict-origin-when-cross-origin";
more_set_headers "Permissions-Policy: camera=(), microphone=()";
configuration-snippet은 NGINX 설정 블록을 직접 주입하는 강력한 기능이다. 다만 보안 위험이 있어 프로덕션에서는 allow-snippet-annotations를 신중히 관리해야 한다.
6. 업스트림 해싱과 세션 어피니티
WebSocket이나 상태 유지가 필요한 서비스에서는 같은 클라이언트 요청을 같은 Pod로 보내야 한다.
annotations:
# 쿠키 기반 세션 어피니티
nginx.ingress.kubernetes.io/affinity: "cookie"
nginx.ingress.kubernetes.io/affinity-mode: "persistent"
nginx.ingress.kubernetes.io/session-cookie-name: "SERVERID"
nginx.ingress.kubernetes.io/session-cookie-max-age: "3600"
nginx.ingress.kubernetes.io/session-cookie-samesite: "Strict"
nginx.ingress.kubernetes.io/session-cookie-secure: "true"
IP 해싱 방식도 있지만, 프록시 뒤에서는 쿠키 기반이 더 정확하다. SameSite=Strict와 Secure 플래그로 세션 쿠키 보안도 강화하자.
7. ModSecurity WAF 통합
NGINX Ingress에 내장된 ModSecurity를 활성화하면 OWASP 룰셋 기반 웹 방화벽(WAF)을 적용할 수 있다.
# ConfigMap (전역 설정)
apiVersion: v1
kind: ConfigMap
metadata:
name: ingress-nginx-controller
namespace: ingress-nginx
data:
enable-modsecurity: "true"
enable-owasp-modsecurity-crs: "true"
modsecurity-snippet: |
SecRuleEngine On
SecRequestBodyLimit 10485760
SecAuditEngine RelevantOnly
SecAuditLogParts ABCFHZ
특정 경로에 대해 WAF를 비활성화하려면 Ingress Annotation을 사용한다:
annotations:
nginx.ingress.kubernetes.io/enable-modsecurity: "false"
API 업로드 엔드포인트처럼 오탐이 잦은 경로는 선별적으로 제외하되, 나머지는 WAF를 유지하는 것이 좋다. K8s 보안 전반은 Admission Webhook 심화 글도 함께 참고하자.
실전 종합 예제
위 기법들을 조합한 프로덕션 Ingress 예제다:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: production-api
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
nginx.ingress.kubernetes.io/limit-rps: "20"
nginx.ingress.kubernetes.io/limit-burst-multiplier: "3"
nginx.ingress.kubernetes.io/enable-cors: "true"
nginx.ingress.kubernetes.io/cors-allow-origin: "https://app.example.com"
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
nginx.ingress.kubernetes.io/proxy-send-timeout: "60"
nginx.ingress.kubernetes.io/enable-modsecurity: "true"
nginx.ingress.kubernetes.io/configuration-snippet: |
more_set_headers "X-Frame-Options: DENY";
more_set_headers "X-Content-Type-Options: nosniff";
spec:
ingressClassName: nginx
tls:
- hosts:
- api.example.com
secretName: api-tls-prod
rules:
- host: api.example.com
http:
paths:
- path: /api(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: api-service
port:
number: 8080
마무리
NGINX Ingress Controller는 단순 리버스 프록시가 아니다. Rate Limiting, 카나리 배포, WAF, 세션 어피니티까지 트래픽 관리의 대부분을 Annotation 하나로 해결할 수 있다. 핵심은 각 Annotation의 동작 원리를 정확히 이해하고, 서비스 특성에 맞게 조합하는 것이다.