1. 소개

Docker는 독립형 애플리케이션을 만들기 위한 사실상의 표준입니다. 버전 2.3.0부터 Spring Boot 에는 효율적인 Docker 이미지를 생성하는 데 도움이 되는 몇 가지 개선 사항이 포함되어 있습니다. 따라서 응용 프로그램을 다른 계층으로 분해수 있습니다 .

즉, 소스 코드는 자체 레이어에 있습니다. 따라서 독립적으로 재구축할 수 있어 효율성과 시작 시간이 향상됩니다. 이 예제에서는 Docker 레이어를 재사용하기 위해 Spring Boot의 새로운 기능을 활용하는 방법을 볼 것입니다.

2. Docker의 계층화된 항아리

Docker 컨테이너는 기본 이미지와 추가 레이어로 구성됩니다. 레이어가 빌드되면 캐시된 상태로 유지됩니다. 따라서 다음 세대는 훨씬 더 빠릅니다.

하위 수준 레이어의 변경 사항은 상위 수준 레이어도 다시 작성합니다. 따라서 자주 변경되지 않는 레이어는 맨 아래에 두고 자주 변경되는 레이어는 맨 위에 놓아야 합니다.

같은 방식으로 Spring Boot는 아티팩트의 내용을 레이어로 매핑할 수 있습니다. 레이어의 기본 매핑을 살펴보겠습니다.

보시다시피 응용 프로그램에는 자체 계층이 있습니다. 소스 코드를 수정할 때 독립 레이어만 재구축됩니다. 로더와 의존성은 캐시된 상태로 유지되어 Docker 이미지 생성 및 시작 시간을 줄입니다. Spring Boot로 하는 방법을 알아봅시다!

3. 스프링 부트로 효율적인 도커 이미지 생성

Docker 이미지를 구축하는 전통적인 방식에서 Spring Boot는 fat jar 접근 방식을 사용합니다 . 결과적으로 단일 아티팩트에 모든 의존성과 애플리케이션 소스 코드가 포함됩니다. 따라서 소스 코드가 변경되면 전체 계층을 강제로 다시 빌드해야 합니다.

3.1. 스프링 부트를 사용한 계층 구성

Spring Boot 버전 2.3.0 은 Docker 이미지 생성을 개선하기 위해 두 가지 새로운 기능을 도입했습니다 .

  • Buildpack 지원  은 애플리케이션에 대한 Java 런타임을 제공하므로 이제 Dockerfile을 건너뛰고 Docker 이미지를 자동으로 빌드할 수 있습니다.
  • 계층화된 항아리 는 Docker 계층 생성을 최대한 활용하는 데 도움이 됩니다.

이 사용방법(예제)에서는 계층화된 jar 접근 방식을 확장합니다.

처음에는 Maven 에서 계층화된 jar를 설정합니다 . 아티팩트를 패키징할 때 레이어를 생성합니다. jar 파일을 살펴보겠습니다.

jar tf target/spring-boot-docker-0.0.1-SNAPSHOT.jar

보시다시피 , fat jar 내부의 BOOT-INF 폴더에 새 레이어 .idx 파일이 생성됩니다. 확실히 의존성, 리소스 및 애플리케이션 소스 코드를 독립 계층에 매핑합니다.

BOOT-INF/layers.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/"

3.2. 레이어와 상호 작용

아티팩트 내부의 레이어를 나열해 보겠습니다.

java -Djarmode=layertools -jar target/docker-spring-boot-0.0.1.jar list

결과는 layer.idx 파일 의 내용에 대한 간단한 보기를 제공 합니다.

dependencies
spring-boot-loader
snapshot-dependencies
application

레이어를 폴더로 추출할 수도 있습니다.

java -Djarmode=layertools -jar target/docker-spring-boot-0.0.1.jar extract

그런 다음 다음 섹션에서 볼 수 있듯이 Dockerfile 내부의 폴더를 재사용할 수 있습니다.

$ ls
application/
snapshot-dependencies/
dependencies/
spring-boot-loader/

3.3. 도커파일 구성

Docker 기능을 최대한 활용하려면 이미지에 레이어를 추가해야 합니다.

먼저 기본 이미지에 fat jar 파일을 추가해 보겠습니다.

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

마지막으로 추출된 폴더를 복사하여 해당 Docker 레이어를 추가해 보겠습니다.

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"]

이 구성을 사용하면 소스 코드를 변경할 때 응용 프로그램 계층만 다시 빌드합니다. 나머지는 캐시된 상태로 유지됩니다.

4. 커스텀 레이어

