← 전체 글로 돌아가기

Docker

Docker 멀티스테이지 빌드에서 데이터가 사라지는 이유

Docker의 멀티스테이지 빌드에서 빌드 스테이지의 산출물이 최종 이미지에 복사되지 않는 문제를 다루고 확인하는 방법을 정리했다.

Docker의 멀티스테이지 빌드는 이미지 크기를 줄이는 강력한 도구지만, 잘못 쓰면 빌드는 성공하는데 런타임에 필요한 파일이 사라지는 일이 생긴다.

멀티스테이지 빌드의 구조 이해하기

멀티스테이지 빌드에서는 각 FROM 명령이 새로운 이미지 레이어를 시작한다:

# 첫 번째 스테이지: 빌드
FROM node:18 as builder
WORKDIR /app
COPY . .
RUN npm ci && npm run build

# 두 번째 스테이지: 런타임
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
CMD ["node", "dist/index.js"]

핵심은 builder 스테이지에서 만든 파일을 명시적으로 COPY --from=builder로 가져와야 한다는 것이다. 그렇지 않으면 최종 이미지에는 없다.

가장 흔한 실수: COPY 누락

빌드 스테이지에서 파일을 만들었는데 최종 이미지로 복사하지 않은 경우:

FROM node:18 as builder
WORKDIR /app
COPY . .
RUN npm run build
# dist 폴더 생성됨

FROM node:18-alpine
WORKDIR /app
COPY . .  # 이전 스테이지의 것이 아니라 호스트의 파일을 복사
# 호스트의 dist는 아직 없거나 이전 빌드 결과일 수 있음

이 경우 런타임에 dist 폴더가 없어서 앱이 실행되지 않는다.

COPY --from을 제대로 쓰기

# 올바른 방법
FROM node:18 as builder
WORKDIR /app
COPY . .
RUN npm ci && npm run build

FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist /app/dist
COPY --from=builder /app/node_modules /app/node_modules
CMD ["node", "dist/index.js"]

주의할 점:

  • COPY --from=builder /app/dist ./dist : 경로를 정확히 지정해야 한다
  • 스테이지 이름 대신 스테이지 인덱스(0, 1)로도 가능: COPY --from=0 /app/dist ./dist

배포 후 데이터 확인 방법

이미지가 제대로 빌드됐는지 확인하는 가장 간단한 방법은 컨테이너를 실행한 후 파일이 있는지 보는 것이다:

# 이미지에서 dist 폴더 존재 확인
docker run --rm <image-name> ls -la /app/dist

# 혹은 컨테이너 내부 셸 접속
docker run --rm -it <image-name> /bin/sh
ls -la /app/dist

빌드 로그 확인

docker build 명령의 출력을 자세히 보면, 각 스테이지가 몇 단계를 거쳤는지 알 수 있다:

docker build --progress=plain -t myapp .
# STEP 1부터 N까지의 진행 과정을 볼 수 있다

만약 빌드 명령(예: npm run build)이 실패했다면 여기서 에러가 보인다.

스테이지 네이밍으로 실수 방지하기

여러 스테이지가 있을 때는 명확하게 이름을 붙이는 게 좋다:

FROM node:18 as base
WORKDIR /app
COPY package*.json ./
RUN npm ci

FROM base as builder
COPY . .
RUN npm run build

FROM node:18-alpine as runtime
WORKDIR /app
COPY --from=builder /app/dist /app/dist
COPY --from=base /app/node_modules /app/node_modules
CMD ["node", "dist/index.js"]

이렇게 하면 어느 스테이지에서 뭘 복사하는지 명확해진다.

로컬 테스트 시 최종 이미지 검증

Docker 이미지를 만들기 전에 로컬에서 빌드가 제대로 되는지 항상 확인하자:

npm run build
ls -la dist

멀티스테이지 빌드의 문제는 대부분 경로 불일치나 누락된 COPY 명령에서 비롯된다. 배포 전에 한 번 docker run으로 실제 실행해보면 이런 문제를 미리 잡을 수 있다.