본문 바로가기
실전 개발·아키텍처/K8S

Contour HTTPProxy 전환 시 NGINX client-max-body-size, proxy-body-size

by 플로거 2026. 7. 21.

Contour HTTPProxy 전환 시 NGINX client-max-body-size 500m 대체 방법

기존 Kubernetes 환경에서 NGINX Ingress Controller를 사용하다가 Contour 기반 환경으로 이전할 때, 파일 업로드 용량 제한 설정도 함께 점검해야 합니다.

기존 Ingress에는 다음과 같은 설정이 적용되어 있을 수 있습니다.

client-max-body-size: "500m"

Ingress-NGINX에서는 일반적으로 다음 annotation을 사용합니다.

metadata:
  annotations:
    nginx.ingress.kubernetes.io/proxy-body-size: "500m"

이 설정은 클라이언트가 서버로 전송하는 HTTP Request Body의 최대 크기를 제한합니다. 주로 Multipart 파일 업로드 요청에 적용되며, 파일 다운로드 크기를 제한하는 설정은 아닙니다.

Contour의 HTTPProxy에는 NGINX의 client_max_body_size 또는 proxy-body-size와 정확히 대응하는 일반 설정 필드가 없습니다.

따라서 Contour 의존성을 최소화하면서 기존 500MB 업로드 정책을 유지하려면 다음과 같이 역할을 분리하는 것이 좋습니다.

Contour
- TLS 종료
- HTTP → HTTPS Redirect
- 모든 요청을 API Gateway로 전달

API Gateway
- 전체 Request Body 크기 제한
- 제한 초과 시 413 응답

Backend
- 파일 한 개의 최대 크기 검증
- Multipart 전체 요청 크기 검증
- 파일 확장자, MIME Type, 파일 개수 검증

1. 기존 NGINX 설정의 의미

기존 설정이 다음과 같다고 가정해 보겠습니다.

metadata:
  annotations:
    nginx.ingress.kubernetes.io/proxy-body-size: "500m"

NGINX에서는 이 설정이 내부적으로 다음과 같은 역할을 합니다.

client_max_body_size 500m;

클라이언트의 요청 본문이 설정값을 초과하면 NGINX는 Backend로 요청을 전달하지 않고 일반적으로 다음 응답을 반환합니다.

HTTP/1.1 413 Payload Too Large

중요한 점은 이 값이 파일 한 개의 크기만 의미하는 것이 아니라는 것입니다.

HTTP Request Body
├─ 업로드 파일
├─ Multipart Boundary
├─ 파일명과 Content-Type
├─ Form 데이터
└─ 기타 Multipart Header

따라서 500MB 파일 한 개를 실제로 허용하려면 전체 Request Body 제한은 500MB보다 조금 크게 설정하는 것이 안전합니다.


2. Contour HTTPProxy에는 Body Size 설정을 넣지 않는다

Contour HTTPProxy는 다음처럼 TLS와 API Gateway 연결만 담당하도록 단순하게 유지합니다.

apiVersion: projectcontour.io/v1
kind: HTTPProxy
metadata:
  name: app-proxy
  namespace: app
spec:
  ingressClassName: contour

  virtualhost:
    fqdn: app.company.com
    tls:
      secretName: app-tls

  routes:
    - conditions:
        - prefix: /
      services:
        - name: api-gateway
          port: 8080

다음과 같은 NGINX 전용 설정은 HTTPProxy에 그대로 적용할 수 없습니다.

nginx.ingress.kubernetes.io/proxy-body-size: "500m"

또한 다음과 같은 HTTPProxy 필드는 존재하지 않습니다.

# 실제로 지원되지 않는 예시
routes:
  - maxRequestBodySize: 500MB
    services:
      - name: api-gateway
        port: 8080

Contour에서는 HTTP와 HTTPS 처리, TLS 종료, 외부 요청 전달만 담당하고 파일 크기 정책은 API Gateway와 Backend에서 관리합니다.


3. Spring Cloud Gateway에 RequestSize 적용

API Gateway가 Spring Cloud Gateway라면 RequestSize 필터를 사용할 수 있습니다.

기존 NGINX의 500m을 500MiB 기준으로 변환하면 다음과 같습니다.

500 × 1024 × 1024
= 524,288,000 bytes

파일 업로드 Route에 다음과 같이 적용합니다.

