1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        構(gòu)建 Java 鏡像的 10 個(gè)最佳實(shí)踐

        共 8677字,需瀏覽 18分鐘

         ·

        2022-08-24 23:52

        你想構(gòu)建一個(gè) Java 應(yīng)用程序并在 Docker 中運(yùn)行它嗎?你知道在使用 Docker 構(gòu)建 Java 容器有哪些最佳實(shí)踐?


        在下面的速查表中,我將為你提供構(gòu)建生產(chǎn)級(jí) Java 容器的最佳實(shí)踐,旨在優(yōu)化和保護(hù)要投入生產(chǎn)環(huán)境中的 Docker 鏡像。


        構(gòu)建一個(gè)簡(jiǎn)單的 Java 容器鏡像


        讓我們從簡(jiǎn)單的 Dockerfile 開(kāi)始,在構(gòu)建 Java 容器時(shí),我們經(jīng)常會(huì)有如下類(lèi)似的內(nèi)容:


        FROM mavenRUN mkdir /appWORKDIR /appCOPY . /appRUN mvn clean installCMD "mvn" "exec:java"


        $ docker build . -t java-application$ docker run -p 8080:8080 java-application


        這很簡(jiǎn)單,而且有效。但是,此鏡像充滿(mǎn)錯(cuò)誤。


        我們不僅應(yīng)該了解如何正確使用 Maven,而且還應(yīng)避免像上述示例那樣構(gòu)建 Java 容器。


        下面,讓我們開(kāi)始逐步改進(jìn)這個(gè)Dockerfile,使你的Java應(yīng)用程序生成高效,安全的Docker鏡像。


        1. Docker 鏡像使用確定性的標(biāo)簽


        當(dāng)使用 Maven 構(gòu)建 Java 容器鏡像時(shí),我們首先需要基于 Maven 鏡像。但是,你知道使用 Maven 基本鏡像時(shí)實(shí)際上引入了哪些內(nèi)容嗎?


        當(dāng)你使用下面的代碼行構(gòu)建鏡像時(shí),你將獲得該 Maven 鏡像的最新版本:


        FROM maven


        這似乎是一個(gè)有趣的功能,但是這種采用 Maven 默認(rèn)鏡像的策略可能存在一些潛在問(wèn)題:


        • 你的 Docker 構(gòu)建不是冪等的。這意味著每次構(gòu)建的結(jié)果可能會(huì)完全不同,今天的最新鏡像可能不同于明天或下周的最新鏡像,導(dǎo)致你的應(yīng)用程序的字節(jié)碼也是不同的,并且可能發(fā)生意外。因此,構(gòu)建鏡像時(shí),我們希望具有可復(fù)制的確定性行為;

        • Maven Docker 鏡像是基于完整的操作系統(tǒng)鏡像。這樣會(huì)導(dǎo)致許多其他二進(jìn)制文件出現(xiàn)在最終的生產(chǎn)鏡像中,但是運(yùn)行你的 Java 應(yīng)用程序不需要很多這些二進(jìn)制文件。因此,將它們作為 Java 容器鏡像的一部分存在一些缺點(diǎn):1) 鏡像體積變大,導(dǎo)致更長(zhǎng)的下載和構(gòu)建時(shí)間。2) 額外的二進(jìn)制文件可能會(huì)引入安全漏洞。


        如何解決?


        • 使用適合你需求的最小基礎(chǔ)鏡像??紤]一下——你是否需要一個(gè)完整的操作系統(tǒng)(包括所有額外的二進(jìn)制文件)來(lái)運(yùn)行你的程序?如果沒(méi)有,也許基于 alpine 鏡像或 Debian 的鏡像會(huì)更好;

        • 使用特定的鏡像 如果使用特定的鏡像,則已經(jīng)可以控制和預(yù)測(cè)某些行為。如果我使用 maven:3.6.3-jdk-11-slim 鏡像,則已經(jīng)確定我正在使用 JDK 11 和 Maven 3.6.3。JDK 和 Maven 的更新,將不再影響 Java 容器的行為。為了更加精確,你也可以使用鏡像的 SHA256 哈希值。使用哈希將確保你每次構(gòu)建鏡像時(shí)都使用完全相同的基礎(chǔ)鏡像。


        讓我們用這些知識(shí)更新我們的 Dockerfile:


        FROM maven:3.6.3-jdk-11-slim@sha256:68ce1cd457891f48d1e137c7d6a4493f60843e84c9e2634e3df1d3d5b381d36cRUN mkdir /appWORKDIR /appCOPY . /appRUN mvn clean package -DskipTests


        2. 在 Java 鏡像中僅安裝需要的內(nèi)容


        以下命令會(huì)在容器中構(gòu)建 Java 程序,包括其所有依賴(lài)項(xiàng)。這意味著源代碼和構(gòu)建系統(tǒng)都將會(huì)是 Java 容器的一部分。


        RUN mvn clean package -DskipTests


        我們都知道 Java 是一種編譯語(yǔ)言。這意味著我們只需要由你的構(gòu)建環(huán)境創(chuàng)建的工件,而不需要代碼本身。這也意味著構(gòu)建環(huán)境不應(yīng)成為 Java 鏡像的一部分。


        要運(yùn)行 Java 鏡像,我們也不需要完整的 JDK。一個(gè) Java 運(yùn)行時(shí)環(huán)境(JRE)就足夠了。因此,從本質(zhì)上講,如果它是可運(yùn)行的 JAR,那么只需要使用 JRE 和已編譯的 Java 構(gòu)件來(lái)構(gòu)建鏡像。


        使用 Maven 在 CI 流水線(xiàn)中都構(gòu)建編譯程序,然后將JAR復(fù)制到鏡像中,如下面的更新的 Dockerfile 中所示:


        FROM openjdk:11-jre-slim@sha256:31a5d3fa2942eea891cf954f7d07359e09cf1b1f3d35fb32fedebb1e3399fc9eRUN mkdir /appCOPY ./target/java-application.jar /app/java-application.jarWORKDIR /appCMD "java" "-jar" "java-application.jar"


        3. 使用多階段構(gòu)建 Java 鏡像


        在本文的前面,我們談到了我們不需要在容器中構(gòu)建 Java 應(yīng)用程序。但是,在某些情況下,將我們的應(yīng)用程序構(gòu)建為 Docker 鏡像的一部分很方便。


        我們可以將 Docker 鏡像的構(gòu)建分為多個(gè)階段。我們可以使用構(gòu)建應(yīng)用程序所需的所有工具來(lái)構(gòu)建鏡像,并在最后階段創(chuàng)建實(shí)際的生產(chǎn)鏡像。


        FROM maven:3.6.3-jdk-11-slim@sha256:68ce1cd457891f48d1e137c7d6a4493f60843e84c9e2634e3df1d3d5b381d36c AS buildRUN mkdir /projectCOPY . /projectWORKDIR /projectRUN mvn clean package -DskipTests


        FROM adoptopenjdk/openjdk11:jre-11.0.9.1_1-alpine@sha256:b6ab039066382d39cfc843914ef1fc624aa60e2a16ede433509ccadd6d995b1fRUN mkdir /appCOPY --from=build /project/target/java-application.jar /app/java-application.jarWORKDIR /appCMD "java" "-jar" "java-application.jar"


        4. 防止敏感信息泄漏


        在創(chuàng)建 Java 應(yīng)用程序和 Docker 鏡像時(shí),很有可能需要連接到私有倉(cāng)庫(kù),類(lèi)似 settings.xml 的配置文件經(jīng)常會(huì)泄露敏感信息。但在使用多階段構(gòu)建時(shí),你可以安全地將 settings.xml 復(fù)制到你的構(gòu)建容器中。帶有憑據(jù)的設(shè)置將不會(huì)出現(xiàn)在你的最終鏡像中。此外,如果將憑據(jù)用作命令行參數(shù),則可以在構(gòu)建鏡像中安全地執(zhí)行此操作。


        使用多階段構(gòu)建,你可以創(chuàng)建多個(gè)階段,僅將結(jié)果復(fù)制到最終的生產(chǎn)鏡像中。這種分離是確保在生產(chǎn)環(huán)境中不泄漏數(shù)據(jù)的一種方法。


        哦,順便說(shuō)一句,使用 docker history 命令查看 Java 鏡像的輸出:


        $ docker history java-application


        輸出僅顯示來(lái)自容器鏡像的信息,而不顯示構(gòu)建鏡像的過(guò)程。


        5.不要以 root 用戶(hù)運(yùn)行容器


        創(chuàng)建 Docker 容器時(shí),你需要應(yīng)用最小特權(quán)原則,防止由于某種原因攻擊者能夠入侵你的應(yīng)用程序,則你不希望他們能夠訪(fǎng)問(wèn)所有內(nèi)容。


        擁有多層安全性,可以幫助你減少系統(tǒng)威脅。因此,必須確保你不以 root 用戶(hù)身份運(yùn)行應(yīng)用程序。


        但默認(rèn)情況下,創(chuàng)建 Docker 容器時(shí),你將以 root 身份運(yùn)行它。盡管這對(duì)于開(kāi)發(fā)很方便,但是你不希望在生產(chǎn)鏡像中使用它。假設(shè)由于某種原因,攻擊者可以訪(fǎng)問(wèn)終端或可以執(zhí)行代碼。在那種情況下,它對(duì)正在運(yùn)行的容器具有顯著的特權(quán),并且訪(fǎng)問(wèn)主機(jī)文件系統(tǒng)。


        解決方案非常簡(jiǎn)單。創(chuàng)建一個(gè)有限特權(quán)的特定用戶(hù)來(lái)運(yùn)行你的應(yīng)用程序,并確保該用戶(hù)可以運(yùn)行該應(yīng)用程序。最后,在運(yùn)行應(yīng)用程序之前,不要忘記使用新創(chuàng)建的用戶(hù)。

        讓我們相應(yīng)地更新我們的 Dockerfile。


        FROM maven:3.6.3-jdk-11-slim@sha256:68ce1cd457891f48d1e137c7d6a4493f60843e84c9e2634e3df1d3d5b381d36c AS buildRUN mkdir /projectCOPY . /projectWORKDIR /projectRUN mvn clean package -DskipTests


        FROM adoptopenjdk/openjdk11:jre-11.0.9.1_1-alpine@sha256:b6ab039066382d39cfc843914ef1fc624aa60e2a16ede433509ccadd6d995b1fRUN mkdir /appRUN addgroup --system javauser && adduser -S -s /bin/false -G javauser javauserCOPY --from=build /project/target/java-application.jar /app/java-application.jarWORKDIR /appRUN chown -R javauser:javauser /appUSER javauserCMD "java" "-jar" "java-application.jar"


        6. Java 應(yīng)用程序不要使用 PID 為 1 的進(jìn)程


        在許多示例中,我看到了使用構(gòu)建環(huán)境來(lái)啟動(dòng)容器化 Java 應(yīng)用程序的常見(jiàn)錯(cuò)誤。

        上面,我們了解了要在  Java 容器中使用 Maven 或 Gradle 的重要性,但是使用如下命令,會(huì)有不同的效果:


        • CMD “mvn” “exec:java”

        • CMD [“mvn”, “spring-boot run”]

        • CMD “gradle” “bootRun”

        • CMD “run-app.sh”


        在 Docker 中運(yùn)行應(yīng)用程序時(shí),第一個(gè)應(yīng)用程序?qū)⒁赃M(jìn)程 ID 為 1(PID=1)運(yùn)行。Linux內(nèi)核會(huì)以特殊方式處理 PID 為 1 的進(jìn)程。通常,進(jìn)程號(hào)為 1 的 PID 上的過(guò)程是初始化過(guò)程。如果我們使用 Maven 運(yùn)行 Java 應(yīng)用程序,那么如何確定 Maven 將類(lèi)似 SIGTERM 信號(hào)轉(zhuǎn)發(fā)給 Java 進(jìn)程呢?


        如果像下面的示例,那樣運(yùn)行 Docker 容器,則Java應(yīng)用程序?qū)⒕哂?PID 為 1 的進(jìn)程。


        CMD "java" "-jar" "application.jar"


        請(qǐng)注意,docker kill 和 docker stop 命令僅向 PID 為 1 的容器進(jìn)程發(fā)送信號(hào)。例如,如果你正在運(yùn)行 Java 應(yīng)用的 shell 腳本,/bin/sh 不會(huì)將信號(hào)轉(zhuǎn)發(fā)給子進(jìn)程。


        更為重要的是,在 Linux 中,PID 為 1 的容器進(jìn)程還有一些其他職責(zé)。因此,在某些情況下,你不希望應(yīng)用程序成為 PID 為 1 的進(jìn)程,因?yàn)槟悴恢廊绾翁幚磉@些問(wèn)題。一個(gè)很好的解決方案是使用 dumb-init。


        RUN apk add dumb-initCMD "dumb-init" "java" "-jar" "java-application.jar"


        當(dāng)你像這樣運(yùn)行 Docker 容器時(shí),dumb-init 會(huì)占用 PID 為1的容器進(jìn)程并承擔(dān)所有責(zé)任。你的 Java 流程不再需要考慮這一點(diǎn)。


        我們更新后的 Dockerfile 現(xiàn)在看起來(lái)像這樣:


        FROM maven:3.6.3-jdk-11-slim@sha256:68ce1cd457891f48d1e137c7d6a4493f60843e84c9e2634e3df1d3d5b381d36c AS buildRUN mkdir /projectCOPY . /projectWORKDIR /projectRUN mvn clean package -DskipTests


        FROM adoptopenjdk/openjdk11:jre-11.0.9.1_1-alpine@sha256:b6ab039066382d39cfc843914ef1fc624aa60e2a16ede433509ccadd6d995b1fRUN apk add dumb-initRUN mkdir /appRUN addgroup --system javauser && adduser -S -s /bin/false -G javauser javauserCOPY --from=build /project/target/java-code-workshop-0.0.1-SNAPSHOT.jar /app/java-application.jarWORKDIR /appRUN chown -R javauser:javauser /appUSER javauserCMD "dumb-init" "java" "-jar" "java-application.jar"


        7. 優(yōu)雅下線(xiàn) Java 應(yīng)用程序


        當(dāng)你的應(yīng)用程序收到關(guān)閉信號(hào)時(shí),理想情況下,我們希望所有內(nèi)容都能正常關(guān)閉。根據(jù)你開(kāi)發(fā)應(yīng)用程序的方式,中斷信號(hào)(SIGINT)或 CTRL + C 可能導(dǎo)致立即終止進(jìn)程。

        這可能不是你想要的東西,因?yàn)橹T如此類(lèi)的事情可能會(huì)導(dǎo)致意外行為,甚至導(dǎo)致數(shù)據(jù)丟失。


        當(dāng)你將應(yīng)用程序作為 Payara 或 Apache Tomcat 之類(lèi)的 Web 服務(wù)器的一部分運(yùn)行時(shí),該 Web 服務(wù)器很可能會(huì)正常關(guān)閉。對(duì)于某些支持可運(yùn)行應(yīng)用程序的框架也是如此。例如,Spring Boot 具有嵌入式 Tomcat 版本,可以有效地處理關(guān)機(jī)問(wèn)題。


        當(dāng)你創(chuàng)建一個(gè)獨(dú)立的 Java 應(yīng)用程序或手動(dòng)創(chuàng)建一個(gè)可運(yùn)行的 JAR 時(shí),你必須自己處理這些中斷信號(hào)。


        解決方案非常簡(jiǎn)單。添加一個(gè)退出鉤子(hook),如下面的示例所示。收到類(lèi)似 SIGINT 信號(hào)后,優(yōu)雅下線(xiàn)應(yīng)用程序的進(jìn)程將會(huì)被啟動(dòng)。


        Runtime.getRuntime().addShutdownHook(new Thread() {   @Override   public void run() {       System.out.println("Inside Add Shutdown Hook");   }});


        誠(chéng)然,與 Dockerfile 相關(guān)的問(wèn)題相比,這是一個(gè)通用的 Web 應(yīng)用程序問(wèn)題,但在容器環(huán)境中更重要。


        8. 使用 .dockerignore 文件


        為了防止不必要的文件污染 git 存儲(chǔ)庫(kù),你可以使用 .gitignore 文件。


        對(duì)于 Docker 鏡像,我們有類(lèi)似的東西—— .dockerignore 文件。類(lèi)似于 git 的忽略文件,它是為了防止 Docker 鏡像中出現(xiàn)不需要的文件或目錄。同時(shí),我們也不希望敏感信息泄漏到我們的 Docker 鏡像中。


        請(qǐng)參閱以下示例的 .dockerignore:


        .dockerignore**/*.logDockerfile.git.gitignore


        使用 .dockerignore 文件的要點(diǎn)是:


        • 跳過(guò)僅用于測(cè)試目的的依賴(lài)項(xiàng);

        • 使你免于泄露密鑰或憑據(jù)信息進(jìn)入 Java Docker 鏡像的文件;

        • 另外,日志文件也可能包含你不想公開(kāi)的敏感信息;

        • 保持 Docker 鏡像的美觀(guān)和整潔,本質(zhì)上是使鏡像變小。除此之外,它還有助于防止意外行為。


        9. 確保 Java 版本支持容器


        Java 虛擬機(jī)(JVM)是一件了不起的事情。它會(huì)根據(jù)其運(yùn)行的系統(tǒng)進(jìn)行自我調(diào)整。有基于行為的調(diào)整,可以動(dòng)態(tài)優(yōu)化堆的大小。但是,在 Java 8 和 Java 9 等較舊的版本中,JVM 無(wú)法識(shí)別容器設(shè)置的CPU限制或內(nèi)存限制。這些較舊的 Java 版本的 JVM 看到了主機(jī)系統(tǒng)上的全部?jī)?nèi)存和所有 CPU 容量。Docker 設(shè)置的限制將被忽略。


        隨著 Java 10 的發(fā)布,JVM 現(xiàn)在可以感知容器,并且可以識(shí)別容器設(shè)置的約束。該功能 UseContainerSupport 是 JVM 標(biāo)志,默認(rèn)情況下設(shè)置為活動(dòng)狀態(tài)。Java 10 中發(fā)布的容器感知功能也已移植到 Java-8u191。


        對(duì)于 Java 8 之前的版本,你可以手動(dòng)嘗試使用該 -Xmx 標(biāo)志來(lái)限制堆大小,但這是一個(gè)痛苦的練習(xí)。緊接著,堆大小不等于 Java 使用的內(nèi)存。對(duì)于 Java-8u131 和 Java 9,容器感知功能是實(shí)驗(yàn)性的,你必須主動(dòng)激活。


        -XX:+ UnlockExperimentalVMOptions -XX:+ UseCGroupMemoryLimitForHeap

        最好的選擇是將 Java 更新到 10 以上的版本,以便默認(rèn)情況下支持容器。不幸的是,許多公司仍然嚴(yán)重依賴(lài) Java 8。這意味著你應(yīng)該在 Docker 鏡像中更新到 Java 的最新版本,或者確保至少使用 Java 8 update 191 或更高版本。


        10. 謹(jǐn)慎使用容器自動(dòng)化生成工具


        你可能會(huì)偶然發(fā)現(xiàn)適用于構(gòu)建系統(tǒng)的出色工具和插件。除了這些插件,還有一些很棒的工具可以幫助你創(chuàng)建 Java 容器,甚至可以根據(jù)需要自動(dòng)發(fā)布應(yīng)用。


        從開(kāi)發(fā)人員的角度來(lái)看,這看起來(lái)很棒,因?yàn)槟悴槐卦趧?chuàng)建實(shí)際應(yīng)用程序時(shí),還要花費(fèi)精力維護(hù) Dockerfile。


        這樣的插件的一個(gè)例子是 JIB。如下所示,我只需要調(diào)用 mvn jib:dockerBuild 命令可以構(gòu)建鏡像:


        <plugin>   <groupId>com.google.cloud.tools</groupId>   <artifactId>jib-maven-plugin</artifactId>   <version>2.7.1</version>   <configuration>       <to>           <image>myimage</image>       </to>   </configuration></plugin>


        它將為我構(gòu)建一個(gè)具有指定名稱(chēng)的 Docker 鏡像,而沒(méi)有任何麻煩。

        使用 2.3 及更高版本時(shí),可以通過(guò)調(diào)用 mvn 命令進(jìn)行操作:


        mvn spring-boot:build-image

        在這種情況下,系統(tǒng)都會(huì)自動(dòng)為我創(chuàng)建一個(gè) Java 鏡像。這些鏡像還比較小,那是因?yàn)樗麄冋谑褂梅前l(fā)行版鏡像或 buildpack 作為鏡像的基礎(chǔ)。但是,無(wú)論鏡像大小如何,你如何知道這些容器是安全的?你需要進(jìn)行更深入的調(diào)查,即使這樣,你也不確定將來(lái)是否會(huì)保持這種狀態(tài)。


        我并不是說(shuō)你在創(chuàng)建 Java Docker 時(shí)不應(yīng)使用這些工具。但是,如果你打算發(fā)布這些鏡像,則應(yīng)研究 Java 鏡像所有方面的安全。鏡像掃描將是一個(gè)好的開(kāi)始。從安全性的角度來(lái)看,我的觀(guān)點(diǎn)是,以完全控制和正確的方式創(chuàng)建 Dockerfile,是創(chuàng)建鏡像更好,更安全的方式。


        轉(zhuǎn)自:程序員朱朱,

        鏈接:toutiao.com/article/6959742944421200387/


        推薦閱讀:

        世界的真實(shí)格局分析,地球人類(lèi)社會(huì)底層運(yùn)行原理

        不是你需要中臺(tái),而是一名合格的架構(gòu)師(附各大廠(chǎng)中臺(tái)建設(shè)PPT)

        企業(yè)IT技術(shù)架構(gòu)規(guī)劃方案

        論數(shù)字化轉(zhuǎn)型——轉(zhuǎn)什么,如何轉(zhuǎn)?

        華為干部與人才發(fā)展手冊(cè)(附PPT)

        企業(yè)10大管理流程圖,數(shù)字化轉(zhuǎn)型從業(yè)者必備!

        【中臺(tái)實(shí)踐】華為大數(shù)據(jù)中臺(tái)架構(gòu)分享.pdf

        華為的數(shù)字化轉(zhuǎn)型方法論

        華為如何實(shí)施數(shù)字化轉(zhuǎn)型(附PPT)

        超詳細(xì)280頁(yè)Docker實(shí)戰(zhàn)文檔!開(kāi)放下載

        華為大數(shù)據(jù)解決方案(PPT)


        瀏覽 29
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            少妇又紧又色又爽又刺激视频网站 | 日韩少妇一区二区三区 | 操逼黄视频| 一级A婬片试看60分钟 | 国产精品视频1区2区3区 | 高清无吗一区二区三区 | 亚洲人视频在线观看 | 一女被多男玩喷潮3p免费视频 | 国产精品久久久久久久久绿色 | 操逼一级黄片 |