大幅提升 Docker 构建性能的 3 个步骤
Docker 是我们日常开发中使用的工具,但是你在等待 Docker 构建完成上浪费了多少时间?你又该如何处理巨大的镜像呢?
如果我告诉您有更好的方法来构建您的容器呢?
您最喜欢的下一个工具叫做 Buildkit!
在本教程中,我们将深入探讨 Docker 的高级用法,以优化您的开发流程,包括构建时间和镜像本身的大小。我们将使用 Buildkit并行多阶段构建来实现。
Buildkit
Buildkit是Moby 项目开发的工具包,用于增强使用容器的软件构建和打包。
主要特点
Buildkit 提供多种功能,包括自动垃圾收集以清理不需要的资源、并发依赖项解析以及高效的指令缓存。Buildkitdocker build
自 Docker 18.06 起成为其中的一部分。
如何启用 Buildkit
如果您想使用 Buildkit 驱动的构建引擎,您可以使用环境变量来实现DOCKER_BUILDKIT=1 docker build
。
也可以默认启用 Buildkit:
- 编辑守护进程配置
/etc/docker/daemon.json
并添加
{ "features": { "buildkit": true } }
- 使用以下命令重启守护进程
sudo systemctl daemon-reload
sudo systemctl restart docker
代码示例
在本教程中,我们将准备一个镜像,用于在生产环境中部署Prometheus实例。我们将从一个标准的 Dockerfile 开始,并对其进行重构以提高性能。
旧版 Dockerfile
我们将从源代码构建 Prometheus,为此我们需要一个包含所有构建依赖项的 Docker 镜像:golang
、、和。nodejs
yarn
make
FROM ubuntu:bionic
ENV GOPATH=$HOME/go
ENV PATH=$PATH:/usr/local/go/bin:$GOPATH/bin
RUN apt-get update \
&& apt-get install -y curl git build-essential \
&& curl -sL https://deb.nodesource.com/setup_14.x | bash - \
&& apt-get install -y nodejs \
&& npm install -g yarn \
&& curl -O https://storage.googleapis.com/golang/go1.15.2.linux-amd64.tar.gz \
&& tar -xvf go1.15.2.linux-amd64.tar.gz \
&& mv go /usr/local \
&& git clone https://github.com/prometheus/prometheus.git prometheus/ \
&& cd prometheus/ \
&& make build
# RUN ./prometheus --config.file=your_config.yml
让我们用以下代码来构建它:
$ time docker build --no-cache -t prometheus . -f Dockerfile.prometheus
...
Successfully built 54b5d99ef76a
Successfully tagged prometheus:latest
real 19m56,395s
user 0m0,506s
sys 0m0,334s
图像尺寸为:
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
prometheus latest 54b5d99ef76a 25 minutes ago 2.38GB
旧版构建性能
从结果来看,我们花了将近20 分钟才创建一个大小为2.38GB的 Prometheus 实例。
这将是我们的起点。
多阶段构建
现在我们已经有了一个可以投入生产的图像,所以我们很高兴,对吗?
不,我们绝对不是
你可能已经注意到,我们刚刚创建的镜像非常庞大,使用 Docker 的高级功能“多阶段构建”绝对可以做得更好。Docker
从 17.05 版本开始支持多阶段构建,这是优化镜像大小的首选方法。你可以使用FROM ... AS ...
指令定义构建阶段,并使用COPY --from
指令在阶段之间共享构建成果。
重构旧版 Dockerfile 以使用多阶段构建
让我们将这些概念应用到旧的 Dockerfile 中。
FROM ubuntu:bionic as base-builder
ENV GOPATH=$HOME/go
ENV PATH=$PATH:/usr/local/go/bin:$GOPATH/bin
RUN apt-get update \
&& apt-get install -y curl git build-essential \
&& curl -sL https://deb.nodesource.com/setup_14.x | bash - \
&& apt-get install -y nodejs \
&& npm install -g yarn \
&& curl -O https://storage.googleapis.com/golang/go1.15.2.linux-amd64.tar.gz \
&& tar -xvf go1.15.2.linux-amd64.tar.gz \
&& mv go /usr/local \
&& git clone https://github.com/prometheus/prometheus.git prometheus/ \
&& cd prometheus/ \
&& make build
FROM ubuntu:bionic as final
COPY --from=base-builder prometheus/prometheus prometheus
# RUN ./prometheus --config.file=your_config.yml
我们需要做的是创建一个仅包含 Prometheus 可执行文件的微型阶段。我们可以利用上一个阶段final
来完成。COPY --from
现在是时候构建 Docker 镜像了。
$ time docker build --no-cache -t prometheus-multistage . -f Dockerfile.prometheus-multistage
...
Successfully built ab2217626102
Successfully tagged prometheus-multistage:latest
real 19m19,570s
user 0m0,418s
sys 0m0,459s
图像尺寸为。
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
prometheus-multistage latest ab2217626102 31 seconds ago 151MB
多阶段构建性能
从新的结果来看,我们花了 19 分钟来构建图像,但尺寸的改善却显著减少了 99.94%!
并行多阶段构建
因此,我们能够减小镜像大小,但构建时间仍然过长。我们仍然可以利用Buildkit 构建引擎来优化这一点。
旧版 Docker 构建引擎按顺序执行各个阶段的构建,而 Buildkit 会计算各个阶段的依赖关系图并并行执行构建。考虑到这一点,我们可以重构 Dockerfile 来加快构建时间。
重构 Dockerfile 以使用并行多阶段构建
让我们看看如何做到这一点。
FROM ubuntu:bionic as base-builder
ENV GOPATH=$HOME/go
ENV PATH=$PATH:/usr/local/go/bin:$GOPATH/bin
RUN apt-get update \
&& apt-get install -y curl git build-essential
FROM base-builder as base-builder-extended
RUN curl -sL https://deb.nodesource.com/setup_14.x | bash - \
&& apt-get install -y nodejs \
&& npm install -g yarn
FROM base-builder as golang
RUN curl -O https://storage.googleapis.com/golang/go1.15.2.linux-amd64.tar.gz \
&& tar -xvf go1.15.2.linux-amd64.tar.gz
FROM base-builder as source-code
RUN git clone https://github.com/prometheus/prometheus.git prometheus/
FROM base-builder-extended as builder
COPY --from=golang go /usr/local
COPY --from=source-code prometheus/ prometheus/
RUN cd prometheus/ && make build
FROM ubuntu:bionic as final
COPY --from=builder prometheus/prometheus prometheus
# RUN ./prometheus --config.file=your_config.yml
我们创建一个名为 的第一阶段base-builder
,其中包含基本工具,并将作为后续层的基础。
继承自 ,base-builder
我们定义:
golang
,包含go
;source-code
,我们用它来获取 Prometheus 源代码;base-builder-extended
base-builder
这是包含nodejs
和的增强yarn
;
这三个阶段彼此不依赖,因此构建将并行进行。
至此,我们已准备好构建代码builder
。在此阶段,我们将COPY --from
前几个阶段所需的构件整合到构建中。然后,我们再次创建一个final
仅包含 Prometheus 可执行文件的微型阶段。
现在我们可以运行构建了。
$ DOCKER_BUILDKIT=1 docker build --no-cache -t prometheus-parallel-multistage . -f Dockerfile.prometheus-parallel-multistage
[+] Building 734.4s (13/13) FINISHED
=> [internal] load build definition from Dockerfile.prometheus-parallel-multistage 1.1s
=> => transferring dockerfile: 963B 0.1s
=> [internal] load .dockerignore 0.8s
=> => transferring context: 2B 0.1s
=> [internal] load metadata for docker.io/library/ubuntu:bionic 0.0s
=> CACHED [final 1/2] FROM docker.io/library/ubuntu:bionic 0.0s
=> [base-builder 2/2] RUN apt-get update && apt-get install -y curl git build-essential 195.6s
=> [source-code 1/1] RUN git clone https://github.com/prometheus/prometheus.git prometheus/ 77.6s
=> [base-builder-extended 1/1] RUN curl -sL https://deb.nodesource.com/setup_14.x | bash - && apt-get install -y nodejs 102.1s
=> [golang 1/1] RUN curl -O https://storage.googleapis.com/golang/go1.15.2.linux-amd64.tar.gz && tar -xvf go1.15.2.linux 149.8s
=> [builder 1/3] COPY --from=golang go /usr/local 13.6s
=> [builder 2/3] COPY --from=source-code prometheus/ prometheus/ 9.5s
=> [builder 3/3] RUN cd prometheus/ && make build 338.6s
=> [final 2/2] COPY --from=builder prometheus/prometheus prometheus 2.6s
=> exporting to image 1.9s
=> => exporting layers 1.6s
=> => writing image sha256:c0e59c47a790cb2a6b1229a5fec0014aa2b4540fc79c51531185c9466c9d5584 0.1s
=> => naming to docker.io/library/prometheus-parallel-multistage 0.1s
并检查图像大小。
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
prometheus-parallel-multistage latest c0e59c47a790 About a minute ago 151MB
prometheus-multistage latest ab2217626102 9 minutes ago 151MB
prometheus latest 54b5d99ef76a 39 minutes ago 2.38GB
并行多阶段构建性能
从新的结果来看,我们花了将近 12.5 分钟来构建图像,减少了 30%,同时保持了相同的图像大小。
结果回顾
下表总结了三个不同示例中的构建时间和图像大小。
Dockerfile | 构建时间 | 图像大小 |
---|---|---|
prometheus-并行-多阶段 | 12.5米 | 151MB |
普罗米修斯多阶段 | 19米 | 151MB |
普罗米修斯 | 20米 | 2.38GB |
如您所见,无论是构建时间还是镜像大小,改进都非常显著。在生产环境中,使用多阶段并行构建方法非常有用,因为较小的 Docker 镜像就能带来显著的提升。您只需记住 Buildkit 的工作原理,思考 Dockerfile 中哪些部分可以并行化,并进行相应的开发即可。您可以轻松地将 Buildkit 集成到 Docker 的构建/测试/标记/推送流水线中。
就是这个!
我希望这对你有用,现在去重构你的旧 Dockerfile!
通过 Twitter @gasparevitta联系我并让我了解您的性能改进。
您可以在Github上找到代码片段。
本文最初发表在我的博客上。如果你喜欢这篇文章,并且想阅读其他类似的文章,欢迎访问我的博客!
文章来源:https://dev.to/gasparev/3-steps-to-drastically-improve-your-docker-build-performances-17jg