[IT-방법] 컴포즈 build 옵션 자동화와 CI/CD 연동 – 배포 프로세스를 효율적으로 바꾸는 법

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

수동 배포의 늪에서 벗어나야 하는 이유

새로운 코드를 반영하기 위해 매번 터미널을 열고 docker-compose up --build 명령어를 입력하고 계신가요? 코드를 수정할 때마다 빌드 컨텍스트를 확인하고, 환경 변수를 수동으로 넘기며, 빌드가 끝날 때까지 초조하게 화면을 바라보는 과정은 생각보다 많은 에너지를 갉아먹어요. 단순한 작업처럼 보이지만, 이 과정이 반복될수록 개발자의 집중력은 흐트러지고 실수할 확률은 기하급수적으로 높아져요.

특히 서비스 규모가 커지면서 관리해야 할 컨테이너가 늘어나면 문제는 더 심각해져요. 어떤 컨테이너는 특정 폴더를 빌드 컨텍스트로 잡아야 하고, 어떤 컨테이너는 별도의 환경 변수가 필요해요. 이걸 매번 기억해서 명령어를 조합하는 것은 불가능에 가까워요. 한 번의 실수로 잘못된 빌드 컨텍스트가 전송되면, 빌드 시간은 늘어나고 결국 배포 실패라는 결과로 이어지게 돼요. 수동 배포는 단순히 귀찮은 일이 아니라, 운영의 안정성을 해치는 잠재적 폭탄과 같아요.

이제는 사람이 직접 명령어를 조합하는 시대를 지나, 시스템이 알아서 최적의 옵션을 찾아 빌드하도록 만들어야 해요. 컴포즈 build 옵션 자동화를 구현하면 빌드 과정의 불확실성을 제거할 수 있어요. 빌드 컨텍스트를 명확히 정의하고, 이를 CI/CD 파이프라인에 태우는 것만으로도 배포의 질이 완전히 달라져요. 이 글을 통해 배포 과정에서 느끼던 막막함을 해소하고, 손 하나 까딱하지 않고도 안전하게 코드를 반영하는 방법을 배워보세요.

이 글에서는 다음과 같은 내용을 구체적으로 다뤄요.

  • 빌드 컨텍스트와 옵션 설정의 핵심 원리
  • 반복 작업을 줄여주는 스크립트와 Makefile 활용법
  • CI/CD 파이프라인에 빌드 프로세스를 녹여내는 방법
  • 배포 실패를 막는 검증과 롤백 전략

자동화를 시작하기 위한 사전 준비와 기준

본격적으로 자동화 스크립트를 짜기 전에, 우리가 무엇을 자동화해야 하는지 명확히 구분해야 해요. 무작정 모든 것을 자동화하려고 하면 오히려 시스템이 복잡해져서 유지보수가 힘들어질 수 있어요. 먼저 빌드 컨텍스트(Build Context)가 무엇인지, 그리고 도커 컴포즈의 build 섹션이 어떤 역할을 하는지 정확히 이해하는 것이 첫걸음이에요.

빌드 컨텍스트는 도커 데몬이 빌드 과정에서 접근할 수 있는 파일들의 범위예요. 이 범위가 너무 넓으면 불필요한 파일까지 도커 엔진으로 전송되어 빌드 속도가 느려지고, 너무 좁으면 필요한 파일을 찾지 못해 에러가 발생해요. 따라서 자동화를 설계할 때는 ‘어떤 파일을 포함하고 어떤 파일을 제외할 것인가’를 결정하는 규칙부터 세워야 해요.

💡 알아두기
자동화의 목표는 ‘명령어의 최소화’가 아니라 ‘결과의 일관성’에 두어야 해요. 어떤 환경에서 실행하더라도 항상 동일한 빌드 옵션이 적용되도록 만드는 것이 핵심이에요.

