[IT-방법] 컴포즈 build 옵션 성능 최적화 – 자원 사용량을 줄이는 설정 튜닝법

build 옵션과 빌드 컨텍스트를 설명하는 성능 최적화 대표 이미지

빌드 속도가 곧 비용입니다: 왜 지금 최적화가 필요한가요?

CI/CD 파이프라인을 실행했는데 빌드 단계에서만 10분이 넘게 소요되어 당황했던 경험이 있으신가요? 혹은 개발 환경에서는 멀쩡하던 빌드가 운영 서버로 넘어가는 순간 갑자기 느려지며 CPU 점유율이 치솟는 현상을 목격하셨나요? 이러한 문제는 단순히 기다림의 문제를 넘어, 불필요한 클라우드 컴퓨팅 자원 낭비와 직결됩니다.

도커 컴포즈(Docker Compose)를 사용하는 환경에서 빌드 속도가 저하되는 가장 흔한 원인은 빌드 옵션 설정 오류와 잘못된 빌드 컨텍스트(Build Context) 구성에 있어요. 컨테이너를 생성하기 위해 매번 수 기가바이트(GB)에 달하는 불필요한 파일을 데몬으로 전송하고 있다면, 그것은 마치 가벼운 편지를 보내기 위해 거대한 화물차를 사용하는 것과 같습니다.

인프라 담당자나 데브옵스 엔지니어에게 빌드 최적화는 단순한 기술적 욕심이 아니에요. 이는 배포 주기를 앞당기고, CI/CD 도구의 사용 비용을 줄이며, 결과적으로 서비스의 안정성을 높이는 전략적인 인프라 운영의 핵심입니다. 오늘 이 글에서는 컴포즈 build 옵션을 어떻게 튜닝해야 자원 사용량을 극적으로 줄일 수 있는지 그 실전 방법을 다룰 예정이에요.

이 글을 끝까지 읽고 나면 다음과 같은 내용을 확실히 얻어가실 수 있어요.

  • 빌드 성능을 갉아먹는 주요 병목 지점 파악법
  • 빌드 컨텍스트 크기를 줄이는 실무적인 .dockerignore 활용법
  • 레이어 캐싱을 극대화하는 Dockerfile 작성 전략
  • BuildKit을 활용한 최신 빌드 최적화 기술 적용 방법

최적화 시작 전, 반드시 체크해야 할 기본 지표와 준비 사항

본격적인 튜닝에 들어가기 전에 현재 우리 시스템이 어떤 상태인지 정확히 진단하는 과정이 필요해요. 무턱대고 설정을 바꾸기보다는 무엇이 문제인지 수치로 확인해야 개선 효과를 검증할 수 있습니다.

성능 측정을 위한 핵심 지표

가장 먼저 확인해야 할 것은 빌드 컨텍스트 전송 시간레이어 캐시 적중률(Cache Hit Rate)이에요. 빌드를 시작하자마자 멈춘 듯한 상태가 지속된다면 컨텍스트 전송 단계에서 병목이 발생하고 있는 것이고, 매번 모든 레이어를 새로 생성한다면 캐시 설정이 잘못된 것이에요.

💡 알아두기
빌드 성능을 측정할 때는 단순히 전체 시간을 재는 것보다, 각 단계별(Sending build context, Extracting, Running command 등) 소요 시간을 나누어 기록하는 것이 훨씬 효과적이에요.

환경별 빌드 전략 비교

개발 환경과 운영 환경의 목적은 서로 다릅니다. 따라서 동일한 최적화 전략을 적용하기보다는 상황에 맞는 접근이 필요해요. 아래 표를 통해 어떤 기준을 가지고 최적화 방향을 잡아야 할지 확인해 보세요.

구분
개발 환경(Local) 운영 환경(CI/CD)
핵심 목표 빠른 코드 수정 및 반영 빌드 결과물의 안정성 및 경량화
우선 순위 로컬 캐시 활용 극대화 빌드 컨텍스트 최소화 및 이미지 보안
최적화 도구 Docker Desktop 캐시 설정 BuildKit, Multi-stage Build
권장 사항 소스 코드 실시간 마운트(Volume) 불필요한 파일의 철저한 배제

