Published on

GitLab CI 413 Request Entity Too Large 해결법

Authors

GitLab CI를 돌리다 보면 갑자기 413 Request Entity Too Large 에러가 터질 때가 있습니다. 대부분은 “업로드하려는 요청 본문이 너무 크다”는 의미인데, 문제는 이 요청이 무엇인지(아티팩트 업로드인지, 패키지 업로드인지, LFS 푸시인지, Runner 로그인지)와 어느 프록시/컴포넌트가 제한을 걸었는지가 환경마다 다르다는 점입니다.

이 글에서는 GitLab CI에서 413이 나는 대표 경로를 분류하고, Nginx/Ingress/GitLab(Omnibus)/Runner/캐시·아티팩트 구성까지 단계적으로 해결하는 방법을 정리합니다.

또한 “일단 제한만 올리기”가 아니라, 아티팩트 크기 자체를 줄이거나 업로드 방식을 바꿔 재발 확률을 낮추는 설계도 함께 다룹니다.

관련해서 프록시 뒤에서 설정 불일치/제한으로 장애가 나는 패턴은 비슷합니다. 프록시 계층을 쓰는 환경이라면 Proxy 뒤 Nginx에서 OAuth 리다이렉트 URI 불일치 해결 글도 함께 참고하면 진단 흐름을 잡는 데 도움이 됩니다.

413이 발생하는 “지점” 먼저 특정하기

413은 대개 다음 중 한 곳에서 발생합니다.

  • GitLab 앞단 Nginx(리버스 프록시): client_max_body_size 제한
  • Kubernetes Ingress Controller(Nginx Ingress 등): annotation 또는 ConfigMap 제한
  • GitLab Omnibus 내장 Nginx: Omnibus 설정의 body size 제한
  • Cloud LB / WAF / API Gateway: provider별 request size 제한
  • GitLab 자체 업로드 제한(애플리케이션 레벨): 일부 업로드/아티팩트 정책

가장 빠른 방법은 응답 헤더/로그로 누가 413을 냈는지 보는 겁니다.

1) 브라우저/CLI에서 응답 헤더 확인

예를 들어 Job 로그나 아티팩트 업로드 단계에서 413이 났다면, 해당 요청 URL로 재현이 어렵더라도 Runner 로그나 프록시 로그에서 server 헤더를 확인할 수 있습니다.

curl -I https://gitlab.example.com

응답에 server: nginx가 찍힌다고 해서 “어느 nginx인지”까지 확정되진 않지만, 최소한 프록시 계층이 있다는 단서가 됩니다.

2) Nginx/Ingress 로그에서 413 메시지 찾기

Nginx는 보통 에러 로그에 아래 같은 문구를 남깁니다.

  • client intended to send too large body

Kubernetes Ingress라면 Ingress Controller Pod 로그에서 동일 문구가 보입니다.

kubectl -n ingress-nginx logs deploy/ingress-nginx-controller | grep -n "too large body"

3) GitLab Rails/Workhorse 로그도 확인

GitLab은 업로드 경로에서 gitlab-workhorse가 관여합니다. 프록시가 아니라 GitLab 내부 제한이라면 Workhorse/Rails 쪽에서 단서를 얻을 수 있습니다.

  • Omnibus: /var/log/gitlab/gitlab-workhorse/current
  • Omnibus: /var/log/gitlab/gitlab-rails/production.log

가장 흔한 원인: Nginx client_max_body_size

GitLab CI에서 413이 가장 자주 발생하는 상황은 다음입니다.

  • Job이 큰 아티팩트(예: dist/ 전체, node_modules/, 빌드 산출물 압축)를 업로드
  • Job이 패키지 레지스트리로 큰 파일을 업로드
  • Git LFS를 사용해 큰 바이너리를 푸시

이때 GitLab 앞단 Nginx가 client_max_body_size로 요청을 잘라버리면 413이 납니다.

리버스 프록시 Nginx 설정 예시

http 블록 또는 해당 server 블록에 설정합니다.

