본문 바로가기

운영체제 및 플랫폼/Docker(도커)

Spring Boot 도커 배포 실습 — Dockerfile 작성부터 서버 실행까지

728x90
반응형

한 줄 요약

Spring Boot 앱을 도커 이미지로 만들면 서버 환경과 상관없이 같은 방식으로 실행할 수 있어요. 공식 문서의 계층 추출 방식을 쓰면 이미지 캐시도 효율적으로 활용할 수 있어요.


1. 핵심 개념

 

1.1 Spring Boot를 도커로 배포한다는 것

Spring Boot 앱은 보통 실행 가능한 jar 파일 하나로 빌드돼요. 이 jar와 Java 실행 환경을 함께 이미지로 묶으면, 서버에 Java를 따로 설치하지 않아도 앱을 실행할 수 있어요.

uber jar: 앱 코드와 모든 의존 라이브러리를 하나로 묶은 jar 파일이에요. Spring Boot가 기본으로 만드는 형태예요.

 

1.2 왜 계층(layer)을 나누나요

Spring Boot 공식 문서는 uber jar를 그대로 이미지에 넣는 방식의 단점을 설명해요.

  • 앱 코드와 의존 라이브러리가 한 레이어에 들어가요.
  • 코드 한 줄만 바꿔도 라이브러리까지 포함된 레이어 전체가 바뀌어요.

보통 앱 코드는 자주 바뀌고, 라이브러리는 덜 바뀌어요. 그래서 둘을 다른 레이어로 나누면 바뀐 부분만 다시 만들고 전송할 수 있어요.


2. 쉽게 이해하기

계층 분리는 이사할 때 짐 싸기와 같아요.

  • 가구(라이브러리)는 거의 바뀌지 않아요.
  • 옷과 책(앱 코드)은 자주 바뀌어요.
  • 모든 짐을 상자 하나에 넣으면, 책 한 권 바뀔 때마다 상자 전체를 다시 싸야 해요.
  • 상자를 나눠 두면 바뀐 상자만 다시 싸면 돼요.

3. 실습 절차

 

3.1 jar 파일 빌드

# Gradle
./gradlew bootJar

# Maven
./mvnw package

결과물 위치는 Gradle이 build/libs/, Maven이 target/이에요.

Gradle에서 ./gradlew build를 쓰면 -plain.jar도 함께 생길 수 있어요. 이 경우 아래 Dockerfile의 *.jar가 두 파일과 일치해 오류가 나요. bootJar만 실행하거나 build.gradle에서 jar { enabled = false }로 plain jar 생성을 끌 수 있어요.

 

3.2 Dockerfile 작성

Spring Boot 공식 문서의 예시를 Gradle 경로로 맞춘 내용이에요.

# 1단계: jar에서 계층 추출
FROM bellsoft/liberica-openjre-debian:25-cds AS builder
WORKDIR /builder

