Published on

EKS ALB Ingress 413, NGINX는 정상인데 왜?

Authors

서버 업로드(파일/JSON)가 커질 때 EKS에서 413 Request Entity Too Large가 발생하면 대부분은 “NGINX client_max_body_size 올렸는데도 왜 안 되지?”로 시작합니다. 특히 EKS + AWS Load Balancer Controller(ALB Ingress) 조합에서는 요청이 NGINX까지 도달하기 전에 다른 계층에서 잘릴 수 있어, NGINX 설정만으로는 해결되지 않습니다.

이 글은 ALB Ingress 환경에서 413이 발생하는데 NGINX 설정은 정상일 때를 전제로, “413이 어디에서 발생했는지”를 증거 기반으로 판별하고, 계층별로 해결하는 실전 체크리스트를 제공합니다.

> 참고로 502/timeout류 이슈와 진단 흐름이 비슷합니다. ALB 타깃/헬스체크/시간초과가 섞여 보인다면 EKS ALB Ingress 502 target timeout 원인·해결도 같이 보면 원인 분리가 쉬워집니다.


1) 먼저 결론: “NGINX는 정상”인데 413이 나는 대표 원인

ALB Ingress 경로에서 413은 대체로 아래 중 하나입니다.

  1. AWS WAF(또는 Managed Rules)가 요청 본문 크기/검사 제한으로 차단
  2. ALB 앞단(CloudFront/API Gateway/프록시)에서 본문 크기 제한
  3. 애플리케이션/프레임워크 레벨의 body size 제한 (예: Spring, Express, Django, FastAPI, Gunicorn 등)
  4. gRPC/HTTP2 메시지 제한이 413처럼 보이거나 502로 변형

중요 포인트는:

  • ALB 자체는 “nginx의 client_max_body_size 같은” 명시적 body size 제한 설정을 제공하지 않습니다.
  • 즉, “ALB가 413을 직접 내는 경우”보다 WAF/상위 프록시/백엔드가 413을 내고, 클라이언트는 ALB에서 온 것처럼 보이는 경우가 많습니다.

2) 413이 어디서 발생했는지 3분 안에 판별하기

2.1 응답 헤더로 1차 판별

클라이언트에서 헤더를 확인합니다.

curl -v -X POST "https://api.example.com/upload" \
  -H 'Content-Type: application/json' \
  --data-binary @big.json
  • 응답 헤더에 server: awselb/2.0가 보인다고 해서 ALB가 범인이라고 단정하면 안 됩니다.
  • 아래 단서들을 같이 봅니다.

단서 A: WAF 차단

  • 응답이 403인 경우가 흔하지만, 룰/구성에 따라 413/400처럼 보일 수도 있습니다.
  • x-amzn-RequestId, x-amz-cf-id(CloudFront가 앞단이면) 등도 함께 확인합니다.

단서 B: NGINX가 실제로 413을 냈는지

  • NGINX가 413을 내면 보통 nginx/1.xx가 보이거나, NGINX access/error log에 흔적이 남습니다.

2.2 “NGINX까지 요청이 갔는지”를 로그로 확정

NGINX Ingress(혹은 앱 파드) 로그에 요청이 찍히는지 확인합니다.

kubectl -n ingress-nginx logs deploy/ingress-nginx-controller --tail=200 | grep upload
  • 로그가 없다: 요청이 NGINX에 도달하기 전에 잘림 → ALB/WAF/상위 프록시 영역
  • 로그가 있고 413: NGINX 설정/annotation/ConfigMap 미적용 가능성
  • 로그가 있고 2xx인데 클라이언트는 413: 중간 계층(CloudFront/WAF) 또는 경로가 다른 Ingress로 라우팅

2.3 ALB 액세스 로그로 “ALB가 받은 요청 크기” 확인