server {
  listen 443 ssl;
  server_name gitlab.example.com;

  client_max_body_size 2g;

  location / {
    proxy_pass http://gitlab_upstream;
    proxy_read_timeout 3600;
    proxy_connect_timeout 3600;
    proxy_send_timeout 3600;
  }
}

적용 후 재시작:

sudo nginx -t && sudo systemctl reload nginx

주의할 점

  • 단순히 413만 해결하려다 proxy_read_timeout이 짧아 504/499로 바뀌는 경우가 흔합니다. 대용량 업로드/압축/스캔이 있다면 타임아웃도 함께 점검하세요.
  • client_max_body_size를 무작정 올리면, WAF나 LB 제한에 걸릴 수 있습니다. “가장 바깥 계층”부터 제한을 맞춰야 합니다.

Kubernetes Ingress에서의 413 해결

GitLab을 쿠버네티스에서 운영하거나, GitLab 앞단에 Nginx Ingress가 있다면 annotation으로 해결하는 경우가 많습니다.

Nginx Ingress annotation

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: gitlab
  annotations:
    nginx.ingress.kubernetes.io/proxy-body-size: "2g"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
spec:
  rules:
    - host: gitlab.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: gitlab-webservice
                port:
                  number: 8080

적용:

kubectl apply -f ingress.yaml

ConfigMap으로 전역 설정하는 경우

Ingress Controller의 ConfigMap에서 proxy-body-size를 전역으로 올릴 수도 있습니다. 다만 다른 서비스까지 영향이 갈 수 있어, 가능하면 먼저 Ingress 단위로 적용하는 편이 안전합니다.

GitLab Omnibus 내장 Nginx를 쓰는 경우

외부 프록시 없이 Omnibus GitLab 자체 Nginx를 사용한다면 gitlab.rb에서 설정합니다.

# /etc/gitlab/gitlab.rb
nginx['client_max_body_size'] = '2048m'

적용:

sudo gitlab-ctl reconfigure
sudo gitlab-ctl restart

환경에 따라 외부 Nginx와 Omnibus Nginx를 둘 다 쓰는 구성도 있습니다. 이 경우 “바깥 Nginx”가 낮으면 여전히 413이 납니다. 즉, 업로드 경로의 모든 홉에서 제한이 일관되어야 합니다.

GitLab CI에서 특히 자주 터지는 케이스 4가지

1) 아티팩트 업로드가 너무 큰 경우

Job 마지막에 Uploading artifacts...가 뜬 뒤 413이 나면 거의 이 케이스입니다.

해결 A: 아티팩트를 줄이는 게 1순위

  • node_modules/, .venv/, target/ 같은 디렉터리를 아티팩트로 올리지 않기
  • 산출물만 선별해서 업로드
  • 압축률이 좋은 포맷 사용(단, CPU 비용 증가)

예시 .gitlab-ci.yml:

build:
  stage: build
  script:
    - npm ci
    - npm run build
  artifacts:
    expire_in: 7 days
    paths:
      - dist/
    exclude:
      - dist/**/*.map
      - dist/cache/

exclude를 적극적으로 써서 용량을 줄이는 게 효과적입니다.

해결 B: 캐시와 아티팩트를 구분

캐시는 “재사용을 위한 의존성”이고, 아티팩트는 “다음 스테이지로 넘길 산출물”입니다. 둘을 섞으면 아티팩트가 비대해집니다.

cache:
  key: ${CI_COMMIT_REF_SLUG}
  paths:
    - .npm/

build:
  script:
    - npm ci --cache .npm --prefer-offline
    - npm run build
  artifacts:
    paths:
      - dist/

이렇게 하면 의존성은 캐시로, 산출물은 아티팩트로 분리됩니다.

2) Docker 이미지 레이어/컨텍스트가 큰 경우

Docker build에서 413이 나는 경우는 보통 GitLab이 아니라 레지스트리 업로드(또는 프록시)에서 제한에 걸립니다. 하지만 CI 관점에서는 다음이 핵심입니다.

  • Docker build context가 너무 큼(불필요 파일 포함)
  • .dockerignore 미구성

.dockerignore 예시:

.git
node_modules
dist
coverage
*.log
.env

