優化Docker中的Spring Boot應用:雙層鏡像方法

本文探討雙層方法比單層方法具有具體的好處,並且這些好處是以迭代開發環境中的效率形式出現的。在優化Docker中的Spring Boot應用:單層鏡像方法中,我們介紹了為Spring Boot應用構建Docker鏡像的單層方法及其對CI/CD的影響。我提出了雙層方法比單層方法有更大的好處,並且這些好處可以提高開發環境中的迭代效率。

在這裡,我們介紹一種使用Open Liberty中的新工具springBootUtility,為現有Spring Boot應用創建雙層Docker鏡像。目前有多種方法可以為Spring Boot應用創建多層Docker鏡像,但是本文方法的重點是從現有應用創建雙層鏡像,而不是更改maven或gradle構建步驟。

雙層方法

在雙層方法中,我們構建這樣的Docker鏡像:以使Spring Boot應用的依賴存在於應用代碼下方的一層中。通過將不經常更改的依賴推入一個單獨的層,並且僅將應用類保留在頂層,這樣迭代重建和重新部署就會更快。


優化Docker中的Spring Boot應用:雙層鏡像方法

為了做到這一點,我們需要一種將Spring Boot應用拆分為這些獨立組件的工具,例如:springBootUtility。

springBootUtility

springBootUtility是Open Liberty中的新工具,它將把Spring Boot應用分為兩部分:依賴庫(例如Spring Boot啟動程序和其他第三方庫)以及應用代碼。依賴庫放置在緩存中,應用代碼用於構造一個精簡後的應用程序。精簡後的應用程序包含一個文件,該文件引用類路徑上所需的庫。然後可以將此精簡應用程序部署到Open Liberty,它將從庫緩存中生成完整的類路徑。

Docker多階段構建

用於構建雙鏡像層的Dockerfile,可以使用多階段構建。多階段構建允許單個Dockerfile創建多個鏡像,其中一個鏡像的內容可以複製到另一個鏡像中,從而丟棄臨時內容。這使你可以大幅度減小最終鏡像的大小,而無需涉及多個Docker文件。我們使用此功能在Docker構建過程中拆分Spring Boot應用。

Docker鏡像

Docker鏡像可以使用帶有Open J9和 Open Liberty的Open JDK。Open JDK為開源Java技術提供了堅實的基礎。與 Open JDK附帶的默認Java虛擬機相比, Open J9 帶來了一些性能改進。Open Liberty是一個多程序模型運行時,支持Java EE,MicroProfile和Spring。這就方便開發團隊可以使用具有運行時堆棧一致的各種編程模型。

Dockerfile示例

Dockerfile(我們將逐步介紹它的每一步操作)。

<code>FROM adoptopenjdk/openjdk8-openj9 as staging

ARG JAR_FILE
ENV SPRING_BOOT_VERSION 2.0

