지난 포스트 Spring Boot(Gradle) 프로젝트 Docker 이미지 빌드 및 배포하기 에서 Gradle 기반으로 Jar를 빌드하고 실행하는 Spring Boot 컨테이너 이미지를 빌드하고 배포해보았습니다. 이번에는 Maven 기반의 프로젝트를 Docker 이미지로 빌드하기 위한 Dockerfile 작성법에 대해 알아봅시다.
다음은 Spring Boot 컨테이너 이미지를 빌드하기 위한 Dockerfile
의 전체 내용입니다. Gradle 기반의 프로젝트와 마찬가지로 JVM 기반의 언어(Java, Kotlin)로 작성된 Spring Boot는 Jar 빌드와 런타임 과정이 모두 Docker 이미지 빌드 과정에 포함되어야 합니다.
# 빌드 스테이지
FROM eclipse-temurin:17-alpine AS build
RUN apk add --no-cache bash
WORKDIR /app
COPY mvnw .
COPY .mvn .mvn
COPY pom.xml .
RUN ./mvnw dependency:go-offline -B
COPY . .
RUN chmod +x ./mvnw
RUN ./mvnw package -DskipTests
# 런타임 스테이지
FROM eclipse-temurin:17-jre-alpine as runtime
WORKDIR /app
RUN addgroup -g 1000 worker && \
adduser -u 1000 -G worker -s /bin/sh -D worker
COPY --from=build --chown=worker:worker /app/target/*.jar ./main.jar
USER worker:worker
ENV PROFILE=${PROFILE}
EXPOSE 8080
ENTRYPOINT ["java", "-Dspring.profiles.active=${PROFILE}", "-jar", "main.jar"]
1. FROM
은 Docker 이미지의 바탕이 되는 베이스 이미지를 세팅하는 명령어 입니다. 소스 코드를 바탕으로 Jar 파일을 빌드하는 단계이기 때문에 AS build
를 작성하여 빌드 스테이지임을 명시합니다.
빌드 스테이지에서 사용된 이미지는 alpine linux를 바탕으로 한 eclipse-temurin:17-alpine
입니다. 구글, 레드햇, 마이크로소프트 등 여러 글로벌 IT 기업으로 구성된 Adoptium 워킹 그룹이 Java 생태계 활성화를 위해 OpenJDK를 바탕으로 개발한 오픈소스 JDK 구현체이며, 라이선스에 구애 받지 않고 사용할 수 있습니다.
FROM eclipse-temurin:17-alpine AS build
2. Maven 스크립트 실행을 위해 베이스 이미지에 bash를 설치 합니다. --no-cache
옵션을 사용하면 로컬 캐시가 아닌 최신 패키지 정보를 로드하여 설치를 진행합니다.
RUN apk add --no-cache bash
3. 루트 디렉토리에서의 작업을 피하고 빌드할 대상 어플리케이션을 다른 디렉토리와 분리하기 위해 작업 디렉토리를 /app
으로 설정합니다. /app
은 다른 어플리케이션을 도커 이미지로 빌드할 때에도 관례로 사용되는 디렉토리명입니다.
WORKDIR /app
4. Maven 빌드에 필요한 파일 및 디렉토리를 빌드 스테이지로 복사합니다.
COPY mvnw .
COPY .mvn .mvn
COPY pom.xml .
5. Spring Boot 어플리케이션에서 사용되는 의존성을 다운로드합니다. Docker는 컨테이너 이미지 빌드 시 하나의 명령을 하나의 레이어로 관리하는데, 각각의 레이어는 캐시 처리가 되어 변동 사항이 있는 부분에 대해서만 추가로 빌드하게 됩니다. 즉, 새로 이미지를 빌드 하더라도 모든 의존성을 재설치 하지 않아도 되기 때문에 보다 더 효율적인 작업이 가능합니다.
go-offline
는 모든 의존성을 로컬에 설치하는 dependency
명령의 옵션이며, 대화형 프롬프트에 의해 이미지 빌드가 중지되는 것을 방지하기 위해 -B
옵션도 추가합니다.
RUN ./mvnw dependency:go-offline -B
6. 소스 코드를 빌드 스테이지로 전부 복사합니다.
COPY . .
7. ./mvnw
에 실행 권한을 부여합니다.
RUN chmod +x ./mvnw
8. Maven을 통한 jar 빌드 명령을 실행합니다. 테스트 과정을 제외하기 위해 -DskipTests
를 작성합니다. 만약 테스트 관련 태스크를 실행해야 하는 경우라면 이 부분을 적절히 수정해야 합니다. 여기서 패키징된 jar 파일은 이어지는 런타임 스테이지에서 활용됩니다.
RUN ./mvnw package -DskipTests
9. 런타임 스테이지에서는 Java 빌드 없이 Jar 파일을 실행하는 과정만 수행하면 되므로 크기가 더 작은 eclipse-temurin:17-jre-alpine
이미지를 사용합니다.
FROM eclipse-temurin:17-jre-alpine AS runtime
10. 작업 디렉토리를 /app
으로 변경합니다. 여기서 가리키는 /app
은 빌드 스테이지의 /app
과는 다른 위치이므로 유의합니다.
WORKDIR /app
11. 어플리케이션을 실행 업무를 수행하는 리눅스 그룹 및 사용자를 worker
라는 이름으로 신규 생성합니다. 1000
은 리눅스에서 루트가 아닌 첫 번째 일반 사용자를 가리키는 UID/GID입니다. 만약 이러한 설정 없이 루트 사용자로 하여금 어플리케이션을 실행하도록 하면 악의적인 공격에 의해 루트 사용자 권한이 탈취되어 시스템 전체에 영향을 끼치는 결과를 초래할 수 있습니다.
RUN addgroup -g 1000 worker && \
adduser -u 1000 -G worker -s /bin/sh -D worker
12. 빌드 스테이지에서 생성된 Jar 파일을 --from=build
옵션을 사용하여 런타임 스테이지로 복사합니다. 이 때, 복사할 파일의 그룹 및 사용자를 앞에서 생성했던 1000번 그룹/사용자 worker
로 세팅합니다. Gradle과 빌드 산출물의 위치가 다른 점에 유의합니다.
COPY --from=build --chown=worker:worker /app/target/*.jar ./main.jar
13. 컨테이너를 루트가 아닌 worker:worker
가 실행하도록 설정합니다.
USER worker:worker
14. Spring Boot 런타임에 적용할 프로필을 환경변수로 세팅합니다. 여기서 세팅한 환경변수는 마지막 단계의 Jar 런타임 옵션에 적용됩니다.
ENV PROFILE=${PROFILE}
15. 개방할 포트를 8080
으로 설정합니다. 이는 실제로 컨테이너 포트를 개방하는 작업을 수행하지 않으며, 배포 담당자에게 개방할 포트를 알려주는 역할을 합니다. 컨테이너의 개방 포트가 결정되는 시점은 docker run
명령어를 사용하는 것과 같이 실제 컨테이너로 구동될 때이고 이 때 옵션을 통해 호스트측과 컨테이너측의 포트를 각각 설정합니다.
EXPOSE 8080
16. java -jar
명령에 빌드 스테이지에서 복사한 Jar 파일을 인수로 넘겨 Spring Boot 어플리케이션을 실행합니다. 여기서 JVM 옵션이나 Spring 관련 설정을 옵션으로 추가할 수 있습니다.
ENTRYPOINT ["java", "-Dspring.profiles.active=${PROFILE}", "-jar", "main.jar"]
Docker 이미지를 빌드는 과정에는 소스 코드를 복사하는 과정이 필요합니다. 그런데 이 때, 이미지의 용량을 불필요하게 키우거나 보안 측면에 문제를 야기할 수 있는 파일은 이미지 빌드 과정에서 제외하는 것이 좋습니다. 다음의 내용을 담은 .dockerignore
파일을 루트 디렉토리에 추가합니다.
.gradle/
build/
target/
.idea/
*.iml
*.iws
*.ipr
.vscode/
*.log
.DS_Store
Thumbs.db
src/main/resources/application-*.yml
src/main/resources/application-*.properties
src/test/
README.md
LICENSE
*.md
.git/
.gitignore
# Dockerfile
# docker-compose.yml
node_modules/
*.class
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
hs_err_pid*
실습은 아래의 Spring Boot 어플리케이션을 통해 진행됩니다. 저장소를 clone 하거나 fork 해주세요.
클라우드타입의 프로젝트 페이지에서 ➕ 버튼을 누르고 Dockerfile를 선택한 후, 미리 fork 해놓은 springboot-crud-maven 를 선택합니다. Dockerfile의 경로 및 이름의 변경이 필요한 경우 Dockerfile path 필드에 원하는 값을 입력합니다.
배포가 완료되면 접속하기 버튼을 누르고 주소창에 /api/users
경로를 추가하여 접속한 후 초기 데이터가 조회되는지 확인합니다.