[IT-방법] 컴포즈 build 옵션 자동화와 CI/CD 연동 – 배포 과정을 자동화하여 운영 효율 높이는 법

build 옵션과 빌드 컨텍스트를 설명하는 자동화와 CI/CD 연동 대표 이미지

매번 반복되는 수동 빌드, 과연 안전할까요?

금요일 오후 늦은 시간, 마지막 배포를 위해 터미널에 명령어를 입력해요. docker-compose build를 치고 나서 잠시 기다리는데, 갑자기 화면에 붉은색 에러 메시지가 가득 차요. 알고 보니 빌드 컨텍스트에 포함되지 말아야 할 거대한 로그 파일이나 임시 데이터가 포함되어 빌드 시간이 평소보다 5배나 늘어난 상황이에요. 혹은 어제와 똑같은 명령어를 입력했다고 생각했지만, 특정 환경 변수를 빠뜨리는 바람에 운영 서버가 엉뚱한 설정으로 돌아가기 시작해요.

이런 경험은 개발자라면 누구나 한 번쯤 겪어봤을 거예요. 단순히 명령어를 한 번 잘못 입력하는 문제가 아니에요. 사람이 직접 입력하는 과정에서 발생하는 아주 작은 실수가 전체 서비스의 가용성을 흔들 수 있다는 점이 진짜 무서운 부분이에요. 컴포즈 build 옵션 자동화가 필요한 이유는 바로 여기에 있어요. 반복되는 수동 작업을 제거하고, 기계가 정해진 규칙에 따라 빌드하도록 만들어야 해요.

이제 우리는 단순히 명령어를 실행하는 수준을 넘어서야 해요. 빌드할 때 어떤 파일을 넘겨줄지(Build Context), 어떤 설정값을 주입할지(Build Args), 그리고 이 과정을 어떻게 검증할지를 모두 시스템의 영역으로 넘겨야 하죠. 이 글을 다 읽고 나면, 더 이상 배포할 때마다 가슴을 졸이지 않고 자동화된 파이프라인을 통해 안심하고 코드를 반영할 수 있게 돼요.

💡 알아두기
자동화는 단순히 명령어를 대신 입력해 주는 것이 아니에요. 빌드 환경의 일관성을 보장하고, 인간의 개입을 최소화하여 ‘예측 가능한 배포’를 만드는 과정이에요.

이 글에서 함께 다룰 내용들

  • 빌드 컨텍스트 최적화를 통한 속도와 보안 개선 방법
  • 환경 변수와 build 옵션을 스크립트로 관리하는 요령
  • CI/CD 파이프라인에 컴포즈 빌드를 녹여내는 실무 설계
  • 빌드 실패 시 즉시 복구할 수 있는 롤백 전략

자동화를 시작하기 전 반드시 갖춰야 할 기초 체력

무턱대고 스크립트부터 짜기 시작하면 나중에 더 큰 유지보수의 늪에 빠져요. 자동화를 설계하기 전에 우리가 제어할 수 있는 영역이 어디까지인지 명확히 이해해야 해요. 특히 빌드 컨텍스트(Build Context)는 자동화의 성패를 결정짓는 가장 중요한 개념이에요. 빌드 컨텍스트란 도커 클라이언트가 빌드 명령을 내릴 때 데몬으로 전송하는 파일들의 집합을 말해요. 만약 이 범위가 너무 넓으면 네트워크 부하가 생기고, 너무 좁으면 필요한 파일을 찾지 못해 빌드가 실패해요.

또한, 컴포즈 파일 내에서 사용할 build 옵션들이 어떻게 정의되어 있는지도 다시 살펴봐야 해요. `args`를 통해 전달할 변수들이 무엇인지, `target` 옵션으로 멀티 스테이지 빌드의 어느 단계까지 실행할 것인지 미리 정리해 두어야 해요. 준비물이 완벽해야 자동화라는 기계가 멈추지 않고 돌아갈 수 있어요.