자동화 수준을 결정하기 위해 아래의 비교 표를 참고해 보세요. 현재 여러분의 팀이 처한 상황에 가장 적합한 단계가 무엇인지 판단하는 데 도움이 될 거예요.

구분 수동 배포 스크립트 기반 CI/CD 연동
작업 방식 터미널에 직접 입력 Shell/Makefile 실행 코드 푸시 시 자동 실행
실수 가능성 매우 높음 낮음 매우 낮음
환경 일관성 개인마다 다름 팀 내 동일 적용 전사적 표준화 가능
추천 대상 1인 프로젝트 소규모 팀 운영 환경이 있는 팀

준비 과정에서 가장 중요한 체크리스트는 다음과 같아요. 이 항목들이 준비되지 않은 상태에서 자동화를 시작하면, 나중에 스크립트 자체를 고치는 데 더 많은 시간을 쓰게 될 수도 있어요.

  • Dockerfile의 모듈화: 서비스별로 Dockerfile이 명확히 구분되어 있나요?
  • 환경 변수의 분리: .env 파일이나 시스템 환경 변수를 통해 설정값을 관리하고 있나요?
  • 의존성 정의: 빌드 시 필요한 외부 패키지나 라이브러리가 Dockerfile 내에 선언되어 있나요?
  • 네트워크 구성: 컨테이너 간 통신을 위한 도커 네트워크가 정의되어 있나요?

단계별 빌드 자동화 실행 가이드

이제 본격적으로 컴포즈 build 옵션 자동화를 설계하고 실행하는 단계를 살펴볼게요. 단순히 명령어를 묶는 수준을 넘어, 실제 운영 환경에서 견고하게 작동하는 파이프라인을 만드는 것이 목표예요. 효율적인 자동화를 위해 6가지 핵심 단계를 제안해요.

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

자동화의 첫 단추는 빌드 속도를 결정하는 컨텍스트 최적화예요. 많은 개발자가 실수하는 부분 중 하나가 빌드 컨텍스트를 프로젝트 루트(.)로 설정하고 아무런 제한 없이 빌드하는 것이에요. 이렇게 하면 node_modules, .git, 혹은 거대한 데이터 로그 파일까지 전부 도커 데몬으로 전송돼요. 이는 빌드 시간을 몇 분 단위로 늘리는 주범이에요.

가장 먼저 해야 할 일은 프로젝트 루트에 .dockerignore 파일을 만드는 것이에요. 여기에 포함될 파일들을 명확히 작성하세요. 예를 들어, 로컬 환경의 의존성 패키지나 빌드 결과물, 민감한 설정 파일들을 제외해야 해요. 이렇게 하면 도커 엔진이 전송받는 데이터 양이 줄어들어 빌드 속도가 비약적으로 빨라지고, 빌드 결과물의 크기도 최적화돼요. 컨텍스트를 가볍게 만드는 것이 자동화 성능의 절반을 결정해요.

STEP 2. docker-compose.yml의 build 섹션 정교화

컴포즈 파일 내의 build 옵션을 수동으로 명령행에 입력하는 대신, 파일 안에 명시적으로 정의해 두어야 해요. 특히 여러 개의 서비스를 운영한다면 각 서비스의 경로와 Dockerfile 위치를 정확히 지정하는 것이 중요해요.

예를 들어, 서비스의 구조가 다음과 같다고 가정해 볼게요.

  • project-root/
  • ├── service-a/
  • │ └── Dockerfile
  • ├── service-b/
  • │ └── Dockerfile
  • └── docker-compose.yml

이 경우 docker-compose.yml은 아래와 같이 작성되어야 해요.

services:
app-a:
build:
context: ./service-a
dockerfile: Dockerfile
args:
- NODE_ENV=production
app-b:
build:
context: ./service-b
dockerfile: Dockerfile

