本文探討雙層方法比單層方法具有具體的好處,並且這些好處是以迭代開發環境中的效率形式出現的。在優化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應用:雙層鏡像方法](http://p2.ttnews.xyz/loading.gif)
為了做到這一點,我們需要一種將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應用:雙層鏡像方法](http://p2.ttnews.xyz/loading.gif)
注意:整個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
閱讀更多 blueapple 的文章