자동화 방식 선택을 위한 판단 기준

현재 우리 팀의 상황이 어떤지, 어떤 수준의 자동화가 필요한지 결정해야 해요. 아래 표를 보고 현재 우리 팀에 가장 적합한 단계를 골라보세요.

구분 수동 명령 실행 쉘 스크립트 자동화 CI/CD 통합형
주요 특징 터미널에 직접 명령어 입력 자주 쓰는 명령을 파일로 저장 코드 push 시 자동 실행
추천 대상 개인 로컬 테스트 단계 소규모 팀, 반복 작업 과다 안정적 운영이 필요한 서비스
관리 비용 매우 낮음 (별도 관리 없음) 낮음 (스크립트 업데이트 필요) 중간 (파이프라인 관리 필요)
실수 가능성 매우 높음 낮음 (스크립트 검증 시) 거의 없음

단순히 명령어를 기록해 두는 것만으로는 부족해요. 환경의 격리가 이루어지고 있는지, 빌드 시 사용되는 `.env` 파일이 보안상 안전하게 관리되고 있는지 확인해야 해요. 만약 중요한 API 키나 비밀번호가 빌드 컨텍스트에 포함되어 이미지에 박혀버린다면, 자동화는 오히려 보안 사고를 자동화하는 도구가 될 수 있어요. 이 점을 항상 경계하며 다음 단계를 준비해야 해요.

⚠️ 주의
빌드 컨텍스트에 `.git` 폴더나 대규모 데이터베이스 덤프 파일이 포함되지 않도록 반드시 `.dockerignore` 파일을 먼저 작성하세요. 컨텍스트가 커질수록 빌드 속도는 기하급수적으로 느려져요.

성공적인 배포를 위한 5단계 자동화 프로세스

이제 본격적으로 자동화 설계를 시작해 봐요. 단순히 명령어를 합치는 것이 아니라, 빌드 환경을 최적화하고 이를 시스템이 이해할 수 있는 구조로 만드는 과정이에요. 아래 단계들을 따라 하나씩 구축해 나가면 안정적인 컨테이너 운영 환경을 갖출 수 있어요.

STEP 1. 빌드 컨텍스트와 .dockerignore 최적화

가장 먼저 할 일은 도커가 읽어들이는 범위를 정교하게 다듬는 일이에요. 많은 개발자가 실수하는 부분 중 하나가 프로젝트 루트 디렉토리를 통째로 빌드 컨텍스트로 사용하는 것이에요. 이렇게 하면 `node_modules`, `.git`, `dist`, 혹은 로컬 로그 파일까지 모두 도커 데몬으로 전송되어 빌드 시간이 엄청나게 늘어나요.

최적화 방법은 다음과 같아요:
1. 프로젝트 루트에 `.dockerignore` 파일을 생성하세요.
2. 불필요한 폴더와 파일을 명시하세요 (예: `node_modules`, `.git`, `*.log`, `tmp/`).
3. 빌드에 꼭 필요한 소스 코드와 설정 파일만 남기세요.

이렇게 하면 빌드 명령을 내리는 즉시 컨텍스트 전송 속도가 빨라지는 것을 체감할 수 있어요. 특히 네트워크를 통해 원격 도커 데몬에 빌드를 요청하는 환경이라면 이 차이는 수 분에서 수십 분까지 벌어질 수 있어요. 빌드 컨텍스트를 작게 유지하는 것은 속도뿐만 아니라 보안 측면에서도 매우 중요해요. 민감한 정보가 담긴 파일이 이미지 레이어에 실수로 포함되는 것을 원천 차단할 수 있기 때문이에요.

STEP 2. 환경 변수와 build-arg 자동화

개발(dev), 스테이징(staging), 운영(prod) 환경마다 서로 다른 설정값이 필요하죠? 매번 명령어를 칠 때마다 `-e` 옵션을 붙이는 건 너무 비효율적이에요. 컴포즈의 `build` 옵션 내에 있는 `args` 기능을 활용하면 이 문제를 우격다짐이 아닌 구조적으로 해결할 수 있어요.