여기서 args를 활용하면 빌드 시점에 필요한 변수를 주입할 수 있어요. 이렇게 하면 하나의 Dockerfile로 개발용과 운영용 이미지를 모두 효율적으로 만들 수 있어요. 빌드 시점에 결정되어야 하는 값들을 코드와 분리하여 관리하는 것이 핵심이에요.

STEP 3. Makefile을 이용한 명령어 추상화

CI/CD를 도입하기 전, 로컬 환경에서 가장 먼저 구축해야 할 자동화 도구는 Makefile이에요. 복잡한 도커 명령어를 짧은 단어로 추상화하여 팀원 모두가 동일한 명령어를 사용할 수 있게 해줘요.

단순히 docker-compose up --build를 적어두는 것에 그치지 마세요. 빌드, 테스트, 배포, 로그 확인을 하나의 흐름으로 묶어야 해요. 아래는 실무에서 바로 사용할 수 있는 Makefile 예시예요.

build:
docker-compose build --no-cache
test:
docker-compose run --rm app-a npm test
deploy:
docker-compose up -d --remove-orphans
logs:
docker-compose logs -f

이제 개발자는 터미널에 make deploy 한 줄만 입력하면 돼요. 이 짧은 명령 안에는 컨테이너 재시작, 고립된 컨테이너 삭제, 빌드 옵션 적용 등이 모두 포함되어 있어요. Makefile은 복잡한 명령어를 하나의 약속된 단어로 압축하는 강력한 도구예요.

STEP 4. CI/CD 파이프라인 설계 (GitHub Actions 예시)

이제 로컬에서의 자동화를 넘어, 코드가 푸시될 때마다 서버에 자동으로 반영되는 파이프라인을 구축할 단계예요. GitHub Actions를 사용하면 매우 간단하게 구현할 수 있어요. 파이프라인의 흐름은 [코드 푸시 → 빌드 → 이미지 태깅 → 서버 전송 → 배포] 순으로 이어져야 해요.

여기서 주의할 점은 서버에서 직접 빌드하는 것이 아니라, CI 환경에서 빌드한 이미지를 레지스트리(Docker Hub, ECR 등)에 올리고 서버는 그 이미지를 가져오기만(Pull) 하는 방식이 훨씬 안정적이라는 것이에요. 서버의 자원을 빌드에 쓰지 않아도 되고, 빌드된 결과물이 보장되기 때문이에요.

파이프라인의 핵심 흐름은 다음과 같아요.

  1. 코드 변경 사항 감지 (push/merge event)
  2. 도커 빌드 및 태그 생성 (예: commit hash 사용)
  3. 컨테이너 레지스트리에 이미지 푸시
  4. 배포 대상 서버에 접속 (SSH)
  5. 서버에서 docker-compose pulldocker-compose up -d 실행

이 방식을 사용하면 배포 과정에서 빌드 오류로 인해 서비스가 중단되는 상황을 원천 차단할 수 있어요. 빌드가 실패하면 이미 레지스트리에 올라가지 않으므로, 서버는 기존의 잘 작동하던 이미지를 그대로 유지하게 돼요.

STEP 5. 배포 검증 단계 추가 (Healthcheck)

배포가 성공했다고 해서 서비스가 정상이라고 단정할 수 없어요. 컨테이너는 떴지만 내부 애플리케이션이 에러로 인해 무한 루프를 돌거나, DB 연결에 실패해 좀비 상태가 될 수도 있기 때문이에요. 이를 방지하기 위해 Healthcheck 기능을 반드시 활용해야 해요.

docker-compose.yml 파일에 서비스별 상태 확인 로직을 넣으세요. 예를 들어, 웹 서버라면 특정 엔드포인트인 /health로 요청을 보내 응답 코드가 200인지를 확인하는 방식이에요.

이렇게 설정해 두면, 컨테이너가 ‘Running’ 상태라 하더라도 실제 서비스가 불가능한 상태라면 도커가 이를 감지할 수 있어요. 자동화 파이프라인에서 이 검증 단계를 포함하면, 서비스가 정상 궤도에 올랐을 때만 배포를 완료하도록 설계할 수 있어요. 검증 없는 배포는 눈을 감고 고속도로를 달리는 것과 같아요.