위의 기준을 바탕으로 현재 여러분의 빌드 프로세스가 어느 쪽에 치우쳐 있는지, 혹은 어느 쪽의 최적화가 시급한지 판단해 보세요. 준비가 되었다면 이제 본격적인 설정 튜닝 단계로 넘어갈 준비가 된 것입니다.

컴포즈 빌드 성능을 극대화하는 5단계 튜닝 가이드

성능 최적화는 단순히 명령어 하나를 바꾸는 것이 아니라, 데이터의 흐름과 레이어의 구조를 설계하는 과정이에요. 다음 5단계 과정을 순서대로 적용해 보세요.

STEP 1. 빌드 컨텍스트 다이어트: .dockerignore 활용하기

도커 컴포즈 빌드를 실행하면, 현재 디렉토리의 모든 파일이 빌드 데몬으로 전송됩니다. 만약 프로젝트 폴더 안에 무거운 데이터 파일, `.git` 디렉토리, 혹은 로컬의 `node_modules`가 들어있다면 빌드 시작 전부터 엄청난 시간이 소요돼요. 빌드 컨텍스트의 크기를 줄이는 것은 최적화의 시작이자 끝입니다.

프로젝트 루트에 `.dockerignore` 파일을 생성하고, 빌드에 필요 없는 항목들을 반드시 명시해야 해요. 예를 들어 다음과 같은 패턴을 권장합니다.

  • .git: 버전 관리 이력은 빌드 이미지에 포함될 필요가 없어요.
  • node_modules: 로컬 환경의 종속성은 이미지 내부의 패키지 매니저를 통해 새로 설치하는 것이 안전합니다.
  • *.log: 불필요한 로그 파일은 용량만 차지합니다.
  • dist/ 또는 `build/`: 이전 빌드의 결과물이 섞이는 것을 방지합니다.

이렇게 컨텍스트를 관리하면 빌드 명령을 내리자마자 나타나는 “Sending build context to Docker daemon” 단계의 시간을 수 초 내로 단축할 수 있어요.

STEP 2. 레이어 캐시 전략: 명령 순서 재배치하기

도커는 Dockerfile의 각 명령어를 레이어(Layer) 단위로 저장합니다. 만약 상단에 있는 명령어가 변경되면, 그 아래에 있는 모든 명령어는 캐시를 사용하지 못하고 새로 실행되어야 해요. 이것이 바로 캐시 파괴(Cache Invalidation) 현상입니다.

효율적인 캐싱을 위해서는 변경 빈도가 낮은 명령어를 위로, 자주 바뀌는 명령어를 아래로 배치하는 것이 핵심이에요. 다음은 최적화된 순서의 예시입니다.

  1. 베이스 이미지 선택 (`FROM`)
  2. 시스템 패키지 설치 (`RUN apt-get update…`) – 매우 드물게 변경됨
  3. 의존성 파일만 먼저 복사 (`COPY package.json .`) – 소스 코드보다 변경 빈도가 낮음
  4. 의존성 설치 실행 (`RUN npm install`) – 소스 수정 시에도 캐시 유지 가능
  5. 실제 소스 코드 복사 (`COPY . .`) – 매번 변경됨
  6. 애플리케이션 실행 명령 (`CMD`)

이렇게 구성하면 소스 코드 한 줄을 수정하더라도, 무거운 패키지 설치 과정을 건너뛰고 즉시 빌드를 완료할 수 있어요.

STEP 3. 멀티 스테이지 빌드(Multi-stage Build)로 경량화하기

빌드할 때 필요한 도구(컴파일러, 빌드 도구 등)와 실제 실행할 때 필요한 런타임 환경은 완전히 다릅니다. 멀티 스테이지 빌드를 사용하면 빌드 단계에서만 사용하는 무거운 도구들을 최종 이미지에서 완전히 제거할 수 있어요. 이는 이미지 크기를 수백 MB에서 수십 MB로 줄이는 마법 같은 방법입니다.

💡 알아두기
멀티 스테이지를 사용하면 최종 이미지에 컴파일러나 소스 코드 원본이 포함되지 않으므로 보안 측면에서도 훨씬 유리합니다.

예를 들어, Go 언어나 Java 애플리케이션을 빌드할 때, 첫 번째 스테이지에서 전체 빌드 환경을 사용해 바이너리를 만들고, 두 번째 스테이지에서는 아주 가벼운 `alpine` 이미지에 결과물만 가져와서 실행하는 식이죠. 이렇게 하면 배포 속도와 저장 공간 활용도가 동시에 개선됩니다.

STEP 4. BuildKit 활성화 및 고급 기능 활용

최신 도커 환경에서는 BuildKit이라는 고성능 빌드 엔진을 사용할 수 있습니다. BuildKit은 병렬 빌드를 지원하고, 사용하지 않는 레이어를 더 똑똑하게 관리하며, 훨씬 효율적인 캐시 공유를 가능하게 해요. 도커 컴포즈 설정에서 이를 활성화하는 것은 필수입니다.

환경 변수로 `DOCKER_BUILDKIT=1`과 `COMPOSE_DOCKER_CLI_BUILD=1`을 설정하면 됩니다. 또한, BuildKit의 `–mount=type=cache` 옵션을 사용하면 패키지 매니저(npm, pip 등)의 캐시 디렉토리를 호스트와 공유하여, 빌드할 때마다 매번 패키지를 새로 다운로드하는 비효율을 막을 수 있어요.

STEP 5. 실전 적용 시나리오: 최적화 전후 비교

이해를 돕기 위해 가상의 Node.js 프로젝트를 기준으로 최적화 효과를 비교해 볼게요.

비교 항목
최적화 전 (비효율적) 최적화 후 (튜닝 완료)
빌드 컨텍스트 크기 1.2 GB (전체 폴더 포함) 45 MB (.dockerignore 적용)
평균 빌드 시간 8분 30초 1분 15초 (캐시 및 BuildKit)
최종 이미지 크기 950 MB 120 MB (멀티 스테이지 적용)
CI/CD 리소스 비용 높음 (대기 시간 및 용량) 매우 낮음 (효율적 자원 사용)

이처럼 단계적인 접근만으로도 빌드 인프라의 효율성을 완전히 바꿀 수 있습니다.

자주 하는 실수와 해결법 + FAQ

튜닝을 적용하다 보면 예상치 못한 변수가 생길 수 있어요. 현장에서 가장 자주 발생하는 문제들을 정리했습니다.

자주 하는 실수와 해결법

실수: .dockerignore를 만들었지만 파일이 여전히 포함되는 경우
왜 발생하는가: 파일 경로 설정이 잘못되었거나, 빌드 컨텍스트 루트가 생각한 위치와 다르기 때문이에요.
✅ 해결법: `.dockerignore` 파일 내의 경로가 프로젝트 루트를 기준으로 정확한지 확인하고, `docker build` 실행 시 지정하는 컨텍스트 경로를 다시 체크하세요.

실수: 소스 코드를 수정했는데 캐시가 작동하지 않고 전부 새로 빌드됨
왜 발생하는가: `COPY . .` 명령어를 의존성 설치(`RUN npm install`)보다 먼저 배치했기 때문입니다.
✅ 해결법: 의존성 파일(`package.json`, `requirements.txt` 등)을 먼저 `COPY`하고 설치 명령을 수행한 뒤, 마지막에 소스 코드를 `COPY`하도록 순서를 바꾸세요.

실수: 멀티 스테이지 빌드를 썼는데 이미지 용량이 줄지 않음
왜 발생하는가: 최종 스테이지에서 베이스 이미지를 너무 무거운 것을 선택했거나, 빌드 결과물을 복사할 때 불필요한 파일까지 통째로 가져왔기 때문이에요.
✅ 해결법: 최종 스테이지는 `alpine`이나 `distroless` 같은 초경량 이미지를 사용하고, 필요한 실행 바이너리나 빌드 결과물만 COPY –from=build 명령어로 골라 담으세요.

실수: BuildKit을 켰는데 명령어가 먹히지 않음
왜 발생하는가: 환경 변수가 현재 세션에 적용되지 않았거나, 도커 버전이 너무 낮을 수 있어요.
✅ 해결법: `export DOCKER_BUILDKIT=1` 명령어로 현재 쉘에 적용하거나, 도커 데몬 설정에서 기본 엔진을 확인하세요.