먼저 `docker-compose.yml` 파일에 빌드 인자를 정의해요.
build:
context: .
args:
- APP_VERSION=${APP_VERSION}
- BUILD_ENV=${BUILD_ENV}

이렇게 작성해 두면, 실행 시점에 환경 변수를 주입할 수 있어요. 그다음, 이를 관리할 `.env` 파일을 환경별로 만들어 두세요. 예를 들어 `.env.prod`와 `.env.dev`를 각각 작성한 뒤, 자동화 스크립트에서 실행할 때 `docker-compose –env-file .env.prod build`와 같이 호출하면 돼요. 이렇게 하면 사람이 직접 값을 입력할 필요가 없어 오타로 인한 사고를 방지할 수 있어요.

STEP 3. 빌드 과정을 캡슐화하는 쉘 스크립트 작성

복잡한 명령어를 하나의 완성된 워크플로우로 만드는 단계예요. 단순히 `docker-compose build`만 실행하는 스크립트는 반쪽짜리 자동화예요. 진정한 자동화 스크립트는 빌드 전 준비 작업, 빌드 실행, 빌드 결과 확인, 그리고 불필요한 이미지 정리까지 포함해야 해요.

추천하는 스크립트 구성 요소:
– **Pre-check**: 필수 환경 변수가 설정되어 있는지 확인해요.
– **Build**: `docker-compose build –no-cache`를 사용하여 깨끗한 상태에서 빌드하거나, 캐시를 활용하도록 옵션을 조절해요.
– **Validation**: 빌드된 이미지가 로컬에 정상적으로 생성되었는지 `docker images` 명령어로 체크해요.
– **Cleanup**: 빌드 과정에서 생성된 중간 레이어나 미사용 이미지를 `docker image prune`으로 정리하여 디스크 용량을 확보해요.

스크립트를 짤 때는 반드시 에러 처리 코드를 넣어야 해요. 명령어가 실패했을 경우 즉시 종료되고 에러 메시지를 남기도록 `set -e` 옵션을 사용하는 것을 추천해요. 그래야 중간에 실패했는데 성공한 것처럼 다음 단계로 넘어가는 대참사를 막을 수 있어요.

STEP 4. CI/CD 파이프라인에 빌드 로직 통합

이제 로컬 스크립트를 넘어, GitHub Actions나 GitLab CI 같은 도구에 이 로직을 이식할 차례예요. CI 도구가 코드를 가져오면(Checkout), 자동으로 스크립트를 실행하도록 설정하는 것이죠. 이 단계에서는 사람이 개입할 여지가 아예 사라져요.

예를 들어 GitHub Actions를 사용한다면, `.github/workflows/deploy.yml` 파일에 빌드 단계를 정의해요. 여기서 중요한 것은 도커 컨텍스트를 전달하는 방식이에요. CI 서버의 용량이 제한적일 수 있으므로, 빌드 결과물인 이미지를 바로 Docker Hub나 AWS ECR 같은 레지스트리에 `push` 하도록 설계해야 해요. 빌드가 성공하면 이미지를 푸시하고, 실패하면 즉시 알림(Slack 등)을 보내도록 구성하는 것이 핵심이에요.

STEP 5. 멀티 스테이지 빌드를 통한 배포 이미지 경량화

자동화의 마지막 정점은 ‘결과물의 품질’이에요. 빌드할 때는 수많은 컴파일러와 라이브러리가 필요하지만, 실제로 서비스를 돌릴 때는 실행 파일만 있으면 되죠. 멀티 스테이지 빌드(Multi-stage build)를 사용하면 이 과정을 자동화할 수 있어요.

Dockerfile 작성 시 `FROM node:18 AS builder` 단계에서 모든 빌드를 마치고, `FROM nginx:alpine` 단계에서 빌드된 결과물만 `COPY –from=builder`로 가져오는 식이에요. 이렇게 하면 최종 이미지는 수백 MB에서 수십 MB로 줄어들어요. 이미지가 가벼워지면 CI/CD 파이프라인의 전송 속도가 빨라지고, 서버 배포 시에도 훨씬 빠르게 컨테이너를 띄울 수 있어요. 이는 결국 전체적인 배포 주기를 단축시키는 선순환을 만들어요.

💡 알아두기
실제 운영 환경에서는 `docker-compose build`를 직접 서버에서 실행하는 것보다, 빌드 전용 서버(CI)에서 이미지를 만들어 레지스트리에 올린 뒤, 운영 서버에서는 `docker-compose pull`만 수행하는 방식이 가장 안전하고 효율적이에요.

실무 적용 시나리오: 통합 배포 워크플로우

이 모든 과정을 하나의 흐름으로 묶으면 다음과 같아요.

  1. 개발자가 코드를 작성하고 `git push`를 해요.
  2. GitHub Actions가 트리거되어 서버 환경을 준비해요.
  3. 미리 작성된 쉘 스크립트가 실행되며 `.env.prod`를 불러와 빌드를 시작해요.
  4. 멀티 스테이지 빌드로 가벼워진 이미지가 생성되어 ECR 레지스트리에 저장돼요.
  5. 배포 서버에 신호가 가고, 서버는 새 이미지를 `pull` 한 뒤 컨테이너를 교체해요.

이 과정을 한 번 구축해 두면, 개발자는 오직 코드 작성에만 집중할 수 있고 배포는 배경에서 조용히, 그리고 완벽하게 이루어지게 돼요.

자주 하는 실수와 해결법

자동화를 구축하다 보면 예상치 못한 벽에 부딪히곤 해요. 가장 흔히 발생하는 문제들을 정리했으니, 비슷한 상황이라면 아래 해결책을 참고해 보세요.

  • 실수: 빌드 컨텍스트에 너무 많은 파일이 포함됨
    왜 발생하는가: `.dockerignore` 파일을 작성하지 않거나 루트 디렉토리를 너무 넓게 잡았기 때문이에요.
    ✅ 해결법: `.dockerignore`를 최우선으로 작성하여 `node_modules`나 대용량 데이터 파일을 제외하세요.
  • 실수: 환경 변수가 이미지 내부에 하드코딩됨
    왜 발생하는가: `ARG`가 아닌 `ENV`를 사용하여 빌드 시점에 값을 고정해 버렸기 때문이에요.
    ✅ 해결법: 빌드 시점에만 필요한 값은 `ARG`를 사용하고, 실행 시점에 필요한 값은 런타임 환경 변수로 주입하세요.
  • 실수: CI/CD에서 캐시를 사용하지 못해 빌드가 너무 느림
    왜 발생하는가: 매번 새로운 환경에서 빌드를 시작하거나, Dockerfile의 레이어 순서가 비효율적이기 때문이에요.
    ✅ 해결법: 자주 변하지 않는 패키지 설치 단계를 상단에 배치하고, CI 도구의 캐시 기능을 활성화하세요.
  • 실수: 빌드 성공 후 실행 단계에서 에러 발생
    왜 발생하는가: 빌드 단계(Build time)와 실행 단계(Run time)의 환경 차이를 고려하지 않았기 때문이에요.
    ✅ 해결법: 빌드 단계에서 사용한 라이브러리가 실행용 베이스 이미지에도 포함되어 있는지 확인하세요.
  • 실수: 자동화 스크립트가 실패해도 계속 진행됨
    왜 발생하는가: 스크립트 내에 에러 발생 시 중단하는 로직(`set -e`)이 없기 때문이에요.
    ✅ 해결법: 모든 쉘 스크립트 상단에 `set -e`를 추가하여 에러 발생 즉시 프로세스를 멈추도록 하세요.