STEP 6. 실패 시 롤백(Rollback) 전략 수립

모든 자동화의 완성은 ‘실패했을 때 어떻게 되돌릴 것인가’에 달려 있어요. 가장 권장하는 방법은 이미지를 태그할 때 latest 태그를 지양하고, 커밋 해시나 버전 번호(v1.0.1 등)를 사용하는 것이에요.

만약 새로운 버전의 배포가 실패했다면, 이전 버전의 이미지를 다시 Pull 하여 실행하는 것만으로 즉시 복구가 가능해요. 자동화 스크립트나 CI/CD 도구에 롤백 명령어를 미리 정의해 두세요. 예를 들어, make rollback 명령어가 실행되면 마지막으로 성공했던 이미지 태그로 컴포즈를 재시작하도록 만드는 것이죠. 이렇게 하면 장애 발생 시 대응 시간을 몇 분 단위에서 몇 초 단위로 줄일 수 있어요.

⚠️ 주의
자동화 프로세스를 구축할 때, 데이터베이스 마이그레이션(Migration)은 별도로 관리해야 해요. 애플리케이션 이미지는 롤백할 수 있지만, 이미 변경된 데이터베이스 스키마를 이전 상태로 되돌리는 것은 매우 까다롭고 위험한 작업이기 때문이에요.

자주 하는 실수와 해결법

자동화를 구축하다 보면 예상치 못한 지점에서 막히곤 해요. 실무에서 가장 빈번하게 발생하는 실수들과 그 해결책을 정리했어요.

  • 실수: 빌드 컨텍스트에 불필요한 파일이 포함되어 빌드가 너무 느려짐
    → 왜 발생하는가: .dockerignore 파일을 만들지 않거나 설정이 미흡하여 대용량 데이터나 라이브러리가 전송됨
    → ✅ 해결법: .dockerignore 파일을 생성하고 node_modules, .git, build/, logs/ 등을 반드시 명시하세요.
  • 실수: ‘latest’ 태그만 사용하여 롤백이 불가능함
    → 왜 발생하는가: 새로운 빌드마다 기존 이미지를 덮어쓰기 때문에 이전 버전의 이미지가 남아있지 않음
    → ✅ 해결법: 이미지 태그에 Git 커밋 해시나 세부 버전 번호를 붙여 관리하세요.
  • 실수: 환경 변수가 빌드 시점에 반영되지 않음
    → 왜 발생하는가: .env 파일의 값을 docker-compose.yml의 build: args 섹션에 전달하지 않음
    → ✅ 해결법: build 섹션 내에 args를 정의하고, 필요한 값을 환경 변수로부터 주입받도록 설정하세요.
  • 실수: CI/CD 서버의 자원이 빌드 작업으로 인해 고갈됨
    → 왜 발생하는가: 서버 내에서 직접 docker build 명령을 실행함
    → ✅ 해결법: 빌드는 CI 환경(Runner)에서 수행하고, 결과물인 이미지만 레지스트리에 올려 서버는 Pull만 하도록 구조를 바꾸세요.
  • 실수: 컨테이너는 떴지만 실제 앱은 작동하지 않음
    → 왜 발생하는가: 애플리케이션 내부 에러나 DB 연결 실패를 감지할 수 있는 장치가 없음
    → ✅ 해결법: docker-compose.yml에 healthcheck 옵션을 추가하여 서비스 상태를 실시간으로 확인하세요.

자주 묻는 질문

Q. 빌드할 때마다 매번 전체를 다시 빌드하는데, 캐시를 쓸 수는 없나요?

