- Published on
ingress-nginx 프로덕션 튜닝 — 성능, 타임아웃, 커넥션, keepalive
- Authors

- Name
- Youngju Kim
- @fjvbn20031
- 들어가며
- worker와 커넥션 튜닝
- upstream keepalive
- 타임아웃 3종
- proxy buffering
- gzip과 brotli 압축
- 대용량 업로드
- rate limiting 어노테이션
- HPA와 컨트롤러 스케일
- graceful reload와 drain
- 메트릭 기반 용량 산정
- 세션 어피니티와 부하 분산 알고리즘
- 커넥션 처리와 client 측 keepalive
- 부하 시나리오와 함정
- WebSocket과 gRPC 장수명 연결
- 전역 설정과 인그레스 설정의 우선순위
- 워밍업과 스케일 아웃 안정화
- Gateway API와의 관계
- 타임아웃 예산과 종단 간 일관성
- 실전 튜닝 적용 절차
- 자주 쓰는 ConfigMap 옵션 요약
- 핵심 요약
- 마치며
- 참고 자료
들어가며
ingress-nginx를 설치하고 인그레스 한두 개를 띄우는 것은 어렵지 않습니다. 진짜 어려움은 트래픽이 늘어나면서 시작됩니다. 평소에는 멀쩡하던 인그레스가 피크 시간대에 간헐적으로 502를 뱉고, 대용량 업로드는 413으로 막히며, 백엔드가 느린 날에는 504가 쏟아집니다. 그리고 배포 때마다 reload가 일어나면서 레이턴시가 튀기도 합니다.
이 글은 ingress-nginx를 프로덕션에서 안정적으로 굴리기 위한 튜닝 노하우를 정리합니다. worker와 커넥션부터 keepalive, 타임아웃, 버퍼링, 압축, rate limiting, 스케일링, graceful reload, 그리고 502/504 디버깅 플로우까지 실무에서 바로 적용할 수 있는 형태로 다룹니다. 단, 모든 설정의 정답은 트래픽 특성에 따라 달라지므로, 수치는 출발점으로만 받아들이고 메트릭으로 검증하세요.
worker와 커넥션 튜닝
nginx의 처리 능력은 결국 worker 프로세스 수와 각 worker가 다룰 수 있는 커넥션 수의 곱으로 결정됩니다. ingress-nginx에서는 이를 ConfigMap으로 제어합니다.
apiVersion: v1
kind: ConfigMap
metadata:
name: ingress-nginx-controller
namespace: ingress-nginx
data:
worker-processes: "auto"
max-worker-connections: "16384"
max-worker-open-files: "65536"
- worker-processes를 auto로 두면 컨테이너에 할당된 CPU 수에 맞춰집니다. CPU limit이 명확하다면 그 값과 정합을 맞추세요.
- max-worker-connections는 worker 하나가 동시에 열 수 있는 커넥션입니다. 클라이언트와 upstream 양쪽을 모두 세므로 넉넉히 잡되, 파일 디스크립터 한계(max-worker-open-files)와 함께 올려야 합니다.
worker가 CPU보다 과하게 많으면 컨텍스트 스위칭 오버헤드가, 너무 적으면 처리량이 병목입니다. 일반적으로 CPU 코어 수에 1:1로 맞추는 auto가 무난합니다.
upstream keepalive
성능 튜닝에서 가장 효과가 큰 항목 중 하나가 upstream keepalive입니다. 기본 설정에서는 백엔드로 가는 요청마다 TCP 연결을 새로 맺을 수 있는데, 이는 TLS 핸드셰이크와 TIME_WAIT 소켓을 양산합니다.
data:
upstream-keepalive-connections: "320"
upstream-keepalive-requests: "10000"
upstream-keepalive-timeout: "60"
upstream-keepalive-connections는 각 worker가 백엔드와 유지하는 idle 커넥션 풀 크기입니다. 이를 켜면 커넥션 재사용으로 레이턴시와 CPU가 함께 줄어듭니다. 단, 백엔드(특히 keep-alive를 짧게 끊는 애플리케이션 서버)와 timeout이 어긋나면 오히려 race로 인한 502가 발생할 수 있으니, 백엔드 keepalive보다 짧게 설정하는 것이 안전합니다.
타임아웃 3종
타임아웃은 504와 502를 가르는 핵심입니다. 가장 자주 다루는 세 가지를 명확히 구분합시다.
| 항목 | 의미 | 어노테이션 |
|---|---|---|
| connect timeout | 백엔드 TCP 연결 수립 대기 | proxy-connect-timeout |
| send timeout | 요청을 백엔드로 보내는 동안 대기 | proxy-send-timeout |
| read timeout | 백엔드 응답을 읽는 동안 대기 | proxy-read-timeout |
metadata:
annotations:
nginx.ingress.kubernetes.io/proxy-connect-timeout: "5"
nginx.ingress.kubernetes.io/proxy-send-timeout: "60"
nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
connect timeout이 길면 백엔드 다운 시 장애 감지가 느려집니다. 보통 짧게(수 초) 둡니다. read timeout은 느린 백엔드 작업(리포트 생성 등)을 고려해 충분히 두되, 무한정 늘리면 worker 커넥션이 고갈됩니다. 504가 자주 보인다면 read timeout과 실제 백엔드 처리 시간을 함께 비교하세요.
proxy buffering
버퍼링은 느린 클라이언트로부터 백엔드를 보호하는 메커니즘입니다. nginx가 백엔드 응답을 버퍼에 받아두면, 백엔드는 빠르게 응답을 마치고 다음 요청을 처리할 수 있습니다.
data:
proxy-buffering: "on"
proxy-buffer-size: "8k"
proxy-buffers-number: "4"
다만 스트리밍(SSE, 긴 다운로드, gRPC 스트림)에서는 버퍼링이 오히려 응답을 지연시킵니다. 이런 경로에는 인그레스 단위로 버퍼링을 끄세요.
metadata:
annotations:
nginx.ingress.kubernetes.io/proxy-buffering: "off"
gzip과 brotli 압축
텍스트 응답(JSON, HTML, JS)은 압축으로 대역폭과 체감 속도를 크게 개선할 수 있습니다.
data:
use-gzip: "true"
gzip-level: "5"
gzip-types: "application/json application/javascript text/css text/plain"
압축 레벨은 CPU와 압축률의 트레이드오프입니다. 보통 4~6 수준이 적절합니다. brotli는 빌드에 따라 모듈이 포함되어 있어야 하며, 정적 자산에서 gzip보다 더 나은 압축률을 보입니다. 이미 압축된 콘텐츠(이미지, 비디오)는 압축 대상에서 제외하세요.
대용량 업로드
기본값으로는 1MB가 넘는 본문이 413으로 막힙니다. 파일 업로드 경로에는 본문 크기와 함께 버퍼/타임아웃을 조정해야 합니다.
metadata:
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "200m"
nginx.ingress.kubernetes.io/proxy-request-buffering: "off"
nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
proxy-request-buffering을 off로 두면 nginx가 요청 본문을 전부 받기 전에 백엔드로 스트리밍하므로, 대용량 업로드에서 디스크/메모리 사용을 줄일 수 있습니다. 단, 백엔드가 스트리밍 본문을 처리할 수 있어야 합니다.
rate limiting 어노테이션
ingress-nginx는 인그레스 단위 rate limiting을 어노테이션으로 제공합니다.
metadata:
annotations:
nginx.ingress.kubernetes.io/limit-rps: "20"
nginx.ingress.kubernetes.io/limit-burst-multiplier: "3"
nginx.ingress.kubernetes.io/limit-connections: "10"
limit-rps는 클라이언트 IP당 초당 요청 수, limit-connections는 동시 연결 수입니다. burst multiplier로 순간 폭주를 일부 허용합니다. 다만 이는 컨트롤러 파드 단위로 카운팅되므로, 컨트롤러가 여러 개라면 정밀한 전역 제한은 어렵습니다. 엄밀한 전역 rate limit이 필요하면 API Gateway 계층을 별도로 두는 것이 낫습니다.
HPA와 컨트롤러 스케일
ingress-nginx 컨트롤러 자체도 트래픽에 맞춰 스케일해야 합니다. 컨트롤러는 보통 Deployment 또는 DaemonSet으로 배포하며, Deployment라면 HPA를 붙일 수 있습니다.
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: ingress-nginx-controller
namespace: ingress-nginx
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: ingress-nginx-controller
minReplicas: 3
maxReplicas: 12
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60
컨트롤러는 단일 장애점이 되어서는 안 되므로 minReplicas를 2 이상(가급적 3)으로 두고, 노드 분산을 위한 topologySpreadConstraints나 anti-affinity를 함께 설정하세요. 스케일 아웃 시 새 파드가 워밍업되는 동안 일시적 reload 비용이 있다는 점도 고려해야 합니다.
graceful reload와 drain
배포나 설정 변경 시 reload는 피할 수 없습니다. 핵심은 reload와 파드 종료가 진행 중인 연결을 끊지 않도록 하는 것입니다.
data:
worker-shutdown-timeout: "240s"
# 파드 spec
terminationGracePeriodSeconds: 300
lifecycle:
preStop:
exec:
command: ["/wait-shutdown"]
worker-shutdown-timeout은 reload 후 기존 worker가 진행 중인 요청을 끝낼 때까지 기다리는 시간입니다. preStop 훅과 충분한 terminationGracePeriod로 파드가 종료될 때 in-flight 요청을 드레인하도록 합니다. readiness probe를 통해 종료 중인 파드를 로드밸런서 풀에서 먼저 빼는 것도 중요합니다.
메트릭 기반 용량 산정
튜닝은 추측이 아니라 메트릭으로 해야 합니다. ingress-nginx는 Prometheus 메트릭을 노출합니다.
data:
enable-metrics: "true"
주요 관찰 지표는 다음과 같습니다.
| 지표 | 의미 | 활용 |
|---|---|---|
| nginx_ingress_controller_requests | 요청 수(상태코드별) | 트래픽/에러율 |
| request_duration_seconds | 요청 처리 시간 분포 | 레이턴시 p50/p95/p99 |
| nginx_ingress_controller_nginx_process_connections | 활성 커넥션 | 커넥션 한계 점검 |
| nginx_ingress_controller_config_last_reload_successful | reload 성공 여부 | reload 실패 감지 |
p99 레이턴시와 활성 커넥션 추이를 보며 worker-connections와 keepalive 풀을 조정하고, 에러율 급증 시점과 reload 시점을 겹쳐 보면 reload가 원인인지 백엔드가 원인인지 분리할 수 있습니다.
세션 어피니티와 부하 분산 알고리즘
상태를 가지는 애플리케이션이나 캐시 효율을 위해 같은 클라이언트를 같은 백엔드로 보내야 할 때가 있습니다. ingress-nginx는 쿠키 기반 세션 어피니티를 제공합니다.
metadata:
annotations:
nginx.ingress.kubernetes.io/affinity: "cookie"
nginx.ingress.kubernetes.io/affinity-mode: "persistent"
nginx.ingress.kubernetes.io/session-cookie-name: "route"
nginx.ingress.kubernetes.io/session-cookie-max-age: "3600"
다만 세션 어피니티는 부하 분산의 균형을 깨뜨릴 수 있습니다. 특정 백엔드에 트래픽이 쏠리면 스케일링의 의미가 줄어들므로, 정말 필요한 경로에만 적용하고 가능하면 애플리케이션을 무상태로 설계하는 것이 좋습니다.
부하 분산 알고리즘도 전역 ConfigMap으로 조정할 수 있습니다.
data:
load-balance: "ewma"
기본은 round_robin이지만, ewma(Exponentially Weighted Moving Average)는 각 백엔드의 최근 응답 시간을 가중치로 삼아 더 빠른 백엔드로 트래픽을 보냅니다. 백엔드 응답 시간 편차가 큰 환경에서 p99 레이턴시를 개선하는 경우가 많습니다.
커넥션 처리와 client 측 keepalive
지금까지 upstream(백엔드) 방향 keepalive를 다뤘다면, 클라이언트 방향 keepalive도 중요합니다. 클라이언트와의 연결을 적절히 유지하면 TLS 핸드셰이크 비용을 줄일 수 있습니다.
data:
keep-alive: "75"
keep-alive-requests: "1000"
keep-alive는 클라이언트 연결을 유지하는 시간(초), keep-alive-requests는 한 연결에서 처리할 최대 요청 수입니다. CDN이나 로드밸런서가 앞단에 있다면 그쪽의 idle timeout과 정합을 맞춰야 합니다. 앞단 LB의 idle timeout이 ingress의 keep-alive보다 길면, 이미 닫힌 연결을 LB가 재사용하려다 502가 발생할 수 있으니, ingress 쪽을 더 길게 두는 것이 안전합니다.
부하 시나리오와 함정
가장 흔한 함정은 reload 폭주입니다. ConfigMap, Secret, 또는 인그레스가 빈번하게 바뀌면 nginx가 계속 reload되고, reload 때마다 worker가 새로 뜨면서 메모리와 커넥션이 출렁입니다. cert-manager가 인증서를 자주 갱신하거나, 자동화가 인그레스를 반복 수정하는 경우 특히 위험합니다. 변경을 배치하고 빈도를 낮추세요.
[ 502 vs 504 원인 플로우차트 ]
요청 실패
│
├─ 504 Gateway Timeout?
│ │
│ ├─ 백엔드 응답이 느림 ──▶ proxy-read-timeout 확인 + 백엔드 처리시간
│ └─ 백엔드 연결 지연 ──▶ proxy-connect-timeout + 백엔드 헬스
│
└─ 502 Bad Gateway?
│
├─ upstream keepalive race ──▶ keepalive-timeout을 백엔드보다 짧게
├─ 백엔드가 연결을 일찍 끊음 ──▶ 백엔드 keep-alive 설정
├─ 백엔드 OOM/크래시 ──▶ 파드 로그/재시작 확인
└─ reload 중 일시적 ──▶ worker-shutdown-timeout/그레이스 조정
504는 거의 항상 "느림"의 문제(타임아웃 또는 느린 백엔드)이고, 502는 "끊김"의 문제(연결이 부적절하게 종료됨)입니다. 이 둘을 먼저 구분하면 원인을 훨씬 빠르게 좁힐 수 있습니다.
WebSocket과 gRPC 장수명 연결
WebSocket과 gRPC 스트리밍은 일반 HTTP 요청과 다른 튜닝이 필요합니다. 연결이 길게 유지되므로 read/send 타임아웃이 짧으면 정상 연결이 끊깁니다.
metadata:
annotations:
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
nginx.ingress.kubernetes.io/backend-protocol: "GRPC"
WebSocket은 ingress-nginx가 Upgrade 헤더를 자동으로 처리하므로 별도 설정이 적지만, 타임아웃은 충분히 늘려야 합니다. gRPC는 backend-protocol을 GRPC로 지정해야 HTTP/2 기반 프록시가 동작합니다. 장수명 연결이 많은 서비스는 worker-connections 한계에 빨리 도달할 수 있으므로, 커넥션 메트릭을 평소보다 더 주의 깊게 관찰해야 합니다.
전역 설정과 인그레스 설정의 우선순위
같은 옵션을 ConfigMap(전역)과 어노테이션(인그레스)에서 모두 지정하면 어느 쪽이 이길까요? 일반적으로 인그레스 어노테이션이 해당 인그레스에 한해 전역값을 덮어씁니다. 이는 전역 기본값을 보수적으로 두고, 특정 경로만 예외 처리하는 패턴을 가능하게 합니다.
| 계층 | 적용 범위 | 우선순위 |
|---|---|---|
| ConfigMap | 컨트롤러 전체 | 낮음(기본값) |
| 어노테이션 | 개별 인그레스 | 높음(덮어씀) |
예를 들어 proxy-body-size를 전역 ConfigMap에서 10m으로 두고, 업로드 경로 인그레스에서만 어노테이션으로 200m을 지정하는 식입니다. 이렇게 하면 대다수 경로는 보수적인 한계로 보호하면서, 필요한 경로만 완화할 수 있습니다. 단, snippet 비활성 정책이나 risk-level 같은 보안 관련 전역 설정은 어노테이션으로 무력화되지 않도록 설계되어 있다는 점을 기억하세요.
워밍업과 스케일 아웃 안정화
스케일 아웃 직후 새 컨트롤러 파드는 캐시가 비어 있고 upstream keepalive 풀도 비어 있어 일시적으로 레이턴시가 높습니다. readiness probe가 너무 일찍 통과하면, 준비되지 않은 파드로 트래픽이 흘러 502가 튈 수 있습니다.
# 파드 spec
readinessProbe:
httpGet:
path: /healthz
port: 10254
initialDelaySeconds: 10
periodSeconds: 5
successThreshold: 1
failureThreshold: 3
새 파드가 충분히 워밍업된 뒤에 트래픽을 받도록 initialDelaySeconds를 적절히 두고, HPA의 스케일 아웃 속도를 너무 공격적으로 잡지 않는 것이 안정적입니다. 트래픽 급증이 예측 가능하다면(예: 이벤트, 세일) 사전 스케일링(prewarming)을 고려하세요.
Gateway API와의 관계
성능 튜닝 관점에서도 2026년의 흐름을 염두에 둘 필요가 있습니다. 여기서 다룬 worker/커넥션/keepalive/타임아웃 개념 자체는 nginx 데이터 플레인의 본질이므로, Gateway API 구현체(특히 NGINX Gateway Fabric처럼 nginx를 데이터 플레인으로 쓰는 경우)에서도 대부분 그대로 적용됩니다. 다만 표현 방식은 어노테이션이 아니라 표준 CRD나 정책 리소스로 바뀝니다. Ingress API가 동결된 만큼, 새 튜닝 정책은 가능하면 Gateway API의 정책 모델로 설계해 두는 것이 장기적으로 유리합니다.
타임아웃 예산과 종단 간 일관성
타임아웃은 단일 컴포넌트가 아니라 경로 전체에서 일관돼야 합니다. 클라이언트 → LB → ingress → 백엔드 → DB로 이어지는 체인에서 각 단계의 타임아웃이 어긋나면, 한쪽은 이미 포기했는데 다른 쪽은 계속 기다리는 낭비가 생깁니다.
[ 종단 간 타임아웃 예산 예시 ]
클라이언트 LB ingress 백엔드 DB
30s ──▶ 28s ──▶ 25s ──▶ 20s ──▶ 10s
(가장 김) (가장 짧음)
원칙은 바깥쪽일수록 길고, 안쪽일수록 짧게 두는 것입니다. 안쪽(DB) 타임아웃이 바깥쪽(ingress)보다 길면, ingress가 504로 끊은 뒤에도 백엔드는 DB 응답을 계속 기다리며 커넥션과 자원을 점유합니다. 이는 부하가 몰릴 때 자원 고갈을 가속합니다. 반대로 안쪽을 짧게 두면, 느린 작업은 빠르게 실패하고 재시도 정책이 깔끔하게 동작합니다.
ingress의 proxy-read-timeout은 이 예산에서 백엔드 처리 시간보다 약간 길게 잡되, LB의 idle timeout보다는 짧게 두어 "누가 먼저 끊는가"를 예측 가능하게 만드는 것이 핵심입니다.
예를 들어 위 예산을 인그레스에 반영하면 다음과 같습니다.
metadata:
annotations:
nginx.ingress.kubernetes.io/proxy-connect-timeout: "3"
nginx.ingress.kubernetes.io/proxy-read-timeout: "25"
nginx.ingress.kubernetes.io/proxy-send-timeout: "25"
이렇게 단계별 예산을 명시적으로 문서화하고 각 컴포넌트에 반영하면, 장애 시 "어디서 끊겼는가"를 추정하기가 훨씬 쉬워집니다. 타임아웃은 숫자 하나가 아니라 경로 전체의 계약이라고 생각하세요.
실전 튜닝 적용 절차
지금까지 다룬 항목을 무작정 한 번에 적용하면, 어떤 변경이 어떤 효과를 냈는지 알 수 없습니다. 다음 절차로 단계적으로 적용하길 권합니다.
[ 튜닝 적용 워크플로 ]
1. 베이스라인 측정 ── 현재 p50/p95/p99, 에러율, 커넥션, reload 빈도 기록
2. 가설 수립 ────── 어떤 지표를 어떤 변경으로 개선할지 명확히
3. 단일 변경 ────── 한 번에 하나의 옵션만 변경(스테이징 우선)
4. 부하 테스트 ──── 실제 트래픽 패턴을 모사한 부하 인가
5. 비교/판단 ────── 베이스라인 대비 개선/회귀 확인
6. 롤아웃/롤백 ──── 개선이면 프로덕션 카나리, 회귀면 즉시 롤백
가장 흔한 실수는 여러 옵션을 동시에 바꾸는 것입니다. keepalive, 타임아웃, 버퍼를 한꺼번에 조정하면 p99가 좋아져도 원인을 특정할 수 없고, 나중에 회귀가 생겨도 어디서 비롯됐는지 추적하기 어렵습니다. 한 번에 하나씩, 메트릭으로 검증하는 규율이 결국 가장 빠른 길입니다.
부하 테스트 도구로는 k6, vegeta, wrk 등이 흔히 쓰입니다. 단순 RPS뿐 아니라 동시 연결 수, 본문 크기, 장수명 연결 비율 등 실제 서비스의 트래픽 형태를 모사해야 의미 있는 결과를 얻을 수 있습니다.
자주 쓰는 ConfigMap 옵션 요약
마지막으로 프로덕션에서 자주 조정하는 전역 옵션을 한 테이블로 정리합니다.
| 옵션 | 역할 | 출발점 |
|---|---|---|
| worker-processes | worker 수 | auto |
| max-worker-connections | worker당 커넥션 | 16384 |
| upstream-keepalive-connections | 백엔드 idle 풀 | 320 |
| keep-alive | 클라이언트 keepalive 초 | 75 |
| proxy-body-size | 본문 크기 한계 | 용도별 |
| use-gzip | gzip 압축 | true |
| load-balance | 분산 알고리즘 | round_robin 또는 ewma |
| worker-shutdown-timeout | graceful 종료 대기 | 240s |
출발점일 뿐 정답은 아닙니다. 각 환경의 메트릭으로 반드시 검증하세요.
핵심 요약
튜닝에서 기억할 원칙을 정리합니다.
- 추측하지 말고 베이스라인을 측정한 뒤, 한 번에 하나씩 바꾸고 메트릭으로 검증하라.
- worker-processes(auto)와 worker-connections로 용량의 상한을 잡아라.
- upstream keepalive는 효과가 크지만, 백엔드 keepalive보다 짧게 두어 502 race를 피하라.
- connect는 짧게, read/send는 작업 특성에 맞게. 단 LB보다 짧게 두어 끊김 주체를 예측 가능하게 하라.
- 스트리밍/업로드 경로는 버퍼링을 끄고, 텍스트 응답은 압축하라.
- reload 폭주가 가장 흔한 함정이다. 변경을 배치하고 GitOps로 빈도를 낮춰라.
- 504는 "느림", 502는 "끊김". 이 구분이 디버깅 속도를 좌우한다.
이 원칙들은 nginx 데이터 플레인의 본질이므로, Gateway API로 넘어가도 그대로 통용됩니다.
마치며
ingress-nginx 튜닝의 핵심은 데이터 플레인의 동작 원리를 이해하고, 추측 대신 메트릭으로 검증하는 것입니다. worker와 커넥션으로 용량을 잡고, upstream keepalive로 효율을 끌어올리며, 세 가지 타임아웃으로 장애를 빠르게 감지하고, 버퍼링과 압축으로 자원을 아끼고, graceful reload로 무중단을 지킵니다. 그리고 502와 504를 구분하는 플로우만 익혀두어도 장애 대응 속도가 크게 달라집니다. 이 모든 감각은 Gateway API 시대로 넘어가도 그대로 자산이 됩니다.
참고 자료
- ingress-nginx ConfigMap 옵션: https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/
- ingress-nginx 어노테이션: https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/
- ingress-nginx 메트릭/모니터링: https://kubernetes.github.io/ingress-nginx/user-guide/monitoring/
- Kubernetes HPA: https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/
- Kubernetes Ingress 개념: https://kubernetes.io/docs/concepts/services-networking/ingress/
- Gateway API: https://gateway-api.sigs.k8s.io/
- NGINX Gateway Fabric: https://docs.nginx.com/nginx-gateway-fabric/
- HAProxy Kubernetes Ingress: https://www.haproxy.com/documentation/kubernetes-ingress/
- Traefik 문서: https://doc.traefik.io/traefik/
- Envoy/Contour: https://projectcontour.io/