# Install unzip; needed to unzip Open Liberty
RUN apt-get update \\
    && apt-get install -y --no-install-recommends unzip \\
    && rm -rf /var/lib/apt/lists/*

# Install Open Liberty
ENV LIBERTY_SHA 4170e609e1e4189e75a57bcc0e65a972e9c9ef6e
ENV LIBERTY_URL https://public.dhe.ibm.com/ibmdl/export/pub/software/openliberty/runtime/release/2018-06-19_0502/openliberty-18.0.0.2.zip

RUN curl -sL "$LIBERTY_URL" -o /tmp/wlp.zip \\
   && echo "$LIBERTY_SHA  /tmp/wlp.zip" > /tmp/wlp.zip.sha1 \\
   && sha1sum -c /tmp/wlp.zip.sha1 \\
   && mkdir /opt/ol \\
   && unzip -q /tmp/wlp.zip -d /opt/ol \\
   && rm /tmp/wlp.zip \\
   && rm /tmp/wlp.zip.sha1 \\
   && mkdir -p /opt/ol/wlp/usr/servers/springServer/ \\
   && echo spring.boot.version="$SPRING_BOOT_VERSION" > /opt/ol/wlp/usr/servers/springServer/bootstrap.properties \\
   && echo \\
' \\
<server> \\
  <featuremanager> \\
    <feature>jsp-2.3/<feature> \\
    <feature>transportSecurity-1.0/<feature> \\
    <feature>websocket-1.1/<feature> \\
    <feature>springBoot-${spring.boot.version}/<feature> \\
  /<featuremanager> \\
  <httpendpoint> \\
  <include> \\
/<server>' > /opt/ol/wlp/usr/servers/springServer/server.xml \\
   && /opt/ol/wlp/bin/server start springServer \\
   && /opt/ol/wlp/bin/server stop springServer \\
   && echo \\
' \\
<server> \\

  <springbootapplication> \\
/<server>' > /opt/ol/wlp/usr/servers/springServer/appconfig.xml

# Stage the fat JAR
COPY ${JAR_FILE} /staging/myFatApp.jar

# Thin the fat application; stage the thin app output and the library cache
RUN /opt/ol/wlp/bin/springBootUtility thin \\
 --sourceAppPath=/staging/myFatApp.jar \\
 --targetThinAppPath=/staging/myThinApp.jar \\
 --targetLibCachePath=/staging/lib.index.cache

# unzip thin app to avoid cache changes for new JAR
RUN mkdir /staging/myThinApp \\
   && unzip -q /staging/myThinApp.jar -d /staging/myThinApp

# Final stage, only copying the liberty installation (includes primed caches)
# and the lib.index.cache and thin application
FROM adoptopenjdk/openjdk8-openj9

VOLUME /tmp

# Create the individual layers
COPY --from=staging /opt/ol/wlp /opt/ol/wlp
COPY --from=staging /staging/lib.index.cache /opt/ol/wlp/usr/shared/resources/lib.index.cache
COPY --from=staging /staging/myThinApp /opt/ol/wlp/usr/servers/springServer/apps/app

# Start the app on port 9080
EXPOSE 9080
CMD ["/opt/ol/wlp/bin/server", "run", "springServer"]/<code>

Dockerfile細節解讀

使用Docker的多階段構建和Open Liberty中的springBootUtility,Dockerfile就可以把Spring Boot應用拆分。

我們把基礎鏡像openjdk8-openj9命名為stagging鏡像。

首先,我們安裝unzip。

接下來,我們下載Open Liberty並進行一些配置。所有這些準備工作都需要使用Open Liberty工具。雖然它現在很簡陋,但是我們將在Docker鏡像18.0.0.2發佈 時改進完善。

鏡像具有所需的所有工具後,將程序JAR文件複製到stagging鏡像中並進行拆分。

在/staging/myFatApp.jar基礎上我們會創建一個精簡後的應用,將其解壓後可以進行進一步的優化。解壓使應用程序直接從類文件中託管。如果類文件沒有更改,後續重建可以重新使用應用層。

現在準備工作已經完成,然後我們就可以使用COPY命令,拷貝整個Liberty安裝,依賴庫和精簡後的應用。在Dockerfile中,單獨的COPY命令會生成單獨的層。較大的庫依賴層(34.2MB)和較小的應用層(1.01MB)將會是“雙重層”的含義。

<code>$ docker history openlibertyio/spring-petclinic
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
883ee6374f66        7 minutes ago       /bin/sh -c #(nop)  CMD ["/opt/ol/wlp/bin/ser…   0B
e3ba1351fc05        7 minutes ago       /bin/sh -c #(nop)  EXPOSE 9080                  0B
86c646de6626        7 minutes ago       /bin/sh -c #(nop) COPY dir:589967d5ae0ade9a5…   1.01MB
8f98ce0a6c10        7 minutes ago       /bin/sh -c #(nop) COPY dir:d764c6a82219ed564…   34.2MB
240306c081cd        7 minutes ago       /bin/sh -c #(nop) COPY dir:0b45938a62d056d88…   200MB
161006b94f8e        22 minutes ago      /bin/sh -c #(nop)  VOLUME [/tmp]                0B
f50ba84462ab        3 weeks ago         /bin/sh -c #(nop)  ENV PATH=/opt/java/openjd…   0B
<missing>           3 weeks ago         /bin/sh -c set -eux;     ARCH="$(dpkg --prin…   193MB
<missing>           3 weeks ago         /bin/sh -c #(nop)  ENV JAVA_VERSION=jdk8u162…   0B
<missing>           3 weeks ago         /bin/sh -c rm -rf /var/lib/apt/lists/* && ap…   16MB
<missing>           3 weeks ago         /bin/sh -c #(nop)  MAINTAINER Dinakar Gunigu…   0B
<missing>           2 months ago        /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B
<missing>           2 months ago        /bin/sh -c mkdir -p /run/systemd && echo 'do…   7B
<missing>           2 months ago        /bin/sh -c sed -i 's/^#\\s*\\(deb.*universe\\)$…   2.76kB
<missing>           2 months ago        /bin/sh -c rm -rf /var/lib/apt/lists/*          0B
<missing>           2 months ago        /bin/sh -c set -xe   && echo '#!/bin/sh' > /…   745B
<missing>           2 months ago        /bin/sh -c #(nop) ADD file:592c2540de1c70763…   113MB/<missing>/<missing>/<missing>/<missing>/<missing>/<missing>/<missing>/<missing>/<missing>/<missing>/<code>

現在,當進行應用更改時,僅需要更改應用層。

Dockerfile運行效果

你可以複製此Dockerfile,並運行它。

<code>$ docker build --build-arg JAR_FILE=target/spring-petclinic-2.0.0.BUILD-SNAPSHOT.jar -t openlibertyio/spring-petclinic .//<code>

生成的Docker鏡像如下所示:


優化Docker中的Spring Boot應用:雙層鏡像方法

注意:整個Docker鏡像並不像單層方法那麼小。因為基本鏡像不是基於Alpine Linux的,Liberty的安裝也不是最簡化。我們也正在努力改善這一點。

未來的方向

到目前為止,我們對所構建的內容感到滿意,但是,構建這些鏡像的用戶體驗不是很好。我們將在接下來的幾個月中繼續努力。還將發佈包含預配置的Open Liberty實例的Docker鏡像。這將大大降低Dockerfile的複雜性。

我們還認識到,將這些雙層構建集成到持續交付中,還有改進的空間。我們也有興趣去解決在Docker中的Spring Boot應用體驗。

最後,這種從應用中分離靜態依賴的方法並不是Spring Boot應用獨有的!使用Java EE或MicroProfile應用也可以獲得類似的效率。這也是我們正在探索的另一個領域。

參考文獻

https://github.com/gclayburg/dockerPreparePlugin


譯文地址:https://openliberty.io/blog/2018/07/02/creating-dual-layer-docker-images-for-spring-boot-apps.html


分享到:


相關文章: