Docker 빌드 컨텍스트가 1.06GB까지 커진 이유

Docker 빌드가 느리면 패키지 설치나 애플리케이션 컴파일부터 보게 됩니다. 저도 타입 검사 실패를 고치는 데 집중하다가 그보다 앞선 load build context 단계에서 40초가 넘게 쓰이고 있다는 사실을 뒤늦게 봤습니다. 실패 원인과는 별개였지만, 매 배포마다 반복되는 낭비였습니다.

실패 로그 옆에 있던 1.06GB

처음에는 타입 오류만 고치면 배포 문제도 끝난다고 생각했습니다. 컨텍스트 전송 로그는 빌드 앞부분에 늘 붙는 정보 정도로 넘길 뻔했습니다. 실제 기록은 이랬습니다.

transferring context: 1.06GB 41.5s done
load build context: DONE 42.6s

애플리케이션 소스가 정말 큰지 로컬 디렉터리부터 측정했습니다.

2.2G .next
983M node_modules
19M .git
9.3M src
2.0M public

실제 소스와 정적 파일은 합쳐도 12MB가 채 되지 않았습니다. 용량 대부분은 이미 만들어진 Next.js 빌드 산출물과 설치된 의존성이었습니다.

Docker는 Git의 추적 여부를 보지 않았습니다

저장소에는 .gitignore가 있었지만 .dockerignore는 없었습니다. Git이 .next와 node_modules를 무시하고 있어도 Docker 빌드 컨텍스트에는 영향을 주지 않습니다. Docker는 컨텍스트 디렉터리 아래 파일을 별도 규칙이 없으면 빌드 엔진으로 전송합니다.

멀티 스테이지 Dockerfile에서 나중에 필요한 파일만 COPY한다고 해도 컨텍스트 전송 자체가 먼저 일어납니다. 최종 이미지에 node_modules를 직접 복사하지 않는다는 사실도 이 1.06GB 전송을 막아주지 않았습니다.

빌드에 필요 없는 로컬 상태를 제외했습니다

저는 프로젝트 루트에 .dockerignore를 추가했습니다. 핵심은 재생성 가능한 디렉터리와 로컬 비밀 파일을 컨텍스트에 넣지 않는 것이었습니다.

.git
.github
.next
node_modules
build
coverage
out

*.tsbuildinfo
npm-debug.log*

.env
.env.*
**/.env
**/.env.*

패치 디렉터리처럼 의존성 설치에 실제로 필요한 파일은 제외하지 않았습니다. .dockerignore를 넓게 잡아 **/*에서 허용 목록을 만드는 방식은 새 파일이 조용히 누락될 여지가 있어 사용하지 않았습니다.

수정 뒤 같은 단계의 로그는 다음과 같이 바뀌었습니다.

transferring context: 4.83MB 1.6s done
load build context: DONE 1.7s

전송량은 1.06GB에서 4.83MB로, 컨텍스트 준비 시간은 42.6초에서 1.7초로 줄었습니다. 이 수치는 애플리케이션 컴파일 시간이 아니라 Docker가 빌드를 시작하기 위해 파일을 모으는 시간만 비교한 것입니다.

이번에는 타입 오류가 배포를 멈춰 세워서 로그를 자세히 보게 됐고, 그 옆의 별도 병목까지 찾았습니다. 이후에는 Docker 빌드가 느릴 때 설치 시간만 보지 않고 load build context의 전송량과 시간부터 확인합니다.

같은 작업에서 겪은 다른 문제:

로컬에서는 통과한 Next.js 타입 검사가 Docker 배포에서만 실패하는 이유