比较现代容器镜像构建器:Jib、Buildpacks 和 Docker
最初发表于 SUSE 和 Rancher社区 简介 自 2013 年首次亮相以来,Docker 获得了极大的欢迎。许多企业依靠 Docker 来容器化他们的应用程序。许多事情都对 Docker 有利。成本效益,提供隔离,一致的环境,并且非常适合微服务用例,这使其成为创建容器的首选。然而,在科技界,没有什么是永恒的,总有替代方案。经常会出现选择来解决工具的缺点。那么Docker的缺点是什么?我将包括
最初发表于 SUSE 和 Rancher社区
简介
自 2013 年首次亮相以来,Docker 获得了极大的欢迎。许多企业依靠 Docker 来容器化他们的应用程序。许多事情都对 Docker 有利。成本效益,提供隔离,一致的环境,并且非常适合微服务用例,这使其成为创建容器的首选。然而,在科技界,没有什么是永恒的,总有替代方案。经常会出现选择来解决工具的缺点。那么Docker的缺点是什么?我将包括需要 root 访问权限和对守护进程的依赖,仅举两个。
对 Docker 没有仇恨。但是每种技术都有其用例。假设您的用例相对简单,并且您对学习新技术来容器化您的 Java 应用程序不感兴趣。在这种情况下,有比 Docker 更好的选择。由于开发人员在容器化应用程序时遇到的困难,这些工具在过去几年中出现了。容器化 Java 应用程序并非易事。由于您必须熟悉特定于工具的命令等,因此涉及到一个学习曲线。不要期望 Java 开发人员也能成为容器专家。
在这篇博文中,我们将讨论 Docker 和值得考虑的替代方案,用于从头开始容器化您的 Java 应用程序。这些工具将允许您以最少的配置和设置获得容器化应用程序。您甚至可能不需要安装 Docker 或处理 Dockerfile。在本文结束时,您将能够决定哪个容器映像构建器最快、消耗的资源最少、最容易设置并且最适合您的用例。
背景
在本文中,我们将快速演练并涵盖以下容器映像构建器工具的核心概念。
-
吊臂
-
构建包
-
码头工人
比较框架
我们将使用以下标准和特征来分析容器映像构建器工具。
- 易用性
2、资源消耗(内存、磁盘空间等)
- 构建镜像所花费的时间(第一次构建与后续构建)
4.图像层优化
先决条件
如果您想跟随,请确保您具备以下条件:
1.Rancher for dev如果你计划部署和运行创建的镜像
2.DockerHub账号
- Docker for桌面
4.OpenJDK11
5.您选择的IDE
6.2.3或更高版本的Spring Boot项目
Spring Boot 应用剖析
我们用于此比较的 Spring Boot 应用程序是一个简单的 Hello World 应用程序。当通过 curl 或 REST 客户端(如 Postman)在本地访问时,我们暴露了一个 REST 端点 /hello 将返回字符串消息“Hello World!!!”。 Spring Boot 应用程序的源代码可在此处获取。在本文中,我们有目的地精简命令输出以减少日志的详细程度。
备选方案一:吊臂
Jib是来自 Google 的 Java 容器化程序,它允许 Java 开发人员使用 Maven 和 Gradle 等构建工具构建容器。
Jib 的重要之处在于,您无需了解有关安装 Docker 或维护 Dockerfile 的任何信息。吉布是无恶魔的。此外,作为开发人员,您只关心您将生成的工件(jar、war 等),而不必处理任何 Docker 废话(构建/推送等)。 Java 开发人员只需将插件添加到他们选择的构建工具(Maven/Gradle)中,仅此而已。您不必阅读大量教程来学习像 Docker 这样的技术来容器化您的 Java 应用程序。
使用 Jib 构建镜像
在构建镜像之前,您需要为您的 Spring Boot 应用程序添加 Jib 支持。我们只需将 Maven 或 Gradle 插件添加到您的 pom.xml 或 build.gradle 文件即可启用此支持。开始使用 Jib 很简单。
对于 Maven Spring Boot 项目,将以下内容添加到您的 pom.xml 文件中:
<project>
...
<build>
<plugins>
...
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>2.7.1</version>
<configuration>
<to>
<image>docker.io/my-docker-id/my-app</image>
</to>
</configuration>
</plugin>
...
</plugins>
</build>
...
</project>
对于 Gradle Spring Boot 项目,将以下内容添加到您的 build.gradle 文件中:
plugins {
id 'com.google.cloud.tools.jib' version '2.7.1'
}
jib.to.image = 'my-docker-id/my-app'
此外,Jib 创建的层是建立在无发行版基础映像之上的。默认情况下,Jib 使用distrolessJava 8 映像,但您可以选择您选择的映像。对于这个演示,我们将专注于使用 Maven Jib 插件对应用程序进行容器化。要将映像推送到您选择的容器注册表,您需要将注册表凭据添加到 maven settings.xml。您还应该查看这篇文章如何为不同的容器注册表进行配置。例如,我们可以使用以下内容将镜像推送到 DockerHub 容器注册表。
<server>
<id>registry.hub.docker.com</id>
<username>username</username>
<password>password</password>
</server>
我们可以使用以下命令开始构建镜像:
mvn compile jib:build
它将编译、构建然后将应用程序的映像推送到配置的容器注册表。以下是输出。
time mvn compile jib:build
..........
[INFO] Containerizing application to registry.hub.docker.com/hiashish/spring-boot-jib...
[WARNING] Base image 'gcr.io/distroless/java:11' does not use a specific image digest - build may not be reproducible
[INFO] Using credentials from Maven settings file for registry.hub.docker.com/hiashish/spring-boot-jib
[INFO] Using base image with digest: sha256:449c1c57fac9560ee06cd50f8a3beeb9b8cc22f1ed128f068457f7607bcfcac6
[INFO]
[INFO] Container entrypoint set to [java, -cp, /app/resources:/app/classes:/app/libs/*, com.compare.imagebuilder.Application]
[INFO]
[INFO] Built and pushed image as registry.hub.docker.com/hiashish/spring-boot-jib
[INFO] Executing tasks:
[INFO] [==============================] 100.0% complete
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 31.183 s
[INFO] Finished at: 2021-02-19T16:51:35+05:30
[INFO] ------------------------------------------------------------------------
mvn compile jib:build -DskipTests 20.81s user 1.78s system 68% cpu 33.032 total
Jib 还可以使用以下命令使用本地 docker 守护程序创建和存储图像。但是,我们必须使用docker push
命令手动推送镜像。
mvn compile jib:dockerBuild
以下是输出:
.......
[INFO] Containerizing application to Docker daemon as hiashish/spring-boot-jib...
[WARNING] Base image 'gcr.io/distroless/java:11' does not use a specific image digest - build may not be reproducible
[INFO] Using base image with digest: sha256:449c1c57fac9560ee06cd50f8a3beeb9b8cc22f1ed128f068457f7607bcfcac6
[INFO]
[INFO] Container entrypoint set to [java, -cp, /app/resources:/app/classes:/app/libs/*, com.compare.imagebuilder.Application]
[INFO]
[INFO] Built image to Docker daemon as hiashish/spring-boot-jib
[INFO] Executing tasks:
[INFO] [==============================] 100.0% complete
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 21.340 s
[INFO] Finished at: 2021-02-19T18:48:41+05:30
[INFO] ------------------------------------------------------------------------
mvn compile jib:dockerBuild 15.90s user 2.57s system 78% cpu 23.568 total
图片尺寸:
REPOSITORY TAG IMAGE ID CREATED SIZE
hiashish/spring-boot-jib latest eacedad2d476 51 years ago 214MB
以下是在将我们的 Spring Boot Hello-World 应用程序作为 Docker 容器运行后使用docker stats
的内存利用率。
CONTAINER ID NAME CPU % MEM USAGE / LIMIT
de0358ed9920 epic_varahamihira 1.04% 116.8MiB / 1.944GiB
备选方案二:Buildpacks
Cloud Native Buildpacks 将您的应用程序源代码转换为可以在任何云上运行的图像。
Buildpacks最初由 Heroku 在 2011 年构思,但现在是 CNCF 基金会的一部分。就像 Jib 一样,Buildpacks 也可以在不需要 Dockerfile 的情况下工作,但是你需要一个 docker 守护进程来发挥它的魔力。使用 Buildpack,输入是您的应用程序源代码,输出是容器映像。这部分与 Jib 非常相似,但 Jib 可以在没有 docker daemon 的情况下完成。在后台,Buildpack 做了很多工作,比如检索依赖项、处理资产、处理缓存和编译应用程序内置的任何语言的代码。
使用 Buildpacks 构建镜像
从 2.3 版开始,Spring Boot 包括对 Maven 和 Gradle 的直接 Buildpack支持。一个命令可以为您提供一个合理的映像到本地运行的 Docker 守护程序中。 Buildpack 需要一个 docker 守护进程来启动和运行。如果您没有运行 docker 守护进程,则在执行 Maven 命令时会出现以下错误。
Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:2.4.2:build-image (default-cli) on project imagebuilder: Execution default-cli of goal org.springframework.boot:spring-boot-maven-plugin:2.4.2:build-image failed: Connection to the Docker daemon at 'localhost' failed with error "[61] Connection refused"; ensure the Docker daemon is running and accessible
对于 Maven Spring Boot 项目,使用以下命令运行构建:
mvn spring-boot:build-image
对于 Gradle Spring Boot 项目,使用以下命令运行构建:
gradle bootBuildImage
Spring Boot 的 Buildpacks 默认行为是将图像本地存储到 docker daemon。但是,您也可以将图像推送到远程容器注册表。为此,我们需要在 Maven pom.xml 文件中进行以下更改。
<project>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<name>docker.example.com/library/${project.artifactId}</name>
<publish>true</publish>
</image>
<docker>
<publishRegistry>
<username>user</username>
<password>secret</password>
<url>https://docker.example.com/v1/</url>
<email>user@example.com</email>
</publishRegistry>
</docker>
</configuration>
</plugin>
</plugins>
</build>
</project>
首先,我们将尝试构建镜像并将其发布到本地 Docker 守护进程。让我们使用mvn spring-boot:build-image
Maven Build 命令开始构建。以下是输出。
[INFO] [creator] Adding layer 'paketo-buildpacks/ca-certificates:helper'
[INFO] [creator] Adding layer 'paketo-buildpacks/bellsoft-liberica:helper'
[INFO] [creator] Adding layer 'paketo-buildpacks/bellsoft-liberica:java-security-properties'
[INFO] [creator] Adding layer 'paketo-buildpacks/bellsoft-liberica:jre'
[INFO] [creator] Adding layer 'paketo-buildpacks/bellsoft-liberica:jvmkill'
[INFO] [creator] Adding layer 'paketo-buildpacks/executable-jar:class-path'
[INFO] [creator] Adding layer 'paketo-buildpacks/spring-boot:helper'
[INFO] [creator] Adding layer 'paketo-buildpacks/spring-boot:spring-cloud-bindings'
[INFO] [creator] Adding layer 'paketo-buildpacks/spring-boot:web-application-type'
[INFO] [creator] Adding 5/5 app layer(s)
[INFO] [creator] Adding layer 'launcher'
[INFO] [creator] Adding layer 'config'
[INFO] [creator] Adding layer 'process-types'
[INFO] [creator] Adding label 'io.buildpacks.lifecycle.metadata'
[INFO] [creator] Adding label 'io.buildpacks.build.metadata'
[INFO] [creator] Adding label 'io.buildpacks.project.metadata'
[INFO] [creator] Adding label 'org.opencontainers.image.title'
[INFO] [creator] Adding label 'org.opencontainers.image.version'
[INFO] [creator] Adding label 'org.springframework.boot.spring-configuration-metadata.json'
[INFO] [creator] Adding label 'org.springframework.boot.version'
[INFO] [creator] Setting default process type 'web'
[INFO] [creator] *** Images (20569cdcf777):
[INFO] [creator] docker.io/library/buildpack:0.0.1-SNAPSHOT
[INFO]
[INFO] Successfully built image 'docker.io/library/buildpack:0.0.1-SNAPSHOT'
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 01:49 min
[INFO] Finished at: 2021-02-20T01:07:08+05:30
[INFO] ------------------------------------------------------------------------
mvn compile spring-boot:build-image -DskipTests 19.33s user 1.40s system 18% cpu 1:51.21 total
现在我们将构建镜像并将其推送到远程容器注册表。运行mvn spring-boot:build-image
命令。
[INFO] [creator] Adding label 'io.buildpacks.build.metadata'
[INFO] [creator] Adding label 'io.buildpacks.project.metadata'
[INFO] [creator] Adding label 'org.opencontainers.image.title'
[INFO] [creator] Adding label 'org.opencontainers.image.version'
[INFO] [creator] Adding label 'org.springframework.boot.spring-configuration-metadata.json'
[INFO] [creator] Adding label 'org.springframework.boot.version'
[INFO] [creator] Setting default process type 'web'
[INFO] [creator] *** Images (dc8e2a8dc2e2):
[INFO] [creator] registry.hub.docker.com/hiashish/buildpack:latest
[INFO]
[INFO] Successfully built image 'registry.hub.docker.com/hiashish/buildpack:latest'
[INFO]
[INFO] > Pushing image 'registry.hub.docker.com/hiashish/buildpack:latest' 0%
[INFO] > Pushing image 'registry.hub.docker.com/hiashish/buildpack:latest' 9%
[INFO] > Pushing image 'registry.hub.docker.com/hiashish/buildpack:latest' 15%
[INFO] > Pushing image 'registry.hub.docker.com/hiashish/buildpack:latest' 18%
[INFO] > Pushing image 'registry.hub.docker.com/hiashish/buildpack:latest' 18%
[INFO] > Pushing image 'registry.hub.docker.com/hiashish/buildpack:latest' 18%
[INFO] > Pushing image 'registry.hub.docker.com/hiashish/buildpack:latest' 32%
[INFO] > Pushing image 'registry.hub.docker.com/hiashish/buildpack:latest' 38%
[INFO] > Pushing image 'registry.hub.docker.com/hiashish/buildpack:latest' 40%
[INFO] > Pushing image 'registry.hub.docker.com/hiashish/buildpack:latest' 45%
[INFO] > Pushing image 'registry.hub.docker.com/hiashish/buildpack:latest' 45%
[INFO] > Pushing image 'registry.hub.docker.com/hiashish/buildpack:latest' 55%
[INFO] > Pushing image 'registry.hub.docker.com/hiashish/buildpack:latest' 60%
[INFO] > Pushing image 'registry.hub.docker.com/hiashish/buildpack:latest' 61%
[INFO] > Pushing image 'registry.hub.docker.com/hiashish/buildpack:latest' 62%
[INFO] > Pushing image 'registry.hub.docker.com/hiashish/buildpack:latest' 67%
[INFO] > Pushing image 'registry.hub.docker.com/hiashish/buildpack:latest' 77%
[INFO] > Pushing image 'registry.hub.docker.com/hiashish/buildpack:latest' 79%
[INFO] > Pushing image 'registry.hub.docker.com/hiashish/buildpack:latest' 89%
[INFO] > Pushing image 'registry.hub.docker.com/hiashish/buildpack:latest' 90%
[INFO] > Pushing image 'registry.hub.docker.com/hiashish/buildpack:latest' 91%
[INFO] > Pushing image 'registry.hub.docker.com/hiashish/buildpack:latest' 92%
[INFO] > Pushing image 'registry.hub.docker.com/hiashish/buildpack:latest' 94%
[INFO] > Pushing image 'registry.hub.docker.com/hiashish/buildpack:latest' 95%
[INFO] > Pushing image 'registry.hub.docker.com/hiashish/buildpack:latest' 95%
[INFO] > Pushing image 'registry.hub.docker.com/hiashish/buildpack:latest' 95%
[INFO] > Pushing image 'registry.hub.docker.com/hiashish/buildpack:latest' 100%
[INFO] > Pushed image 'registry.hub.docker.com/hiashish/buildpack:latest'
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 02:58 min
[INFO] Finished at: 2021-02-20T01:19:26+05:30
[INFO] ------------------------------------------------------------------------
mvn compile spring-boot:build-image -DskipTests 19.33s user 1.38s system 11% cpu 3:00.29 total
图片尺寸:
REPOSITORY TAG IMAGE ID CREATED SIZE
hiashish/buildpack 0.0.1-SNAPSHOT 20569cdcf777 41 years ago 258MB
以下是使用docker stats
将我们的 Spring Boot Hello World 应用程序作为 Docker 容器运行后的内存利用率。
CONTAINER ID NAME CPU % MEM USAGE / LIMIT
a05ee6e6a07b strange_goodall 0.54% 121.8MiB / 1.944GiB
备选方案三:Docker
Docker 是创建容器化应用程序的事实标准。 Docker 依赖于一个守护进程,它必须运行才能为您的所有 Docker 命令提供服务。 Docker CLI 向 docker daemon 发出命令,它会执行必要的操作(即推送/拉取图像、运行容器等)。Docker 使用一个名为 Dockerfile 的文件,由您编写,其中包含 Docker 理解的步骤和说明。然后使用此 Dockerfile 使用docker build
之类的命令创建应用程序的容器映像。这里的优点是它允许不同的自定义级别,同时根据您的需要制作应用程序的容器映像。
用 Docker 构建镜像的幼稚方式
要使用 Docker 构建映像,我们需要在 Dockerfile 中添加一些指令。这些指令充当输入,然后 Docker 守护进程使用这些指令创建映像。这种方法的缺点是我们需要使用mvn clean package
命令创建一个工件,以便 Docker 可以复制最新版本的 jar。这将增加图像创建过程的时间。在这种方法中,我们没有考虑使用多阶段构建等技术进行图像分层优化。我们只想要应用程序的容器化版本,但这效率低下,因为我们使用了应用程序的大型基础映像。以下是使用 maven package 命令创建工件所需的时间。
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 8.734 s
[INFO] Finished at: 2021-02-18T22:07:40+05:30
[INFO] ------------------------------------------------------------------------
这是我们的 Spring Boot Hello-World 应用程序的 Dockerfile。
# Rookie way of writing Dockerfile
FROM openjdk:11
COPY target/*.jar app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
- FROM 指令表示我们应用程序的基础镜像
2.COPY指令,顾名思义,就是将Maven构建的本地jar复制到我们的镜像中
3.入口点指令在启动时充当我们容器的可执行文件
好的,那我们开始吧。以下是使用docker build
命令执行此 Dockerfile 的输出。
time docker build -t hiashish/imagebuilder:latest .
[+] Building 50.8s (8/8) FINISHED
=> [internal] load build definition from Dockerfile 0.1s
=> => transferring dockerfile: 121B 0.1s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/openjdk:11 3.3s
=> [auth] library/openjdk:pull token for registry-1.docker.io 0.0s
=> [internal] load build context 2.3s
=> => transferring context: 17.04MB 2.3s
=> [1/2] FROM docker.io/library/openjdk:11@sha256:3805f5303af58ebfee1d2f5cd5a897e97409e48398144afc2233f7b778337017 46.7s
=> => resolve docker.io/library/openjdk:11@sha256:3805f5303af58ebfee1d2f5cd5a897e97409e48398144afc2233f7b778337017 0.0s
=> => sha256:0ecb575e629cd60aa802266a3bc6847dcf4073aa2a6d7d43f717dd61e7b90e0b 50.40MB / 50.40MB 0.0s
=> => sha256:feab2c490a3cea21cc051ff29c33cc9857418edfa1be9966124b18abe1d5ae16 10.00MB / 10.00MB 0.0s
=> => sha256:f15a0f46f8c38f4ca7daecf160ba9cdb3ddeafda769e2741e179851cfaa14eec 51.83MB / 51.83MB 0.0s
=> => sha256:26cb1dfcbebb21160622ee663cb64f65e625a9f3d98c55b9555e21e2cb15e400 5.29MB / 5.29MB 0.0s
=> => sha256:242c5446d23fd9e18b2a08efc86e19bcf271b95038f7a2a58f4819fb362dee36 208B / 208B 0.0s
=> => sha256:3805f5303af58ebfee1d2f5cd5a897e97409e48398144afc2233f7b778337017 1.04kB / 1.04kB 0.0s
=> => sha256:2d17e02b6902d28c8546b2a1feff7e4a1fd74c703339bca6ae1c45584b9a0b67 1.79kB / 1.79kB 0.0s
=> => sha256:82e02728b3fd3958c5ca23fb86b9f06ba2e4bb834c0e456fe2d278a932923d53 6.27kB / 6.27kB 0.0s
=> => sha256:7467d1831b6947c294d92ee957902c3cd448b17c5ac2103ca5e79d15afb317c3 7.83MB / 7.83MB 0.0s
=> => sha256:f22708c7c9c1856c05e56ae8f5812a24b7304cb80ebfc9a34dd4f4cbaf3dd6d2 202.80MB / 202.80MB 0.0s
=> => extracting sha256:0ecb575e629cd60aa802266a3bc6847dcf4073aa2a6d7d43f717dd61e7b90e0b 9.3s
=> => extracting sha256:7467d1831b6947c294d92ee957902c3cd448b17c5ac2103ca5e79d15afb317c3 1.1s
=> => extracting sha256:feab2c490a3cea21cc051ff29c33cc9857418edfa1be9966124b18abe1d5ae16 1.2s
=> => extracting sha256:f15a0f46f8c38f4ca7daecf160ba9cdb3ddeafda769e2741e179851cfaa14eec 9.5s
=> => extracting sha256:26cb1dfcbebb21160622ee663cb64f65e625a9f3d98c55b9555e21e2cb15e400 0.7s
=> => extracting sha256:242c5446d23fd9e18b2a08efc86e19bcf271b95038f7a2a58f4819fb362dee36 0.0s
=> => extracting sha256:f22708c7c9c1856c05e56ae8f5812a24b7304cb80ebfc9a34dd4f4cbaf3dd6d2 20.6s
=> [2/2] COPY target/*.jar app.jar 0.2s
=> exporting to image 0.3s
=> => exporting layers 0.2s
=> => writing image sha256:6704ddf7df3398be458722ea1c4d8c17393dc656a1a8ae89152f99c5462b0306 0.0s
=> => naming to docker.io/hiashish/imagebuilder:latest 0.0s
docker build -t hiashish/imagebuilder:latest . 0.82s user 0.64s system 2% cpu 51.572 total
接下来,我们使用docker push
命令发布了镜像。
time docker push hiashish/imagebuilder
Using default tag: latest
The push refers to repository [docker.io/hiashish/imagebuilder]
baebe8d2c101: Pushed
ebab439b6c1b: Mounted from hiashish/helloworld
c44cd007351c: Mounted from hiashish/helloworld
02f0a7f763a3: Mounted from hiashish/helloworld
da654bc8bc80: Mounted from hiashish/helloworld
4ef81dc52d99: Mounted from hiashish/helloworld
909e93c71745: Mounted from hiashish/helloworld
7f03bfe4d6dc: Mounted from hiashish/helloworld
latest: digest: sha256:166c00c1ba605a360e4555405f57d2f5b93ec7abc979ecb4a43de7c9366639d0 size: 2006
docker push hiashish/imagebuilder 0.26s user 0.38s system 1% cpu 32.655 total
图片尺寸
REPOSITORY TAG IMAGE ID CREATED SIZE
hiashish/dockersimple latest 3cdf4936ec77 20 hours ago 664MB
要检查内存利用率,我们将使用docker stats
命令。以下是运行我们的 Spring Boot Hello-Wolrd 应用程序 Docker 容器后的输出。
CONTAINER ID NAME CPU % MEM USAGE / LIMIT
f8f34e0ffaa4 magical_lamport 0.89% 138.7MiB / 1.944GiB
Pro 用 Docker Multistage Build 构建镜像的方式
在上一节中,我们的过程有点繁琐,而且输出图像的尺寸很大。我们可以改进这个过程并使用 Docker 多阶段构建创建纤薄的镜像。先前方法的另一个问题是在创建实际图像之前必须先使用 mvn package 命令打包我们的应用程序。在本节中,我们将尽量避免这种情况。
这是我们的 Spring Boot Hello-World 应用程序的 Dockerfile,它使用多阶段构建。让我解释一下 Dockerfile。我在第一阶段使用 maven-openjdk11 映像为我的项目构建和创建了一个 jar,但这不会是最终输出。在第二阶段,我复制了我们在前一个构建阶段制作的 jar,并基于一个明显更小的 Java 11 JRE 基础镜像创建了一个新的最终镜像。最终的 Docker 映像不包含 JDK 或 maven,而仅包含 JRE。如果您已经注意到,这里的构建时间更长,因为所有必需的依赖项都需要在构建的第一阶段下载。我想这是这种方法的唯一缺点。
# Pro way of writing Dockerfile
FROM maven:3-openjdk-11 as build
RUN mkdir /app
COPY . /app
WORKDIR /app
RUN mvn clean package -DskipTests
FROM openjdk:11-jre-slim
RUN mkdir /project
COPY --from=build /app/target/imagebuilder-0.0.1-SNAPSHOT.jar /project
WORKDIR /project
ENTRYPOINT ["java","-jar","imagebuilder-0.0.1-SNAPSHOT.jar"]
以下是使用docker build
命令执行此 Dockerfile 的输出。
time docker build -t hiashish/multistagebuild:latest .
[+] Building 213.4s (17/17) FINISHED
=> [internal] load build definition from Dockerfile 0.1s
=> => transferring dockerfile: 503B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/openjdk:11-jre-slim 2.7s
=> [internal] load metadata for docker.io/library/maven:3-openjdk-11 3.0s
=> [auth] library/maven:pull token for registry-1.docker.io 0.0s
=> [auth] library/openjdk:pull token for registry-1.docker.io 0.0s
=> [build 1/5] FROM docker.io/library/maven:3-openjdk-11@sha256:7376ab3120aeb575cd6cd336a423c6c8f62f26d9fb1ddc2659a299c3433a2fa2 35.8s
=> => resolve docker.io/library/maven:3-openjdk-11@sha256:7376ab3120aeb575cd6cd336a423c6c8f62f26d9fb1ddc2659a299c3433a2fa2 0.0s
=> => sha256:1539a43fb9fbebf79ac9e96adfae8dada3b6caa0ab0e6586e837dae4b8eabc3c 355B / 355B 0.0s
=> => sha256:7376ab3120aeb575cd6cd336a423c6c8f62f26d9fb1ddc2659a299c3433a2fa2 549B / 549B 0.0s
=> => sha256:12b1cd95e6bf321d358f4a8484584acc1354728c4c6f82f0568a2a3fdfbb0f34 2.42kB / 2.42kB 0.0s
=> => sha256:feab2c490a3cea21cc051ff29c33cc9857418edfa1be9966124b18abe1d5ae16 10.00MB / 10.00MB 0.0s
=> => sha256:f15a0f46f8c38f4ca7daecf160ba9cdb3ddeafda769e2741e179851cfaa14eec 51.83MB / 51.83MB 0.0s
=> => sha256:26cb1dfcbebb21160622ee663cb64f65e625a9f3d98c55b9555e21e2cb15e400 5.29MB / 5.29MB 0.0s
=> => sha256:f22708c7c9c1856c05e56ae8f5812a24b7304cb80ebfc9a34dd4f4cbaf3dd6d2 202.80MB / 202.80MB 0.0s
=> => sha256:8749bb0dc5132a22fbd2b2b23d4179568f3a758dd0c53dc2b87048656d19b8d7 852B / 852B 0.0s
=> => sha256:8de5407a7bebd1d5f75cc154bf8c74535dde31e10b8baa60347d04a7b3f338c5 8.92kB / 8.92kB 0.0s
=> => sha256:0ecb575e629cd60aa802266a3bc6847dcf4073aa2a6d7d43f717dd61e7b90e0b 50.40MB / 50.40MB 0.0s
=> => sha256:7467d1831b6947c294d92ee957902c3cd448b17c5ac2103ca5e79d15afb317c3 7.83MB / 7.83MB 0.0s
=> => sha256:242c5446d23fd9e18b2a08efc86e19bcf271b95038f7a2a58f4819fb362dee36 208B / 208B 0.0s
=> => sha256:b6129cf0dd9130eddbcd914da7d0808d038216f3ea3e599da8e3d3b80d7ce427 9.58MB / 9.58MB 0.0s
=> => extracting sha256:0ecb575e629cd60aa802266a3bc6847dcf4073aa2a6d7d43f717dd61e7b90e0b 10.0s
=> => extracting sha256:7467d1831b6947c294d92ee957902c3cd448b17c5ac2103ca5e79d15afb317c3 1.0s
=> => extracting sha256:feab2c490a3cea21cc051ff29c33cc9857418edfa1be9966124b18abe1d5ae16 1.0s
=> => extracting sha256:f15a0f46f8c38f4ca7daecf160ba9cdb3ddeafda769e2741e179851cfaa14eec 7.7s
=> => extracting sha256:26cb1dfcbebb21160622ee663cb64f65e625a9f3d98c55b9555e21e2cb15e400 0.5s
=> => extracting sha256:242c5446d23fd9e18b2a08efc86e19bcf271b95038f7a2a58f4819fb362dee36 0.0s
=> => extracting sha256:f22708c7c9c1856c05e56ae8f5812a24b7304cb80ebfc9a34dd4f4cbaf3dd6d2 11.4s
=> => extracting sha256:b6129cf0dd9130eddbcd914da7d0808d038216f3ea3e599da8e3d3b80d7ce427 0.5s
=> => extracting sha256:8749bb0dc5132a22fbd2b2b23d4179568f3a758dd0c53dc2b87048656d19b8d7 0.0s
=> => extracting sha256:1539a43fb9fbebf79ac9e96adfae8dada3b6caa0ab0e6586e837dae4b8eabc3c 0.0s
=> [stage-1 1/4] FROM docker.io/library/openjdk:11-jre-slim@sha256:34744515bdde22d92a627555165e72b4e3e7aca2094cc025cb0f4000058a3c1c 16.0s
=> => resolve docker.io/library/openjdk:11-jre-slim@sha256:34744515bdde22d92a627555165e72b4e3e7aca2094cc025cb0f4000058a3c1c 0.0s
=> => sha256:33964aae81418d20eee9035f0a64d71bded866a765423048f456d6835b1e2a3d 7.58kB / 7.58kB 0.0s
=> => sha256:45b42c59be334ecda0daaa139b2f7d310e45c564c5f12263b1b8e68ec9e810ed 27.10MB / 27.10MB 0.0s
=> => sha256:a91c0c19c84860aaa974864243509770a5b009f3a88b4a228010a9ade71ac968 3.27MB / 3.27MB 0.0s
=> => sha256:dbe61a45ef1807a5db8d1f61021955a6ee0c370a88bb9c568a2ae31417afbda6 209B / 209B 0.0s
=> => sha256:6eeb16e47bf12a88503349a2056e84ea8b6e1329850c1ad859e2d01401f45a1f 47.04MB / 47.04MB 0.0s
=> => sha256:34744515bdde22d92a627555165e72b4e3e7aca2094cc025cb0f4000058a3c1c 549B / 549B 0.0s
=> => sha256:1317197ccd52971d38949536ba5a27ad61de3bf78ef792033622a4694eb0c373 1.16kB / 1.16kB 0.0s
=> => extracting sha256:45b42c59be334ecda0daaa139b2f7d310e45c564c5f12263b1b8e68ec9e810ed 6.8s
=> => extracting sha256:a91c0c19c84860aaa974864243509770a5b009f3a88b4a228010a9ade71ac968 0.8s
=> => extracting sha256:dbe61a45ef1807a5db8d1f61021955a6ee0c370a88bb9c568a2ae31417afbda6 0.0s
=> => extracting sha256:6eeb16e47bf12a88503349a2056e84ea8b6e1329850c1ad859e2d01401f45a1f 5.9s
=> [internal] load build context 2.4s
=> => transferring context: 17.20MB 2.3s
=> [stage-1 2/4] RUN mkdir /project 1.2s
=> [build 2/5] RUN mkdir /app 0.6s
=> [build 3/5] COPY . /app 0.2s
=> [build 4/5] WORKDIR /app 0.1s
=> [build 5/5] RUN mvn clean package -DskipTests 172.8s
=> [stage-1 3/4] COPY --from=build /app/target/imagebuilder-0.0.1-SNAPSHOT.jar /project 0.1s
=> [stage-1 4/4] WORKDIR /project 0.1s
=> exporting to image 0.2s
=> => exporting layers 0.2s
=> => writing image sha256:079ca9681f614fe441730f4baaf3494841fd672fa814cfd7baeb52097e7c83d5 0.0s
=> => naming to docker.io/hiashish/multistagebuild:latest
docker build -t hiashish/multistagebuild:latest . 4.26s user 3.04s system 3% cpu 3:33.89 total
接下来,我们使用docker push
命令发布了镜像。
time docker push hiashish/multistagebuild
Using default tag: latest
The push refers to repository [docker.io/hiashish/multistagebuild]
5f70bf18a086: Mounted from paketobuildpacks/builder
df8f2c4d6677: Pushed
657f5a5c28df: Pushed
027810cd859b: Mounted from library/openjdk
513adf10febc: Mounted from library/openjdk
08664b16f94c: Mounted from library/openjdk
9eb82f04c782: Mounted from library/openjdk
latest: digest: sha256:91dca4aebfda20cd93b7fb29d946ab694bdcc2b9351a44de6d2a16822b689f18 size: 1784
docker push hiashish/multistagebuild 0.20s user 0.28s system 1% cpu 30.667 total
使用 Docker 多阶段构建的图像大小。
REPOSITORY TAG IMAGE ID CREATED SIZE
hiashish/multistagebuild latest 079ca9681f61 13 seconds ago 237MB
以下是运行我们的 Spring Boot Hello-World 应用程序 Docker 容器后的内存利用率。
CONTAINER ID NAME CPU % MEM USAGE / LIMIT
7b64ea0aabf2 keen_bohr 0.80% 139.8MiB / 1.944GiB
数据对比
https://gist.github.com/yrashish/7b1fa1801e04ed64abd6fde0fe85b919
比较图像层
本节将使用Dive来检查使用我们在本文中介绍的容器化方法创建的镜像层。 Dive 足够聪明,可以判断您是否在浪费任何空间,如果是,您可以找到缩小 Docker/OCI 映像大小的方法。
1.Docker简单
-
Docker 多级
-
吊臂
第一个构建包
我们根据产品如何满足以下部分中的每个标准对产品进行了评级。
标准一:易用性
Jib 和 Spring Boot Buildpack 方法都很简单。只需添加插件即可轻松开始使用它们。它们已融入 Spring Boot 生态系统。它们确实提供了一些合理的默认值来使用,这取决于您所期望的自定义级别。但是,这两个选项可能不支持 Dockerfile 所做的所有功能,例如使用类似于 RUN 的 Dockerfile 指令和 Jib 或 Buildpack 指令。 Docker 需要单独安装,你必须熟悉它支持的命令。正如我们之前看到的,我们还必须遵循一些 Docker 最佳实践来为我们的应用程序创建更精简的镜像。但是,Jib 提供(默认情况下)无发行版基础镜像,这些镜像是轻量级且更安全的,因为它们不包含典型 Linux 发行版附带的包管理器。
标准二:镜像创建时间
正如您在数据比较表中看到的,在比较图像创建时间时,Jib 是性能最高的选项。当我们在没有 Docker 守护程序的情况下使用 Jib 时,它会在第一次构建运行时创建镜像并将其推送到远程容器注册表,只需约 33 秒,后续构建甚至更快。我们探索的其他选择都没有接近。
标准三:资源利用率
Spring Boot Jib 支持提供了一些合理的默认值,它的 distroless 基础镜像比其他选项小得多,导致最终的镜像更小。与 Jib 相比,Docker 并没有那么瘦,即使是多阶段构建的基础镜像也很薄。与 Jib 和 Docker 的多阶段构建相比,Buildpack 支持 Spring Boot 创建的镜像很大。最后,Jib 消耗的内存最少。
标准四:图像层
在上面比较镜像层的部分中,您可以看到 Docker 的简单和多阶段构建镜像将依赖项、资源和类合并到一个低效的层中。此外,还有空间浪费,还有改进的余地。使用 Jib,我们为类、资源和依赖项扩展了目录结构,并且它们的大小最小。这种方法的优势在于,当您进行任何代码更改时,只会重建您的更改,而不是整个应用程序。这意味着 Jib 仅推动层,该层已更改,其余层保持不变。使用 Buildpack,与 Jib 相比,我们或多或少具有相同的分层,但也有一些空间被浪费,因为图像效率得分为 99%。 Buildpack 的另一个问题是它将资源组合在一起,使分层效率低下。
结论
在本文中,我们介绍了适用于 Java 开发人员的各种容器镜像构建方法,包括 Jib、Buildpacks 和 Docker。我们在多个参数上比较了镜像构建方法,例如镜像创建时间(构建和推送)、资源利用率(磁盘空间和内存),以及每个工具上手的难易程度。通过我们提供的数据,您可以看到 Jib 是一种为您的应用程序创建容器映像的有效方法。 Buildpacks 是一个不错的选择。但是,它们有时比 Jib 慢,并且不允许您更改父图像。最后,如果您正在寻找更好的控制或定制,那么 Docker 可能是您的最佳选择。也就是说,Jib 自成立以来已经发展了如此之多,以至于您几乎可以使用 Docker 使用其扩展框架做任何事情。
更多推荐
所有评论(0)