로컬에서는 파일의 경로를 찾지만, 도커 환경에서는 Spring Boot 컨테이너에서 파일 경로 못찾는 에러가 발생했다.
Caused by: java.io.FileNotFoundException: src/main/resources/logback-spring.xml (No such file or directory)
at java.base/java.io.FileInputStream.open0(Native Method)
at java.base/java.io.FileInputStream.open(FileInputStream.java:216)
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:157)
at ch.qos.logback.core.joran.GenericXMLConfigurator.doConfigure(GenericXMLConfigurator.java:92)
우선 src/main/resources/
내부에 있는 파일들은 Jar 파일의 BOOT-INF/classes/
에 위치한다. 따라서 혹시나 빌드 과정에서 파일이 포함되지 않았는지 확인해보자.
나와 같은 경우는 스프링 부트 컨테이너 내부로 접속해서, JAR 파일 압축을 풀어 확인해봤더니 logback-spring.xml 이 위치하고 있었다.
sudo docker exec -it {컨테이너 ID} /bin/bash
1번에서 파일이 잘 있는 것을 확인했으니, 컨테이너에서 제대로 인식을 하지 못하는 원인만 남아있었다. 근데 곰곰히 생각해보니 완전 기본적인 것을 놓치고 있었던 것이다.
File
클래스는 파일 시스템 내의 절대 경로를 path로 전달해야 정상적으로 인식할 수 있다. 따라서, 파일 시스템 경로를 참조하는 경우가 아니라면 일반적으로 프로젝트 루트 디렉터리 기준 경로 또는 classpath:logback-xml
과 같은 클래스패스 경로를 통해 파일을 참조할 수 있다.
🔗 classpath: 접두사
JVM의 클래스패스에서 리소스를 찾을 때(ex)ResourceLoader
,@Value
) 사용한다. 도커 컨테이너에서는 JAR 내부의 파일을 직접 참조하는 게 일반적이기 때문에, OS 시스템에 독립적인classpath:
을 사용하는게 더 권장된다.
하지만 당연히 logback-spring.xml
파일은 압축된 JAR 파일 내부에 포함된 리소스기 때문에, 파일 시스템 상에 직접 존재하지 않아 파일 경로를 찾지 못한다는 오류가 났던 것이다.
그렇다면 우리는 도커를 사용하고 있으니, 빌드 과정에서 해당 파일을 도커 컨테이너의 파일 시스템으로 복사해주면 문제를 해결할 수 있다.
도커 파일 시스템은 크게 컨테이너 레이어와 이미지 레이어로 나누어진다.
docker run
이후 만들어지는 Read-Write 레이어docker build
시 만들어지는 Only Read 레이어이다. 이때 도커에서는 Copy-On-Write
전략에 의해, 쓰기 작업은 상위 컨테이너 레이어로 복사되어 이루어진다. 따라서 하나의 이미지로부터 복수의 컨테이너를 실행시켜도 정상적으로 동작하게 된다.
실제 컨테이너 사이즈를 출력했을 때 나오는 크기는 컨테이너 레이어의 크기를 의미한다.
sudo docker ps -s -- size 출력
Size
: 컨테이너 레이어의 데이터의 크기Virtual Size
: 컨테이너 레이어 + 이미지 레이어 데이터 크기깃허브 액션을 통해서 배포를 하고 있었기 때문에, CI 서버 내에 파일을 업로드
할 때 logback-spring.xml
을 추가해주었다.
- name: Build with Gradle
run: SPRING_PROFILES_ACTIVE=test ./gradlew :${{ env.MODULE_NAME }}:clean :${{ env.MODULE_NAME }}:build
shell: bash
- name: Upload build artifact (JAR and Dockerfile)
uses: actions/upload-artifact@v4
with:
name: build-artifacts
path: |
./${{ env.MODULE_NAME }}/build/libs/*.jar
./${{ env.MODULE_NAME }}/build/resources/main/logback-spring.xml
./${{ env.MODULE_NAME }}/*.Dockerfile
그리고 DockerFile에 COPY 명령어를 통해 CI 서버에 있던 logback-spring.xml
파일을 도커 이미지의 config/ 디렉토리로 복사하도록 했다.
FROM eclipse-temurin:17-jdk-jammy
...
COPY build/resources/main/logback-spring.xml config/logback-spring.xml
ENTRYPOINT ["java", "-Duser.timezone=Asia/Seoul", "-jar", "app.jar", "--spring.profiles.active=${PROFILE}"]
COPY
: 도커 이미지 빌드 시, 해당 파일을 이미지 내부로 포함시킨다.즉 아까 위에서 언급한 파일 시스템을 참고해보면, 다음과 같이 복사 과정이 일어나게 된다.
logback-spring.xml
파일이 새로운 이미지 레이어에 저장된다.참고 자료
https://docs.docker.com/reference/dockerfile/#copy
https://docs.docker.com/get-started/docker-concepts/building-images/understanding-image-layers/#create-a-base-image