怎么把一個Java應用打包成Docker鏡像
本文轉載自微信公眾號「網管叨bi叨」,作者KevinYan11。轉載本文請聯系網管叨bi叨公眾號。
怎么把Java應用打包成Docker鏡像?對熟悉Docker的同學這應該是一個很簡單的問題,把項目打包成JAR包然后在Dockerfile里用ADD命令把JAR文件放到鏡像里,啟動命令設置執行這個JAR文件即可。
比如一個使用Maven構建的Spring應用就可以用下面這個Dockerfile構建鏡像。
- FROM openjdk:8-jre
- ADD target/*.jar /application.jar
- ENTRYPOINT ["java", "-jar","/application.jar"]
上面這個Dockerfile很好理解,使用Maven構建的Java項目的目錄結構統一是
- project
- │ pom.xml
- └───src // 源文件目錄
- │ │
- │ └───main
- │ │
- │ └───java
- │
- └───target // class和jar文件的目錄
用mvn clean package打包后會把JAR文件生成在target目錄里,通過java -jar命令即可執行編譯好的程序。
所以上面的Dockerfile里就進行了把JAR從target目錄里添加到Docker鏡像中以及將jar -jar /application.jar 設置成容器的啟動命令這兩步操作。
不過除了這種最原始的方法外我們還可以使用Maven的一些插件,或者Docker的多階段打包功能來完成把Java應用打包成Docker鏡像的動作。
Maven插件構建鏡像
Spotify公司的dockerfile-maven-plugin和Google公司出品的jib-maven-plugin是兩款比較有名的插件,下面簡單介紹一下dockerfile-maven-plugin的配置和使用。
其實使用方法很簡單,我們在POM文件里引入這個plugin,并結合上面那個Dockerfile就能讓插件幫助我們完成應用鏡像的打包。
- <groupId>com.example</groupId>
- <artifactId>hello-spring</artifactId>
- <version>0.0.1-SNAPSHOT</version>
- <name>helloworld</name>
- <plugin>
- <groupId>com.spotify</groupId>
- <artifactId>dockerfile-maven-plugin</artifactId>
- <version>1.4.10</version>
- <executions>
- <execution>
- <id>default</id>
- <goals>
- <goal>build</goal>
- <goal>push</goal>
- </goals>
- </execution>
- </executions>
- <configuration>
- <repository>${docker.registry.url}/${image.prefix}/${artifactId}</repository>
- <tag>${project.version}</tag>
- <buildArgs>
- <JAR_FILE>${project.build.finalName}.jar</JAR_FILE>
- </buildArgs>
- </configuration>
- </plugin>
插件里使用的docker.registry.url和image.prefix是我單獨為Docker的鏡像倉庫設置的屬性。
- <properties>
- <java.version>1.8</java.version>
- <image.prefix>kevinyan001</image.prefix>
- <docker.registry.url></private.registry.url>
- </properties>
這里可以隨意設置成私有倉庫的遠程地址和鏡像前綴,比如在阿里云的鏡像服務上創建一個叫docker-demo的空間,上面的屬性就需要這樣配置:
- <properties>
- <java.version>1.8</java.version>
- <image.prefix>docker-demo</image.prefix>
- <docker.registry.url>registry.cn-beijing.aliyuncs.com</docker.registry.url>
- </properties>
在POM文件里配置好插件后伴隨著我們打包應用執行mvc clean package操作時dockerfile-maven-plugin就會自動根據我們的配置打包好一個叫做kevinyan001/hello-spring:0.0.1-SNAPSHOT的Docker鏡像。
dockerfile-maven-plugin除了能幫助我們打包應用鏡像外還可以讓它幫助我們把鏡像push到遠端倉庫,不過我覺得用處不大,感興趣的同學可以去網上搜搜看這部分功能怎么配置。
用Docker的多階段構建打包鏡像
上面介紹了使用Maven插件幫助我們打包Java應用的鏡像,其實我們還可以把mvn clean package這一步也交給Docker來完成。當然把Java應用的源碼放在Docker鏡像里再編譯打包在發布出去肯定是有問題的,我們知道在Dockerfile里每個指令ADD、RUN這些都是在單獨的層上進行,指令越多會造成鏡像越大,而且包含Java項目的源碼也是一種風險。
不過好在后來Docker支持了多階段構建,允許我們在一個Dockerfile里定義多個構建階段,先拉起一個容器完成用于的構建,比如說我們可以在這個階段里完成JAR的打包,然后第二個階段重新使用一個jre鏡像把上階段打包好的JAR文件拷貝到新的鏡像里。
“這一點在Go語言比較有優勢,第一階段編譯好的二進制執行文件直接拷貝到一個最基礎的linux鏡像里就能讓Go的應用在容器里運行。關于Go應用的多階段打包,可以查看我以前的文章線上Go項目的Docker鏡像應該怎么構建? 進行了解。
使用下面的Dockerfile可以通過多階段構建完成Java應用的Docker鏡像打包。
- # Dockerfile也可以不放在項目目錄下,通過 -f 指定Dockerfile的位置,比如在項目根下執行以下命令
- docker build -t <some tag> -f <dirPath/Dockerfile> .
- FROM kevinyan001/aliyun-mvn:0.0.1 AS MAVEN_BUILD
- COPY pom.xml /build/
- COPY src /build/src
- WORKDIR /build/
- # mount anonymous host directory as .m2 storage for contianer
- VOLUME /root/.m2
- RUN mvn clean package -Dmaven.test.skip=true --quiet
- FROM openjdk:8-jre
- COPY --from=MAVEN_BUILD /build/target/*.jar /app/application.jar
- ENTRYPOINT ["java", "-jar", "/app/application.jar"]
上面我們用的這些Dockerfile也可以不用放在項目的根目錄里,現在已經支持通過 -f 指定Dockerfile的位置,比如在項目根下執行以下命令完成鏡像的打包。
- docker build -t kevinyan001/hello-spring:0.0.1 -f <dirPath/Dockerfile> .
上面第一個鏡像是我自己做的,因為Maven官方的鏡像的遠程倉庫慢的一批,只能自己包裝一下走阿里云的鏡像源了。試了試速度也不快,主要是隨隨便便一個Spring項目依賴就太多了。大家如果這塊有什么加快Docker 構建速度的方法也可以留言一起討論討論。
不可否認用多階段構建打出來的Go鏡像基本上是10M左右,但是Spring的應用隨隨便便就是上百兆,這個對容器的構建速度、網絡傳輸成本是有影響的,那么Spring應用的鏡像怎么瘦身呢,這個就留到以后的文章進行探討了。