ALB access log를 S3로 켜고, 해당 요청의 elb_status_code, target_status_code를 확인합니다.

  • elb_status_code=413이고 target_status_code=-타깃으로 전달 전 ALB/WAF/리스너 레벨에서 차단 가능성이 큽니다.
  • elb_status_code=413이고 target_status_code=413이면 백엔드가 413을 반환했을 확률이 큽니다.

3) 가장 흔한 원인: AWS WAF의 Body 검사 한도

EKS에서 ALB Ingress를 쓰는 팀에서 “NGINX는 정상인데 413”의 1순위는 WAF입니다.

3.1 WAF가 본문을 다 못 보고 차단하는 패턴

WAF는 요청 본문을 전부 검사하지 않고 제한된 크기까지만 검사합니다. 이때 룰이 “검사 불가/오버사이즈”를 공격으로 간주하면 차단이 발생합니다.

  • 예: SQLi/XSS Managed Rule이 큰 JSON/멀티파트 업로드에서 오탐
  • 예: OversizeHandlingMATCH로 되어 있어, 본문이 크면 무조건 룰 매치로 처리

3.2 WAF 로그로 확정

WAF Web ACL에서 Logging을 켜고, CloudWatch Logs/S3/Firehose로 떨어지는 이벤트에서:

  • terminatingRuleId
  • action (BLOCK/ALLOW)
  • ruleGroupList / managedRuleGroupList
  • oversizeFields

를 확인합니다.

3.3 해결 전략

  1. 업로드 엔드포인트는 WAF 예외(allow/skip) 처리
  • /upload, /files 같은 경로는 Managed Rule 일부를 제외
  1. OversizeHandling 조정
  • 룰에 따라 NO_MATCH로 바꾸면 “검사 못 했으니 매치로 처리”를 피할 수 있음
  1. 업로드는 프리사인드 URL(S3)로 우회
  • 대용량 업로드는 애초에 ALB/WAF/Ingress를 통과시키지 않는 것이 운영적으로 가장 안전합니다.

4) CloudFront / API Gateway가 앞단이면 여기서 잘릴 수 있음

아키텍처가 Client → CloudFront → ALB → Ingress/Service라면, 413의 범인은 CloudFront일 가능성도 있습니다.

  • CloudFront는 동작/설정(오리진 요청 정책, 캐시 정책, 헤더/쿠키 전달 등)에 따라 업로드가 깨지거나 제한을 만납니다.
  • API Gateway(REST/HTTP API) 앞단이면 payload 제한이 더 명확합니다.

4.1 판별 팁

  • 응답 헤더에 via: 1.1 ... cloudfront 또는 x-amz-cf-id가 있으면 CloudFront 경유
  • CloudFront 표준 로그에서 sc-status=413 확인

4.2 해결 전략

  • 업로드 경로는 CloudFront를 우회(별도 도메인으로 ALB 직접)
  • 또는 업로드는 S3 presigned URL로 전환

5) “NGINX 설정은 올렸는데” 실제로는 미적용인 경우

NGINX Ingress를 사용 중이라면 아래 실수가 많습니다.

5.1 annotation/ConfigMap 적용 위치가 다름

  • Ingress 리소스에 annotation을 넣어야 하는데 Service나 Deployment에 넣음
  • 여러 IngressClass가 공존하여, 내가 수정한 컨트롤러가 실제 트래픽을 받는 컨트롤러가 아님

예시(nginx ingress에서 업로드 제한 해제):

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: api
  annotations:
    nginx.ingress.kubernetes.io/proxy-body-size: "50m"
spec:
  ingressClassName: nginx
  rules:
  - host: api.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: api
            port:
              number: 80

확인:

kubectl get ingress api -o yaml | yq '.metadata.annotations'

5.2 NGINX Ingress가 아니라 “ALB Ingress”인데 NGINX만 보고 있는 상황

질문 주제처럼 ALB Ingress를 쓰는데 NGINX 설정을 만지고 있다면, NGINX는 아예 트래픽 경로에 없을 수 있습니다.

  • ingressClassName: alb 또는 annotation에 kubernetes.io/ingress.class: alb
  • AWS Load Balancer Controller가 생성한 ALB로 직접 라우팅

