乐趣区

关于docker:牛逼下一代-Docker-镜像构建神器

Docker 通过读取 Dockerfile 中的指令主动构建镜像,Dockerfile 是一个文本文件,其中顺次蕴含构建给定镜像所需的所有命令。

下面的解释摘自 Docker 的官网文档并总结了 Dockerfile 的用处。Dockerfile 的应用十分重要,因为它是咱们的蓝图,是咱们增加到 Docker 镜像中的层的记录。

本文,咱们将学习如何利用 BuildKit 性能,这是 Docker v18.09 上引入的一组加强性能。集成 BuildKit 将为咱们提供更好的性能,存储管理和安全性。

先决条件

  • Docker 概念常识
  • 已装置 Docker(以后应用 v19.03)
  • 一个 Java 应用程序(在本文中,我应用了一个 Jenkins Maven 示例应用程序)

让咱们开始吧!

简略的 Dockerfile 示例

以下是一个蕴含 Java 应用程序的未优化 Dockerfile 的示例。咱们将逐渐进行一些优化。

FROM debian
COPY . /app
RUN apt-get update
RUN apt-get -y install openjdk-11-jdk ssh emacs
CMD [“java”,“-jar”,“/app/target/my-app-1.0-SNAPSHOT.jar”]

在这里,咱们可能会问本人:构建须要多长时间?为了答复这个问题,让咱们在本地开发环境上创立该 Dockerfile,并让 Docker 构建镜像。

# enter your Java app folder
cd simple-java-maven-app-master
# create a Dockerfile
vim Dockerfile
# write content, save and exit
docker pull debian:latest # pull the source image
time docker build --no-cache -t docker-class . # overwrite previous layers
# notice the build time
0,21s user 0,23s system 0% cpu 1:55,17 total

此时,咱们的构建须要 1m55s。

如果咱们仅启用 BuildKit 而没有其余更改,会有什么不同吗?

启用 BuildKit

BuildKit 能够通过两种办法启用:

在调用 Docker build 命令时设置 DOCKER_BUILDKIT = 1 环境变量,例如:

time DOCKER_BUILDKIT=1 docker build --no-cache -t docker-class

将 Docker BuildKit 设置为默认开启,须要在 /etc/docker/daemon.json 进行如下设置,而后重启:

{"features": { "buildkit": true} }

BuildKit 最后的成果

DOCKER_BUILDKIT=1 docker build --no-cache -t docker-class .
0,54s user 0,93s system 1% cpu 1:43,00 total

此时,咱们的构建须要 1m43s。在雷同的硬件上,构建破费的工夫比以前少了约 12 秒。这意味着构建简直无需费劲即可节约 10%左右的工夫。

当初让咱们看看是否能够采取一些额定的步骤来进一步改善。

从最小到最频繁变动的程序

因为程序对于缓存很重要,所以咱们将 COPY 命令移到更凑近 Dockerfile 开端的地位。

FROM debian
RUN apt-get update
RUN apt-get -y install openjdk-11-jdk ssh emacs
RUN COPY . /app
CMD [“java”,“-jar”,“/app/target/my-app-1.0-SNAPSHOT.jar”]

防止应用“COPY .”

抉择更具体的 COPY 参数,以防止缓存中断。仅复制所需内容。

FROM debian
RUN apt-get update
RUN apt-get -y install openjdk-11-jdk ssh vim
COPY target/my-app-1.0-SNAPSHOT.jar /app
CMD [“java”,“-jar”,“/app/my-app-1.0-SNAPSHOT.jar”]

apt-get update 和 install 命令一起应用

这样能够避免应用过期的程序包缓存。

FROM debian
RUN apt-get update && \
    apt-get -y install openjdk-11-jdk ssh vim
COPY target/my-app-1.0-SNAPSHOT.jar /app
CMD [“java”,“-jar”,“/app/my-app-1.0-SNAPSHOT.jar”]

删除不必要的依赖

在开始时,不要装置调试和编辑工具,当前能够在须要时装置它们。

FROM debian
RUN apt-get update && \
    apt-get -y install --no-install-recommends \
    openjdk-11-jdk
COPY target/my-app-1.0-SNAPSHOT.jar /app
CMD [“java”,“-jar”,“/app/my-app-1.0-SNAPSHOT.jar”]