모든 것이 매력처럼 작동하는 것 같습니다. 그러나 주의 깊게 살펴보면 의존성 계층이 빌드 간에 공유되지 않습니다 . 즉, 내부 레이어까지 모두 단일 레이어로 나타납니다. 따라서 내부 라이브러리의 클래스를 변경하면 모든 의존성 계층을 다시 빌드합니다.

4.1. Spring Boot를 사용한 사용자 지정 계층 구성

Spring Boot에서는 별도의 구성 파일을 통해 사용자 정의 레이어 를 조정할 수 있습니다.

<layers xmlns="http://www.springframework.org/schema/boot/layers"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/boot/layers
                     https://www.springframework.org/schema/boot/layers/layers-2.3.xsd">
    <application>
        <into layer="spring-boot-loader">
            <include>org/springframework/boot/loader/**</include>
        </into>
        <into layer="application" />
    </application>
    <dependencies>
        <into layer="snapshot-dependencies">
            <include>*:*:*SNAPSHOT</include>
        </into>
        <into layer="dependencies" />
    </dependencies>
    <layerOrder>
        <layer>dependencies</layer>
        <layer>spring-boot-loader</layer>
        <layer>snapshot-dependencies</layer>
        <layer>application</layer>
    </layerOrder>
</layers>

보시다시피 의존성과 리소스를 계층에 매핑하고 정렬합니다. 또한 원하는 만큼 사용자 정의 레이어를 추가할 수 있습니다.

파일의 이름을 layer.xml 로 지정 하겠습니다 . 그런 다음 Maven에서 이 파일을 구성하여 레이어를 사용자 지정할 수 있습니다.

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <layers>
            <enabled>true</enabled>
            <configuration>${project.basedir}/src/layers.xml</configuration>
        </layers>
    </configuration>
</plugin>

아티팩트를 패키징하면 결과가 기본 동작과 유사합니다.

4.2. 새 레이어 추가

애플리케이션 클래스를 추가하여 내부 의존성을 생성해 보겠습니다.

<into layer="internal-dependencies">
    <include>com.baeldung.docker:*:*</include>
</into>

또한 새 레이어를 주문합니다.

<layerOrder>
    <layer>internal-dependencies</layer>
</layerOrder>

결과적으로 fat jar 내부의 레이어를 나열하면 새로운 내부 의존성이 나타납니다.

dependencies
spring-boot-loader
internal-dependencies
snapshot-dependencies
application

4.3. 도커파일 구성

추출이 완료되면 Docker 이미지에 새 내부 계층을 추가할 수 있습니다.

COPY --from=builder internal-dependencies/ ./

따라서 이미지를 생성하면 Docker가 내부 의존성을 새 계층으로 빌드하는 방법을 볼 수 있습니다.

$ mvn package
$ docker build -f src/main/docker/Dockerfile . --tag spring-docker-demo
....
Step 8/11 : COPY --from=builder internal-dependencies/ ./
 ---> 0e138e074118
.....

그런 다음 Docker 이미지에서 레이어 구성 기록을 확인할 수 있습니다.

$ docker history --format "{{.ID}} {{.CreatedBy}} {{.Size}}" spring-docker-demo
c0d77f6af917 /bin/sh -c #(nop)  ENTRYPOINT ["java" "org.s… 0B
762598a32eb7 /bin/sh -c #(nop) COPY dir:a87b8823d5125bcc4… 7.42kB
80a00930350f /bin/sh -c #(nop) COPY dir:3875f37b8a0ed7494… 0B
0e138e074118 /bin/sh -c #(nop) COPY dir:db6f791338cb4f209… 2.35kB
e079ad66e67b /bin/sh -c #(nop) COPY dir:92a8a991992e9a488… 235kB
77a9401bd813 /bin/sh -c #(nop) COPY dir:f0bcb2a510eef53a7… 16.4MB
2eb37d403188 /bin/sh -c #(nop)  ENV JAVA_HOME=/opt/java/o… 0B

보시다시피 레이어에는 이제 프로젝트의 내부 의존성이 포함됩니다.

5. 결론

이 사용방법(예제)에서는 효율적인 Docker 이미지를 생성하는 방법을 보여주었습니다. 요컨대, 우리는 새로운 Spring Boot 기능을 사용하여 계층화된 항아리를 생성했습니다. 간단한 프로젝트의 경우 기본 구성을 사용할 수 있습니다. 또한 레이어를 재사용하기 위한 고급 구성을 시연했습니다.

항상 그렇듯이 코드는 GitHub 에서 사용할 수 있습니다 .

Generic footer banner