- controller 패키지를 구성하고 TestController.java 파일을 생성하였습니다.
- [GET] http://localhost:8080/api/v1/test/hello로 호출하면 “API 통신에 성공하였습니다.”라고 출력이 되도록 구성하였습니다.
package com.example.simplespringboot.controller;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* Please explain the class!!
*
* @author : jonghoon
* @fileName : TestController
* @since : 2/5/24
*/
@RestController
@RequestMapping("/api/v1/test")
public class TestController {
@GetMapping("/hello")
public ResponseEntity<Object> testApi() {
String result = "API 통신에 성공하였습니다.";
return new ResponseEntity<>(result, HttpStatus.OK);
}
}
2) Dockerfile + Spring Boot App 사용예시 : 구성하기
1. 프로젝트 루트 경로에 Dockerfile을 생성합니다.
2. Dockerfile을 구성합니다.
💡 Dockerfile을 구성합니다
- 아래의 과정은 '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 - 또한 이미지 크기를 줄이기 위해 만들어졌지만, 알파인보다는 더 많은 기능을 포함하고 있습니다.
3) Dockerfile + Spring Boot App 사용예시 : 실행하기
1. 컨테이너 이미지 생성
💡 컨테이너 이미지 생성
- 생성된 이미지를 기반으로 컨테이너를 실행합니다.
# format
$ docker build -t <컨테이너 이미지 이름> .
# 컨테이너 이미지 생성
$ docker build -t simple-spring-boot-app .
💡 [참고] Docker Desktop에서 생성된 컨테이너 이미지를 확인합니다.
2. 컨테이너 실행
💡 컨테이너 실행
- 생성된 이미지를 기반으로 컨테이너를 실행합니다.
명령어
설명
docker run
Docker 이미지를 실행하는 명령어
-d
컨테이너를 백그라운드에서 실행하도록 설정
--name simple-spring-boot-app
실행하는 컨테이너의 이름을 'simple-spring-boot-app'으로 지정
-p 3000:3000
호스트 기기의 8080번 포트와 컨테이너의 8080번 포트를 연결
simple-spring-boot-app
실행할 Docker 이미지의 이름
# format
$ docker run -d --name <컨테이너 이름> -p 8080:8080 <실행할 이미지 이름>
# 컨테이너 생성 및 실행
$ docker run -d --name simple-spring-boot-app -p 8080:8080 simple-spring-boot-app
💡 [참고] Docker Desktop에서 생성된 컨테이너 상태를 확인합니다.
3. 구성한 Spring Boot API로 호출을 해봅니다.
💡 구성한 Spring Boot API로 호출을 해봅니다 - 위에서 구성한 Endpoint로 호출하였을대 결과값을 확인할 수 있습니다.