컨텍스트를 줄이면 레이어도 줄고, 업로드/푸시 실패(413 포함) 확률이 크게 내려갑니다.

3) Git LFS 푸시/풀에서 413

대용량 바이너리를 LFS로 다루는 경우, LFS 엔드포인트 업로드에서 413이 발생할 수 있습니다. 이때도 결국은 프록시의 body size 제한인 경우가 많습니다.

  • 앞단 Nginx/Ingress의 제한 상향
  • LFS 객체를 무작정 GitLab에 쌓기보다, 조직 정책에 따라 별도 오브젝트 스토리지도 검토

4) 패키지 레지스트리 업로드에서 413

예: npm/pypi/maven 패키지를 GitLab Package Registry에 publish할 때 tarball이 크면 413이 납니다.

  • 프록시 제한 상향
  • 패키지에 불필요 파일이 포함되지 않도록 ignore 설정 적용

예: npm의 경우 .npmignore 또는 files 필드로 포함 파일을 제한합니다.

“제한 상향” 외에 꼭 해야 하는 운영 체크리스트

413을 없애려고 상한만 올리면, 결국 저장소/스토리지 비용과 백업 시간이 폭증합니다. 아래를 같이 점검하는 편이 장기적으로 안전합니다.

아티팩트 보관 정책

  • expire_in을 반드시 지정
  • 브랜치/태그별로 보관 기간 차등
artifacts:
  expire_in: 3 days

로그/산출물 과다 업로드 방지

  • 테스트 리포트/커버리지 파일이 과도하게 커지는지 확인
  • 바이너리 덤프/코어 덤프가 아티팩트에 섞이지 않는지 확인

프록시 계층별 제한 일치

  • Cloud LB의 최대 요청 크기 제한
  • WAF의 body inspection 제한
  • Ingress 제한
  • GitLab 내장 Nginx 제한

한 곳이라도 낮으면 413이 재발합니다.

트러블슈팅 시나리오: 어디를 올려야 할지 헷갈릴 때

다음 순서로 접근하면 빠릅니다.

  1. Runner 로그에서 413이 난 시점의 URL/단계 확인
  2. 가장 바깥(사용자 요청이 처음 닿는) 계층부터 로그 확인
  3. client intended to send too large body가 찍히는 컴포넌트를 찾기
  4. 해당 계층의 body size와 timeout을 함께 조정
  5. 아티팩트/캐시 전략으로 업로드 크기 자체를 줄이기

프록시 체인이 길수록 “겉보기 증상은 413인데 원인은 프록시 설정 불일치”가 많습니다. 이런 류의 문제는 원리 자체가 비슷하니, 프록시 환경에서의 진단 관점은 Proxy 뒤 Nginx에서 OAuth 리다이렉트 URI 불일치 해결도 같이 읽어두면 재사용할 수 있습니다.

재발 방지: CI 산출물 설계를 바꾸는 팁

  • 아티팩트는 “다음 스테이지에 필요한 최소”만 남긴다
  • 의존성은 캐시로 돌리고, 캐시 키 전략을 명확히 한다
  • Docker는 .dockerignore로 컨텍스트를 줄인다
  • 대용량 파일은 Git LFS/오브젝트 스토리지 등 목적에 맞는 저장소를 사용한다
  • 보관 기간(expire_in)을 강제해 스토리지 폭증을 막는다

마무리

413 Request Entity Too Large는 단순히 값을 키우면 끝나는 에러처럼 보이지만, 실제로는 “업로드 경로의 어느 계층이 제한을 걸었는지”를 찾는 게 핵심입니다. Nginx/Ingress/Omnibus/LB 제한을 일관되게 맞추고, 동시에 아티팩트·캐시·Docker 컨텍스트를 정리하면 해결과 재발 방지를 함께 달성할 수 있습니다.

만약 조정 후에도 413이 계속된다면, 같은 요청이 실제로는 다른 엔드포인트(예: 패키지 레지스트리, LFS, 아티팩트)로 가고 있는지, 그리고 가장 바깥쪽 프록시/LB가 별도의 상한을 가지고 있는지부터 다시 확인해보세요.