当前位置: 首页 > 科技观察

BuildKit优化的Dockerfile构建

时间:2023-03-14 14:13:48 科技观察

Docker通过读取Dockerfile中的指令自动构建图像,Dockerfile是一个文本文件,其中包含构建给定图像所需的所有命令。以上解释摘自Docker官方文档,总结了Dockerfile的用途。Dockerfile的使用非常重要,因为它是我们的蓝图,是我们添加到Docker镜像中的层的记录。在本文中,我们将学习如何利用BuildKit功能,这是Dockerv18.09中引入的一组增强功能。集成BuildKit将为我们提供更好的性能、存储管理和安全性。本文的目标是减少构建时间;减小图像尺寸;获得可维护性;获得可重复性;了解多阶段Dockerfile;了解BuildKit函数。先决条件Docker概念知识安装Docker(当前使用v19.03)Java应用程序(对于本文,我使用JenkinsMaven示例应用程序)让我们开始吧!简单Dockerfile示例以下是包含Java应用程序的未优化Dockerfile示例。我们将逐步进行一些优化。FROMdebianCOPY./appRUNapt-getupdateRUNapt-get-yinstallopenjdk-11-jdkshemacsCMD["java","-jar","/app/target/my-app-1.0-SNAPSHOT.jar"]在这里,我们可能会问自己:多久需要建造吗?为了回答这个问题,让我们在本地开发环境中创建这个Dockerfile并让Docker构建镜像。#enteryourJavaappfoldercdsimple-java-maven-app-master#createaDockerfilevimDockerfile#writecontent,saveandexitdockerpulldebian:latest#pullthesourceimagetimedockerbuild--no-cache-tdocker-class.#overwritepreviouslayers#noticethebuildtime0,21suser0,23stalsystem0%cpu1:55,1这时候我们的构建需要1分55秒。如果我们只启用BuildKit而不做任何更改,会有什么不同吗?EnableBuildKitBuildKit可以通过两种方式开启:调用Docker构建命令时设置DOCKER_BUILDKIT=1环境变量,例如:timeDOCKER_BUILDKIT=1dockerbuild--no-cache-tdocker-class会设置DockerBuildKit默认开启,这需要在/etc/docker/daemon.json中设置如下,然后重启:{"features":{"buildkit":true}}BuildKit初始效果DOCKER_BUILDKIT=1dockerbuild--no-cache-tdocker-class.0,54suser0,93ssystem1%cpu1:43,00total此时,我们的构建耗时1m43s。在相同的硬件上,构建时间比以前少了大约12秒。这意味着几乎可以毫不费力地节省大约10%的构建时间。现在让我们看看我们是否可以采取一些额外的步骤来进一步改进它。从最少到最频繁更改的顺序因为顺序对于缓存很重要,所以我们将COPY命令移到了更靠近Dockerfile末尾的位置。FROMdebianRUNapt-getupdateRUNapt-get-yinstallopenjdk-11-jdkshemacsRUNCOPY./appCMD["java","-jar","/app/target/my-app-1.0-SNAPSHOT.jar"]避免使用“COPY”。选择更具体的COPY参数以避免缓存中断。仅复制需要的内容。从debianRUNapt-getupdateRUNapt-get-yinstallopenjdk-11-jdksshvimCOPYtarget/my-app-1.0-SNAPSHOT.jar/appCMD["java","-jar","/app/my-app-1.0-SNAPSHOT.jar"]apt-使用getupdate和install命令来防止使用过时的包缓存。FROMdebianRUNapt-getupdate&&\apt-get-yinstallopenjdk-11-jdksshvimCOPYtarget/my-app-1.0-SNAPSHOT.jar/appCMD["java","-jar","/app/my-app-1.0-SNAPSHOT.jar"]去除不必要的依赖一开始不要安装调试和编辑工具,以后需要的时候再安装。FROMdebianRUNapt-getupdate&&\apt-get-yinstall--no-install-recommends\openjdk-11-jdkCOPYtarget/my-app-1.0-SNAPSHOT.jar/appCMD["java","-jar","/app/my-app-1.0-SNAPSHOT.jar"]删除包管理器缓存您的图像不需要此缓存数据。借此机会释放一些空间。FROMdebianRUNapt-getupdate&&\apt-get-yinstall--no-install-recommends\openjdk-11-jdk&&\rm-rf/var/lib/apt/lists/*COPYtarget/my-app-1.0-SNAPSHOT.jar/appCMD["java","-jar","/app/my-app-1.0-SNAPSHOT.jar"]尽量使用官方镜像使用官方镜像有很多原因,比如减少镜像维护时间和镜像容器使用的大小和预配置镜像。FROMopenjdkCOPYtarget/my-app-1.0-SNAPSHOT.jar/appCMD["java","-jar","/app/my-app-1.0-SNAPSHOT.jar"]使用特定标签不要使用最新标签。FROMopenjdk:8COPYtarget/my-app-1.0-SNAPSHOT.jar/appCMD["java","-jar","/app/my-app-1.0-SNAPSHOT.jar"]寻找最小镜像下面是列表openjdk镜像。选择最适合您的最轻的镜子。REPOSITORYTAG标记SIZE大小openjdk8634MBopenjdk8-jre443MBopenjdk8-jre-slim204MBopenjdk8-jre-alpine83MB在一致的环境中从源构建如果不需要整个JDK,可以使用MavenDocker映像作为构建基础。FROMmaven:3.6-jdk-8-alpineWORKDIR/appCOPYpom.xml.COPYsrc./srcRUNmvn-e-BpackageCMD["java","-jar","/app/my-app-1.0-SNAPSHOT.jar"]在单独的步骤中可以缓存get-dependencies–用于获取依赖项的Dockerfile命令。缓存此步骤将加快构建速度。FROMmaven:3.6-jdk-8-alpineWORKDIR/appCOPYpom.xml.RUNmvn-e-Bdependency:resolveCOPYsrc./srcRUNmvn-e-BpackageCMD["java","-jar","/app/my-app-1.0-SNAPSHOT.jar"]多阶段构建:删除构建依赖项为什么要使用多阶段构建?从运行时环境中分离构建DRY方式对开发、测试等环境具有不同的细节线性化依赖关系具有平台特定阶段FROMmaven:3.6-jdk-8-alpineASbuilderWORKDIR/appCOPYpom.xml.RUNmvn-e-Bdependency:resolveCOPYsrc./srcRUNmvn-e-BpackageFROMopenjdk:8-jre-alpineCOPY--from=builder/app/target/my-app-1.0-SNAPSHOT.jar/CMD["java","-jar","/my-app-1.0-SNAPSHOT.jar"]如果此时构建我们的应用程序,timeDOCKER_BUILDKIT=1dockerbuild--no-cache-tdocker-class.0,41suser0,54ssystem2%cpu35,656total你会注意到我们的应用程序构建大约需要。时间为35.66秒。这是一个受欢迎的发展。下面,我们描述其他场景的功能。多阶段构建:不同的图像样式下面的Dockerfile显示了基于Debian和基于Alpine的图像的不同阶段。FROMmaven:3.6-jdk-8-alpineASbuilder...FROMopenjdk:8-jre-jessieASrelease-jessieCOPY--from=builder/app/target/my-app-1.0-SNAPSHOT.jar/CMD[“java”,“-jar","/my-app-1.0-SNAPSHOT.jar"]FROMopenjdk:8-jre-alpineASrelease-alpineCOPY--from=builder/app/target/my-app-1.0-SNAPSHOT.jar/CMD["java","-jar","/my-app-1.0-SNAPSHOT.jar"]构建具体镜像,我们可以使用--target参数:timedockerbuild--no-cache--targetrelease-jessie。不同的图像样式(DRY/globalARG)ARGflavor=alpineFROMmaven:3.6-jdk-8-alpineASbuilder...FROMopenjdk:8-jre-$flavorASreleaseCOPY--from=builder/app/target/my-app-1.0-SNAPSHOT.jar/CMD["java","-jar","/my-app-1.0-SNAPSHOT.jar"]ARG命令可以指定要构建的镜像。在上面的示例中,我们指定了alpine作为默认镜像,但我们也可以通过dockerbuild命令中的--build-argflavor=参数来指定镜像。timedockerbuild--no-cache--targetrelease--build-argflavor=jessie。并发在构建Docker镜像时并发很重要,因为它会充分利用可用的CPU线程。在线性Dockerfile中,所有阶段都按顺序执行。通过多阶段构建,我们可以为主阶段准备好较小的依赖阶段来使用它们。BuildKit甚至带来了另一个性能优势。如果该阶段未在未来的构建中使用,它将在最后被简单地跳过,而不是被处理和丢弃。这是一个示例Dockerfile,其中网站的资产是在资产阶段构建的:FROMmaven:3.6-jdk-8-alpineASbuilder...FROMtiborvass/whalesayASassetsRUNwhalesay"HelloDockerCon!">out/assets.htmlFROMopenjdk:8-jre-alpineASreleaseCOPY--from=builder/app/my-app-1.0-SNAPSHOT.jar/COPY--from=assets/out/assetsCMD["java","-jar","/my-app-1.0-SNAPSHOT.jar"]这是另一个Dockerfile,其中C和C++库分别编译并在构建器阶段之后使用。FROMmaven:3.6-jdk-8-alpineASbuilder-base...FROMgcc:8-alpineASbuilder-someClib...RUNgitclone..../configure--prefix=/out&&make&&makeinstallFROMg++:8-alpineASbuilder-someCPPlib...RUNgitclone...&&cmake...FROMbuilder-baseASbuilderCOPY--from=builder-someClib/out/COPY--from=builder-someCpplib/out/BuildKit应用程序缓存BuildKit具有包管理器缓存的特殊功能。以下是缓存文件夹位置的一些示例:包管理器路径apt/var/lib/apt/listsgo~/.cache/go-buildgo-modules$GOPATH/pkg/modnpm~/.npmpip~/.cache/pipusThisDockerfile可以与上面在一致环境中从源构建中描述的Dockerfile进行比较。这个旧的Dockerfile没有特殊的缓存处理。我们可以使用--mount=type=cache来做到这一点。FROMmaven:3.6-jdk-8-alpineASbuilderWORKDIR/appRUN--mount=target=.--mount=type=cache,target/root/.m2\&&mvnpackage-DoutputDirectory=/FROMopenjdk:8-jre-alpineCOPY--from=builder/app/target/my-app-1.0-SNAPSHOT.jar/CMD["java","-jar","/my-app-1.0-SNAPSHOT.jar"]BuildKit安全特性BuildKit有安全特性,下面在例如,我们使用--mount=type=secret来隐藏一些机密文件,例如~/.aws/credentials。从RUN...RUN--mount=type=secret,id=aws,target=/root/.aws/credentials,required\./fetch-assets-from-s3.shRUN./build-scripts.sh要构建此Dockerfile,您需要使用–secret参数:dockerbuild--secretid=aws,src=~/.aws/credentials并为了提高安全性,避免使用COPY./keys/private.pem/根.ssh/私有。像pem这样的命令,我们可以在BuildKit中使用ssh来解决这个问题:requiredgitclonegit@github.com:org/repo/work&&cd/work&&gitcheckout-b$REPO_REF要构建此Dockerfile,您需要在ssh-agent中加载SSH私钥。评估$(ssh-agent)ssh-add~/.ssh/id_rsa#thisistheSSHkeydefaultlocationdockerbuild--ssh=default。结论在本文中,我们介绍了使用DockerBuildKit来优化Dockerfile,从而加快镜像构建时间。这些速度的提升可以帮助我们提高效率,节省计算能力。