물론 가능해요. Docker의 레이어 캐싱을 활용하려면 Dockerfile을 작성할 때 변경이 적은 명령(예: 패키지 설치)을 위쪽에, 변경이 잦은 명령(예: 소스 코드 복사)을 아래쪽에 배치해야 해요. 또한 CI 환경에서는 각 단계의 캐시를 보관하도록 별도의 설정이 필요해요.

Q. 컴포즈 파일이 여러 개일 때(dev, prod) 자동화를 어떻게 관리하나요?

명령어에 -f 옵션을 사용하여 파일을 지정할 수 있어요. 예를 들어 docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d 처럼 사용해요. 이를 Makefile이나 스크립트 내에 미리 정의해 두면 실수 없이 환경별 배포가 가능해요.

Q. 보안을 위해 빌드 시 비밀번호를 전달해야 하는데, 어떻게 하나요?

build args로 비밀번호를 넘기는 것은 보안상 위험해요. 빌드 기록에 노출될 수 있기 때문이에요. 이럴 때는 Docker의 secret mount 기능을 사용하거나, 빌드 후 런타임에 환경 변수로 주입하는 방식을 권장해요.

Q. 빌드 컨텍스트 경로를 잡을 때 서비스 폴더 안에서 실행해야 하나요?

아니요, 보통은 프로젝트 루트에서 실행하는 것이 관리하기 편해요. 대신 docker-compose.yml의 build 섹션 내에 context: ./service-a와 같이 상대 경로를 정확히 지정해 주면 돼요.

Q. 자동화 스크립트가 너무 길어지면 어떻게 관리하는 것이 좋나요?

스크립트 하나에 모든 것을 넣지 마세요. 단계별로 함수를 나누거나, Makefile을 사용해 기능을 쪼개는 것이 좋아요. 복잡한 로직은 별도의 Shell 스크립트로 분리하고 Makefile에서는 그 스크립트를 호출만 하는 방식이 유지보수에 유리해요.

지속 가능한 자동화를 위한 로드맵

지금까지 컴포즈 build 옵션 자동화와 이를 CI/CD에 녹여내는 과정을 상세히 살펴보았어요. 자동화는 한 번 구축했다고 끝나는 것이 아니라, 서비스가 성장함에 따라 계속해서 다듬어야 하는 살아있는 프로세스예요. 처음에는 간단한 스크립트로 시작해도 괜찮아요. 중요한 것은 수동 작업의 위험성을 인지하고, 조금씩 시스템의 영역으로 넘기는 태도예요.

✅ 핵심 요약

  • .dockerignore를 통해 빌드 컨텍스트를 가볍게 유지하세요.
  • build 섹션에 옵션을 명시하여 명령어 조합의 실수를 방지하세요.
  • Makefile을 활용해 복잡한 명령어를 팀 표준으로 만드세요.
  • 이미지 태깅 시 커밋 해시를 사용하여 롤백 가능성을 확보하세요.
  • Healthcheck를 통해 배포된 컨테이너의 실제 동작을 검증하세요.

오늘 바로 실행할 수 있는 다음 단계들을 제안할게요. 너무 거창하게 시작하지 마세요. 작은 것부터 하나씩 자동화해 나가는 것이 중요해요.

  • 오늘 할 일: 현재 프로젝트 루트에 .dockerignore 파일이 있는지 확인하고, 불필요한 파일들을 추가하세요.
  • 이번 주 할 일: 자주 사용하는 docker-compose 명령어를 Makefile로 정리해 보세요.
  • 실행 직전 할 일: CI/CD 파이프라인에 빌드 단계를 추가하고, 이미지 태그를 커밋 해시로 변경해 보세요.

가장 자주 반복하는 명령 하나부터 스크립트로 옮겨 보세요. 그 작은 변화가 여러분의 퇴근 시간을 앞당겨 줄 거예요. 자동화된 배포 시스템은 여러분을 단순 반복 업무에서 해방시켜, 더 가치 있는 코드 작성에 집중할 수 있게 만들어 줄 거예요.

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

댓글 남기기