1. 소개

더 많은 조직이 컨테이너와 가상 서버로 이동함에 따라 Docker는 소프트웨어 개발 워크플로의 중요한 부분이 되고 있습니다. 이를 위해 Spring Boot 2.3의 새로운 기능 중 하나는 Spring Boot 애플리케이션용 Docker 이미지를 쉽게 생성할 수 있는 기능입니다.

이 예제에서는 Spring Boot 애플리케이션을 위한 Docker 이미지를 생성하는 방법을 살펴볼 것입니다.

2. 전통적인 Docker 빌드

Spring Boot로 Docker 이미지를 구축하는 전통적인 방법은 Dockerfile을 사용하는 것입니다. 다음은 간단한 예입니다.

FROM openjdk:8-jdk-alpine
EXPOSE 8080
ARG JAR_FILE=target/demo-app-1.0.0.jar
ADD ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

그런 다음 docker build 명령을 사용하여 Docker 이미지를 만들 수 있습니다. 이것은 대부분의 응용 프로그램에서 잘 작동하지만 몇 가지 단점이 있습니다.

먼저 Spring Boot에서 생성한 fat jar를 사용하고 있습니다. 이는 특히 컨테이너화된 환경에서 시작 시간에 영향을 줄 수 있습니다 . 대신 jar 파일의 분해된 내용을 추가하여 시작 시간을 절약할 수 있습니다.

둘째, Docker 이미지는 레이어로 빌드됩니다. Spring Boot 팻 jar의 특성으로 인해 모든 애플리케이션 코드와 타사 라이브러리가 단일 계층에 배치됩니다. 즉, 한 줄의 코드만 변경되더라도 전체 레이어를 다시 빌드해야 합니다.

빌드하기 전에 jar를 폭발시키면 애플리케이션 코드와 타사 라이브러리가 각각 고유한 레이어를 갖게 됩니다. 이를 통해 Docker의 캐싱 메커니즘을 활용할 수 있습니다. 이제 한 줄의 코드가 변경되면 해당 레이어만 다시 빌드하면 됩니다.

이를 염두에 두고 Spring Boot가 Docker 이미지 생성 프로세스를 어떻게 개선했는지 살펴보겠습니다.

3. 빌드팩

빌드팩은 프레임워크 및 애플리케이션 의존성을 제공하는 도구입니다 .

예를 들어, Spring Boot 팻 jar가 주어지면 빌드팩은 우리에게 자바 런타임을 제공할 것입니다. 이를 통해 Dockerfile을 건너뛰고 합리적인 Docker 이미지를 자동으로 얻을 수 있습니다.

Spring Boot는 빌드팩에 대한 Maven 및 Gradle 지원을 모두 포함합니다. 예를 들어 Maven으로 빌드하는 경우 다음 명령을 실행합니다.

./mvnw spring-boot:build-image

무슨 일이 일어나고 있는지 보기 위해 관련 출력 중 일부를 살펴보겠습니다.

[INFO] Building jar: target/demo-0.0.1-SNAPSHOT.jar
...
[INFO] Building image 'docker.io/library/demo:0.0.1-SNAPSHOT'
...
[INFO]  > Pulling builder image 'gcr.io/paketo-buildpacks/builder:base-platform-api-0.3' 100%
...
[INFO]     [creator]     ===> DETECTING
[INFO]     [creator]     5 of 15 buildpacks participating
[INFO]     [creator]     paketo-buildpacks/bellsoft-liberica 2.8.1
[INFO]     [creator]     paketo-buildpacks/executable-jar    1.2.8
[INFO]     [creator]     paketo-buildpacks/apache-tomcat     1.3.1
[INFO]     [creator]     paketo-buildpacks/dist-zip          1.3.6
[INFO]     [creator]     paketo-buildpacks/spring-boot       1.9.1
...
[INFO] Successfully built image 'docker.io/library/demo:0.0.1-SNAPSHOT'
[INFO] Total time:  44.796 s

첫 번째 줄은 일반적인 maven 패키지와 마찬가지로 표준 팻 항아리를 구축했음을 보여줍니다.

다음 줄은 Docker 이미지 빌드를 시작합니다. 직후에 Packeto 빌더 에서 빌드 풀을 볼 수 있습니다.

Packeto는 클라우드 네이티브 빌드팩의 구현입니다. 우리 프로젝트를 분석하고 필요한 프레임워크와 라이브러리를 결정하는 작업을 수행합니다 . 우리의 경우 Spring Boot 프로젝트가 있는지 확인하고 필요한 빌드팩을 추가합니다.

마지막으로 생성된 Docker 이미지와 총 빌드 시간이 표시됩니다. 처음 빌드할 때 빌드팩을 다운로드하고 다른 레이어를 만드는 데 상당한 시간을 할애합니다.

빌드팩의 훌륭한 기능 중 하나는 Docker 이미지가 여러 계층이라는 것입니다. 따라서 애플리케이션 코드만 변경하면 후속 빌드가 훨씬 빨라집니다.

...
[INFO]     [creator]     Reusing layer 'paketo-buildpacks/executable-jar:class-path'
[INFO]     [creator]     Reusing layer 'paketo-buildpacks/spring-boot:web-application-type'
...
[INFO] Successfully built image 'docker.io/library/demo:0.0.1-SNAPSHOT'
...
[INFO] Total time:  10.591 s