ARG JAR_FILE=build/libs/*.jar
COPY ${JAR_FILE} application.jar

RUN java -Djarmode=tools -jar application.jar extract --layers --destination extracted

# 2단계: 실행 이미지
FROM bellsoft/liberica-openjre-debian:25-cds
WORKDIR /application

COPY --from=builder /builder/extracted/dependencies/ ./
COPY --from=builder /builder/extracted/spring-boot-loader/ ./
COPY --from=builder /builder/extracted/snapshot-dependencies/ ./
COPY --from=builder /builder/extracted/application/ ./

ENTRYPOINT ["java", "-jar", "application.jar"]

 

동작 설명

  • -Djarmode=tools ... extract --layers: jar를 계층별 폴더로 풀어요.
  • COPY가 4번 나뉘어 있어요. 공식 문서 설명대로 COPY 한 번이 레이어 하나가 돼요.
  • 순서는 잘 안 바뀌는 것(dependencies)에서 자주 바뀌는 것(application) 순이에요.

확인할 점

  • 공식 예시는 Java 25 기반 이미지를 사용해요. 프로젝트의 Java 버전에 맞는 이미지로 바꿔야 해요.
  • jarmode=tools 방식은 최신 Spring Boot 문서 기준이에요. 사용 중인 Spring Boot 버전의 공식 문서에서 명령어를 확인하세요. 이전 버전 문서에는 layertools라는 다른 명령이 안내되어 있어요.

 

3.3 이미지 빌드

docker build -t myapp:1.0 .

 

jar 경로를 직접 지정할 수도 있어요.

docker build --build-arg JAR_FILE=build/libs/myapp-0.0.1.jar -t myapp:1.0 .

 

3.4 컨테이너 실행

docker run -d --name myapp \
  -p 8080:8080 \
  -e SPRING_PROFILES_ACTIVE=prod \
  myapp:1.0

Spring Boot는 환경 변수를 설정 값으로 읽어요. SPRING_PROFILES_ACTIVE는 spring.profiles.active, SPRING_DATASOURCE_URL은 spring.datasource.url에 대응해요.

 

3.5 실행 확인

docker ps
docker logs -f myapp
curl http://localhost:8080

4. 서버에 배포하기

로컬에서 만든 이미지를 서버로 옮기는 기본 흐름이에요.

# [로컬] 레지스트리에 업로드
docker tag myapp:1.0 <레지스트리 주소>/<계정>/myapp:1.0
docker push <레지스트리 주소>/<계정>/myapp:1.0

# [서버] 내려받기
docker pull <레지스트리 주소>/<계정>/myapp:1.0

# [서버] 기존 컨테이너 교체
docker stop myapp
docker rm myapp
docker run -d --name myapp \
  -p 8080:8080 \
  --env-file ./app.env \
  <레지스트리 주소>/<계정>/myapp:1.0
  • --env-file: 환경 변수를 파일에서 읽어요. DB 비밀번호 같은 값을 이미지에 넣지 않고 실행 시점에 전달할 수 있어요.
  • 이 방식은 기존 컨테이너를 멈춘 뒤 새 컨테이너를 띄우므로, 교체하는 동안 서비스가 잠시 중단돼요. 중단을 줄이는 방법은 배포 전략(롤링, 블루-그린 등)에서 다뤄요.

5. 핵심 정리

  • Spring Boot 앱은 jar와 Java 실행 환경을 이미지로 묶어 배포해요.
  • 공식 문서는 jar를 계층별로 추출해 레이어를 나누는 방식을 안내해요.
  • 라이브러리와 앱 코드를 나누면 바뀐 레이어만 다시 만들고 전송해요.
  • 설정과 비밀 정보는 환경 변수나 --env-file로 실행 시점에 전달해요.
  • 단순 교체 배포는 교체 시간 동안 서비스가 중단돼요.

6. 추가 정보

Dockerfile 없이 이미지 만들기 (Buildpacks)

Spring Boot는 빌드 도구 플러그인으로 Cloud Native Buildpacks를 지원해요. Dockerfile 없이 이미지를 만들 수 있어요. 실행하려면 도커가 동작 중이어야 해요.

# Gradle
./gradlew bootBuildImage --imageName=myapp:1.0

# Maven
./mvnw spring-boot:build-image -Dspring-boot.build-image.imageName=myapp:1.0

종료 신호 처리

docker stop은 컨테이너의 주 프로세스에 SIGTERM을 보내고, 대기 시간이 지나면 SIGKILL을 보내요. ENTRYPOINT를 exec 형식(JSON 배열)으로 쓰면 Java 프로세스가 신호를 직접 받아요.

SIGTERM: 프로그램에 정상 종료를 요청하는 신호예요.

SIGKILL: 프로그램을 강제로 종료하는 신호예요.

주의사항

  • 서버 주소, 포트, 계정명은 실제 값으로 바꿔야 해요. 공개 글이나 저장소에는 실제 값을 남기지 마세요.
  • 이미지 크기와 시작 시간 개선 폭은 앱마다 달라서, 공식 문서에 확정 수치가 제시되어 있지 않아요.

관련 글

 

반응형