最初发表于 SUSE 和 Rancher社区

简介

自 2013 年首次亮相以来,Docker 获得了极大的欢迎。许多企业依靠 Docker 来容器化他们的应用程序。许多事情都对 Docker 有利。成本效益,提供隔离,一致的环境,并且非常适合微服务用例,这使其成为创建容器的首选。然而,在科技界,没有什么是永恒的,总有替代方案。经常会出现选择来解决工具的缺点。那么Docker的缺点是什么?我将包括需要 root 访问权限和对守护进程的依赖,仅举两个。

对 Docker 没有仇恨。但是每种技术都有其用例。假设您的用例相对简单,并且您对学习新技术来容器化您的 Java 应用程序不感兴趣。在这种情况下,有比 Docker 更好的选择。由于开发人员在容器化应用程序时遇到的困难,这些工具在过去几年中出现了。容器化 Java 应用程序并非易事。由于您必须熟悉特定于工具的命令等,因此涉及到一个学习曲线。不要期望 Java 开发人员也能成为容器专家。

在这篇博文中,我们将讨论 Docker 和值得考虑的替代方案,用于从头开始容器化您的 Java 应用程序。这些工具将允许您以最少的配置和设置获得容器化应用程序。您甚至可能不需要安装 Docker 或处理 Dockerfile。在本文结束时,您将能够决定哪个容器映像构建器最快、消耗的资源最少、最容易设置并且最适合您的用例。

背景

在本文中,我们将快速演练并涵盖以下容器映像构建器工具的核心概念。

  1. 吊臂

  2. 构建包

  3. 码头工人

比较框架

我们将使用以下标准和特征来分析容器映像构建器工具。

  1. 易用性

2、资源消耗(内存、磁盘空间等)

  1. 构建镜像所花费的时间(第一次构建与后续构建)

4.图像层优化

先决条件

如果您想跟随,请确保您具备以下条件:

1.Rancher for dev如果你计划部署和运行创建的镜像

2.DockerHub账号

  1. 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-imageMaven 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"]
  1. 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简单

  1. Docker 多级

  2. 吊臂

第一个构建包

我们根据产品如何满足以下部分中的每个标准对产品进行了评级。

标准一:易用性

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 使用其扩展框架做任何事情。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