이번에는 도커 이미지 빌드에 대한 쉬운 착각이자 오류를 한 가지 얘기해보려고 합니다. 기본적인 서비스를 구축하는 경우에는 보통 이미지를 pull 해와서 run 만 하기 때문에 잘 겪지 않지만, 실제로 서비스를 도커 이미지 기반으로 배포하려고 할 때 대체 왜 안되지? 라는 고민을 가져올 수 있는 케이스입니다.
일반적으로 도커 이미지를 빌드할 때 많이 사용하는 것은 소스 레포의 최상단에 Dockerfile을 만들어놓고 docker build . 이라는 명령어를 실행하는 것일 겁니다. 그런데 만약에 동일한 레포안에서 폴더로 구분되어 있는 각 프로젝트 또는 솔루션마다 Dockerfile을 만들어두고 이미지를 각각 빌드하는 케이스가 필요하다면 어떻게 될까요?
무의식적으로 해당 Dockerfile이 있는 위치를 기준으로 Dockerfile 내부의 상대주소를 설정하게 될 것입니다.
자바 스프링을 사용해서 개발할 경우 가장 간단한 형태로 잡아볼 수 있는 것은 아래의 케이스겠죠.
maven 또는 gradle로 빌드를 한 다음에 빌드된 jar파일을 가지고 이미지를 빌드합니다.
이미지를 빌드할 표준 JDK 버전을 지정하고, 안에 jar파일을 ADD 또는 COPY를 한 다음에 ENTRYPOINT를 지정하는 Dockerfile을 만들어서 빌드하는 것입니다.
FROM OpenJDK11
ADD build/libs/app.jar app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
이런 케이스에서 일반적으로 기대하는 응답은 정상적으로 빌드가 완료되어서 이미지가 생성되는 경우입니다.
그런데 주의를 기울이지 않으면 종종 빌드시에 다음과 같은 에러를 만나게 됩니다.
COPY failed: stat /var/lib/docker/tmp/docker-builder096728530/bin/release/netcoreapp1.1/publish
: no such file or directory
이상하게, 경로를 잘 맞춰줬다고 생각하는데 파일이나 폴더가 없다면서 계속 이미지 빌드가 실패합니다.
에러가 나는 이유는 "실제 빌드를 실행하는 경로"를 기준으로 Dockerfile의 구문을 분석하기 때문입니다.
도커에서는 build context 라고 합니다.
(1)
.
|--- Dockerfile
|--- build
|---libs
|--- app.jar
위와 같은 폴더 구조를 가지고 있는데 Dockerfile의 위치에서 docker build . 이라는 커맨드를 실행하면, 현재 위치를 기준으로 build context가 설정됩니다. 따라서 Dockerfile 내부의 build/libs/app.jar 파일을 찾을 수 있게 됩니다.
(2)
.
|--- project
|--- Dockerfile
|--- build
|---libs
|--- app.jar
하지만 이 구조에서 최상위에서 파일을 지정해서 build를 실행하게 된다면 다른 결과를 가져오게 됩니다.
Dockerfile을 기준으로는 (1)과 (2)번 모두에서 app.jar 파일의 위치가 동일합니다. Dockerfile 위치에서 ./build/libs/app.jar 파일 경로로 들어가면 해당 파일에 접근할 수가 있습니다.
(2)의 경우 해당 폴더 구조의 최상위에서 docker build -f project/Dockerfile . 과 같이 실행하게 되면, docker 이미지를 빌드하기 위한 Dockerfile은 해당 위치의 파일을 직접 찾아가게 되지만 . 이라는 위치를 기준으로 build context가 생성됩니다. 즉, project 를 모두 포함한 상태의 build context 가 지정되는 것이죠.
따라서 도커 이미지를 빌드할 때, 실제 찾아가야하는 app.jar 파일의 위치는 project/build/libs/app.jar 가 됩니다. 반면 위에 있는 Dockerfile은 build/libs/app.jar를 지정하고 있기 때문에 file or folder not found 라는 에러를 만나게 됩니다.
따라서 Dockerfile을 변경하거나 실제 docker build 커맨드를 실행할 위치를 움직이는 방법, 완전한 절대경로로 모든 것을 지정해주는 방법등을 사용해서 문제를 해결할 수 있습니다. 실제로 Dockerfile과 커맨드를 실행하는 위치를 변경하면서 확인해보세요.
댓글