이 경우 NGINX 설정은 아무 영향이 없습니다.


6) 백엔드(애플리케이션)에서 413을 내는 케이스

ALB/WAF가 아니라 앱이 413을 반환하는 경우도 흔합니다. 특히 JSON 업로드/멀티파트 업로드에서 프레임워크 기본값이 낮게 잡혀 있는 경우가 많습니다.

6.1 Spring Boot 예시

  • spring.servlet.multipart.max-file-size
  • spring.servlet.multipart.max-request-size
spring:
  servlet:
    multipart:
      max-file-size: 50MB
      max-request-size: 50MB

6.2 Express(Node.js) 예시

express.json() / body-parser limit:

import express from 'express';

const app = express();
app.use(express.json({ limit: '50mb' }));
app.use(express.urlencoded({ extended: true, limit: '50mb' }));

app.post('/upload', (req, res) => {
  res.json({ ok: true });
});

app.listen(3000);

6.3 Gunicorn / Nginx behind app

앱 앞에 또 다른 프록시(사이드카, 서비스메시, 프레임워크 내장 서버)가 있으면 그 계층도 확인해야 합니다.


7) gRPC/HTTP2면 “413이 아니라 502처럼” 보일 수도

대용량 메시지에서 gRPC는 413 대신 RST_STREAM, RESOURCE_EXHAUSTED, 혹은 ALB 관점에서 502로 보이는 등 증상이 변형됩니다. gRPC를 쓰고 있다면 메시지 최대 크기 설정을 별도로 점검해야 합니다.


8) 실전 체크리스트: “ALB Ingress 413인데 NGINX는 정상”일 때 순서

8.1 트래픽 경로 확정

  1. IngressClass 확인
kubectl get ingress -A -o wide
kubectl get ingressclass
  1. 실제 엔드포인트가 ALB인지, NGINX LB인지 확인
kubectl describe ingress -n <ns> <name>

8.2 로그로 계층 확정

  • NGINX/앱 로그에 요청이 찍히는가?
  • ALB access log에서 elb_status_codetarget_status_code는?
  • WAF log에서 block 이벤트가 있는가?

8.3 해결책 적용 우선순위

  • WAF 차단이면: 룰 예외/오탐 제거/oversize handling 조정
  • 앞단 프록시(CloudFront/API GW)면: 업로드 경로 우회 또는 presigned URL
  • 앱 제한이면: 프레임워크 설정 상향
  • 정말 NGINX가 문제면: annotation/ConfigMap/IngressClass 적용 재검증

9) 권장 아키텍처: 대용량 업로드는 ALB를 통과시키지 말자

운영 관점에서 대용량 업로드를 Client → ALB → Pod로 받는 구조는 다음 비용/리스크가 커집니다.

  • WAF 오탐/검사 한도
  • 타임아웃/재시도 시 대역폭 낭비
  • 파드 스케일링/메모리 압박

대부분의 팀은 결국:

  1. Client → S3 Presigned URL로 직접 업로드
  2. 메타데이터만 API로 전달

패턴으로 안정화합니다.


마무리

ALB Ingress 환경에서 413이 발생하는데 NGINX 설정이 정상이라면, 문제를 “NGINX 설정값”에서 찾기보다 요청이 어느 계층에서 잘리는지를 먼저 확정해야 합니다.

  • NGINX/앱 로그에 요청이 없다 → WAF/CloudFront/ALB 앞단
  • ALB access log의 target_status_code가 - → 타깃 도달 전 차단
  • 앱/프레임워크 기본 제한 → 앱 설정 상향

이 순서로 좁히면, 불필요하게 NGINX만 만지다가 시간을 버리는 일을 크게 줄일 수 있습니다.

추가로 EKS에서 ALB 계층 장애(502/timeout)가 함께 보이면 EKS ALB Ingress 502 target timeout 원인·해결도 같이 체크해 보세요.