删除程序包管理器缓存

你的镜像不须要此缓存数据。借此机会开释一些空间。

FROM debian
RUN apt-get update && \
    apt-get -y install --no-install-recommends \
    openjdk-11-jdk && \
    rm -rf /var/lib/apt/lists/*
COPY target/my-app-1.0-SNAPSHOT.jar /app
CMD [“java”,“-jar”,“/app/my-app-1.0-SNAPSHOT.jar”]

尽可能应用官网镜像

应用官网镜像有很多理由,例如缩小镜像保护工夫和减小镜像尺寸,以及事后配置镜像以供容器应用。

FROM openjdk
COPY target/my-app-1.0-SNAPSHOT.jar /app
CMD [“java”,“-jar”,“/app/my-app-1.0-SNAPSHOT.jar”]

应用特定标签

请勿应用 latest 标签。

FROM openjdk:8
COPY target/my-app-1.0-SNAPSHOT.jar /app
CMD [“java”,“-jar”,“/app/my-app-1.0-SNAPSHOT.jar”]

寻找最小的镜像

以下是 openjdk 镜像列表。抉择最适宜本人的最轻的那个镜像。

REPOSITORY TAG 标签 SIZE 大小
openjdk 8 634MB
openjdk 8-jre 443MB
openjdk 8-jre-slim 204MB
openjdk 8-jre-alpine 83MB

在统一的环境中从源构建

如果你不须要整个 JDK,则能够应用 Maven Docker 镜像作为构建根底。

FROM maven:3.6-jdk-8-alpine
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN mvn -e -B package
CMD [“java”,“-jar”,“/app/my-app-1.0-SNAPSHOT.jar”]

在独自的步骤中获取依赖项

能够缓存–用于获取依赖项的 Dockerfile 命令。缓存此步骤将放慢构建速度。

FROM maven:3.6-jdk-8-alpine
WORKDIR /app
COPY pom.xml .
RUN mvn -e -B dependency:resolve
COPY src ./src
RUN mvn -e -B package
CMD [“java”,“-jar”,“/app/my-app-1.0-SNAPSHOT.jar”]

多阶段构建:删除构建依赖项

为什么要应用多阶段构建?

  • 将构建与运行时环境离开
  • DRY 形式

    • 具备开发,测试等环境的不同详细信息
  • 线性化依赖关系
  • 具备特定于平台的阶段

    FROM maven:3.6-jdk-8-alpine AS builder
    WORKDIR /app
    COPY pom.xml .
    RUN mvn -e -B dependency:resolve
    COPY src ./src
    RUN mvn -e -B package
    
    FROM openjdk:8-jre-alpine
    COPY --from=builder /app/target/my-app-1.0-SNAPSHOT.jar /
    CMD [“java”,“-jar”,“/my-app-1.0-SNAPSHOT.jar”]

    如果你此时构建咱们的应用程序,

    time DOCKER_BUILDKIT=1 docker build --no-cache -t docker-class .
    0,41s user 0,54s system 2% cpu 35,656 total

    你会留神到咱们的应用程序构建须要大概 35.66 秒的工夫。这是一个令人欢快的提高。

上面,咱们将介绍其余场景的性能。

多阶段构建:不同的镜像格调

上面的 Dockerfile 显示了基于 Debian 和基于 Alpine 的镜像的不同阶段。

FROM maven:3.6-jdk-8-alpine AS builder
…
FROM openjdk:8-jre-jessie AS release-jessie
COPY --from=builder /app/target/my-app-1.0-SNAPSHOT.jar /
CMD [“java”,“-jar”,“/my-app-1.0-SNAPSHOT.jar”]

FROM openjdk:8-jre-alpine AS release-alpine
COPY --from=builder /app/target/my-app-1.0-SNAPSHOT.jar /
CMD [“java”,“-jar”,“/my-app-1.0-SNAPSHOT.jar”]

要构建特定的镜像,咱们能够应用–target 参数:

time docker build --no-cache --target release-jessie .

不同的镜像格调(DRY / 全局 ARG)

ARG flavor=alpine
FROM maven:3.6-jdk-8-alpine AS builder
…
FROM openjdk:8-jre-$flavor AS release
COPY --from=builder /app/target/my-app-1.0-SNAPSHOT.jar /
CMD [“java”,“-jar”,“/my-app-1.0-SNAPSHOT.jar”]

ARG 命令能够指定要构建的镜像。在下面的例子中,咱们指定 alpine 为默认的镜像,但咱们也能够在 docker build 命令中,通过–build-arg flavor= 参数指定镜像。

time docker build --no-cache --target release --build-arg flavor=jessie .

并发

并发在构建 Docker 镜像时很重要,因为它会充分利用可用的 CPU 线程。在线性 Dockerfile 中,所有阶段均按程序执行。通过多阶段构建,咱们能够让较小的依赖阶段准备就绪,以供主阶段应用它们。

BuildKit 甚至带来了另一个性能上的益处。如果在当前的构建中不应用该阶段,则在完结时将间接跳过这些阶段,而不是对其进行解决和抛弃。

上面是一个示例 Dockerfile,其中网站的资产是在一个 assets 阶段中构建的:

FROM maven:3.6-jdk-8-alpine AS builder
…
FROM tiborvass/whalesay AS assets
RUN whalesay“Hello DockerCon!”> out/assets.html

FROM openjdk:8-jre-alpine AS release
COPY --from=builder /app/my-app-1.0-SNAPSHOT.jar /
COPY --from=assets /out /assets
CMD [“java”,“-jar”,“/my-app-1.0-SNAPSHOT.jar”]

这是另一个 Dockerfile,其中别离编译了 C 和 C ++ 库,并在 builder 当前应用该阶段。

FROM maven:3.6-jdk-8-alpine AS builder-base
…

FROM gcc:8-alpine AS builder-someClib
…
RUN git clone … ./configure --prefix=/out && make && make install

FROM g++:8-alpine AS builder-some CPPlib
…
RUN git clone … && cmake …

FROM builder-base AS builder
COPY --from=builder-someClib /out /
COPY --from=builder-someCpplib /out /

BuildKit 应用程序缓存

BuildKit 具备程序包管理器缓存的非凡性能。以下是一些缓存文件夹地位的示例:

包管理器门路
apt /var/lib/apt/lists
go ~/.cache/go-build
go-modules $GOPATH/pkg/mod
npm ~/.npm
pip ~/.cache/pip

咱们能够将此 Dockerfile 与下面介绍的在统一的环境中从源代码构建中介绍的 Dockerfile 进行比拟。这个较早的 Dockerfile 没有非凡的缓存解决。咱们能够应用–mount=type=cache 来做到这一点。

FROM maven:3.6-jdk-8-alpine AS builder
WORKDIR /app
RUN --mount=target=. --mount=type=cache,target /root/.m2 \
    && mvn package -DoutputDirectory=/

FROM openjdk:8-jre-alpine
COPY --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。

FROM <baseimage>
RUN …
RUN --mount=type=secret,id=aws,target=/root/.aws/credentials,required \
./fetch-assets-from-s3.sh
RUN ./build-scripts.sh

要构建此 Dockerfile,须要应用–secret 参数:

docker build --secret id=aws,src=~/.aws/credentials

还有为了进步安全性,防止应用诸如 COPY ./keys/private.pem /root .ssh/private.pem 之类的命令,咱们能够应用 BuildKit 中的 ssh 解决此问题:

FROM alpine
RUN apk add --no-cache openssh-client
RUN mkdir -p -m 0700 ~/.ssh && ssh-keyscan github.com >> ~/.ssh/known_hosts
ARG REPO_REF=19ba7bcd9976ef8a9bd086187df19ba7bcd997f2
RUN --mount=type=ssh,required git clone git@github.com:org/repo /work && cd /work && git checkout -b $REPO_REF

要构建此 Dockerfile,你须要在 ssh-agent 中加载到你的 SSH 私钥。

eval $(ssh-agent)
ssh-add ~/.ssh/id_rsa # this is the SSH key default location
docker build --ssh=default .

论断

本文,咱们介绍了应用 Docker BuildKit 优化 Dockerfile,并因而放慢了镜像构建工夫。这些速度的进步,能够帮忙咱们提高效率和节俭计算能力。

起源:https://os.51cto.com/art/2021…

退出移动版