실수: ARG와 ENV를 혼동하여 보안 사고가 발생함
왜 발생하는가: 빌드 시점에만 필요한 비밀 정보를 `ENV`로 설정하여 이미지 레이어에 영구히 남겨두었기 때문이에요.
✅ 해결법: 빌드 타임에만 필요한 값은 `ARG`를 사용하고, 런타임 환경 변수는 컨테이너 실행 시점에 주입하는 방식을 사용하세요.

자주 묻는 질문

Q. 빌드 컨텍스트를 줄이는 게 정말 그렇게 효과가 큰가요?

네, 매우 큽니다. 네트워크를 통해 수 GB의 데이터를 전송하는 시간 자체가 빌드 프로세스의 병목이 되는 경우가 많기 때문에, 전송량을 줄이는 것만으로도 체감 속도가 급격히 올라갑니다.

Q. BuildKit을 쓰면 무조건 빨라지나요?
대부분의 경우 그렇습니다. 특히 여러 단계를 병렬로 처리하거나 캐시 마운트를 사용할 수 있어 효율적이지만, 아주 단순한 단일 레이어 빌드에서는 차이가 미미할 수 있습니다.

Q. CI/CD 환경에서 레이어 캐시를 어떻게 유지하나요?
GitHub Actions나 GitLab CI의 경우, 빌드 후 생성된 이미지를 레지스트리에 push하고, 다음 빌드 시 `–cache-from` 옵션을 사용하여 기존 이미지를 캐시로 불러오는 전략을 사용해야 합니다.

Q. .dockerignore에 넣으면 안 되는 파일이 있나요?
빌드 과정에서 반드시 참조해야 하는 설정 파일이나, 빌드 명령어가 실행되는 위치에 꼭 필요한 필수 라이브러리 파일은 제외 대상에서 빼야 합니다.

Q. 멀티 스테이지 빌드가 너무 복잡하게 느껴지는데 꼭 해야 하나요?
이미지 크기가 1GB를 넘어간다면 강력히 권장합니다. 운영 환경의 보안과 배포 속도를 생각한다면 초기 학습 비용을 들일 가치가 충분합니다.

성공적인 컨테이너 운영을 위한 마지막 점검

컴포즈 build 옵션 성능 최적화는 한 번의 설정으로 끝나는 숙제가 아니라, 지속적인 모니터링과 개선이 필요한 운영의 과정이에요. 오늘 배운 내용들을 바탕으로 여러분의 인프라를 다시 한번 점검해 보세요.

✅ 핵심 요약

  • 컨텍스트 최소화: .dockerignore를 통해 불필요한 파일 전송을 차단하세요.
  • 캐시 전략 최적화: 변경이 적은 명령어를 Dockerfile 상단에 배치하세요.
  • 이미지 경량화: 멀티 스테이지 빌드로 실행 환경을 분리하세요.
  • 최신 엔진 활용: BuildKit을 활성화하여 병렬 빌드와 캐시 마운트를 활용하세요.
  • 보안 강화: 빌드 시 비밀 정보는 ENV가 아닌 ARG를 사용하세요.

이제 실행에 옮길 차례입니다. 지금 바로 터미널을 열고 다음 단계를 따라 해 보세요.

  • 지금 당장: 현재 프로젝트의 `.dockerignore` 파일 유무를 확인하고, 불필요한 폴더를 추가하세요.
  • 이번 주 안에: Dockerfile의 명령어 순서를 검토하여 캐시 적중률을 높이는 재배치를 완료하세요.
  • 실행 직전: CI/CD 파이프라인에 BuildKit 설정이 포함되어 있는지 최종 점검하세요.

튜닝 전후의 빌드 시간과 이미지 크기 지표를 반드시 기록해 두세요. 실제 개선 폭을 확인하는 것은 팀 내에서 인프라 개선 성과를 증명하는 가장 강력한 데이터가 될 것입니다. 작은 설정의 차이가 큰 비용 절감과 운영 안정성을 만듭니다.

관련하여 더 깊이 있는 도커 활용법이 궁금하시다면, 도커 컴포즈 기본 개념 완벽 정리 — 개념부터 실무 활용까지 한눈에 보는 가이드 글을 함께 읽어보시는 것을 추천드려요.

댓글 남기기