4. 겹겹이 ​​쌓인 항아리

경우에 따라 빌드팩을 사용하지 않는 것을 선호할 수 있습니다. 인프라가 이미 다른 도구에 연결되어 있거나 재사용하려는 사용자 지정 Dockerfile이 이미 있을 수 있습니다.

이러한 이유로 Spring Boot는 계층화된 jar를 사용하여 Docker 이미지 빌드도 지원합니다 . 작동 방식을 이해하기 위해 일반적인 Spring Boot 팻 jar 레이아웃을 살펴보겠습니다.

org/
  springframework/
    boot/
  loader/
...
BOOT-INF/
  classes/
...
lib/
...

팻 항아리는 3가지 주요 영역으로 구성됩니다.

  • Spring 애플리케이션을 시작하는 데 필요한 부트스트랩 클래스
  • 애플리케이션 코드
  • 타사 라이브러리

계층화된 항아리의 경우 구조가 비슷해 보이지만 팻 항아리의 각 디렉토리를 레이어에 매핑 하는 새로운 layer.idx 파일을 얻습니다 .

- "dependencies":
  - "BOOT-INF/lib/"
- "spring-boot-loader":
  - "org/"
- "snapshot-dependencies":
- "application":
  - "BOOT-INF/classes/"
  - "BOOT-INF/classpath.idx"
  - "BOOT-INF/layers.idx"
  - "META-INF/"

즉시 사용 가능한 Spring Boot는 4개의 계층을 제공합니다.

  • 의존성 : 타사의 일반적인 의존성
  • snapshot-dependencies : 타사의 스냅샷 의존성
  • 리소스 : 정적 리소스
  • application : 애플리케이션 코드 및 리소스

목표는 애플리케이션 코드와 타사 라이브러리를 변경 빈도를 반영하는 레이어에 배치하는 것 입니다.

예를 들어 응용 프로그램 코드는 가장 자주 변경되므로 고유한 계층을 갖게 됩니다. 또한 각 레이어는 자체적으로 진화할 수 있으며 레이어가 변경된 경우에만 Docker 이미지에 대해 다시 빌드됩니다.

이제 새로운 계층화된 jar 구조를 이해했으므로 이를 활용하여 Docker 이미지를 만드는 방법을 살펴보겠습니다.

4.1. 계층화 된 항아리 만들기

먼저, 계층화된 항아리를 생성하도록 프로젝트를 설정해야 합니다. Maven에서 이는 POM의 Spring Boot 플러그인 섹션에 새 구성을 추가하는 것을 의미합니다.

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <layers>
            <enabled>true</enabled>
        </layers>
    </configuration>
</plugin>

이 구성을 사용하면 Maven 패키지 명령(모든 종속 명령과 함께)이 앞에서 언급한 4개의 기본 계층을 사용하여 새 계층화된 jar를 생성합니다.

4.2. 레이어 보기 및 추출

다음으로 Docker 이미지에 적절한 레이어가 포함되도록 jar에서 레이어를 추출해야 합니다.

계층화된 jar의 계층을 검사하기 위해 다음 명령을 실행할 수 있습니다.

java -Djarmode=layertools -jar demo-0.0.1.jar list

그런 다음 추출하기 위해 다음을 실행합니다.

java -Djarmode=layertools -jar demo-0.0.1.jar extract

4.3. 도커 이미지 생성

이러한 레이어를 Docker 이미지에 통합하는 가장 쉬운 방법은 Dockerfile을 사용하는 것입니다.

FROM adoptopenjdk:11-jre-hotspot as builder
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} application.jar
RUN java -Djarmode=layertools -jar application.jar extract

FROM adoptopenjdk:11-jre-hotspot
COPY --from=builder dependencies/ ./
COPY --from=builder snapshot-dependencies/ ./
COPY --from=builder spring-boot-loader/ ./
COPY --from=builder application/ ./
ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]

이 Dockerfile은 우리의 fat jar에서 레이어를 추출한 다음 각 레이어를 Docker 이미지에 복사합니다. COPY 지시문은 최종 Docker 이미지에 새 계층을 생성 합니다.

이 Dockerfile을 빌드하면 계층화된 jar의 각 계층이 Docker 이미지에 자체 계층으로 추가되는 것을 볼 수 있습니다.

...
Step 6/10 : COPY --from=builder dependencies/ ./
 ---> 2c631b8f9993
Step 7/10 : COPY --from=builder snapshot-dependencies/ ./
 ---> 26e8ceb86b7d
Step 8/10 : COPY --from=builder spring-boot-loader/ ./
 ---> 6dd9eaddad7f
Step 9/10 : COPY --from=builder application/ ./
 ---> dc80cc00a655
...

5. 결론

이 예제에서는 Spring Boot를 사용하여 Docker 이미지를 빌드하는 다양한 방법을 보았습니다. 빌드팩을 사용하면 상용구 또는 사용자 지정 구성 없이 적합한 Docker 이미지를 얻을 수 있습니다. 또는 조금 더 노력하면 계층화된 항아리를 사용하여 더 맞춤화된 Docker 이미지를 얻을 수 있습니다.

이 예제의 모든 예제는 GitHub 에서 찾을 수 있습니다 .

Java 및 Docker 사용에 대한 자세한 내용 은 jib 에 대한 사용방법(예제)를 확인하십시오 .

Generic footer banner