자주 묻는 질문

Q. 빌드 컨텍스트를 줄이는 게 보안상으로도 중요한가요?

네, 매우 중요해요. 빌드 컨텍스트에 포함된 파일은 모두 도커 데몬으로 전송되는데, 이 과정에서 민감한 파일이 실수로 이미지 레이어에 포함될 위험이 있어요. 컨텍스트를 최소화하면 공격 표면(Attack Surface)을 줄일 수 있어요.

Q. 빌드 속도를 획기적으로 높이는 팁이 있을까요?
Dockerfile의 레이어를 활용하세요. `COPY package.json .`를 먼저 하고 `RUN npm install`을 수행한 뒤, 나중에 실제 소스 코드를 `COPY` 하는 방식이 좋아요. 이렇게 하면 소스 코드만 수정되었을 때는 무거운 패키지 설치 과정을 건너뛰고 캐시를 사용할 수 있어요.

Q. 멀티 스테이지 빌드를 쓰면 관리가 더 복잡해지지 않나요?
처음에는 조금 복잡하게 느껴질 수 있지만, 장기적으로는 훨씬 이득이에요. 이미지 크기가 작아지면 배포 속도가 빨라지고, 운영 서버에는 실행에 필요한 최소한의 도구만 남게 되어 보안성도 높아지거든요.

Q. 환경 변수 관리는 어떤 도구가 가장 좋은가요?
규모가 작다면 `.env` 파일로 충분하지만, 규모가 커지면 AWS Secrets Manager나 HashiCorp Vault 같은 전문적인 비밀 관리 도구를 CI/CD 파이프라인과 연동하는 것이 가장 안전해요.

이제 배포의 두려움에서 벗어나세요

지금까지 컴포즈 build 옵션 자동화부터 CI/CD 연동까지, 실무에서 즉시 적용 가능한 핵심 전략들을 살펴보았어요. 자동화는 단순히 편의를 위한 기능이 아니에요. 서비스의 안정성을 담보하고, 개발자가 더 가치 있는 비즈니스 로직 구현에 집중할 수 있도록 만드는 필수적인 인프라 구축 과정이에요.

처음부터 모든 것을 완벽하게 자동화하려고 욕심내지 마세요. 작은 것부터 시작하는 것이 중요해요. 가장 자주 반복하면서도 실수하기 쉬운 명령어 하나를 스크립트로 만드는 것, 그것이 위대한 자동화 여정의 시작이에요.

✅ 핵심 요약

  • 컨텍스트 최적화: .dockerignore로 빌드 범위를 좁히고 속도를 높이세요.
  • 변수 관리: build-arg와 .env를 활용해 환경별 설정을 자동화하세요.
  • 스크립트화: 단순 명령어를 넘어 에러 처리와 정리 로직을 포함하세요.
  • CI/CD 통합: 사람이 아닌 시스템이 빌드하고 푸시하도록 만드세요.
  • 경량화: 멀티 스테이지 빌드로 안전하고 가벼운 이미지를 만드세요.

성공적인 자동화를 위한 다음 단계

  • 오늘 할 일: 현재 프로젝트의 `.dockerignore` 파일을 점검하고 불필요한 파일 제거하기
  • 이번 주 할 일: 자주 쓰는 빌드 명령어를 하나의 쉘 스크립트(`.sh`)로 묶어보기
  • 실행 직전 할 일: 작성한 스크립트를 로컬 환경에서 여러 번 테스트하며 에러 처리 확인하기

가장 자주 반복하는 명령 하나부터 스크립트로 옮겨 보세요. 그 작은 변화가 여러분의 퇴근 시간을 앞당겨 줄 거예요. 만약 도커 자체의 기본 개념이 아직 생소하다면, 도커 컴포즈 기본 개념 완벽 정리 — 개념부터 실무 활용까지 한눈에 보는 가이드를 먼저 읽어보시는 것을 추천해요.

댓글 남기기