spring:
  cloud:
    gateway:
      routes:
        - id: file-upload-api
          order: 0
          uri: http://backend:8080
          predicates:
            - Path=/api/files/**,/api/upload/**,/api/attachments/**
          filters:
            - name: RequestSize
              args:
                maxSize: 524288000

        - id: backend-api
          order: 10
          uri: http://backend:8080
          predicates:
            - Path=/api/**

        - id: frontend
          order: 100
          uri: http://frontend:80
          predicates:
            - Path=/**

위 설정은 다음 경로에 대해 Request Body를 최대 500MiB로 제한합니다.

/api/files/**
/api/upload/**
/api/attachments/**

요청 크기가 제한을 초과하면 API Gateway가 Backend로 요청을 전달하지 않고 413 Payload Too Large 응답을 반환합니다.


4. 단위가 포함된 설정 사용

Spring Cloud Gateway 버전에 따라 다음처럼 단위를 포함한 값을 사용할 수도 있습니다.

filters:
  - name: RequestSize
    args:
      maxSize: 500MB

다만 기존 NGINX의 500m과 정확하게 같은 바이트 기준을 적용하려면 숫자값을 직접 사용하는 것이 명확합니다.

filters:
  - name: RequestSize
    args:
      maxSize: 524288000

운영 중인 Spring Cloud Gateway 버전에서 단위 형식을 지원하는지 불확실하다면 바이트 단위 숫자를 사용하는 편이 안전합니다.


5. 파일 업로드 Route에만 적용한다

Request Size 제한을 전체 서비스에 일괄 적용하기보다 파일 업로드 API에만 적용하는 것이 좋습니다.

spring:
  cloud:
    gateway:
      routes:
        - id: file-upload-api
          order: 0
          uri: http://backend:8080
          predicates:
            - Path=/api/files/upload,/api/files/upload/**
          filters:
            - name: RequestSize
              args:
                maxSize: 524288000

        - id: backend-api
          order: 10
          uri: http://backend:8080
          predicates:
            - Path=/api/**

        - id: frontend
          order: 100
          uri: http://frontend:80
          predicates:
            - Path=/**

라우팅 우선순위는 다음과 같습니다.

order: 0
/api/files/upload/**
→ 업로드 전용 Route
→ RequestSize 적용

order: 10
/api/**
→ 일반 Backend API

order: 100
/**
→ Frontend

파일 업로드 Route가 일반 Backend Route보다 먼저 평가되도록 더 낮은 order 값을 지정합니다.


6. Backend에도 Multipart 제한을 적용한다

API Gateway에서 요청 크기를 제한하더라도 Backend에도 파일 크기 제한을 설정해야 합니다.

Backend 제한은 다음 상황을 방어합니다.

  • 클러스터 내부에서 API Gateway를 우회하는 직접 요청
  • 파일 한 개의 최대 크기 초과
  • 여러 파일을 합친 전체 요청 크기 초과
  • 파일 개수 초과
  • 허용하지 않은 확장자 또는 MIME Type 업로드

Backend가 Spring MVC 또는 Spring Boot Servlet 기반이라면 다음과 같이 설정합니다.

spring:
  servlet:
    multipart:
      max-file-size: 500MB
      max-request-size: 500MB

각 설정의 의미는 다음과 같습니다.

설정 의미
max-file-size 업로드 파일 한 개의 최대 크기
max-request-size 파일, Form 필드, Multipart Header를 포함한 전체 요청 최대 크기

7. 500MB 파일 한 개를 실제로 허용하려면

max-file-sizemax-request-size를 모두 500MB로 설정하면 Multipart 부가 데이터 때문에 정확히 500MB인 파일이 거절될 수 있습니다.

따라서 파일 한 개를 최대 500MB까지 허용하려는 목적이라면 전체 Request Body에는 여유를 두는 것이 좋습니다.

Backend 권장 설정

spring:
  servlet:
    multipart:
      max-file-size: 500MB
      max-request-size: 550MB

API Gateway 권장 설정

550MiB를 바이트로 환산하면 다음과 같습니다.

550 × 1024 × 1024
= 576,716,800 bytes
filters:
  - name: RequestSize
    args:
      maxSize: 576716800

이 구성을 적용하면 역할이 다음과 같이 분리됩니다.

API Gateway
전체 HTTP Request Body 최대 550MiB

Backend
파일 한 개 최대 500MB
Multipart 전체 요청 최대 550MB

8. 정확히 기존 NGINX 500m 정책을 유지하는 경우

기존 정책이 “파일 한 개가 최대 500MB”가 아니라 “전체 Request Body가 최대 500MiB”였던 경우에는 다음 구성을 사용할 수 있습니다.

API Gateway

spring:
  cloud:
    gateway:
      routes:
        - id: file-upload-api
          order: 0
          uri: http://backend:8080
          predicates:
            - Path=/api/files/**,/api/upload/**,/api/attachments/**
          filters:
            - name: RequestSize
              args:
                maxSize: 524288000

        - id: backend-api
          order: 10
          uri: http://backend:8080
          predicates:
            - Path=/api/**

        - id: frontend
          order: 100
          uri: http://frontend:80
          predicates:
            - Path=/**

Backend

spring:
  servlet:
    multipart:
      max-file-size: 500MB
      max-request-size: 500MB

이 설정은 전체 요청 크기가 500MiB를 초과하면 API Gateway에서 먼저 차단하는 구조입니다.


9. 여러 파일을 업로드하는 경우

한 요청에서 여러 파일을 업로드할 수 있다면 max-request-size는 파일 한 개의 제한보다 충분히 크게 설정해야 합니다.

예를 들어 500MB 파일을 최대 2개까지 허용한다고 가정해 보겠습니다.

spring:
  servlet:
    multipart:
      max-file-size: 500MB
      max-request-size: 1050MB

API Gateway의 Request Size도 Backend보다 작지 않게 설정해야 합니다.

filters:
  - name: RequestSize
    args:
      maxSize: 1101004800

1101004800은 1050MiB입니다.

하지만 1GB 이상의 요청을 API Gateway를 통해 처리하면 네트워크 연결 유지 시간과 메모리, 임시 디스크, Timeout에 대한 부담이 커질 수 있습니다.

대용량 파일 업로드가 핵심 기능이라면 Object Storage의 Presigned URL을 이용한 직접 업로드 구조도 검토하는 것이 좋습니다.


10. 다운로드 크기와는 다른 설정이다

기존 NGINX의 다음 설정은 파일 다운로드 크기를 제한하지 않습니다.

nginx.ingress.kubernetes.io/proxy-body-size: "500m"

적용 방향은 다음과 같습니다.

파일 업로드

Client
  ↓ Request Body
Contour
  ↓
API Gateway
  ↓
Backend

다운로드는 반대 방향의 Response Body입니다.

파일 다운로드

Backend
  ↓ Response Body
API Gateway
  ↓
Contour
  ↓
Client

대용량 다운로드에서는 크기 제한보다 다음 항목을 점검해야 합니다.

  • Contour 또는 Envoy 응답 Timeout
  • Idle Timeout
  • 클라우드 Load Balancer Timeout
  • Spring Cloud Gateway의 HTTP Client Timeout
  • Backend의 스트리밍 처리
  • HTTP Range 요청 지원
  • API Gateway가 전체 파일을 메모리에 적재하는지 여부

11. 업로드와 다운로드 Timeout 확인

500MB 파일은 네트워크 환경에 따라 업로드 또는 다운로드 시간이 길어질 수 있습니다.

Contour HTTPProxy에서 필요한 경우 Route에 Timeout을 설정할 수 있습니다.

apiVersion: projectcontour.io/v1
kind: HTTPProxy
metadata:
  name: app-proxy
  namespace: app
spec:
  ingressClassName: contour

  virtualhost:
    fqdn: app.company.com
    tls:
      secretName: app-tls

  routes:
    - conditions:
        - prefix: /
      timeoutPolicy:
        response: 600s
        idle: 600s
      services:
        - name: api-gateway
          port: 8080

다만 Contour 의존성을 최소화하려면 실제 파일 API에 필요한 최소 설정만 추가하고, API Gateway와 Backend의 Timeout도 함께 일치시키는 것이 중요합니다.

Cloud Load Balancer Timeout
≥ Contour Timeout
≥ API Gateway Timeout
≥ Backend 처리 시간

앞단 Timeout이 더 짧으면 Backend가 정상적으로 파일을 처리하고 있어도 중간 구간에서 연결이 종료될 수 있습니다.


12. 대용량 요청에서 피해야 할 API Gateway 필터

500MB 업로드 Route에는 요청 본문을 메모리에 복사하거나 변환하는 필터를 신중하게 사용해야 합니다.

특히 다음과 같은 필터는 대용량 Request Body에서 메모리 사용량을 크게 증가시킬 수 있습니다.

CacheRequestBody
ModifyRequestBody
Request Body 로깅 필터
전체 Body를 byte[] 또는 String으로 변환하는 Custom Filter

파일 업로드 Route에서는 요청 본문을 전체 메모리에 적재하지 않고 스트리밍 방식으로 Backend에 전달하는 것이 중요합니다.


13. 413 오류가 발생할 때 확인할 위치

설정을 변경한 후에도 413 Payload Too Large가 발생한다면 어느 계층에서 응답했는지 확인해야 합니다.

Client
  ↓
Cloud Load Balancer
  ↓
Contour / Envoy
  ↓
Spring Cloud Gateway
  ↓
Backend

다음 명령으로 응답 헤더를 확인합니다.

curl -v \
  -F "file=@large-file.zip" \
  https://app.company.com/api/files/upload

각 계층의 로그도 함께 확인합니다.

kubectl logs -n app deployment/api-gateway

kubectl logs -n app deployment/backend

kubectl logs -n projectcontour deployment/contour

kubectl logs -n projectcontour daemonset/envoy

실제 Contour와 Envoy의 namespace 및 Workload 이름은 설치 환경에 따라 다를 수 있습니다.


14. 최종 권장 설정

실제 업무 요구사항이 “파일 한 개를 최대 500MB까지 업로드”하는 것이라면 다음 구성을 권장합니다.

Contour HTTPProxy

apiVersion: projectcontour.io/v1
kind: HTTPProxy
metadata:
  name: app-proxy
  namespace: app
spec:
  ingressClassName: contour

  virtualhost:
    fqdn: app.company.com
    tls:
      secretName: app-tls

  routes:
    - conditions:
        - prefix: /
      services:
        - name: api-gateway
          port: 8080

Spring Cloud Gateway

spring:
  cloud:
    gateway:
      routes:
        - id: file-upload-api
          order: 0
          uri: http://backend:8080
          predicates:
            - Path=/api/files/**,/api/upload/**,/api/attachments/**
          filters:
            - name: RequestSize
              args:
                maxSize: 576716800

        - id: backend-api
          order: 10
          uri: http://backend:8080
          predicates:
            - Path=/api/**

        - id: frontend
          order: 100
          uri: http://frontend:80
          predicates:
            - Path=/**

Spring Boot Backend

spring:
  servlet:
    multipart:
      max-file-size: 500MB
      max-request-size: 550MB

최종 정책은 다음과 같습니다.

항목 관리 위치 권장 설정
TLS 종료 Contour virtualhost.tls
HTTP→HTTPS Redirect Contour TLS 설정에 따른 자동 Redirect
전체 요청 크기 API Gateway 550MiB
파일 한 개 크기 Backend 500MB
Multipart 전체 크기 Backend 550MB
확장자·MIME·파일 개수 Backend 업무 정책에 따라 검증
다운로드 처리 APIGW·Backend 스트리밍 및 Timeout 점검

마무리

기존 NGINX Ingress의 client-max-body-size: "500m" 또는 nginx.ingress.kubernetes.io/proxy-body-size: "500m" 설정은 Contour HTTPProxy에 그대로 옮길 수 없습니다.

Contour에는 TLS 종료와 HTTP→HTTPS Redirect, API Gateway 전달만 유지하고 Request Body 제한은 Spring Cloud Gateway와 Backend에서 관리하는 것이 좋습니다.

기존 NGINX

Ingress
└─ client_max_body_size 500m


신규 Contour

Contour
├─ TLS
├─ HTTP → HTTPS Redirect
└─ APIGW 전달

API Gateway
└─ RequestSize 550MiB

Backend
├─ max-file-size 500MB
└─ max-request-size 550MB

특히 업무 요구사항이 “전체 요청을 500MB로 제한”하는 것인지, “파일 한 개를 실제로 500MB까지 허용”하는 것인지 구분해야 합니다.

파일 한 개를 500MB까지 허용하려면 Multipart Header와 Form 데이터의 여유를 고려해 API Gateway와 Backend의 전체 Request 크기는 약 550MB로 설정하는 구성이 안전합니다.


태그

Kubernetes, K8s, 쿠버네티스, Contour, HTTPProxy, NGINXIngress, client-max-body-size, proxy-body-size, 파일업로드, 대용량파일업로드, SpringCloudGateway, RequestSize, Multipart설정, 413PayloadTooLarge, API게이트웨이, 클라우드이전, DevOps

반응형

댓글