- 아래의 과정은 'Multi-Stage Build' 방법을 통해서 'Build Stage' 과정에서 애플리케이션을 컴파일하고 빌드하는 과정을 수행하며, 'Run Stage' 과정을 통해서 실제 애플리케이션을 실행하는 과정으로 분리를 하였습니다.
💡 해당 과정을 통해서 아래와 같은 장점을 가지고 있습니다.
- 최소한의 런타임 환경만 포함됩니다 - Build stage에서 생성된 실행 파일만 복사됩니다 - 불필요한 빌드 도구와 소스 코드는 제외됩니다 - 보안성이 향상되고 이미지 크기가 최적화됩니다
# Build stage
FROM bellsoft/liberica-openjdk-alpine:17 AS builder
WORKDIR /app
COPY . .
RUN ./gradlew clean build -x test
# Run stage
FROM bellsoft/liberica-openjdk-alpine:17
WORKDIR /app
COPY --from=builder /app/build/libs/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","app.jar"]
단계
명령어
설명
Build Stage
FROM bellsoft/liberica-openjdk-alpine:17 AS builder
bellsoft의 liberica-openjdk를 이용하여 베이스 이미지를 생성하며 'builder'라는 별칭 부여합니다.
Build Stage
WORKDIR /app
컨테이너 내부에서 작업할 디렉토리를 /app으로 설정합니다.
Build Stage
COPY . .
현재 디렉토리의 모든 소스 코드와 설정 파일을 컨테이너의 /app 디렉토리로 복사합니다.
Build Stage
RUN ./gradlew clean build -x test
Gradle을 사용하여 프로젝트를 빌드하며 JAR 파일 생성합니다.
Run Stage
FROM bellsoft/liberica-openjdk-alpine:17
실행 환경을 위한 새로운 스테이지 시작.
Run Stage
WORKDIR /app
실행 환경에서 사용할 작업 디렉토리를 /app으로 설정
Run Stage
COPY --from=builder /app/build/libs/*.jar app.jar
빌드 스테이지에서 생성된 JAR 파일을 실행 환경으로 복사
Run Stage
EXPOSE 8080
컨테이너가 8080 포트를 사용함을 문서화
Run Stage
ENTRYPOINT ["java","-jar","app.jar"]
컨테이너 시작 시 JAR 파일을 실행하는 명령어 설정
[ 더 알아보기 ] 💡 FROM bellsoft/liberica-openjdk-alpine:17 과정을 두 번 수행하는 이유는 무엇일까?
- Build Stage 과정에서는 애플리케이션을 빌드하기 위한 환경을 설정합니다. 이 단계에서는 소스 코드, 빌드 도구, 의존성 등 빌드에 필요한 모든 것이 포함됩니다. - Run Stage 과정에서는 애플리케이션을 실행하기 위한 최소한의 환경만을 포함합니다. 여기서는 첫 번째 스테이지에서 생성된 JAR 파일만 복사하여 사용합니다.
💡 이전에 구성한 Docker 파일의 이슈사항에 대해 확인하기 위해 포함해 두었습니다
- 위에 부분을 구성하였으면, 아래의 이슈 사항을 알아만 두시면 도움이 될 것 같습니다.
- 해당 부분에서는 COPY ${JAR_FILE} app.jar 부분에서 "no such file or directory" 오류가 발생하는 이슈가 발생하였습니다. - CMD ["./gradlew", "clean", "build"]와 COPY ${JAR_FILE} app.jar의 실행 순서가 잘못되었다는 점이 있습니다. - COPY 명령어는 이미지 빌드 시점에 실행되고, CMD는 컨테이너 실행 시점에 실행되기 때문에, 외부에서 미리 빌드하지 않으면 JAR 파일을 찾지 못하는 문제가 발생합니다.
- 예를 들어서 외부에서 ./gradlew clean build를 수행한 뒤 다시 Dockerfile을 수행하면 수행이 됩니다. 그러나, 번거롭다는 문제점이 있어서 위에와 같은 내용으로 수정을 하였습니다.
FROM bellsoft/liberica-openjdk-alpine:17# or# FROM openjdk:8-jdk-alpine# FROM openjdk:11-jdk-alpine
CMD ["./gradlew", "clean", "build"]
# or Maven # CMD ["./mvnw", "clean", "package"]
VOLUME /tmp
ARG JAR_FILE=build/libs/*.jar# or Maven# ARG JAR_FILE_PATH=target/*.jar
COPY ${JAR_FILE} app.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","/app.jar"]
명령어
설명
FROM bellsoft/liberica-openjdk-alpine:17
베이스 이미지로 Java 17버전이 포함된 Docker 이미지를 사용
# FROM openjdk:8-jdk-alpine
필요시, OpenJDK 8 버전을 기반으로 하는 이미지를 설정할 수 있습니다.
# FROM openjdk:11-jdk-alpine
필요시,OpenJDK 11 버전을 기반으로 하는 이미지를 설정할 수 있습니다.
CMD ["./gradlew", "clean", "build"]
Gradle을 사용해 빌드를 실행하는 명령어
# CMD ["./mvnw", "clean", "package"]
필요시, Mavne을 사용해 빌드를 실행하는 명령어
VOLUME /tmp
컨테이너 내에 /tmp 디렉터리를 볼륨으로 설정
ARG JAR_FILE=build/libs/*.jar
Gradle로 빌드한 jar 파일의 위치를 변수로 설정
ARG JAR_FILE_PATH=target/*.jar
필요시, Maven으로 빌드한 jar 파일의 위치를 변수로 설정
COPY ${JAR_FILE} app.jar
JAR_FILE 변수에 지정된 파일을 app.jar라는 이름으로 컨테이너에 추가
EXPOSE 8080
컨테이너가 사용할 포트를 설정, 이 경우에는 8080 포트를 사용
ENTRYPOINT ["java","-jar","/app.jar"]
컨테이너가 실행될 때 기본적으로 실행될 명령어를 설정, 이 경우에는 Java 애플리케이션을 실행하는 명령어
[ 더 알아보기 ] 💡 mvnw(Maven Wrapper) , Gradle Wrapper란?
- 빌드 도구로 Maven과 Gradle을 이용하는 프로젝트에서 더 쉽게 빌드하고 실행할 수 있도록 돕는 도구입니다. - 이를 이용하면 개발자는 빌드 도구를 별도로 설치하거나 관리할 필요 없이 프로젝트를 빌드할 수 있습니다. - 또한, 모든 개발자와 CI/CD 시스템이 동일한 빌드 도구의 버전을 사용하여 프로젝트를 빌드하므로, 빌드의 일관성과 신뢰성을 높일 수 있습니다.
💡 tmp 볼륨을 생성하는 이유는?
- Docker 컨테이너 내부에 임시 파일을 저장하기 위한 볼륨을 생성합니다. 이는 컨테이너와 호스트 시스템 간의 데이터를 공유하거나, 컨테이너 간에 데이터를 공유하거나, 컨테이너의 데이터를 지속적으로 유지하는 데 사용될 수 있습니다. - 또한 임시 파일은 컨테이너가 종료되었을 때 자동으로 삭제되므로, 이를 통해 디스크 공간을 절약할 수 있습니다.
💡 [참고] Java 17 버전 이상은 아래의 Docker Hub에서 이미지를 다운로드합니다.
[ 더 알아보기 ] 1. java:<version> - 표준 Java Docker 이미지이며, 모든 Java 기능을 포함하고 있습니다. 2. java:<version>-alpine - 리눅스 기반의 Java Docker 이미지로, 이미지 크기를 최소화하기 위해 만들어졌습니다. 그러나 모든 Java 기능을 포함하고 있지 않을 수 있습니다 3. java:<version>-slim - 또한 이미지 크기를 줄이기 위해 만들어졌지만, 알파인보다는 더 많은 기능을 포함하고 있습니다.