Docker 빌드 실패 해결: 종합 문제 해결 가이드
잘못된 경로, 누락된 패키지, 캐시 문제, 네트워크 이슈, 권한 또는 디스크 공간으로 인한 Docker 빌드 실패를 디버깅합니다.
Docker 빌드 실패 해결: 종합 문제 해결 가이드
Docker 빌드 실패는 빌드 출력을 기록으로 취급하면 더 쉽게 해결할 수 있습니다. Docker는 어떤 단계에서 실패했는지, 어떤 명령이 실행되었는지, 명령이 출력한 내용을 알려줍니다. 유용한 작업은 빌드가 실패했다는 마지막 줄이 아니라 첫 번째 실제 오류를 찾는 것입니다.
기본 출력이 너무 많은 정보를 숨길 때는 평문 진행 상황으로 빌드를 실행하세요:
docker build --progress=plain -t my-app:debug .
실패한 단계 번호(예: #8)와 그 옆에 있는 명령어를 찾으세요. 실패한 명령어가 COPY라면 빌드 컨텍스트나 경로 문제일 가능성이 높습니다. RUN apt-get install이라면 패키지, 네트워크, 저장소 또는 아키텍처 문제입니다. RUN npm ci 또는 pip install이라면 Docker 설정을 변경하기 전에 패키지 관리자 오류를 읽어보세요.
COPY failed: 파일이 빌드 컨텍스트에 없음
가장 흔한 빌드 오류 중 하나는 가장 간단한 오류이기도 합니다:
COPY failed: file not found in build context or excluded by .dockerignore
Docker는 빌드 컨텍스트 내부에 있는 파일만 복사할 수 있습니다. 빌드 컨텍스트는 일반적으로 docker build의 마지막 인수입니다:
docker build -t my-app .
여기서 .이 컨텍스트입니다. 하위 디렉토리에 있는 Dockerfile은 해당 컨텍스트 외부에서 ../secret.txt를 복사할 수 없습니다. Docker는 빌드가 컨텍스트에서 재현 가능해야 하기 때문에 의도적으로 이를 차단합니다.
세 가지를 확인하세요:
pwd
ls -la
docker build --progress=plain -f path/to/Dockerfile .
Dockerfile이 docker/Dockerfile에 있지만 앱이 저장소 루트에 있다면, 루트에서 빌드하고 -f로 Dockerfile을 지정하세요:
docker build -f docker/Dockerfile -t my-app .
또한 .dockerignore를 확인하세요. 복사하려는 파일을 제외할 수 있습니다. 이는 종종 dist, target, .env 또는 생성된 파일에서 발생합니다. Dockerfile이 파일을 기대한다면, 빌드 내부에서 파일이 생성되지 않는 한 무시하지 마세요.
패키지 설치 실패
패키지 관리자 실패는 일반적으로 몇 가지 범주로 나뉩니다: 오래된 패키지 인덱스, 잘못된 패키지 이름, 누락된 저장소, 네트워크 문제 또는 잘못된 Linux 배포판의 명령 사용.
Alpine은 apt-get을 사용하지 않기 때문에 Alpine에서 실패합니다:
FROM alpine:3.20
RUN apt-get update && apt-get install -y curl
기본 이미지에 맞는 패키지 관리자를 사용하세요:
FROM alpine:3.20
RUN apk add --no-cache curl
Debian 또는 Ubuntu 이미지의 경우 업데이트와 설치를 동일한 레이어에 유지하세요:
RUN apt-get update && apt-get install -y --no-install-recommends curl ca-certificates && rm -rf /var/lib/apt/lists/*
apt-get install이 패키지를 찾을 수 없다고 하면, 해당 배포판 버전의 패키지 이름을 확인하세요. 패키지 이름은 Debian, Ubuntu, Alpine, Fedora 및 언어별 이미지 간에 다릅니다. 최소 이미지에는 bash, curl, git, tar 또는 ca-certificates와 같이 있다고 가정한 도구가 없을 수도 있습니다.
HTTPS 다운로드가 인증서 오류로 실패하면, curl, wget, HTTPS를 통한 git, npm, pip 또는 언어 패키지 관리자를 사용하기 전에 CA 인증서를 설치하세요:
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates && rm -rf /var/lib/apt/lists/*
캐시 문제
Docker의 캐시는 일반적으로 유용하지만, 깨진 빌드를 일관성 없게 만들 수 있습니다. 오래된 캐시가 의심된다면 다음을 실행하세요:
docker build --no-cache --progress=plain -t my-app:debug .
캐시 없이만 빌드가 실패한다면 오래된 레이어에 의존하고 있었을 수 있습니다. 캐시로만 실패한다면 생성된 파일이나 종속성 잠금 파일이 Docker가 예상하는 방식으로 변경되었는지 확인하세요.
종속성 설치의 경우, 전체 소스 트리보다 먼저 잠금 파일을 복사하세요:
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
Python의 경우:
WORKDIR /app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
이 패턴은 더 빠를 뿐만 아니라 종속성 설치가 모든 소스 변경이 아닌 종속성 파일에 의존하기 때문에 빌드 실패를 이해하기 쉽게 만듭니다.
네트워크 및 레지스트리 실패
Docker가 기본 이미지를 가져올 수 없으면 Dockerfile이 실행되기 전에 빌드가 실패할 수 있습니다:
docker pull python:3.12-slim
실패하면 먼저 레지스트리 액세스를 수정하세요. 개인 레지스트리의 인증, 회사 프록시 설정, DNS 및 방화벽 규칙을 확인하세요. 프록시 뒤에서는 Docker 데몬에 프록시 구성이 필요합니다. 대화형 셸에서만 HTTP_PROXY를 설정하는 것으로는 충분하지 않을 수 있습니다.
RUN 단계 내부의 다운로드의 경우, 호스트에서 URL을 테스트한 다음 동일한 네트워크 경로의 임시 컨테이너에서 테스트하세요:
curl -I https://example.com/file.tar.gz
docker run --rm curlimages/curl -I https://example.com/file.tar.gz
가능하다면 고정되지 않은 원격 스크립트에 의존하지 마세요. 이동 중인 브랜치에서 install.sh를 curl하는 Dockerfile은 원격 스크립트가 변경되면 깨질 수 있습니다. 바이너리의 경우 버전이 지정된 다운로드와 체크섬 검증을 선호하세요:
RUN curl -fsSLo tool.tar.gz https://example.com/tool-1.2.3-linux-amd64.tar.gz && echo '<sha256> tool.tar.gz' | sha256sum -c - && tar -xzf tool.tar.gz -C /usr/local/bin && rm tool.tar.gz
<sha256>을 프로젝트 릴리스 페이지의 실제 체크섬으로 바꾸세요.
아키텍처 불일치
Apple Silicon 또는 혼합 CI 환경에서 아키텍처로 인해 빌드 실패가 발생할 수 있습니다. 이미지나 다운로드한 바이너리가 amd64인데 빌더가 arm64이거나 그 반대일 수 있습니다. 증상으로는 exec format error, 아키텍처에 대한 패키지 누락 또는 빌드 중 바이너리 실패가 있습니다.
호스트와 대상을 확인하세요:
docker version
docker buildx ls
필요할 때 특정 플랫폼용으로 빌드하세요:
docker buildx build --platform linux/amd64 -t my-app:amd64 .
주의하세요: 크로스 플랫폼 빌드는 에뮬레이션이 포함될 때 더 느릴 수 있습니다. CI의 경우 각 플랫폼에 대한 네이티브 빌더가 더 빠르고 덜 놀랍습니다.
빌드 중 권한 오류
파일이 예상치 못한 소유권으로 복사되거나, 스크립트가 실행 가능하지 않거나, Dockerfile이 설정 완료 전에 비루트 사용자로 전환될 때 빌드에서 권한이 실패합니다.
스크립트가 permission denied로 실패하면 Dockerfile에 가정을 복사하기 전에 확인하세요:
ls -l scripts/start.sh
그런 다음 git에서 또는 이미지에서 수정하세요:
COPY scripts/start.sh /usr/local/bin/start.sh
RUN chmod +x /usr/local/bin/start.sh
비루트 런타임 사용자를 사용하는 경우, 사용자를 전환하기 전에 디렉토리를 만들고 소유권을 설정하세요:
RUN useradd -r -u 10001 appuser && mkdir -p /app/data && chown -R appuser:appuser /app
USER appuser
COPY --chown=appuser:appuser . .는 루트로 복사한 다음 광범위한 재귀적 chown을 실행하는 것보다 종종 더 깔끔합니다.
디스크 공간 및 빌드 캐시 정리
큰 빌드는 Docker 호스트의 디스크 공간이 부족하여 실패할 수 있습니다. Docker의 사용량을 확인하세요:
docker system df
적절할 때 사용하지 않는 빌드 캐시를 제거하세요:
docker builder prune
광범위한 정리 명령에는 더 주의하세요. docker system prune -a는 사용하지 않는 이미지를 제거하며, 이는 대규모 재가져오기를 강제하거나 로컬 이미지에 의존하는 워크플로우를 깨뜨릴 수 있습니다. 영향을 이해할 때 사용하세요.
빌드가 정기적으로 디스크를 채우는 경우, 더 나은 해결책은 일반적으로 더 작은 빌드 컨텍스트, 다단계 빌드 및 레이어에서 거대한 임시 파일을 피하는 것입니다. 임시 아티팩트를 생성하는 동일한 RUN 명령어에서 정리하세요.
실패한 RUN 단계를 대화형으로 디버그
긴 RUN 줄이 실패하면 임시로 분할하세요:
RUN apt-get update
RUN apt-get install -y --no-install-recommends packageA packageB
RUN some-command-that-fails
실패한 명령을 찾으면 더 깔끔한 이미지를 위해 관련 명령을 다시 결합할 수 있습니다.
또 다른 유용한 트릭은 알려진 좋은 단계에서 멈추는 것입니다. Dockerfile에 단계가 있는 경우 하나의 대상을 빌드하세요:
docker build --target builder -t my-app-builder .
docker run --rm -it my-app-builder sh
거기서 파일을 검사하고, 실패한 명령을 수동으로 실행하고, 환경 변수를 확인하고, 파일 시스템에 실제로 무엇이 있는지 볼 수 있습니다.
셸이 없는 이미지의 경우 프로덕션 이미지를 오염시키지 않도록 임시 디버그 단계를 추가하세요:
FROM builder AS debug
RUN apt-get update && apt-get install -y --no-install-recommends bash curl
--target debug로 빌드하고, 조사한 다음 완료되면 디버그 대상을 제거하거나 무시하세요.
빌드를 예측 가능하게 유지
신뢰할 수 있는 Docker 빌드는 가장 좋은 의미에서 지루합니다. 버전이 지정된 기본 이미지를 사용하세요. 종속성 잠금 파일을 소스 제어에 유지하세요. 프로덕션 Dockerfile에서 latest를 피하세요. 프로세스가 의도적으로 이동하는 태그에 대해 재빌드하고 테스트하지 않는 한 말이죠. .dockerignore를 빡빡하게 유지하세요. 네트워크 다운로드를 버전화하고 검증하세요. 자주 변경되는 소스 코드를 종속성 설치 후에 배치하세요.
빌드가 실패할 때 전체 Dockerfile을 한 번에 다시 작성하지 마세요. 실패한 명령어를 식별하고, 평문 로그로 재현하고, 명령을 분리하고, 가장 작은 실제 원인을 수정하세요. 이 접근 방식이 더 빠르며, 다음 사람이 여전히 이해할 수 있는 Dockerfile을 남깁니다.